aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/features
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-01-18 20:00:47 +0100
committerLinnea Gräf <nea@nea.moe>2024-01-18 20:00:47 +0100
commit608fec9cd0bbe3cf389faaeb7e258b592e3adf14 (patch)
treeef7996342498f64529ac6eb5851b2a61a1973d1c /src/main/kotlin/moe/nea/firmament/features
parentd7902e06cd7285c72cd4ea2be6f18ead56a8775e (diff)
downloadFirmament-608fec9cd0bbe3cf389faaeb7e258b592e3adf14.tar.gz
Firmament-608fec9cd0bbe3cf389faaeb7e258b592e3adf14.tar.bz2
Firmament-608fec9cd0bbe3cf389faaeb7e258b592e3adf14.zip
Add Nearby Burrow Highlighter
Diffstat (limited to 'src/main/kotlin/moe/nea/firmament/features')
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt10
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt23
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt135
-rw-r--r--src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt191
4 files changed, 164 insertions, 195 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt b/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
index c34e68e..459fa3c 100644
--- a/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/diana/AncestralSpadeSolver.kt
@@ -13,6 +13,7 @@ import net.minecraft.sound.SoundEvents
import net.minecraft.util.math.Vec3d
import moe.nea.firmament.events.ParticleSpawnEvent
import moe.nea.firmament.events.SoundReceiveEvent
+import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext
@@ -28,6 +29,8 @@ object AncestralSpadeSolver {
fun onParticleSpawn(event: ParticleSpawnEvent) {
if (!DianaWaypoints.TConfig.ancestralSpadeSolver) 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()
@@ -90,4 +93,11 @@ object AncestralSpadeSolver {
}
}
+ fun onSwapWorld(event: WorldReadyEvent) {
+ nextGuess = null
+ particlePositions.clear()
+ pitches.clear()
+ lastDing = TimeMark.farPast()
+ }
+
}
diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt b/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt
index eb20852..6422f67 100644
--- a/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt
+++ b/src/main/kotlin/moe/nea/firmament/features/diana/DianaWaypoints.kt
@@ -6,26 +6,41 @@
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.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: String
- get() = "diana-waypoints"
- override val config: ManagedConfig?
- get() = TConfig
+ override val identifier get() = "diana-waypoints"
+ override val config get() = TConfig
object TConfig : ManagedConfig(identifier) {
val ancestralSpadeSolver by toggle("ancestral-spade") { false }
}
override fun onLoad() {
+ ParticleSpawnEvent.subscribe(NearbyBurrowsSolver::onParticles)
+ WorldReadyEvent.subscribe(NearbyBurrowsSolver::onSwapWorld)
+ WorldRenderLastEvent.subscribe(NearbyBurrowsSolver::onRender)
+ UseBlockEvent.subscribe {
+ NearbyBurrowsSolver.onBlockClick(it.hitResult.blockPos)
+ }
+ AttackBlockEvent.subscribe {
+ NearbyBurrowsSolver.onBlockClick(it.blockPos)
+ }
+ ProcessChatEvent.subscribe(NearbyBurrowsSolver::onChatEvent)
+
+
ParticleSpawnEvent.subscribe(AncestralSpadeSolver::onParticleSpawn)
SoundReceiveEvent.subscribe(AncestralSpadeSolver::onPlaySound)
WorldRenderLastEvent.subscribe(AncestralSpadeSolver::onWorldRender)
+ WorldReadyEvent.subscribe(AncestralSpadeSolver::onSwapWorld)
}
}
diff --git a/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt b/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt
new file mode 100644
index 0000000..9fd7792
--- /dev/null
+++ b/src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt
@@ -0,0 +1,135 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+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.events.ParticleSpawnEvent
+import moe.nea.firmament.events.ProcessChatEvent
+import moe.nea.firmament.events.WorldReadyEvent
+import moe.nea.firmament.events.WorldRenderLastEvent
+import moe.nea.firmament.util.TimeMark
+import moe.nea.firmament.util.mutableMapWithMaxSize
+import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld
+
+object NearbyBurrowsSolver {
+
+
+ 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>()
+
+ 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()
+ }
+
+ fun onParticles(event: ParticleSpawnEvent) {
+
+ 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
+ }
+ }
+
+ fun onRender(event: WorldRenderLastEvent) {
+ 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)
+ }
+ }
+ }
+
+ fun onSwapWorld(worldReadyEvent: WorldReadyEvent) {
+ burrows.clear()
+ recentEnchantParticles.clear()
+ recentlyDugBurrows.clear()
+ lastBlockClick = null
+ }
+
+ fun onBlockClick(blockPos: BlockPos) {
+ lastBlockClick = blockPos
+ }
+}
+
+fun Position.toBlockPos(): BlockPos {
+ return BlockPos(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z))
+}
diff --git a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt b/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt
deleted file mode 100644
index 74d0040..0000000
--- a/src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-package moe.nea.firmament.features.fishing
-
-import kotlin.math.abs
-import kotlin.math.absoluteValue
-import kotlin.math.atan2
-import kotlin.math.cos
-import kotlin.math.min
-import kotlin.math.sin
-import kotlin.math.sqrt
-import kotlin.time.Duration.Companion.seconds
-import net.minecraft.entity.projectile.FishingBobberEntity
-import net.minecraft.particle.ParticleTypes
-import net.minecraft.util.math.Vec3d
-import moe.nea.firmament.Firmament
-import moe.nea.firmament.events.ParticleSpawnEvent
-import moe.nea.firmament.events.WorldReadyEvent
-import moe.nea.firmament.events.WorldRenderLastEvent
-import moe.nea.firmament.features.FirmamentFeature
-import moe.nea.firmament.features.debug.DebugView
-import moe.nea.firmament.gui.config.ManagedConfig
-import moe.nea.firmament.util.MC
-import moe.nea.firmament.util.TimeMark
-import moe.nea.firmament.util.render.RenderInWorldContext.Companion.renderInWorld
-
-object FishingWarning : FirmamentFeature {
- override val identifier: String
- get() = "fishing-warning"
-
- object TConfig : ManagedConfig("fishing-warning") {
- // Display a warning when you are about to hook a fish
- val displayWarning by toggle("display-warning") { false }
- val highlightWakeChain by toggle("highlight-wake-chain") { false }
- }
-
- override val config: ManagedConfig get() = TConfig
-
-
- data class WakeChain(
- val delta: Vec3d,
- val momentum: Vec3d,
- val lastContinued: TimeMark,
- )
-
-
- val chains = mutableListOf<WakeChain>()
-
- private fun areAnglesClose(a: Double, b: Double, tolerance: Double): Boolean {
- var dist = (a - b).absoluteValue
- if (180 < dist) dist = 360 - dist;
- return dist <= tolerance
- }
-
- private fun calculateAngleFromOffsets(xOffset: Double, zOffset: Double): Double {
- // See also: Vanilla 1.8.9 Fishing particle code.
- var angleX = Math.toDegrees(Math.acos(xOffset / 0.04))
- var angleZ = Math.toDegrees(Math.asin(zOffset / 0.04))
- if (xOffset < 0) {
- // Old: angleZ = 180 - angleZ;
- angleZ = 180 - angleZ
- }
- if (zOffset < 0) {
- angleX = 360 - angleX
- }
- angleX %= 360.0
- angleZ %= 360.0
- if (angleX < 0) angleX += 360.0
- if (angleZ < 0) angleZ += 360.0
- var dist = angleX - angleZ
- if (dist < -180) dist += 360.0
- if (dist > 180) dist -= 360.0
- return Math.toDegrees(Math.atan2(xOffset, zOffset))
- return angleZ + dist / 2 + 180
- }
-
- val π = Math.PI
- val τ = Math.PI * 2
-
- private fun toDegrees(d: Double) = Math.toDegrees(d).mod(360.0)
- private fun toRadians(d: Double) = Math.toRadians(d).mod(τ)
-
- fun isHookPossible(hook: FishingBobberEntity, particlePos: Vec3d, angle1: Double, angle2: Double): Boolean {
- val dx = particlePos.x - hook.trackedPosition.withDelta(0, 0, 0).x
- val dz = particlePos.z - hook.trackedPosition.withDelta(0, 0, 0).z
- val dist = sqrt(dx * dx + dz * dz)
-
- if (dist < 0.2) return true
- val tolerance = toDegrees(atan2(0.03125, dist)) * 1.5
- val angleToHook = toDegrees(atan2(dz, dx))
- return areAnglesClose(angle1, angleToHook, tolerance) || areAnglesClose(angle2, angleToHook, tolerance)
- }
-
- val recentParticles = mutableListOf<Pair<Vec3d, TimeMark>>()
-
- data class Candidate(
- val angle1: Double,
- val angle2: Double,
- val hookOrigin: Vec3d,
- val position: Vec3d,
- val timeMark: TimeMark = TimeMark.now()
- )
-
- val recentCandidates = mutableListOf<Candidate>()
-
- private fun onParticleSpawn(event: ParticleSpawnEvent) {
- if (event.particleEffect.type != ParticleTypes.FISHING) return
- if (!(abs(event.offset.y - 0.01f) < 0.001f)) return
- val hook = MC.player?.fishHook ?: return
- val actualOffset = event.offset
- val candidate1 = calculateAngleFromOffsets(-actualOffset.x, (-actualOffset.z))
- val candidate2 = calculateAngleFromOffsets(actualOffset.x, actualOffset.z)
- recentCandidates.add(Candidate(candidate1, candidate2, hook.trackedPosition.withDelta(0, 0, 0), event.position))
-
- if (isHookPossible(hook, event.position, candidate1, candidate2)) {
- recentParticles.add(Pair(event.position, TimeMark.now()))
- }
- }
-
- override fun onLoad() {
- ParticleSpawnEvent.subscribe(::onParticleSpawn)
- WorldReadyEvent.subscribe {
- recentParticles.clear()
- }
- WorldRenderLastEvent.subscribe {
- recentParticles.removeIf { it.second.passedTime() > 5.seconds }
- recentCandidates.removeIf { it.timeMark.passedTime() > 5.seconds }
- renderInWorld(it) {
- color(0f, 0f, 1f, 1f)
- recentParticles.forEach {
- tinyBlock(it.first, 0.1F)
- }
-
- if (Firmament.DEBUG) {
- recentCandidates.forEach {
- color(1f, 1f, 0f, 1f)
- line(it.hookOrigin, it.position)
- color(1f, 0f, 0f, 1f)
- fun P(yaw: Double) = Vec3d(cos(yaw), 0.0, sin(yaw))
- line(
- it.position,
- P(π - toRadians(it.angle1)).multiply(5.0).add(it.position)
- )
- color(0f, 1f, 0f, 1f)
- line(
- it.position,
- P(π - toRadians(it.angle2)).multiply(5.0).add(it.position)
- )
- val tolerance = (atan2(0.03125, it.position.distanceTo(it.hookOrigin))).absoluteValue * 1.5
- val diff = it.hookOrigin.subtract(it.position)
- val rd = atan2(diff.z, diff.x).mod(τ)
- color(0.8f, 0f, 0.8f, 1f)
- DebugView.showVariable("tolerance", tolerance)
- DebugView.showVariable("angle1Rad", toRadians(180 - it.angle1))
- DebugView.showVariable("angle1Diff", (toRadians(it.angle1) - rd).mod(τ))
- DebugView.showVariable("angle1Deg", it.angle1.mod(360.0))
- DebugView.showVariable("angle2Rad", toRadians(180 - it.angle2))
- DebugView.showVariable("angle2Deg", it.angle2.mod(360.0))
- DebugView.showVariable("angle2Diff", (toRadians(it.angle2) - rd).mod(τ))
- DebugView.showVariable("rd", rd)
- DebugView.showVariable("minT", (rd + tolerance).mod(τ))
- DebugView.showVariable("maxT", (rd - tolerance).mod(τ))
- DebugView.showVariable(
- "passes",
- if (min(
- (rd - toRadians(180 - it.angle2)).mod(τ),
- (rd - toRadians(180 - it.angle1)).mod(τ)
- ) < tolerance
- ) {
- "§aPasses"
- } else {
- "§cNo Pass"
- }
- )
-
- line(it.position, P(rd + tolerance).add(it.position))
- line(it.position, P(rd - tolerance).add(it.position))
- }
- color(0.8F, 0.8F, 0.8f, 1f)
- val fishHook = MC.player?.fishHook
- if (fishHook != null)
- tinyBlock(fishHook.trackedPosition.withDelta(0, 0, 0), 0.2f)
- }
- }
- }
- }
-}