diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 45c91de91bfa..c1daf704b9a7 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -204,6 +204,7 @@ import at.hannibal2.skyhanni.features.garden.inventory.plots.GardenPlotIcon import at.hannibal2.skyhanni.features.garden.inventory.plots.GardenPlotMenuHighlighting import at.hannibal2.skyhanni.features.garden.pests.PestAPI import at.hannibal2.skyhanni.features.garden.pests.PestFinder +import at.hannibal2.skyhanni.features.garden.pests.PestParticleWaypoint import at.hannibal2.skyhanni.features.garden.pests.PestSpawn import at.hannibal2.skyhanni.features.garden.pests.PestSpawnTimer import at.hannibal2.skyhanni.features.garden.pests.SprayDisplay @@ -781,6 +782,7 @@ class SkyHanniMod { loadModule(PestSpawn()) loadModule(PestSpawnTimer) loadModule(PestFinder()) + loadModule(PestParticleWaypoint()) loadModule(SprayFeatures()) loadModule(DojoRankDisplay()) loadModule(SprayDisplay()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java new file mode 100644 index 000000000000..59c54ec7fe07 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java @@ -0,0 +1,43 @@ +package at.hannibal2.skyhanni.config.features.garden.pests; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import com.google.gson.annotations.Expose; +import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean; +import io.github.moulberry.moulconfig.annotations.ConfigEditorSlider; +import io.github.moulberry.moulconfig.annotations.ConfigOption; + +public class PestWaypointConfig { + + @Expose + @ConfigOption( + name = "Enabled", + desc = "Show a waypoint of the next pest when using a vacuum. Uses the particles and math to detect the location from everywhere in the garden." + ) + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = true; + + @Expose + @ConfigOption( + name = "Hide Particles", + desc = "Hide the particles of the ability." + ) + @ConfigEditorBoolean + public boolean hideParticles = true; + + @Expose + @ConfigOption( + name = "Draw Line", + desc = "Draw a line to the waypoint." + ) + @ConfigEditorBoolean + public boolean drawLine = true; + + @Expose + @ConfigOption( + name = "Show For Seconds", + desc = "The waypoint will disappear after this number of seconds." + ) + @ConfigEditorSlider(minValue = 5, maxValue = 20, minStep = 1) + public int showForSeconds = 15; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestsConfig.java index 94e972dfe57f..95c476da8361 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestsConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestsConfig.java @@ -16,6 +16,11 @@ public class PestsConfig { @Accordion public PestFinderConfig pestFinder = new PestFinderConfig(); + @Expose + @ConfigOption(name = "Pest Waypoint", desc = "") + @Accordion + public PestWaypointConfig pestWaypoint = new PestWaypointConfig(); + @Expose @ConfigOption(name = "Pest Timer", desc = "") @Accordion diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonCleanEnd.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonCleanEnd.kt index 7e436eb3678c..c65e34964640 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonCleanEnd.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonCleanEnd.kt @@ -109,7 +109,7 @@ class DungeonCleanEnd { } @SubscribeEvent - fun onPlayParticle(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { if (shouldBlock()) { event.isCanceled = true } diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt index 11a114a7caec..ef82d2d06c77 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt @@ -65,7 +65,7 @@ class TotemOfCorruption { } @SubscribeEvent - fun onChatPacket(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { if (!isHideParticlesEnabled()) return for (totem in totems) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt index bab4cb3a8bcd..351613d90bf8 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt @@ -8,6 +8,7 @@ import at.hannibal2.skyhanni.features.misc.LockMouseLook import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LocationUtils.isInside import at.hannibal2.skyhanni.utils.LocationUtils.isPlayerInside import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine @@ -177,6 +178,8 @@ object GardenPlotAPI { fun Plot.isPlayerInside() = box.isPlayerInside() + fun closestCenterPlot(location: LorenzVec) = plots.find {it.box.isInside(location)}?.middle + fun Plot.sendTeleportTo() { if (isBarn()) ChatUtils.sendCommandToServer("tptoplot barn") else ChatUtils.sendCommandToServer("tptoplot $name") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt index 9b1a73a05494..c7c827b5b0e3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt @@ -47,7 +47,7 @@ class PestFinder { } private fun drawDisplay() = buildList { - add(Renderable.string("§6Total pests in garden: §e${PestAPI.scoreboardPests}§6/§e8")) + add(Renderable.string("§6Total pests: §e${PestAPI.scoreboardPests}§6/§e8")) for (plot in PestAPI.getInfestedPlots()) { val pests = plot.pests diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt new file mode 100644 index 000000000000..96a14c059a19 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt @@ -0,0 +1,162 @@ +package at.hannibal2.skyhanni.features.garden.pests + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.ClickType +import at.hannibal2.skyhanni.events.ItemClickEvent +import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.PacketEvent +import at.hannibal2.skyhanni.events.ReceiveParticleEvent +import at.hannibal2.skyhanni.features.garden.GardenAPI +import at.hannibal2.skyhanni.features.garden.GardenPlotAPI +import at.hannibal2.skyhanni.test.GriffinUtils.drawWaypointFilled +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer +import at.hannibal2.skyhanni.utils.LocationUtils.playerLocation +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine +import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText +import at.hannibal2.skyhanni.utils.RenderUtils.exactPlayerEyeLocation +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import net.minecraft.client.Minecraft +import net.minecraft.network.play.server.S0EPacketSpawnObject +import net.minecraft.util.EnumParticleTypes +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.math.absoluteValue +import kotlin.time.Duration.Companion.seconds + +class PestParticleWaypoint { + + private val config get() = SkyHanniMod.feature.garden.pests.pestWaypoint + + private var lastPestTrackerUse = SimpleTimeMark.farPast() + + private var firstParticlePoint: LorenzVec? = null + private var secondParticlePoint: LorenzVec? = null + private var lastParticlePoint: LorenzVec? = null + private var guessPoint: LorenzVec? = null + private var locations = mutableListOf() + private var particles = 0 + private var lastParticles = 0 + + @SubscribeEvent + fun onItemClick(event: ItemClickEvent) { + if (!isEnabled()) return + if (PestAPI.hasVacuumInHand()) { + if (event.clickType == ClickType.LEFT_CLICK && !Minecraft.getMinecraft().thePlayer.isSneaking) { + lastPestTrackerUse = SimpleTimeMark.now() + reset() + } + } + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + lastPestTrackerUse = SimpleTimeMark.farPast() + reset() + } + + private fun reset() { + locations.clear() + guessPoint = null + lastParticlePoint = null + firstParticlePoint = null + secondParticlePoint = null + particles = 0 + lastParticles = 0 + } + + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) + fun onReceiveParticle(event: ReceiveParticleEvent) { + if (!isEnabled()) return + if (event.type != EnumParticleTypes.REDSTONE) return + val location = event.location + if (config.hideParticles) event.cancel() + if (lastPestTrackerUse.passedSince() > 3.seconds) return + + if (particles > 5) return + if (firstParticlePoint == null) { + if (playerLocation().distance(location) > 5) return + firstParticlePoint = location + } else if (secondParticlePoint == null) { + secondParticlePoint = location + lastParticlePoint = location + locations.add(location) + } else { + val firstDistance = secondParticlePoint?.let { firstParticlePoint?.distance(it) } ?: return + val distance = lastParticlePoint?.distance(location) ?: return + if ((distance - firstDistance).absoluteValue > 0.1) return + lastParticlePoint = location + locations.add(location) + } + ++particles + } + + @SubscribeEvent + fun onFireWorkSpawn(event: PacketEvent.ReceiveEvent) { + if (event.packet !is S0EPacketSpawnObject) return + if (!GardenAPI.inGarden() || !config.hideParticles) return + val fireworkId = 76 + if (event.packet.type == fireworkId) event.cancel() + } + + @SubscribeEvent + fun onRenderWorld(event: LorenzRenderWorldEvent) { + if (!isEnabled()) return + if (locations.isEmpty()) return + if (lastPestTrackerUse.passedSince() > config.showForSeconds.seconds) { + reset() + return + } + + val waypoint = getWaypoint() ?: return + val distance = GardenPlotAPI.closestCenterPlot(waypoint)?.distanceIgnoreY(waypoint) ?: return + val isCloseToPlotCenter = distance < 4 + + val text = if (isCloseToPlotCenter) "§cInfected Plot Guess" else "§aPest Guess" + val color = if (isCloseToPlotCenter) LorenzColor.RED else LorenzColor.GREEN + + event.drawWaypointFilled(waypoint, color.toColor(), beacon = true) + event.drawDynamicText(waypoint, text, 1.3) + if (config.drawLine) event.draw3DLine( + event.exactPlayerEyeLocation(), + waypoint, + LorenzColor.AQUA.toColor(), + 3, + false + ) + } + + private fun getWaypoint() = if (lastParticles != particles || guessPoint == null) { + calculateWaypoint(locations)?.also { + guessPoint = it + lastParticles = particles + } + } else guessPoint + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) return + if (lastPestTrackerUse.passedSince() !in 1.seconds..config.showForSeconds.seconds) return + val guessPoint = guessPoint ?: return + if (guessPoint.distanceToPlayer() > 8.0) return + + lastPestTrackerUse = SimpleTimeMark.farPast() + reset() + } + + private fun calculateWaypoint(list: MutableList): LorenzVec? { + val firstParticle = firstParticlePoint ?: return null + + var pos = LorenzVec(0.0, 0.0, 0.0) + for ((i, particle) in list.withIndex()) { + pos = pos.add(particle.subtract(firstParticle).divide(i.toDouble() + 1.0)) + } + return firstParticle.add(pos.multiply(120.0 / list.size)) + } + + fun isEnabled() = GardenAPI.inGarden() && config.enabled + +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/FireVeilWandParticles.kt b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/FireVeilWandParticles.kt index aa3c407407ba..b5e7fc313e6f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/FireVeilWandParticles.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/FireVeilWandParticles.kt @@ -28,7 +28,7 @@ class FireVeilWandParticles { private var lastClick = SimpleTimeMark.farPast() @SubscribeEvent - fun onChatPacket(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { if (!LorenzUtils.inSkyBlock) return if (config.display == DisplayEntry.PARTICLES) return if (lastClick.passedSince() > 5.5.seconds) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/MiscFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/MiscFeatures.kt index 32d412d85358..2dac01bb2986 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/MiscFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/MiscFeatures.kt @@ -24,7 +24,7 @@ class MiscFeatures { } @SubscribeEvent - fun onHypExplosions(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { if (!LorenzUtils.inSkyBlock) return if (!SkyHanniMod.feature.misc.hideExplosions) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ParticleHider.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ParticleHider.kt index 43bd181ddc57..3101ec039969 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/ParticleHider.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ParticleHider.kt @@ -16,7 +16,7 @@ class ParticleHider { private fun inM7Boss() = LorenzUtils.inDungeons && DungeonAPI.dungeonFloor == "M7" && DungeonAPI.inBossRoom @SubscribeEvent - fun onHypExplosions(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { val distanceToPlayer = event.distanceToPlayer if (SkyHanniMod.feature.misc.particleHiders.hideFarParticles && distanceToPlayer > 40 && !inM7Boss()) { event.isCanceled = true diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/VampireSlayerFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/VampireSlayerFeatures.kt index 011f181ce53f..77c616cd6cd0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/slayer/VampireSlayerFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/VampireSlayerFeatures.kt @@ -358,7 +358,7 @@ object VampireSlayerFeatures { } @SubscribeEvent - fun onParticle(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { if (!isEnabled()) return val loc = event.location EntityUtils.getEntitiesNearby(loc, 3.0).forEach { diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index cd5d9bf0b122..7e31bdb3dd6f 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -500,7 +500,7 @@ class SkyHanniDebugsAndTests { } @SubscribeEvent - fun onParticlePlay(event: ReceiveParticleEvent) { + fun onReceiveParticle(event: ReceiveParticleEvent) { // val particleType = event.type // val distance = LocationUtils.playerLocation().distance(event.location).round(2) // diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index ad85619b1a9f..21aff40d308e 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -60,6 +60,8 @@ data class LorenzVec( fun multiply(d: Int): LorenzVec = LorenzVec(x multiplyZeroSave d.toDouble(), y multiplyZeroSave d.toDouble(), z multiplyZeroSave d.toDouble()) + fun divide(d : Double) = multiply(1.0/d) + fun add(other: LorenzVec) = LorenzVec(x + other.x, y + other.y, z + other.z) fun subtract(other: LorenzVec) = LorenzVec(x - other.x, y - other.y, z - other.z)