aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornea <nea@nea.moe>2023-05-07 12:22:36 +0200
committernea <nea@nea.moe>2023-05-08 00:17:34 +0200
commit942dd629efbf12183b1c5f5a966c36d4b0647a6b (patch)
tree407e8d2cdcfefa77673c768e5126857a86ddb89a
parent229f724ef4a0a4cbc426f31e27f9a57e9b1307c9 (diff)
downloadFirmament-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
-rw-r--r--gradle/libs.versions.toml4
-rw-r--r--src/main/java/moe/nea/notenoughupdates/mixins/MixinClientPacketHandler.java23
-rw-r--r--src/main/java/moe/nea/notenoughupdates/mixins/devenv/MixinScoreboard.java16
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/events/ParticleSpawnEvent.kt13
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/features/FeatureManager.kt2
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/features/fishing/FishingWarning.kt117
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/features/world/FairySouls.kt2
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/util/MC.kt1
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/util/TimeMark.kt15
-rw-r--r--src/main/kotlin/moe/nea/notenoughupdates/util/render/block.kt13
-rw-r--r--src/main/resources/assets/notenoughupdates/lang/en_us.json6
-rw-r--r--src/main/resources/notenoughupdates.mixins.json4
-rw-r--r--src/test/kotlin/AngleTest.kt28
13 files changed, 238 insertions, 6 deletions
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 934f853..3c87247 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -12,6 +12,7 @@ ktor = "2.3.0"
dbus_java = "4.2.1"
architectury = "8.1.79"
neurepoparser = "0.0.1"
+qolify = "1.2.2-1.19.4"
[libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
@@ -30,11 +31,12 @@ architectury_fabric = { module = "dev.architectury:architectury-fabric", version
rei_fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
devauth = { module = "me.djtheredstoner:DevAuth-fabric", version.ref = "devauth" }
modmenu = { module = "maven.modrinth:modmenu", version.ref = "modmenu" }
+qolify = { module = "maven.modrinth:qolify", version.ref = "qolify" }
[bundles]
dbus = ["dbus_java_core", "dbus_java_unixsocket"]
runtime_required = ["architectury_fabric", "rei_fabric"]
-runtime_optional = ["devauth", "modmenu"]
+runtime_optional = ["devauth", "modmenu", "qolify"]
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())}")
+ }
+ }
+}