diff options
author | nea <nea@nea.moe> | 2023-05-07 12:22:36 +0200 |
---|---|---|
committer | nea <nea@nea.moe> | 2023-05-08 00:17:34 +0200 |
commit | 942dd629efbf12183b1c5f5a966c36d4b0647a6b (patch) | |
tree | 407e8d2cdcfefa77673c768e5126857a86ddb89a /src | |
parent | 229f724ef4a0a4cbc426f31e27f9a57e9b1307c9 (diff) | |
download | Firmament-942dd629efbf12183b1c5f5a966c36d4b0647a6b.tar.gz Firmament-942dd629efbf12183b1c5f5a966c36d4b0647a6b.tar.bz2 Firmament-942dd629efbf12183b1c5f5a966c36d4b0647a6b.zip |
Add fishing particle highlighter
Currently does not work when sneaking or when the bobber moves too much, since the position desyncs with the server
Diffstat (limited to 'src')
12 files changed, 235 insertions, 5 deletions
diff --git a/src/main/java/moe/nea/notenoughupdates/mixins/MixinClientPacketHandler.java b/src/main/java/moe/nea/notenoughupdates/mixins/MixinClientPacketHandler.java new file mode 100644 index 0000000..dc57113 --- /dev/null +++ b/src/main/java/moe/nea/notenoughupdates/mixins/MixinClientPacketHandler.java @@ -0,0 +1,23 @@ +package moe.nea.notenoughupdates.mixins; + +import moe.nea.notenoughupdates.events.ParticleSpawnEvent; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; +import net.minecraft.util.math.Vec3d; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientPlayNetworkHandler.class) +public class MixinClientPacketHandler { + @Inject(method = "onParticle", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkThreadUtils;forceMainThread(Lnet/minecraft/network/packet/Packet;Lnet/minecraft/network/listener/PacketListener;Lnet/minecraft/util/thread/ThreadExecutor;)V", shift = At.Shift.AFTER)) + public void onParticleSpawn(ParticleS2CPacket packet, CallbackInfo ci) { + ParticleSpawnEvent.Companion.publish(new ParticleSpawnEvent( + packet.getParameters(), + new Vec3d(packet.getX(), packet.getY(), packet.getZ()), + new Vec3d(packet.getOffsetX(), packet.getOffsetY(), packet.getOffsetZ()), + packet.isLongDistance() + )); + } +} diff --git a/src/main/java/moe/nea/notenoughupdates/mixins/devenv/MixinScoreboard.java b/src/main/java/moe/nea/notenoughupdates/mixins/devenv/MixinScoreboard.java new file mode 100644 index 0000000..306b900 --- /dev/null +++ b/src/main/java/moe/nea/notenoughupdates/mixins/devenv/MixinScoreboard.java @@ -0,0 +1,16 @@ +package moe.nea.notenoughupdates.mixins.devenv; + +import net.minecraft.scoreboard.Scoreboard; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(Scoreboard.class) +public class MixinScoreboard { + @Redirect(method = "addTeam", at=@At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V")) + public void onExistingteam(Logger instance, String s, Object o) { + // Ignore creations of existing teams + } +} diff --git a/src/main/kotlin/moe/nea/notenoughupdates/events/ParticleSpawnEvent.kt b/src/main/kotlin/moe/nea/notenoughupdates/events/ParticleSpawnEvent.kt new file mode 100644 index 0000000..23c67a0 --- /dev/null +++ b/src/main/kotlin/moe/nea/notenoughupdates/events/ParticleSpawnEvent.kt @@ -0,0 +1,13 @@ +package moe.nea.notenoughupdates.events + +import net.minecraft.particle.ParticleEffect +import net.minecraft.util.math.Vec3d + +data class ParticleSpawnEvent( + val particleEffect: ParticleEffect, + val position: Vec3d, + val offset: Vec3d, + val longDistance: Boolean, +) : NEUEvent() { + companion object : NEUEventBus<ParticleSpawnEvent>() +} diff --git a/src/main/kotlin/moe/nea/notenoughupdates/features/FeatureManager.kt b/src/main/kotlin/moe/nea/notenoughupdates/features/FeatureManager.kt index bc9a916..2512992 100644 --- a/src/main/kotlin/moe/nea/notenoughupdates/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/notenoughupdates/features/FeatureManager.kt @@ -3,6 +3,7 @@ package moe.nea.notenoughupdates.features import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import moe.nea.notenoughupdates.NotEnoughUpdates +import moe.nea.notenoughupdates.features.fishing.FishingWarning import moe.nea.notenoughupdates.features.world.FairySouls import moe.nea.notenoughupdates.util.data.DataHolder @@ -24,6 +25,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature synchronized(this) { if (hasAutoloaded) return loadFeature(FairySouls) + loadFeature(FishingWarning) hasAutoloaded = true } } diff --git a/src/main/kotlin/moe/nea/notenoughupdates/features/fishing/FishingWarning.kt b/src/main/kotlin/moe/nea/notenoughupdates/features/fishing/FishingWarning.kt new file mode 100644 index 0000000..c44201c --- /dev/null +++ b/src/main/kotlin/moe/nea/notenoughupdates/features/fishing/FishingWarning.kt @@ -0,0 +1,117 @@ +package moe.nea.notenoughupdates.features.fishing + +import kotlin.math.abs +import kotlin.math.absoluteValue +import kotlin.math.acos +import kotlin.math.asin +import kotlin.math.atan2 +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.notenoughupdates.events.ParticleSpawnEvent +import moe.nea.notenoughupdates.events.WorldReadyEvent +import moe.nea.notenoughupdates.events.WorldRenderLastEvent +import moe.nea.notenoughupdates.features.NEUFeature +import moe.nea.notenoughupdates.util.MC +import moe.nea.notenoughupdates.util.TimeMark +import moe.nea.notenoughupdates.util.config.ManagedConfig +import moe.nea.notenoughupdates.util.render.RenderBlockContext.Companion.renderBlocks + +object FishingWarning : NEUFeature { + override val name: String + get() = "Fishing Warning" + 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(acos(xOffset / 0.04)) + var angleZ = Math.toDegrees(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 angleZ + dist / 2 + } + + private fun toDegrees(d: Double) = d * 180 / Math.PI + + fun isHookPossible(hook: FishingBobberEntity, particlePos: Vec3d, angle1: Double, angle2: Double): Boolean { + val dx = particlePos.x - hook.pos.x + val dz = particlePos.z - hook.pos.z + val dist = sqrt(dx * dx + dz * dz) + + if (dist < 0.2) return true + val tolerance = toDegrees(atan2(0.03125, dist)) * 1.5 + var angleToHook = toDegrees(atan2(dx, dz)) % 360 + if (angleToHook < 0) angleToHook += 360 + return areAnglesClose(angle1, angleToHook, tolerance) || areAnglesClose(angle2, angleToHook, tolerance) + } + + val recentParticles = mutableListOf<Pair<Vec3d, TimeMark>>() + + 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) + + 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 } + renderBlocks(it.matrices, it.camera) { + color(0f, 0f, 1f, 1f) + recentParticles.forEach { + tinyBlock(it.first, 0.1F) + } + } + } + } +} diff --git a/src/main/kotlin/moe/nea/notenoughupdates/features/world/FairySouls.kt b/src/main/kotlin/moe/nea/notenoughupdates/features/world/FairySouls.kt index 2a5219a..ae045c0 100644 --- a/src/main/kotlin/moe/nea/notenoughupdates/features/world/FairySouls.kt +++ b/src/main/kotlin/moe/nea/notenoughupdates/features/world/FairySouls.kt @@ -31,7 +31,7 @@ object FairySouls : NEUFeature { object DConfig : ProfileSpecificDataHolder<Data>(serializer(), "found-fairysouls", ::Data) - object TConfig : ManagedConfig("fairysouls") { + object TConfig : ManagedConfig("fairy-souls") { val displaySouls by toggle("show") { false } val resetSouls by button("reset") { diff --git a/src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt b/src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt index 36aa726..956a330 100644 --- a/src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt +++ b/src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt @@ -6,6 +6,7 @@ import net.minecraft.util.math.BlockPos object MC { inline val player get() = MinecraftClient.getInstance().player + inline val world get() = MinecraftClient.getInstance().world } val Coordinate.blockPos: BlockPos diff --git a/src/main/kotlin/moe/nea/notenoughupdates/util/TimeMark.kt b/src/main/kotlin/moe/nea/notenoughupdates/util/TimeMark.kt new file mode 100644 index 0000000..5196606 --- /dev/null +++ b/src/main/kotlin/moe/nea/notenoughupdates/util/TimeMark.kt @@ -0,0 +1,15 @@ +package moe.nea.notenoughupdates.util + +import kotlin.time.Duration +import kotlin.time.ExperimentalTime +import kotlin.time.TimeSource + +@OptIn(ExperimentalTime::class) +class TimeMark private constructor(private val timeMark: TimeSource.Monotonic.ValueTimeMark?) { + fun passedTime() = timeMark?.elapsedNow() ?: Duration.INFINITE + + companion object { + fun now() = TimeMark(TimeSource.Monotonic.markNow()) + fun farPast() = TimeMark(null) + } +} diff --git a/src/main/kotlin/moe/nea/notenoughupdates/util/render/block.kt b/src/main/kotlin/moe/nea/notenoughupdates/util/render/block.kt index 5fc70ef..0d6c63b 100644 --- a/src/main/kotlin/moe/nea/notenoughupdates/util/render/block.kt +++ b/src/main/kotlin/moe/nea/notenoughupdates/util/render/block.kt @@ -11,6 +11,7 @@ import net.minecraft.client.render.VertexFormat import net.minecraft.client.render.VertexFormats import net.minecraft.client.util.math.MatrixStack import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d class RenderBlockContext private constructor(private val tesselator: Tessellator, private val matrixStack: MatrixStack) { private val buffer = tesselator.buffer @@ -21,7 +22,16 @@ class RenderBlockContext private constructor(private val tesselator: Tessellator fun block(blockPos: BlockPos) { matrixStack.push() matrixStack.translate(blockPos.x.toFloat(), blockPos.y.toFloat(), blockPos.z.toFloat()) - RenderSystem.setShader(GameRenderer::getPositionColorProgram) + buildCube(matrixStack.peek().positionMatrix, buffer) + tesselator.draw() + matrixStack.pop() + } + + fun tinyBlock(vec3d: Vec3d, size: Float) { + matrixStack.push() + matrixStack.translate(vec3d.x, vec3d.y, vec3d.z) + matrixStack.scale(size, size, size) + matrixStack.translate(-.5, -.5, -.5) buildCube(matrixStack.peek().positionMatrix, buffer) tesselator.draw() matrixStack.pop() @@ -74,6 +84,7 @@ class RenderBlockContext private constructor(private val tesselator: Tessellator RenderSystem.disableDepthTest() RenderSystem.enableBlend() RenderSystem.defaultBlendFunc() + RenderSystem.setShader(GameRenderer::getPositionColorProgram) matrices.push() matrices.translate(-camera.pos.x, -camera.pos.y, -camera.pos.z) diff --git a/src/main/resources/assets/notenoughupdates/lang/en_us.json b/src/main/resources/assets/notenoughupdates/lang/en_us.json index e9d8ea5..e688eb7 100644 --- a/src/main/resources/assets/notenoughupdates/lang/en_us.json +++ b/src/main/resources/assets/notenoughupdates/lang/en_us.json @@ -19,6 +19,8 @@ "notenoughupdates.sbinfo.gametype": "Locraw Gametype: %s", "notenoughupdates.sbinfo.mode": "Locraw Mode: %s", "notenoughupdates.sbinfo.map": "Locraw Map: %s", - "neu.config.fairysouls.show": "Show Fairy Soul Waypoints", - "neu.config.fairysouls.reset": "Reset Collected Fairy Souls" + "neu.config.fairy-souls.show": "Show Fairy Soul Waypoints", + "neu.config.fairy-souls.reset": "Reset Collected Fairy Souls", + "neu.config.fishing-warning.display-warning": "Display a warning when you are about to hook a fish", + "neu.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles" } diff --git a/src/main/resources/notenoughupdates.mixins.json b/src/main/resources/notenoughupdates.mixins.json index ce2317a..2b3a4e2 100644 --- a/src/main/resources/notenoughupdates.mixins.json +++ b/src/main/resources/notenoughupdates.mixins.json @@ -12,7 +12,9 @@ "devenv.DisableCommonPacketWarnings" ], "mixins": [ - "devenv.DisableInvalidFishingHook" + "MixinClientPacketHandler", + "devenv.DisableInvalidFishingHook", + "devenv.MixinScoreboard" ], "injectors": { "defaultRequire": 1 diff --git a/src/test/kotlin/AngleTest.kt b/src/test/kotlin/AngleTest.kt new file mode 100644 index 0000000..4be6086 --- /dev/null +++ b/src/test/kotlin/AngleTest.kt @@ -0,0 +1,28 @@ +import kotlin.math.atan + +private fun calculateAngleFromOffsets(xOffset: Double, zOffset: Double): Double { + var angleX = Math.toDegrees(Math.acos(xOffset / 0.04f)) + var angleZ = Math.toDegrees(Math.asin(zOffset / 0.04f)) + if (xOffset < 0) { + 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 angleZ + dist / 2 +} + +fun main() { + for(i in 0..10) { + for (j in 0..10) { + println("${calculateAngleFromOffsets(i.toDouble(),j.toDouble())} ${atan(i.toDouble() / j.toDouble())}") + } + } +} |