@@ -23,7 +23,7 @@ import com.lambda.config.groups.Targeting
2323import com.lambda.context.SafeContext
2424import com.lambda.event.events.RenderEvent
2525import com.lambda.event.listener.SafeListener.Companion.listen
26- import com.lambda.graphics.renderer.esp.builders.build
26+ import com.lambda.graphics.renderer.esp.builders.ofShape
2727import com.lambda.interaction.RotationManager.rotate
2828import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock
2929import com.lambda.module.Module
@@ -34,20 +34,14 @@ import com.lambda.util.combat.CombatUtils.explosionDamage
3434import com.lambda.util.math.VecUtils.dist
3535import com.lambda.util.math.VecUtils.vec3d
3636import com.lambda.util.math.transform
37- import com.lambda.util.world.blockSearch
38- import com.lambda.util.world.fastEntitySearch
37+ import net.minecraft.block.BlockState
3938import net.minecraft.block.Blocks
40- import net.minecraft.entity.Entity
4139import net.minecraft.entity.LivingEntity
42- import net.minecraft.entity.decoration.EndCrystalEntity
43- import net.minecraft.item.EndCrystalItem
4440import net.minecraft.util.Hand
4541import net.minecraft.util.math.BlockPos
4642import net.minecraft.util.math.Box
47- import net.minecraft.util.math.Direction
4843import net.minecraft.util.math.Vec3d
4944import java.awt.Color
50- import java.time.Instant
5145
5246object CrystalAura : Module(
5347 name = " CrystalAura" ,
@@ -57,17 +51,13 @@ object CrystalAura : Module(
5751 private val page by setting(" Page" , Page .Targeting )
5852
5953 /* Targeting */
60- private val targeting = Targeting .Combat (this ) { page == Page .Targeting }
61-
62- /* Rotation */
63- private val rotate by setting(" Rotate" , true ) { page == Page .Rotation }
64- private val rotation = RotationSettings (this ) { page == Page .Rotation && rotate }
54+ // ToDo: Targeting Range should be reach + crystal range (also based on min damage)
55+ private val targeting = Targeting .Combat (this , 10.0 ) { page == Page .Targeting }
6556
6657 /* Placing */
6758 private val doPlace by setting(" Do Place" , true ) { page == Page .Placing }
68- private val placing = InteractionSettings (this ) { page == Page .Placing }
6959 private val swap by setting(" Swap" , Hand .MAIN_HAND , description = " Automatically swap to place crystals" ) { page == Page .Placing }
70- private val placeMethod by setting(" Place Sort" , DamageSort .Deadly )
60+ private val placeMethod by setting(" Place Sort" , DamageSort .Deadly ) { page == Page . Placing }
7161 // private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing }
7262 private val minSeparation by setting(" Minimum Crystal Separation" , 1 , 1 .. 3 , 1 , description = " The minimum space between crystals" , unit = " blocks" ) { page == Page .Placing }
7363 private val placeDelay by setting(" Place Delay" , 0L , 0L .. 1000L , 10L , description = " Delay between crystal placements" , unit = " ms" ) { page == Page .Placing }
@@ -79,117 +69,109 @@ object CrystalAura : Module(
7969 private val doExplode by setting(" Do Explode" , true ) { page == Page .Exploding }
8070 private val explodeDelay by setting(" Explode Delay" , 0 , 0 .. 20 , 1 , description = " Delay between crystal explosions" , unit = " ticks" ) { page == Page .Exploding }
8171 private val explodeRange by setting(" Explode Range" , 5.0 , 0.1 .. 7.0 , 0.1 , description = " Range to explode crystals" ) { page == Page .Exploding }
82- private val explodeMethod by setting(" Explode Sort" , DamageSort .Deadly )
72+ private val explodeMethod by setting(" Explode Sort" , DamageSort .Deadly ) { page == Page . Exploding }
8373 private val explodeMinDamage by setting(" Explode Min Damage" , 6.0 , 0.0 .. 20.0 , 0.5 , description = " Minimum damage to explode a crystal" ) { page == Page .Exploding }
8474
75+ /* Rotation */
76+ private val rotation = RotationSettings (this ) { page == Page .Rotation }
77+
78+ /* Interaction */
79+ private val interact = InteractionSettings (this ) { page == Page .Interaction }
80+
8581 /* Rendering */
8682 private val renderCrystals by setting(" Render Crystal" , true ) { page == Page .Rendering }
8783 private val crystalColor by setting(" Color Comparator" , ColorComparator .Damage ) { page == Page .Rendering && renderCrystals }
88- private val crystalAlpha by setting(" Color Alpha" , 128 , 0 .. 255 )
84+ private val crystalAlpha by setting(" Color Alpha" , 128 , 0 .. 255 ) { page == Page . Rendering && renderCrystals }
8985
90- private val placedCrystal = LimitedDecayQueue <BlockPos >(64 , 1000L )
86+ private val placements = LimitedDecayQueue <BlockPos >(64 , 1000L )
9187 private val target: LivingEntity ? get() = targeting.target()
9288
93- private fun SafeContext.worldCrystals (target : LivingEntity ): List <EndCrystalEntity > {
94- return fastEntitySearch<EndCrystalEntity >(explodeRange, pos = target.blockPos)
95- .sortedByDescending { explodeMethod.sorted(this , target, it.blockPos) }
96- }
97-
98- private fun SafeContext.validPositions (target : LivingEntity ): Sequence <BlockPos > {
99- return blockSearch(range = placing.reach.toInt()) { pos, _ -> canPlace(pos, target) }
100- .keys.asSequence()
101- .sortedByDescending { placeMethod.sorted(this , target, it.up()) }
102- }
103-
104- private fun SafeContext.canPlace (pos : BlockPos , target : LivingEntity ): Boolean {
105- // Checks if the distance between the player and the target
106- // and the player and the place position is <= to the placing range
107- return player dist target <= placing.reach &&
108- player dist pos <= placing.reach &&
109- // Checks if the position is within the player hitbox
110- ! player.boundingBox.intersects(Box .of(pos.vec3d, 1.0 , 1.0 , 1.0 )) &&
111- // Checks if the support block is either obsidian or bedrock
112- (pos.blockState(world).isOf(Blocks .OBSIDIAN )
113- || pos.blockState(world).isOf(Blocks .BEDROCK )) &&
114- // Checks if the crystal can be placed on top of a block
115- // It checks if there's another entity in the air block
116- // Or if there's another crystal 2 blocks around the air block
117- world.isAir(pos.up()) &&
118- fastEntitySearch<Entity >(0.5 , pos.up()).isEmpty() &&
119- fastEntitySearch<EndCrystalEntity >(2.0 , pos.up()).isEmpty() && // Replace by intersection check bc blockpos is corner
120-
121- // player.health + player.absorptionAmount >= placeMinHealth &&
122- explosionDamage(pos.up(), target, 6.0 ) >= placeMinDamage &&
123- explosionDamage(pos.up(), player, 6.0 ) <= placeMaxSelfDamage
124- // Checks if the last crystal was set more than [placeDelay] ms ago
125- // placedCrystal.lastOrNull()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true
126- // if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true
127- }
128-
129- private val testRender = mutableListOf<BlockPos >()
130-
131-
13289 init {
13390 rotate {
134- onUpdate {
135- if (! rotate) return @onUpdate null
136-
137- val poss = validPositions(
138- target ? : return @onUpdate null
139- )
140-
141- testRender.addAll(poss)
91+ onUpdate {
92+ val targetEntity = target ? : return @onUpdate null
93+ val validPositions = findTargetPositions(targetEntity)
14294
143- val blockpos = poss.firstOrNull() ? : return @onUpdate null
95+ testRender.addAll(validPositions.map { it.blockPos })
14496
145- // Taskflow interact block
146- // placedCrystal.add( )
147- lookAtBlock(blockpos, rotation, placing, sides = setOf ( Direction . UP ))
148- }
97+ validPositions.firstNotNullOfOrNull {
98+ lookAtBlock(it.blockPos, rotation, interact )
99+ }
100+ }
149101 }
150102
151103 listen<RenderEvent .StaticESP > {
152- worldCrystals(target ? : return @listen).forEach { crystal ->
153- it.renderer.build(
154- Box .of(crystal.blockPos.toCenterPos(), 1.0 , 1.0 , 1.0 ),
155- crystalColor.compared(this , target ? : return @forEach, crystal.pos),
156- crystalColor.compared(this , target ? : return @forEach, crystal.pos)
157- )
158- }
159-
160- testRender.forEachIndexed { index, pos ->
161- val center = pos.toCenterPos()
162- val blurple = Color (85 , 57 , 204 , 50 )
163- it.renderer.build(
164- Box .of(center, 1.0 , 1.0 , 1.0 ),
165- blurple,
166- blurple
167- )
104+ // worldCrystals(target ?: return@listen).forEach { crystal ->
105+ // it.renderer.build(
106+ // Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0),
107+ // crystalColor.compared(this, target ?: return@forEach, crystal.pos),
108+ // crystalColor.compared(this, target ?: return@forEach, crystal.pos)
109+ // )
110+ // }
111+
112+ val blurple = Color (85 , 57 , 204 , 50 )
113+ testRender.forEach { pos ->
114+ it.renderer.ofShape(pos, blurple, blurple)
168115 }
169116
170117 testRender.clear()
171118 }
172119 }
173120
174- private enum class Page {
175- Targeting ,
176- Rotation ,
177- Placing ,
178- Exploding ,
179- Rendering
121+ // private fun SafeContext.worldCrystals(target: LivingEntity) =
122+ // fastEntitySearch<EndCrystalEntity>(explodeRange, pos = target.blockPos)
123+ // .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) }
124+
125+ private fun SafeContext.findTargetPositions (target : LivingEntity ): List <TargetPosition > {
126+ // This formula is derived from the explosion damage scaling logic. The damage decreases linearly
127+ // with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the
128+ // explosion's strength (6.0) and exposure defines how much of the explosion affects the target.
129+ val maximumRange = ((1 - (placeMinDamage / 12.0 )) * 12.0 ).toInt()
130+
131+ return BlockPos .iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos ->
132+ targetData(pos, pos.blockState(world), target)
133+ }.sortedWith(placeMethod.comparator)
134+ }
135+
136+ private fun SafeContext.targetData (pos : BlockPos , state : BlockState , target : LivingEntity ): TargetPosition ? {
137+ val inRange = pos.dist(player.eyePos) < interact.reach + 1
138+ if (! inRange) return null
139+ val isOfBlock = state.isOf(Blocks .OBSIDIAN ) || state.isOf(Blocks .BEDROCK )
140+ if (! isOfBlock) return null
141+ if (pos in placements) return null
142+ val above = pos.up()
143+ val isAirAbove = world.isAir(above)
144+ if (! isAirAbove) return null
145+ val checkBox = Box (above).withMaxY(above.y + 2.0 )
146+ val entitiesAbove = world.getOtherEntities(null , checkBox)
147+ if (entitiesAbove.isNotEmpty()) return null
148+ val crystalPos = above.vec3d.add(0.5 , 0.0 , 0.5 )
149+ val targetDamage = explosionDamage(crystalPos, target, 6.0 )
150+ if (targetDamage <= placeMinDamage) return null
151+ val selfDamage = explosionDamage(crystalPos, player, 6.0 )
152+ if (selfDamage > placeMaxSelfDamage) return null
153+ return TargetPosition (pos.toImmutable(), targetDamage, selfDamage)
180154 }
181155
156+ private val testRender = mutableListOf<BlockPos >()
157+
158+ data class TargetPosition (
159+ val blockPos : BlockPos ,
160+ val targetDamage : Double ,
161+ val selfDamage : Double ,
162+ )
163+
182164 /* *
183165 * Damage sorter parameter
184166 *
185167 * deadly -> always prioritize enemy damage no matter what
186- * balanced -> sort by the highest ratio of enemy damage to self damage
187- * safe -> always prioritize the least amount of self damage
168+ * balanced -> sort by the highest ratio of enemy damage to self- damage
169+ * safe -> always prioritize the least amount of self- damage
188170 */
189- private enum class DamageSort (val sorted : SafeContext .( LivingEntity , BlockPos ) -> Double ) {
190- Deadly ({ target, source -> explosionDamage(source, target, 6.0 ) }),
191- Balanced ({ target, source -> explosionDamage(source, player, 6.0 ) - explosionDamage(source, target, 6.0 ) }),
192- Safe ({ target, source -> explosionDamage(source, target, 6.0 ) - explosionDamage(source, player, 6.0 ) });
171+ private enum class DamageSort (val comparator : Comparator < TargetPosition > ) {
172+ Deadly (compareByDescending< TargetPosition > { it.targetDamage }.thenBy { it.selfDamage }),
173+ Balanced (compareByDescending< TargetPosition > { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }),
174+ Safe (compareBy< TargetPosition > { it.selfDamage }.thenByDescending { it.targetDamage });
193175 }
194176
195177 /* *
@@ -202,7 +184,6 @@ object CrystalAura : Module(
202184 val red = transform(target dist dest, 0.0 , explodeRange, 255.0 , 0.0 ).toInt()
203185 Color (red, 255 - red, 0 , crystalAlpha)
204186 }),
205-
206187 Damage ({ target, dest ->
207188 val damage = explosionDamage(dest, target, 6.0 )
208189 .coerceIn(0.0 , target.health.toDouble())
@@ -211,4 +192,13 @@ object CrystalAura : Module(
211192 Color (red, 255 - red, 0 , crystalAlpha)
212193 })
213194 }
195+
196+ private enum class Page {
197+ Targeting ,
198+ Placing ,
199+ Exploding ,
200+ Rotation ,
201+ Interaction ,
202+ Rendering
203+ }
214204}
0 commit comments