Skip to content

Commit ca63e6d

Browse files
committed
crystal aura
1 parent ca69b2c commit ca63e6d

File tree

3 files changed

+112
-65
lines changed

3 files changed

+112
-65
lines changed

common/src/main/kotlin/com/lambda/config/groups/Targeting.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,8 @@ abstract class Targeting(
162162
* @return The best [LivingEntity] target, or `null` if no valid target is found.
163163
*/
164164
fun target(): LivingEntity? = runSafe {
165-
val predicate = { entity: LivingEntity ->
166-
validate(player, entity)
167-
}
168-
169165
return@runSafe fastEntitySearch<LivingEntity>(targetingRange) {
170-
predicate(it)
166+
validate(player, it)
171167
}.minByOrNull {
172168
priority.factor(this, it)
173169
}

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

Lines changed: 105 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,31 @@ package com.lambda.module.modules.combat
2020
import com.lambda.config.groups.InteractionSettings
2121
import com.lambda.config.groups.RotationSettings
2222
import com.lambda.config.groups.Targeting
23-
import com.lambda.event.events.TickEvent
24-
import com.lambda.event.listener.SafeListener.Companion.listener
25-
import com.lambda.interaction.RotationManager.requestRotation
23+
import com.lambda.context.SafeContext
24+
import com.lambda.event.events.RenderEvent
25+
import com.lambda.event.listener.SafeListener.Companion.listen
26+
import com.lambda.graphics.renderer.esp.builders.build
27+
import com.lambda.interaction.RotationManager.rotate
2628
import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock
2729
import com.lambda.module.Module
2830
import com.lambda.module.tag.ModuleTag
29-
import com.lambda.task.tasks.BreakBlock
30-
import com.lambda.util.combat.Explosion.explosionDamage
31+
import com.lambda.util.BlockUtils.blockState
32+
import com.lambda.util.collections.LimitedDecayQueue
33+
import com.lambda.util.combat.CombatUtils.explosionDamage
3134
import com.lambda.util.math.VecUtils.dist
35+
import com.lambda.util.math.transform
3236
import com.lambda.util.world.blockSearch
37+
import com.lambda.util.world.fastEntitySearch
3338
import net.minecraft.block.Blocks
34-
import net.minecraft.block.SideShapeType
39+
import net.minecraft.entity.Entity
3540
import net.minecraft.entity.LivingEntity
41+
import net.minecraft.entity.decoration.EndCrystalEntity
3642
import net.minecraft.util.Hand
3743
import net.minecraft.util.math.BlockPos
44+
import net.minecraft.util.math.Box
3845
import net.minecraft.util.math.Direction
46+
import java.awt.Color
47+
import java.time.Instant
3948

4049
object CrystalAura : Module(
4150
name = "CrystalAura",
@@ -55,63 +64,91 @@ object CrystalAura : Module(
5564
private val doPlace by setting("Do Place", true) { page == Page.Placing }
5665
private val placing = InteractionSettings(this) { page == Page.Placing }
5766
private val swap by setting("Swap", Hand.MAIN_HAND, description = "Automatically swap to place crystals") { page == Page.Placing }
58-
67+
private val placeMethod by setting("Place Sort", DamageSort.Deadly)
5968
//private val multiPlace by setting("Multi Place", true, description = "Place crystals") { page == Page.Placing }
60-
private val minSeparation by setting("Minimum Crystal Separation", 2, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing }
61-
62-
63-
private val placeDelay by setting("Place Delay", 0, 0..20, 1, description = "Delay between crystal placements", unit = "ticks") { page == Page.Placing }
64-
private val placeMinHealth by setting("Place Min Health", 10.0, 0.0..20.0, 0.5, description = "Minimum health to place a crystal") { page == Page.Placing }
65-
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 minSeparation by setting("Minimum Crystal Separation", 1, 1..3, 1, description = "The minimum space between crystals", unit = "blocks") { page == Page.Placing }
70+
private val placeDelay by setting("Place Delay", 0L, 0L..1000L, 10L, description = "Delay between crystal placements", unit = "ms") { page == Page.Placing }
71+
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 }
6672
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 }
73+
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 }
6774

6875
/* Exploding */
6976
private val doExplode by setting("Do Explode", true) { page == Page.Exploding }
7077
private val explodeDelay by setting("Explode Delay", 0, 0..20, 1, description = "Delay between crystal explosions", unit = "ticks") { page == Page.Exploding }
7178
private val explodeRange by setting("Explode Range", 5.0, 0.1..7.0, 0.1, description = "Range to explode crystals") { page == Page.Exploding }
72-
private val preventDeath by setting("Prevent Death", true, description = "Prevent death from crystal explosions") { page == Page.Exploding }
79+
private val explodeMethod by setting("Explode Sort", DamageSort.Deadly)
7380
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 }
74-
private val noWeakness by setting("No Weakness", true, description = "Switch to a weapon when you have a weakness effect") { page == Page.Exploding }
7581

7682
/* Rendering */
77-
private val rendering = Targeting.ESP(this) { page == Page.Rendering }
83+
private val renderCrystals by setting("Render Crystal", true) { page == Page.Rendering }
84+
private val crystalColor by setting("Color Comparator", ColorComparator.Damage) { page == Page.Rendering && renderCrystals }
85+
private val crystalAlpha by setting("Color Alpha", 128, 0..255)
86+
87+
private val placedCrystal = LimitedDecayQueue<BlockPos>(64, 1000L)
88+
private val target: LivingEntity? get() = targeting.target()
89+
90+
private fun SafeContext.worldCrystals(target: LivingEntity): List<EndCrystalEntity> {
91+
return fastEntitySearch<EndCrystalEntity>(explodeRange, pos = target.blockPos)
92+
.sortedByDescending { explodeMethod.sorted(this, target, it.blockPos) }
93+
}
94+
95+
private fun SafeContext.validPositions(target: LivingEntity): Sequence<BlockPos> {
96+
return blockSearch(
97+
range = placing.reach.toInt(),
98+
step = minSeparation, // Need to change this behavior
99+
pos = player.blockPos,
100+
) { pos, _ -> canPlace(pos, target) }
101+
.keys.asSequence()
102+
.sortedByDescending { placeMethod.sorted(this, target, it.up()) } // The explosion source of the crystal is not at its base
103+
}
104+
105+
private fun SafeContext.canPlace(pos: BlockPos, target: LivingEntity): Boolean {
106+
// Checks if the distance between the player and the target
107+
// and the player and the place position is <= to the placing range
108+
return player dist target <= placing.reach &&
109+
player dist pos <= placing.reach &&
110+
// Checks if the support block is either obsidian or bedrock
111+
(pos.blockState(world).isOf(Blocks.OBSIDIAN)
112+
|| pos.blockState(world).isOf(Blocks.BEDROCK)) &&
113+
// Checks if the crystal can be placed on top of a block
114+
// It checks if there's another entity in the air block
115+
// Or if there's another crystal 2 blocks around the air block
116+
world.isAir(pos.up()) &&
117+
fastEntitySearch<Entity>(0.5, pos.up()).isEmpty() &&
118+
fastEntitySearch<EndCrystalEntity>(1.5, pos.up()).isEmpty() && // Doesn't handle the edge case where there is a crystal floating
119+
120+
player.health + player.absorptionAmount >= placeMinHealth &&
121+
explosionDamage(pos, target, 6.0) >= placeMinDamage &&
122+
explosionDamage(pos, player, 6.0) <= placeMaxSelfDamage &&
123+
// Checks if the last crystal was set more than [placeDelay] ms ago
124+
placedCrystal.peek()?.second?.plusMillis(placeDelay)?.isBefore(Instant.now()) ?: true
125+
//if (multiPlace) !placedCrystal.any { (crystalPos, _) -> pos.up() == crystalPos } else true
126+
}
78127

79-
val targetEntity: LivingEntity? get() = targeting.target()
80-
val validPositions = mutableListOf<BlockPos>()
81128

82129
init {
83-
BreakBlock
84-
requestRotation(
85-
onUpdate = {
86-
val blockpos =
87-
validPositions.removeFirstOrNull() ?: return@requestRotation null
88-
if (!rotate) return@requestRotation null
89-
90-
lookAtBlock(blockpos, rotation, placing)
91-
},
92-
)
93-
94-
listener<TickEvent.Pre> {
95-
targetEntity?.let { target ->
96-
// TODO: If we could provide our own selector to [PlaceFinder] we could use it instead of this
97-
// Or maybe not since we want a fast way of finding the best positions
98-
validPositions.clear() // not good
99-
validPositions.addAll(
100-
blockSearch(
101-
range = placing.reach.toInt(),
102-
step = minSeparation,
103-
pos = target.blockPos, // Search around the target and not the player
104-
) { pos, state ->
105-
!state.isSideSolid(world, pos, Direction.UP, SideShapeType.FULL) &&
106-
(state.isOf(Blocks.OBSIDIAN) || state.isOf(Blocks.BEDROCK)) &&
107-
// If the distance between the player and the place position for the
108-
// target is greater than the explosion range, we cannot use said
109-
// position as it is impossible to reach
110-
player dist pos <= explodeRange
111-
}.keys
112-
.asSequence()
113-
// https://minecraft.wiki/w/Explosion#Damage
114-
.sortedByDescending { explosionDamage(it, target, 6.0) }
130+
rotate(
131+
priority = 100,
132+
) {
133+
onUpdate {
134+
if (!rotate) return@onUpdate null
135+
136+
val blockpos = validPositions(
137+
target ?: return@onUpdate null
138+
).firstOrNull() ?: return@onUpdate null
139+
140+
// Taskflow interact block
141+
// placedCrystal.add()
142+
lookAtBlock(blockpos, rotation, placing, sides = setOf(Direction.UP))
143+
}
144+
}
145+
146+
listen<RenderEvent.StaticESP> {
147+
worldCrystals(target ?: return@listen).forEach { crystal ->
148+
it.renderer.build(
149+
Box.of(crystal.pos, 1.0, 1.0, 1.0),
150+
crystalColor.compared(this, target ?: return@forEach, crystal.blockPos),
151+
crystalColor.compared(this, target ?: return@forEach, crystal.blockPos)
115152
)
116153
}
117154
}
@@ -124,4 +161,22 @@ object CrystalAura : Module(
124161
Exploding,
125162
Rendering
126163
}
164+
165+
private enum class DamageSort(val sorted: SafeContext.(LivingEntity, BlockPos) -> Double) {
166+
Deadly({ target, source -> explosionDamage(source, target, 6.0) }),
167+
Balanced({ target, source -> explosionDamage(source, player, 6.0) - explosionDamage(source, target, 6.0) }),
168+
Safe({ target, source -> explosionDamage(source, target, 6.0) - explosionDamage(source, player, 6.0) });
169+
}
170+
171+
private enum class ColorComparator(val compared: SafeContext.(LivingEntity, BlockPos) -> Color) {
172+
Distance({ target, dest ->
173+
val red = transform(target dist dest, 0.0, explodeRange, 255.0, 0.0).toInt()
174+
Color(red, 255-red, 0, crystalAlpha)
175+
}),
176+
177+
Damage({ target, dest ->
178+
val red = transform(explosionDamage(dest, target, 6.0), 0.0, target.health.toDouble(), 0.0, 255.0).toInt()
179+
Color(red, 255-red, 0, crystalAlpha)
180+
})
181+
}
127182
}

common/src/main/kotlin/com/lambda/util/collections/LimitedDecayQueue.kt

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,13 @@ import java.util.concurrent.ConcurrentLinkedQueue
2323
class LimitedDecayQueue<E>(
2424
private var sizeLimit: Int,
2525
private var interval: Long,
26-
) {
27-
private val queue: ConcurrentLinkedQueue<Pair<E, Instant>> = ConcurrentLinkedQueue()
28-
29-
val size: Int
30-
get() = queue.size
31-
26+
) : ConcurrentLinkedQueue<Pair<E, Instant>>() {
3227
@Synchronized
28+
@JvmName("jvmAdd")
3329
fun add(element: E): Boolean {
3430
cleanUp()
35-
return if (queue.size < sizeLimit) {
36-
queue.add(element to Instant.now())
31+
return if (size < sizeLimit) {
32+
add(element to Instant.now())
3733
true
3834
} else {
3935
false
@@ -55,8 +51,8 @@ class LimitedDecayQueue<E>(
5551
@Synchronized
5652
private fun cleanUp() {
5753
val now = Instant.now()
58-
while (queue.isNotEmpty() && now.minusMillis(interval).isAfter(queue.peek().second)) {
59-
queue.poll()
54+
while (isNotEmpty() && now.minusMillis(interval).isAfter(peek().second)) {
55+
poll()
6056
}
6157
}
6258
}

0 commit comments

Comments
 (0)