Add Nearby Burrow Highlighter

This commit is contained in:
Linnea Gräf
2024-01-18 20:00:47 +01:00
parent d7902e06cd
commit 608fec9cd0
11 changed files with 246 additions and 205 deletions

View File

@@ -13,6 +13,7 @@ import moe.nea.firmament.events.ParticleSpawnEvent;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -33,9 +34,10 @@ public abstract class IncomingPacketListenerPatches {
var event = new ParticleSpawnEvent( var event = new ParticleSpawnEvent(
packet.getParameters(), packet.getParameters(),
new Vec3d(packet.getX(), packet.getY(), packet.getZ()), new Vec3d(packet.getX(), packet.getY(), packet.getZ()),
new Vec3d(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()), new Vector3f(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()),
packet.isLongDistance(), packet.isLongDistance(),
packet.getCount() packet.getCount(),
packet.getSpeed()
); );
ParticleSpawnEvent.Companion.publish(event); ParticleSpawnEvent.Companion.publish(event);
if (event.getCancelled()) if (event.getCancelled())

View File

@@ -45,7 +45,7 @@ import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.events.ItemTooltipEvent import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.events.ScreenRenderPostEvent import moe.nea.firmament.events.ScreenRenderPostEvent
import moe.nea.firmament.events.TickEvent import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.registration.registerFirmamentChatEvents import moe.nea.firmament.events.registration.registerFirmamentEvents
import moe.nea.firmament.features.FeatureManager import moe.nea.firmament.features.FeatureManager
import moe.nea.firmament.repo.HypixelStaticData import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
@@ -139,7 +139,7 @@ object Firmament {
globalJob.cancel() globalJob.cancel()
} }
}) })
registerFirmamentChatEvents() registerFirmamentEvents()
ItemTooltipCallback.EVENT.register { a, b, c -> ItemTooltipCallback.EVENT.register { a, b, c ->
ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c)) ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c))
} }

View File

@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.Hand
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
data class AttackBlockEvent(
val player: PlayerEntity,
val world: World,
val hand: Hand,
val blockPos: BlockPos,
val direction: Direction
) : FirmamentEvent.Cancellable() {
companion object : FirmamentEventBus<AttackBlockEvent>()
}

View File

@@ -6,15 +6,17 @@
package moe.nea.firmament.events package moe.nea.firmament.events
import org.joml.Vector3f
import net.minecraft.particle.ParticleEffect import net.minecraft.particle.ParticleEffect
import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3d
data class ParticleSpawnEvent( data class ParticleSpawnEvent(
val particleEffect: ParticleEffect, val particleEffect: ParticleEffect,
val position: Vec3d, val position: Vec3d,
val offset: Vec3d, val offset: Vector3f,
val longDistance: Boolean, val longDistance: Boolean,
val count: Int, val count: Int,
val speed: Float,
) : FirmamentEvent.Cancellable() { ) : FirmamentEvent.Cancellable() {
companion object : FirmamentEventBus<ParticleSpawnEvent>() companion object : FirmamentEventBus<ParticleSpawnEvent>()
} }

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.events
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.world.World
data class UseBlockEvent(val player: PlayerEntity, val world: World, val hand: Hand, val hitResult: BlockHitResult) : FirmamentEvent.Cancellable() {
companion object : FirmamentEventBus<UseBlockEvent>()
}

View File

@@ -6,24 +6,29 @@
package moe.nea.firmament.events.registration package moe.nea.firmament.events.registration
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents
import net.fabricmc.fabric.api.event.player.AttackBlockCallback
import net.fabricmc.fabric.api.event.player.UseBlockCallback
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import moe.nea.firmament.events.AllowChatEvent import moe.nea.firmament.events.AllowChatEvent
import moe.nea.firmament.events.AttackBlockEvent
import moe.nea.firmament.events.ModifyChatEvent import moe.nea.firmament.events.ModifyChatEvent
import moe.nea.firmament.events.ProcessChatEvent import moe.nea.firmament.events.ProcessChatEvent
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents import moe.nea.firmament.events.UseBlockEvent
import net.minecraft.text.Text
private var lastReceivedMessage: Text? = null private var lastReceivedMessage: Text? = null
fun registerFirmamentChatEvents() { fun registerFirmamentEvents() {
ClientReceiveMessageEvents.ALLOW_CHAT.register(ClientReceiveMessageEvents.AllowChat { message, signedMessage, sender, params, receptionTimestamp -> ClientReceiveMessageEvents.ALLOW_CHAT.register(ClientReceiveMessageEvents.AllowChat { message, signedMessage, sender, params, receptionTimestamp ->
lastReceivedMessage = message lastReceivedMessage = message
!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled !ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled
&& !AllowChatEvent.publish(AllowChatEvent(message)).cancelled && !AllowChatEvent.publish(AllowChatEvent(message)).cancelled
}) })
ClientReceiveMessageEvents.ALLOW_GAME.register(ClientReceiveMessageEvents.AllowGame { message, overlay -> ClientReceiveMessageEvents.ALLOW_GAME.register(ClientReceiveMessageEvents.AllowGame { message, overlay ->
lastReceivedMessage = message lastReceivedMessage = message
overlay || (!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled && overlay || (!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled &&
!AllowChatEvent.publish(AllowChatEvent(message)).cancelled) !AllowChatEvent.publish(AllowChatEvent(message)).cancelled)
}) })
ClientReceiveMessageEvents.MODIFY_GAME.register(ClientReceiveMessageEvents.ModifyGame { message, overlay -> ClientReceiveMessageEvents.MODIFY_GAME.register(ClientReceiveMessageEvents.ModifyGame { message, overlay ->
if (overlay) message if (overlay) message
@@ -39,4 +44,15 @@ fun registerFirmamentChatEvents() {
ProcessChatEvent.publish(ProcessChatEvent(message, true)) ProcessChatEvent.publish(ProcessChatEvent(message, true))
} }
}) })
AttackBlockCallback.EVENT.register(AttackBlockCallback { player, world, hand, pos, direction ->
if (AttackBlockEvent.publish(AttackBlockEvent(player, world, hand, pos, direction)).cancelled)
ActionResult.CONSUME
else ActionResult.PASS
})
UseBlockCallback.EVENT.register(UseBlockCallback { player, world, hand, hitResult ->
if (UseBlockEvent.publish(UseBlockEvent(player, world, hand, hitResult)).cancelled)
ActionResult.CONSUME
else ActionResult.PASS
})
} }

View File

@@ -13,6 +13,7 @@ import net.minecraft.sound.SoundEvents
import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3d
import moe.nea.firmament.events.ParticleSpawnEvent import moe.nea.firmament.events.ParticleSpawnEvent
import moe.nea.firmament.events.SoundReceiveEvent import moe.nea.firmament.events.SoundReceiveEvent
import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.events.WorldRenderLastEvent import moe.nea.firmament.events.WorldRenderLastEvent
import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.render.RenderInWorldContext import moe.nea.firmament.util.render.RenderInWorldContext
@@ -28,6 +29,8 @@ object AncestralSpadeSolver {
fun onParticleSpawn(event: ParticleSpawnEvent) { fun onParticleSpawn(event: ParticleSpawnEvent) {
if (!DianaWaypoints.TConfig.ancestralSpadeSolver) return if (!DianaWaypoints.TConfig.ancestralSpadeSolver) return
if (event.particleEffect != ParticleTypes.DRIPPING_LAVA) 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) particlePositions.add(event.position)
if (particlePositions.size > 20) { if (particlePositions.size > 20) {
particlePositions.removeFirst() particlePositions.removeFirst()
@@ -90,4 +93,11 @@ object AncestralSpadeSolver {
} }
} }
fun onSwapWorld(event: WorldReadyEvent) {
nextGuess = null
particlePositions.clear()
pitches.clear()
lastDing = TimeMark.farPast()
}
} }

View File

@@ -6,26 +6,41 @@
package moe.nea.firmament.features.diana package moe.nea.firmament.features.diana
import moe.nea.firmament.events.AttackBlockEvent
import moe.nea.firmament.events.ParticleSpawnEvent import moe.nea.firmament.events.ParticleSpawnEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.events.SoundReceiveEvent 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.events.WorldRenderLastEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
object DianaWaypoints : FirmamentFeature { object DianaWaypoints : FirmamentFeature {
override val identifier: String override val identifier get() = "diana-waypoints"
get() = "diana-waypoints" override val config get() = TConfig
override val config: ManagedConfig?
get() = TConfig
object TConfig : ManagedConfig(identifier) { object TConfig : ManagedConfig(identifier) {
val ancestralSpadeSolver by toggle("ancestral-spade") { false } val ancestralSpadeSolver by toggle("ancestral-spade") { false }
} }
override fun onLoad() { 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) ParticleSpawnEvent.subscribe(AncestralSpadeSolver::onParticleSpawn)
SoundReceiveEvent.subscribe(AncestralSpadeSolver::onPlaySound) SoundReceiveEvent.subscribe(AncestralSpadeSolver::onPlaySound)
WorldRenderLastEvent.subscribe(AncestralSpadeSolver::onWorldRender) WorldRenderLastEvent.subscribe(AncestralSpadeSolver::onWorldRender)
WorldReadyEvent.subscribe(AncestralSpadeSolver::onSwapWorld)
} }
} }

View File

@@ -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))
}

View File

@@ -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)
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.util
fun <K, V> mutableMapWithMaxSize(maxSize: Int): MutableMap<K, V> = object : LinkedHashMap<K, V>() {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>): Boolean {
return size > maxSize
}
}