aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/features/diana
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
committerLinnea Gräf <nea@nea.moe>2024-08-28 19:04:24 +0200
commitd2f240ff0ca0d27f417f837e706c781a98c31311 (patch)
tree0db7aff6cc14deaf36eed83889d59fd6b3a6f599 /src/main/kotlin/features/diana
parenta6906308163aa3b2d18fa1dc1aa71ac9bbcc83ab (diff)
downloadFirmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.gz
Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.tar.bz2
Firmament-d2f240ff0ca0d27f417f837e706c781a98c31311.zip
Refactor source layout
Introduce compat source sets and move all kotlin sources to the main directory [no changelog]
Diffstat (limited to 'src/main/kotlin/features/diana')
-rw-r--r--src/main/kotlin/features/diana/AncestralSpadeSolver.kt131
-rw-r--r--src/main/kotlin/features/diana/DianaWaypoints.kt35
-rw-r--r--src/main/kotlin/features/diana/NearbyBurrowsSolver.kt144
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))
+}