From 608fec9cd0bbe3cf389faaeb7e258b592e3adf14 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Thu, 18 Jan 2024 20:00:47 +0100 Subject: Add Nearby Burrow Highlighter --- src/main/kotlin/moe/nea/firmament/Firmament.kt | 4 +- .../moe/nea/firmament/events/AttackBlockEvent.kt | 23 +++ .../moe/nea/firmament/events/ParticleSpawnEvent.kt | 4 +- .../moe/nea/firmament/events/UseBlockEvent.kt | 16 ++ .../firmament/events/registration/ChatEvents.kt | 26 ++- .../features/diana/AncestralSpadeSolver.kt | 10 ++ .../nea/firmament/features/diana/DianaWaypoints.kt | 23 ++- .../features/diana/NearbyBurrowsSolver.kt | 135 +++++++++++++++ .../firmament/features/fishing/FishingWarning.kt | 191 --------------------- .../nea/firmament/util/MutableMapWithMaxSize.kt | 13 ++ 10 files changed, 242 insertions(+), 203 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt create mode 100644 src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt create mode 100644 src/main/kotlin/moe/nea/firmament/features/diana/NearbyBurrowsSolver.kt delete mode 100644 src/main/kotlin/moe/nea/firmament/features/fishing/FishingWarning.kt create mode 100644 src/main/kotlin/moe/nea/firmament/util/MutableMapWithMaxSize.kt (limited to 'src/main/kotlin/moe') diff --git a/src/main/kotlin/moe/nea/firmament/Firmament.kt b/src/main/kotlin/moe/nea/firmament/Firmament.kt index a81d66f..5fd9f60 100644 --- a/src/main/kotlin/moe/nea/firmament/Firmament.kt +++ b/src/main/kotlin/moe/nea/firmament/Firmament.kt @@ -45,7 +45,7 @@ import moe.nea.firmament.events.CommandEvent import moe.nea.firmament.events.ItemTooltipEvent import moe.nea.firmament.events.ScreenRenderPostEvent 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.repo.HypixelStaticData import moe.nea.firmament.repo.RepoManager @@ -139,7 +139,7 @@ object Firmament { globalJob.cancel() } }) - registerFirmamentChatEvents() + registerFirmamentEvents() ItemTooltipCallback.EVENT.register { a, b, c -> ItemTooltipEvent.publish(ItemTooltipEvent(a, b, c)) } diff --git a/src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt b/src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt new file mode 100644 index 0000000..3d8ccdd --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf + * + * 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() +} diff --git a/src/main/kotlin/moe/nea/firmament/events/ParticleSpawnEvent.kt b/src/main/kotlin/moe/nea/firmament/events/ParticleSpawnEvent.kt index e1b9ad3..a97b6d0 100644 --- a/src/main/kotlin/moe/nea/firmament/events/ParticleSpawnEvent.kt +++ b/src/main/kotlin/moe/nea/firmament/events/ParticleSpawnEvent.kt @@ -6,15 +6,17 @@ package moe.nea.firmament.events +import org.joml.Vector3f import net.minecraft.particle.ParticleEffect import net.minecraft.util.math.Vec3d data class ParticleSpawnEvent( val particleEffect: ParticleEffect, val position: Vec3d, - val offset: Vec3d, + val offset: Vector3f, val longDistance: Boolean, val count: Int, + val speed: Float, ) : FirmamentEvent.Cancellable() { companion object : FirmamentEventBus() } diff --git a/src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt b/src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt new file mode 100644 index 0000000..5db4104 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf + * + * 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() +} diff --git a/src/main/kotlin/moe/nea/firmament/events/registration/ChatEvents.kt b/src/main/kotlin/moe/nea/firmament/events/registration/ChatEvents.kt index 90a665a..4db0f60 100644 --- a/src/main/kotlin/moe/nea/firmament/events/registration/ChatEvents.kt +++ b/src/main/kotlin/moe/nea/firmament/events/registration/ChatEvents.kt @@ -6,24 +6,29 @@ 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.AttackBlockEvent import moe.nea.firmament.events.ModifyChatEvent import moe.nea.firmament.events.ProcessChatEvent -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents -import net.minecraft.text.Text +import moe.nea.firmament.events.UseBlockEvent private var lastReceivedMessage: Text? = null -fun registerFirmamentChatEvents() { +fun registerFirmamentEvents() { ClientReceiveMessageEvents.ALLOW_CHAT.register(ClientReceiveMessageEvents.AllowChat { message, signedMessage, sender, params, receptionTimestamp -> lastReceivedMessage = message !ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled - && !AllowChatEvent.publish(AllowChatEvent(message)).cancelled + && !AllowChatEvent.publish(AllowChatEvent(message)).cancelled }) ClientReceiveMessageEvents.ALLOW_GAME.register(ClientReceiveMessageEvents.AllowGame { message, overlay -> lastReceivedMessage = message 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 -> if (overlay) message @@ -39,4 +44,15 @@ fun registerFirmamentChatEvents() { 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 + }) } 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 + * + * 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 = mutableMapWithMaxSize(20) + private val recentEnchantParticles: MutableMap = mutableMapWithMaxSize(500) + private var lastBlockClick: BlockPos? = null + + enum class BurrowType { + START, MOB, TREASURE + } + + val burrows = mutableMapOf() + + 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 - * - * 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() - - 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>() - - data class Candidate( - val angle1: Double, - val angle2: Double, - val hookOrigin: Vec3d, - val position: Vec3d, - val timeMark: TimeMark = TimeMark.now() - ) - - val recentCandidates = mutableListOf() - - 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) - } - } - } - } -} diff --git a/src/main/kotlin/moe/nea/firmament/util/MutableMapWithMaxSize.kt b/src/main/kotlin/moe/nea/firmament/util/MutableMapWithMaxSize.kt new file mode 100644 index 0000000..8032053 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/MutableMapWithMaxSize.kt @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2024 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.util + +fun mutableMapWithMaxSize(maxSize: Int): MutableMap = object : LinkedHashMap() { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry): Boolean { + return size > maxSize + } +} -- cgit