Skip to content

Commit 31c2ff8

Browse files
committed
Explosion targeting
1 parent 8be6c5f commit 31c2ff8

File tree

1 file changed

+156
-75
lines changed

1 file changed

+156
-75
lines changed

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

Lines changed: 156 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,28 @@ import com.lambda.context.SafeContext
2424
import com.lambda.event.events.RenderEvent
2525
import com.lambda.event.events.TickEvent
2626
import com.lambda.event.listener.SafeListener.Companion.listen
27+
import com.lambda.graphics.renderer.esp.builders.ofBox
2728
import com.lambda.graphics.renderer.esp.builders.ofShape
2829
import com.lambda.interaction.RotationManager.rotate
2930
import com.lambda.interaction.rotation.RotationContext
3031
import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock
3132
import com.lambda.module.Module
3233
import com.lambda.module.tag.ModuleTag
34+
import com.lambda.threading.runSafe
3335
import com.lambda.util.BlockUtils.blockState
3436
import com.lambda.util.collections.LimitedDecayQueue
3537
import com.lambda.util.combat.CombatUtils.explosionDamage
38+
import com.lambda.util.math.MathUtils.ceilToInt
3639
import com.lambda.util.math.VecUtils.dist
3740
import com.lambda.util.math.VecUtils.vec3d
3841
import com.lambda.util.math.transform
42+
import com.lambda.util.world.fastEntitySearch
43+
import com.lambda.util.world.raycast.RayCastUtils.blockResult
3944
import net.minecraft.block.Blocks
4045
import net.minecraft.entity.LivingEntity
46+
import net.minecraft.entity.decoration.EndCrystalEntity
47+
import net.minecraft.item.Items
4148
import net.minecraft.util.Hand
42-
import net.minecraft.util.hit.BlockHitResult
4349
import net.minecraft.util.math.BlockPos
4450
import net.minecraft.util.math.Box
4551
import net.minecraft.util.math.Vec3d
@@ -50,29 +56,32 @@ object CrystalAura : Module(
5056
description = "Automatically attacks entities with crystals",
5157
defaultTags = setOf(ModuleTag.COMBAT),
5258
) {
53-
private val page by setting("Page", Page.Targeting)
59+
private val page by setting("Page", Page.General)
60+
61+
/* General */
62+
private val strategy by setting("Strategy", Strategy.ExplodeBeforePlace) { page == Page.General }
5463

5564
/* Targeting */
5665
// ToDo: Targeting Range should be reach + crystal range (also based on min damage)
5766
private val targeting = Targeting.Combat(this, 10.0) { page == Page.Targeting }
5867

5968
/* Placing */
60-
private val doPlace by setting("Do Place", true) { page == Page.Placing }
61-
private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing }
62-
private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing }
63-
//private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing }
64-
private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing }
65-
private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, description = "Delay between crystal placements", unit = "ms") { page == Page.Placing }
66-
private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing }
67-
private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, description = "Minimum damage to place a crystal") { page == Page.Placing }
68-
private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, description = "Maximum self damage to place a crystal") { page == Page.Placing }
69+
private val place by setting("Place Crystals", true) { page == Page.Placing }
70+
private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing && place }
71+
private val placeMethod by setting("Place Sort", DamageSort.Deadly) { page == Page.Placing && place }
72+
//private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing && place }
73+
private val minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, "The minimum space between crystals", " blocks") { page == Page.Placing && place }
74+
private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, "Delay between crystal placements", "ms") { page == Page.Placing && place }
75+
private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..36.0, 0.5, "Minimum health to place a crystal") { page == Page.Placing && place }
76+
private val placeMinDamage by setting("Place Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to place a crystal") { page == Page.Placing && place }
77+
private val placeMaxSelfDamage by setting("Place Max Self Damage", 8.0, 0.0..20.0, 0.5, "Maximum self damage to place a crystal") { page == Page.Placing && place }
6978

7079
/* Exploding */
71-
private val doExplode by setting("Do Explode", true) { page == Page.Exploding }
72-
private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding }
73-
private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding }
74-
private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding }
75-
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 }
80+
private val explode by setting("Explode Crystals", true) { page == Page.Exploding }
81+
private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, "Delay between crystal explosions", " ticks") { page == Page.Exploding && explode }
82+
private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, "Range to explode crystals", " blocks") { page == Page.Exploding && explode }
83+
private val explodeMethod by setting("Explode Sort", DamageSort.Deadly) { page == Page.Exploding && explode }
84+
private val explodeMinDamage by setting("Explode Min Damage", 6.0, 0.0..20.0, 0.5, "Minimum damage to explode a crystal") { page == Page.Exploding && explode }
7685

7786
/* Rotation */
7887
private val rotation = RotationSettings(this) { page == Page.Rotation }
@@ -82,28 +91,43 @@ object CrystalAura : Module(
8291

8392
/* Rendering */
8493
private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering }
85-
private val crystalColor by setting("Color Comparator", ColorComparator.Damage) { page == Page.Rendering && renderCrystals }
94+
private val crystalColor by setting("Color Comparator", ColorMode.TargetDamage) { page == Page.Rendering && renderCrystals }
8695
private val crystalAlpha by setting("Color Alpha", 128, 0..255) { page == Page.Rendering && renderCrystals }
8796

97+
private enum class Page {
98+
General,
99+
Targeting,
100+
Placing,
101+
Exploding,
102+
Rotation,
103+
Interaction,
104+
Rendering
105+
}
106+
88107
private val placements = LimitedDecayQueue<BlockPos>(64, 1000L)
108+
private val placementTargets = mutableListOf<PlacementTarget>()
89109
private val target: LivingEntity? get() = targeting.target()
90110

91111
private var currentRotation: RotationContext? = null
92112

93113
init {
94114
listen<TickEvent.Pre> {
95115
currentRotation?.let { rotate ->
116+
if (!place) return@let
96117
if (!rotate.isValid) return@let
97-
(rotate.hitResult as? BlockHitResult)?.let { result ->
98-
interaction.interactBlock(player, Hand.MAIN_HAND, result)
99-
placements.add(result.blockPos)
100-
currentRotation = null
101-
}
118+
val blockHit = rotate.hitResult?.blockResult ?: return@let
119+
val inMainHand = player.mainHandStack.item == Items.END_CRYSTAL
120+
val inOffHand = player.offHandStack.item == Items.END_CRYSTAL
121+
if (!inMainHand && !inOffHand) return@let
122+
val hand = if (inMainHand) Hand.MAIN_HAND else Hand.OFF_HAND
123+
interaction.interactBlock(player, hand, blockHit)
124+
placements.add(blockHit.blockPos)
125+
currentRotation = null
102126
}
103127

104128
target?.let { tar ->
105-
val validPositions = findTargetPositions(tar).filter { it.blockPos !in placements }
106-
testRender.addAll(validPositions.map { it.blockPos })
129+
val validPositions = findPlacementTargets(tar).filter { it.blockPos !in placements }
130+
placementTargets.addAll(validPositions)
107131
currentRotation = validPositions.firstNotNullOfOrNull {
108132
lookAtBlock(it.blockPos.toImmutable(), rotation, interact)
109133
}
@@ -112,44 +136,50 @@ object CrystalAura : Module(
112136

113137
rotate {
114138
request {
139+
if (!place) return@request null
115140
currentRotation
116141
}
117142
}
118143

119144
listen<RenderEvent.StaticESP> {
120-
// worldCrystals(target ?: return@listen).forEach { crystal ->
121-
// it.renderer.build(
122-
// Box.of(crystal.blockPos.toCenterPos(), 1.0, 1.0, 1.0),
123-
// crystalColor.compared(this, target ?: return@forEach, crystal.pos),
124-
// crystalColor.compared(this, target ?: return@forEach, crystal.pos)
125-
// )
126-
// }
127-
128-
val blurple = Color(85, 57, 204, 50)
129-
testRender.forEach { pos ->
130-
it.renderer.ofShape(pos, blurple, blurple)
145+
findExplosionTargets(target ?: return@listen).forEach { crystalExplosion ->
146+
val color = crystalExplosion.color
147+
it.renderer.ofBox(crystalExplosion.crystal.boundingBox, color, color)
131148
}
132149

133-
testRender.clear()
150+
placementTargets.forEach { target ->
151+
val color = target.color
152+
it.renderer.ofShape(target.blockPos, color, color)
153+
}
154+
155+
placementTargets.clear()
134156
}
135157
}
136158

137-
// private fun SafeContext.worldCrystals(target: LivingEntity) =
138-
// fastEntitySearch<EndCrystalEntity>(explodeRange, pos = target.blockPos)
139-
// .sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) }
159+
private fun SafeContext.findExplosionTargets(target: LivingEntity): List<ExplosionTarget> {
160+
val maximumRange = (1 - (explodeMinDamage / 12.0)) * 12.0
161+
return fastEntitySearch<EndCrystalEntity>(maximumRange, target.blockPos)
162+
.filter { it.pos.distanceTo(target.pos) <= maximumRange }
163+
.map { crystal ->
164+
val targetDamage = crystalDamage(crystal.pos, target)
165+
val selfDamage = crystalDamage(crystal.pos, player)
166+
val distance = target.dist(crystal.pos)
167+
ExplosionTarget(crystal, targetDamage, selfDamage, distance)
168+
}
169+
}
140170

141-
private fun SafeContext.findTargetPositions(target: LivingEntity): List<TargetPosition> {
171+
private fun SafeContext.findPlacementTargets(target: LivingEntity): List<PlacementTarget> {
142172
// This formula is derived from the explosion damage scaling logic. The damage decreases linearly
143173
// with distance, modeled as `damage = (1 - (distance / (power * 2))) * exposure`, where power is the
144174
// explosion's strength (6.0) and exposure defines how much of the explosion affects the target.
145-
val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).toInt()
175+
val maximumRange = ((1 - (placeMinDamage / 12.0)) * 12.0).ceilToInt()
146176

147177
return BlockPos.iterateOutwards(target.blockPos, maximumRange, maximumRange, maximumRange).mapNotNull { pos ->
148178
targetData(pos, target)
149179
}.sortedWith(placeMethod.comparator)
150180
}
151181

152-
private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): TargetPosition? {
182+
private fun SafeContext.targetData(pos: BlockPos, target: LivingEntity): PlacementTarget? {
153183
val inRange = pos.dist(player.eyePos) < interact.reach + 1
154184
if (!inRange) return null
155185
val state = pos.blockState(world)
@@ -167,16 +197,73 @@ object CrystalAura : Module(
167197
if (targetDamage <= placeMinDamage) return null
168198
val selfDamage = explosionDamage(crystalPos, player, 6.0)
169199
if (selfDamage > placeMaxSelfDamage) return null
170-
return TargetPosition(pos.toImmutable(), targetDamage, selfDamage)
200+
val distance = target.dist(crystalPos)
201+
return PlacementTarget(pos.toImmutable(), targetDamage, selfDamage, distance)
171202
}
172203

173-
private val testRender = mutableListOf<BlockPos>()
174-
175-
data class TargetPosition(
204+
/**
205+
* @property blockPos The block position associated with this placement target.
206+
* @property targetDamage The damage inflicted on the target.
207+
* @property selfDamage The damage inflicted on the self due to some actions or interactions.
208+
* @property distanceToTarget The distance to the target from the player or reference point.
209+
*/
210+
data class PlacementTarget(
176211
val blockPos: BlockPos,
177-
val targetDamage: Double,
178-
val selfDamage: Double,
179-
)
212+
override val targetDamage: Double,
213+
override val selfDamage: Double,
214+
override val distanceToTarget: Double,
215+
) : Target()
216+
217+
/**
218+
* @property crystal The targeted `EndCrystalEntity` in the context of the explosion.
219+
* @property targetDamage The amount of damage inflicted on the target as part of the explosion.
220+
* @property selfDamage The amount of damage inflicted on the self due to the explosion.
221+
* @property distanceToTarget The distance from the origin point (e.g., player) to the target.
222+
*/
223+
data class ExplosionTarget(
224+
val crystal: EndCrystalEntity,
225+
override val targetDamage: Double,
226+
override val selfDamage: Double,
227+
override val distanceToTarget: Double
228+
) : Target()
229+
230+
/**
231+
* Represents a target in a system where damage, distance, and other attributes are evaluated.
232+
*
233+
* @property targetDamage The damage inflicted on the target.
234+
* @property selfDamage The damage inflicted on the self due to some actions or interactions.
235+
* @property distanceToTarget The distance to the target from the player or reference point.
236+
* @property color The dynamically calculated color based on the color mode and associated parameters.
237+
*/
238+
abstract class Target {
239+
abstract val targetDamage: Double
240+
abstract val selfDamage: Double
241+
abstract val distanceToTarget: Double
242+
243+
val color: Color by lazy {
244+
when (crystalColor) {
245+
ColorMode.TargetDamage -> {
246+
val targetHealth = target?.health?.toDouble() ?: 0.0
247+
val damage = targetDamage.coerceAtMost(targetHealth)
248+
val red = transform(damage, 0.0, targetHealth, 0.0, 255.0).toInt()
249+
Color(red, 255 - red, 0, crystalAlpha)
250+
}
251+
ColorMode.SelfDamage -> {
252+
runSafe {
253+
val selfHealth = player.health.toDouble()
254+
val damage = selfDamage.coerceAtMost(selfHealth)
255+
val red = transform(damage, 0.0, selfHealth, 0.0, 255.0).toInt()
256+
Color(red, 255 - red, 0, crystalAlpha)
257+
} ?: Color.WHITE
258+
}
259+
ColorMode.Distance -> {
260+
val distance = distanceToTarget.coerceAtMost(explodeRange)
261+
val red = transform(distance, 0.0, explodeRange, 0.0, 255.0).toInt()
262+
Color(red, 0, 255 - red, crystalAlpha)
263+
}
264+
}
265+
}
266+
}
180267

181268
/**
182269
* Damage sorter parameter
@@ -185,37 +272,31 @@ object CrystalAura : Module(
185272
* balanced -> sort by the highest ratio of enemy damage to self-damage
186273
* safe -> always prioritize the least amount of self-damage
187274
*/
188-
private enum class DamageSort(val comparator: Comparator<TargetPosition>) {
189-
Deadly(compareByDescending<TargetPosition> { it.targetDamage }.thenBy { it.selfDamage }),
190-
Balanced(compareByDescending<TargetPosition> { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }),
191-
Safe(compareBy<TargetPosition> { it.selfDamage }.thenByDescending { it.targetDamage });
275+
private enum class DamageSort(val comparator: Comparator<PlacementTarget>) {
276+
Deadly(compareByDescending<PlacementTarget> { it.targetDamage }.thenBy { it.selfDamage }),
277+
Balanced(compareByDescending<PlacementTarget> { it.targetDamage / it.selfDamage }.thenBy { it.selfDamage }),
278+
Safe(compareBy<PlacementTarget> { it.selfDamage }.thenByDescending { it.targetDamage });
279+
}
280+
281+
private enum class Strategy {
282+
ExplodeBeforePlace,
283+
PlaceBeforeExplode,
192284
}
193285

194286
/**
195-
* Comparator for different render modes
287+
* Represents the different modes of color categorization or operations.
196288
*
197-
* @param compared Lambda that takes in a living target and crystal position and then returns a color
289+
* Enum values:
290+
* - TargetDamage: Indicates color mode based on damage to a target.
291+
* - SelfDamage: Indicates color mode based on damage to oneself.
292+
* - Distance: Indicates color mode based on distance criteria.
198293
*/
199-
private enum class ColorComparator(val compared: SafeContext.(LivingEntity, Vec3d) -> Color) {
200-
Distance({ target, dest ->
201-
val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt()
202-
Color(red, 255-red, 0, crystalAlpha)
203-
}),
204-
Damage({ target, dest ->
205-
val damage = explosionDamage(dest, target, 6.0)
206-
.coerceIn(0.0, target.health.toDouble())
207-
208-
val red = transform(damage, 0.0, target.health.toDouble(), 0.0, 255.0).toInt()
209-
Color(red, 255-red, 0, crystalAlpha)
210-
})
294+
private enum class ColorMode {
295+
TargetDamage,
296+
SelfDamage,
297+
Distance
211298
}
212299

213-
private enum class Page {
214-
Targeting,
215-
Placing,
216-
Exploding,
217-
Rotation,
218-
Interaction,
219-
Rendering
220-
}
300+
private fun SafeContext.crystalDamage(vec3d: Vec3d, target: LivingEntity) =
301+
explosionDamage(vec3d, target, 6.0)
221302
}

0 commit comments

Comments
 (0)