Skip to content

Commit 89ac694

Browse files
committed
Crystal Aura rewrite
- Collect damage data once and use that for comparison etc - Use actual game checks for valid placements - Added InteractionSettings - Added RotationSettings
1 parent 2ed4623 commit 89ac694

File tree

1 file changed

+89
-99
lines changed

1 file changed

+89
-99
lines changed

common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt

Lines changed: 89 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import com.lambda.config.groups.Targeting
2323
import com.lambda.context.SafeContext
2424
import com.lambda.event.events.RenderEvent
2525
import 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
2727
import com.lambda.interaction.RotationManager.rotate
2828
import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock
2929
import com.lambda.module.Module
@@ -34,20 +34,14 @@ import com.lambda.util.combat.CombatUtils.explosionDamage
3434
import com.lambda.util.math.VecUtils.dist
3535
import com.lambda.util.math.VecUtils.vec3d
3636
import 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
3938
import net.minecraft.block.Blocks
40-
import net.minecraft.entity.Entity
4139
import net.minecraft.entity.LivingEntity
42-
import net.minecraft.entity.decoration.EndCrystalEntity
43-
import net.minecraft.item.EndCrystalItem
4440
import net.minecraft.util.Hand
4541
import net.minecraft.util.math.BlockPos
4642
import net.minecraft.util.math.Box
47-
import net.minecraft.util.math.Direction
4843
import net.minecraft.util.math.Vec3d
4944
import java.awt.Color
50-
import java.time.Instant
5145

5246
object 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

Comments
 (0)