diff options
Diffstat (limited to 'src/main/kotlin/features/diana')
-rw-r--r-- | src/main/kotlin/features/diana/AncestralSpadeSolver.kt | 131 | ||||
-rw-r--r-- | src/main/kotlin/features/diana/DianaWaypoints.kt | 35 | ||||
-rw-r--r-- | src/main/kotlin/features/diana/NearbyBurrowsSolver.kt | 144 |
3 files changed, 310 insertions, 0 deletions
diff --git a/src/main/kotlin/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt new file mode 100644 index 0000000..39ca6d3 --- /dev/null +++ b/src/main/kotlin/features/diana/AncestralSpadeSolver.kt @@ -0,0 +1,131 @@ + +package moe.nea.firmament.features.diana + +import kotlin.time.Duration.Companion.seconds +import net.minecraft.particle.ParticleTypes +import net.minecraft.sound.SoundEvents +import net.minecraft.util.math.Vec3d +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.ParticleSpawnEvent +import moe.nea.firmament.events.SoundReceiveEvent +import moe.nea.firmament.events.WorldKeyboardEvent +import moe.nea.firmament.events.WorldReadyEvent +import moe.nea.firmament.events.WorldRenderLastEvent +import moe.nea.firmament.events.subscription.SubscriptionOwner +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SBData +import moe.nea.firmament.util.SkyBlockIsland +import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.WarpUtil +import moe.nea.firmament.util.render.RenderInWorldContext +import moe.nea.firmament.util.skyBlockId + +object AncestralSpadeSolver : SubscriptionOwner { + var lastDing = TimeMark.farPast() + private set + private val pitches = mutableListOf<Float>() + val particlePositions = mutableListOf<Vec3d>() + var nextGuess: Vec3d? = null + private set + + val ancestralSpadeId = SkyblockId("ANCESTRAL_SPADE") + private var lastTeleportAttempt = TimeMark.farPast() + + fun isEnabled() = + DianaWaypoints.TConfig.ancestralSpadeSolver + && SBData.skyblockLocation == SkyBlockIsland.HUB + && MC.player?.inventory?.containsAny { it.skyBlockId == ancestralSpadeId } == true // TODO: add a reactive property here + + @Subscribe + fun onKeyBind(event: WorldKeyboardEvent) { + if (!isEnabled()) return + if (!event.matches(DianaWaypoints.TConfig.ancestralSpadeTeleport)) return + + if (lastTeleportAttempt.passedTime() < 3.seconds) return + WarpUtil.teleportToNearestWarp(SkyBlockIsland.HUB, nextGuess ?: return) + lastTeleportAttempt = TimeMark.now() + } + + @Subscribe + fun onParticleSpawn(event: ParticleSpawnEvent) { + if (!isEnabled()) return + if (event.particleEffect != ParticleTypes.DRIPPING_LAVA) return + if (event.offset.x != 0.0F || event.offset.y != 0F || event.offset.z != 0F) + return + particlePositions.add(event.position) + if (particlePositions.size > 20) { + particlePositions.removeFirst() + } + } + + @Subscribe + fun onPlaySound(event: SoundReceiveEvent) { + if (!isEnabled()) return + if (!SoundEvents.BLOCK_NOTE_BLOCK_HARP.matchesId(event.sound.value().id)) return + + if (lastDing.passedTime() > 1.seconds) { + particlePositions.clear() + pitches.clear() + } + lastDing = TimeMark.now() + + pitches.add(event.pitch) + if (pitches.size > 20) { + pitches.removeFirst() + } + + if (particlePositions.size < 3) { + return + } + + val averagePitchDelta = + if (pitches.isEmpty()) return + else pitches + .zipWithNext { a, b -> b - a } + .average() + + val soundDistanceEstimate = (Math.E / averagePitchDelta) - particlePositions.first().distanceTo(event.position) + + if (soundDistanceEstimate > 1000) { + return + } + + val lastParticleDirection = particlePositions + .takeLast(3) + .let { (a, _, b) -> b.subtract(a) } + .normalize() + + nextGuess = event.position.add(lastParticleDirection.multiply(soundDistanceEstimate)) + } + + @Subscribe + fun onWorldRender(event: WorldRenderLastEvent) { + if (!isEnabled()) return + RenderInWorldContext.renderInWorld(event) { + nextGuess?.let { + color(1f, 1f, 0f, 0.5f) + tinyBlock(it, 1f) + color(1f, 1f, 0f, 1f) + tracer(it, lineWidth = 3f) + } + if (particlePositions.size > 2 && lastDing.passedTime() < 10.seconds && nextGuess != null) { + color(0f, 1f, 0f, 0.7f) + line(particlePositions) + } + } + } + + @Subscribe + fun onSwapWorld(event: WorldReadyEvent) { + nextGuess = null + particlePositions.clear() + pitches.clear() + lastDing = TimeMark.farPast() + } + + override val delegateFeature: FirmamentFeature + get() = DianaWaypoints + +} diff --git a/src/main/kotlin/features/diana/DianaWaypoints.kt b/src/main/kotlin/features/diana/DianaWaypoints.kt new file mode 100644 index 0000000..0a34eaa --- /dev/null +++ b/src/main/kotlin/features/diana/DianaWaypoints.kt @@ -0,0 +1,35 @@ + +package moe.nea.firmament.features.diana + +import moe.nea.firmament.events.AttackBlockEvent +import moe.nea.firmament.events.ParticleSpawnEvent +import moe.nea.firmament.events.ProcessChatEvent +import moe.nea.firmament.events.SoundReceiveEvent +import moe.nea.firmament.events.UseBlockEvent +import moe.nea.firmament.events.WorldKeyboardEvent +import moe.nea.firmament.events.WorldReadyEvent +import moe.nea.firmament.events.WorldRenderLastEvent +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.gui.config.ManagedConfig + +object DianaWaypoints : FirmamentFeature { + override val identifier get() = "diana" + override val config get() = TConfig + + object TConfig : ManagedConfig(identifier) { + val ancestralSpadeSolver by toggle("ancestral-spade") { true } + val ancestralSpadeTeleport by keyBindingWithDefaultUnbound("ancestral-teleport") + val nearbyWaypoints by toggle("nearby-waypoints") { true } + } + + override fun onLoad() { + UseBlockEvent.subscribe { + NearbyBurrowsSolver.onBlockClick(it.hitResult.blockPos) + } + AttackBlockEvent.subscribe { + NearbyBurrowsSolver.onBlockClick(it.blockPos) + } + } +} + + diff --git a/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt new file mode 100644 index 0000000..7158bb9 --- /dev/null +++ b/src/main/kotlin/features/diana/NearbyBurrowsSolver.kt @@ -0,0 +1,144 @@ + +package moe.nea.firmament.features.diana + +import kotlin.time.Duration.Companion.seconds +import net.minecraft.particle.ParticleTypes +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.MathHelper +import net.minecraft.util.math.Position +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.ParticleSpawnEvent +import moe.nea.firmament.events.ProcessChatEvent +import moe.nea.firmament.events.WorldReadyEvent +import moe.nea.firmament.events.WorldRenderLastEvent +import moe.nea.firmament.events.subscription.SubscriptionOwner +import moe.nea.firmament.features.FirmamentFeature +import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.mutableMapWithMaxSize +import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld + +object NearbyBurrowsSolver : SubscriptionOwner { + + + private val recentlyDugBurrows: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(20) + private val recentEnchantParticles: MutableMap<BlockPos, TimeMark> = mutableMapWithMaxSize(500) + private var lastBlockClick: BlockPos? = null + + enum class BurrowType { + START, MOB, TREASURE + } + + val burrows = mutableMapOf<BlockPos, BurrowType>() + + @Subscribe + fun onChatEvent(event: ProcessChatEvent) { + val lastClickedBurrow = lastBlockClick ?: return + if (event.unformattedString.startsWith("You dug out a Griffin Burrow!") || + event.unformattedString.startsWith(" ☠ You were killed by") || + event.unformattedString.startsWith("You finished the Griffin burrow chain!") + ) { + markAsDug(lastClickedBurrow) + burrows.remove(lastClickedBurrow) + } + } + + + fun wasRecentlyDug(blockPos: BlockPos): Boolean { + val lastDigTime = recentlyDugBurrows[blockPos] ?: TimeMark.farPast() + return lastDigTime.passedTime() < 10.seconds + } + + fun markAsDug(blockPos: BlockPos) { + recentlyDugBurrows[blockPos] = TimeMark.now() + } + + fun wasRecentlyEnchanted(blockPos: BlockPos): Boolean { + val lastEnchantTime = recentEnchantParticles[blockPos] ?: TimeMark.farPast() + return lastEnchantTime.passedTime() < 4.seconds + } + + fun markAsEnchanted(blockPos: BlockPos) { + recentEnchantParticles[blockPos] = TimeMark.now() + } + + @Subscribe + fun onParticles(event: ParticleSpawnEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + + val position: BlockPos = event.position.toBlockPos().down() + + if (wasRecentlyDug(position)) return + + val isEven50Spread = (event.offset.x == 0.5f && event.offset.z == 0.5f) + + if (event.particleEffect.type == ParticleTypes.ENCHANT) { + if (event.count == 5 && event.speed == 0.05F && event.offset.y == 0.4F && isEven50Spread) { + markAsEnchanted(position) + } + return + } + + if (!wasRecentlyEnchanted(position)) return + + if (event.particleEffect.type == ParticleTypes.ENCHANTED_HIT + && event.count == 4 + && event.speed == 0.01F + && event.offset.y == 0.1f + && isEven50Spread + ) { + burrows[position] = BurrowType.START + } + if (event.particleEffect.type == ParticleTypes.CRIT + && event.count == 3 + && event.speed == 0.01F + && event.offset.y == 0.1F + && isEven50Spread + ) { + burrows[position] = BurrowType.MOB + } + if (event.particleEffect.type == ParticleTypes.DRIPPING_LAVA + && event.count == 2 + && event.speed == 0.01F + && event.offset.y == 0.1F + && event.offset.x == 0.35F && event.offset.z == 0.35f + ) { + burrows[position] = BurrowType.TREASURE + } + } + + @Subscribe + fun onRender(event: WorldRenderLastEvent) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + renderInWorld(event) { + for ((location, burrow) in burrows) { + when (burrow) { + BurrowType.START -> color(.2f, .8f, .2f, 0.4f) + BurrowType.MOB -> color(0.3f, 0.4f, 0.9f, 0.4f) + BurrowType.TREASURE -> color(1f, 0.7f, 0.2f, 0.4f) + } + block(location) + } + } + } + + @Subscribe + fun onSwapWorld(worldReadyEvent: WorldReadyEvent) { + burrows.clear() + recentEnchantParticles.clear() + recentlyDugBurrows.clear() + lastBlockClick = null + } + + fun onBlockClick(blockPos: BlockPos) { + if (!DianaWaypoints.TConfig.nearbyWaypoints) return + burrows.remove(blockPos) + lastBlockClick = blockPos + } + + override val delegateFeature: FirmamentFeature + get() = DianaWaypoints +} + +fun Position.toBlockPos(): BlockPos { + return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)) +} |