Add Nearby Burrow Highlighter
This commit is contained in:
@@ -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())
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt
Normal file
23
src/main/kotlin/moe/nea/firmament/events/AttackBlockEvent.kt
Normal 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>()
|
||||||
|
}
|
||||||
@@ -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>()
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt
Normal file
16
src/main/kotlin/moe/nea/firmament/events/UseBlockEvent.kt
Normal 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>()
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user