aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/garden
diff options
context:
space:
mode:
authorILike2WatchMemes <ilike2watchmemes@gmail.com>2024-03-10 20:48:46 +0100
committerGitHub <noreply@github.com>2024-03-10 20:48:46 +0100
commit504924b65b0b1cf6bc5deaf2a46e2162504d63bf (patch)
tree621119b07ab8c79d01f21d90f619074819fe0fb7 /src/main/java/at/hannibal2/skyhanni/features/garden
parent6b19f73c526fc4bbb196b7b547750ebe60feb76d (diff)
downloadskyhanni-504924b65b0b1cf6bc5deaf2a46e2162504d63bf.tar.gz
skyhanni-504924b65b0b1cf6bc5deaf2a46e2162504d63bf.tar.bz2
skyhanni-504924b65b0b1cf6bc5deaf2a46e2162504d63bf.zip
Feature: Lane Switching Notification (#1075)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/garden')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchNotification.kt126
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchUtils.kt104
4 files changed, 238 insertions, 4 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt
index cf9fbea6f..76e4cf872 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt
@@ -143,6 +143,8 @@ object GardenAPI {
fun inGarden() = IslandType.GARDEN.isInIsland()
+ fun isCurrentlyFarming() = inGarden() && GardenCropSpeed.averageBlocksPerSecond > 0.0
+
fun ItemStack.getCropType(): CropType? {
val internalName = getInternalName()
return CropType.entries.firstOrNull { internalName.startsWith(it.toolName) }
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 b6825bf43..cac76452e 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt
@@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.features.garden.pests.SprayType
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.isPlayerInside
import at.hannibal2.skyhanni.utils.LorenzVec
@@ -39,7 +40,7 @@ object GardenPlotAPI {
return plots.firstOrNull { it.isPlayerInside() }
}
- class Plot(val id: Int, var inventorySlot: Int, val box: AxisAlignedBB, val middle: LorenzVec)
+ class Plot(val id: Int, var unlocked: Boolean, var inventorySlot: Int, val box: AxisAlignedBB, val middle: LorenzVec)
class PlotData(
@Expose
@@ -133,7 +134,7 @@ object GardenPlotAPI {
val b = LorenzVec(maxX, 256.0, maxY)
val middle = a.interpolate(b, 0.5).copy(y = 10.0)
val box = a.axisAlignedTo(b).expand(0.0001, 0.0, 0.0001)
- list.add(Plot(id, slot, box, middle))
+ list.add(Plot(id, false, slot, box, middle))
slot++
}
slot += 4
@@ -162,8 +163,9 @@ object GardenPlotAPI {
if (event.inventoryName != "Configure Plots") return
for (plot in plots) {
- val itemName = event.inventoryItems[plot.inventorySlot]?.name ?: continue
- plotNamePattern.matchMatcher(itemName) {
+ val itemStack = event.inventoryItems[plot.inventorySlot] ?: continue
+ plot.unlocked = itemStack.getLore().all { !it.contains("§7Cost:") }
+ plotNamePattern.matchMatcher(itemStack.name) {
plot.name = group("name")
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchNotification.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchNotification.kt
new file mode 100644
index 000000000..ed1c5f074
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchNotification.kt
@@ -0,0 +1,126 @@
+package at.hannibal2.skyhanni.features.garden.farming
+
+import at.hannibal2.skyhanni.config.features.garden.laneswitch.LaneSwitchNotificationSettings
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.features.garden.GardenAPI
+import at.hannibal2.skyhanni.features.garden.GardenPlotAPI
+import at.hannibal2.skyhanni.features.garden.GardenPlotAPI.plots
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.LocationUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.round
+import at.hannibal2.skyhanni.utils.LorenzUtils.sendTitle
+import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.RenderUtils.renderString
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.SoundUtils.playSound
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.math.absoluteValue
+import kotlin.time.Duration.Companion.seconds
+
+class LaneSwitchNotification {
+
+ private val config get() = GardenAPI.config.laneswitch
+
+ private var bps = 0.0 // Blocks per Second
+ private var distancesUntilSwitch: List<Double> = listOf()
+ private var lastBps = 0.0 // Last blocks per Second
+ private var lastPosition = LorenzVec(0, 0, 0)
+ private var lastLaneSwitch = SimpleTimeMark.farPast()
+ private var lastWarning = SimpleTimeMark.farPast()
+ private var lastDistancesUntilSwitch: List<Double> = listOf()
+ private var lastDistance = 0.0
+
+ companion object {
+ private val config get() = GardenAPI.config.laneswitch
+
+ @JvmStatic
+ fun playUserSound() {
+ SoundUtils.createSound(
+ config.notification.sound.notificationSound,
+ config.notification.sound.notificationPitch,
+ ).playSound()
+ }
+ }
+
+ private fun switchPossibleInTime(from: LorenzVec, to: LorenzVec, speed: Double, time: Int): Boolean {
+ return from.distance(to) <= speed * time
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!isEnabled()) return
+ val settings = config.notification.settings
+ val plot = GardenPlotAPI.getCurrentPlot() ?: return
+ if (!plot.unlocked) return
+
+ val plotIndex = plots.indexOf(plot)
+ val positon = LocationUtils.playerLocation()
+ val farmEnd = LaneSwitchUtils.getFarmBounds(plotIndex, positon, lastPosition) ?: return
+ lastPosition = positon
+ bps = LocationUtils.distanceFromPreviousTick()
+ distancesUntilSwitch = farmEnd.map { end -> end.distance(positon).round(2) }
+
+ testForLaneSwitch(settings, farmEnd, positon)
+ lastBps = bps
+ }
+
+ private fun testForLaneSwitch(
+ settings: LaneSwitchNotificationSettings,
+ farmEnd: List<LorenzVec>,
+ positon: LorenzVec,
+ ) {
+ val farmLength = farmEnd[0].distance(farmEnd[1])
+ // farmLength / bps to get the time needed to travel the distance, - the threshold times the farm length divided by the length of 2 plots (to give some room)
+ val threshold = settings.threshold
+ // TODO find a name for this variable
+ val FIND_A_NAME_FOR_ME = threshold * (farmLength / 192)
+ val farmTraverseTime = ((farmLength / bps) - FIND_A_NAME_FOR_ME).seconds
+ val bpsDifference = (bps - lastBps).absoluteValue
+
+ if (farmEnd.isEmpty() || lastLaneSwitch.passedSince() < farmTraverseTime || bpsDifference > 20) return
+ if (!farmEnd.any { switchPossibleInTime(positon, it, bps, threshold) }) return
+
+ with(settings) {
+ sendTitle(color.getChatColor() + text, duration.seconds)
+ }
+ playUserSound()
+ lastLaneSwitch = SimpleTimeMark.now()
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!config.distanceUntilSwitch || !isEnabled()) return
+ if (distancesUntilSwitch.isEmpty()) return
+ if (lastDistancesUntilSwitch.isEmpty()) {
+ lastDistancesUntilSwitch = distancesUntilSwitch
+ }
+
+ val distances = listOf(
+ distancesUntilSwitch[0] - lastDistancesUntilSwitch[0],
+ distancesUntilSwitch[1] - lastDistancesUntilSwitch[1]
+ ) //Get changes in the distances
+ val distance = if (distances.all { it != 0.0 }) {
+ if (distances[0] > 0) distancesUntilSwitch[1] else distancesUntilSwitch[0] // get the direction the player is traveling and get the distance to display from that
+ } else {
+ lastDistance // display last value if no change is detected
+ }
+
+ config.distanceUntilSwitchPos.renderString("Distance until Switch: $distance", posLabel = "Movement Speed")
+ lastDistancesUntilSwitch = distancesUntilSwitch
+ lastDistance = distance
+ }
+
+ private fun plotsLoaded(): Boolean {
+ if (plots.any { it.unlocked }) return true
+
+ if (lastWarning.passedSince() >= 30.seconds) {
+ ChatUtils.clickableChat("§eOpen your configure plots for lane switch detection to work.", "/desk")
+ lastWarning = SimpleTimeMark.now()
+ }
+ return false
+ }
+
+ private fun isEnabled() = GardenAPI.isCurrentlyFarming() && config.enabled && plotsLoaded()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchUtils.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchUtils.kt
new file mode 100644
index 000000000..1bdff35de
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/LaneSwitchUtils.kt
@@ -0,0 +1,104 @@
+package at.hannibal2.skyhanni.features.garden.farming
+
+import at.hannibal2.skyhanni.features.garden.GardenPlotAPI
+import at.hannibal2.skyhanni.features.garden.GardenPlotAPI.isBarn
+import at.hannibal2.skyhanni.utils.LorenzUtils.round
+import at.hannibal2.skyhanni.utils.LorenzVec
+import kotlin.math.absoluteValue
+
+object LaneSwitchUtils {
+
+ enum class Direction {
+ WEST_EAST,
+ NORTH_SOUTH,
+ ;
+ }
+
+ enum class Value {
+ MIN,
+ MAX,
+ TOP,
+ BOTTOM,
+ ;
+ }
+
+ fun getFarmBounds(plotIndex: Int, current: LorenzVec, last: LorenzVec): List<LorenzVec>? {
+ if (GardenPlotAPI.plots[plotIndex].isBarn() || plotIndex == 12) return null
+ val xVelocity = current.x - last.x
+ val zVelocity = current.z - last.z
+ return if (xVelocity.absoluteValue > zVelocity.absoluteValue) {
+ var xValueMin = 0.0
+ var xValueMax = 0.0
+
+ for (i in 0..4) {
+ if (isBoundaryPlot(plotIndex - i, Direction.WEST_EAST, Value.MIN)) {
+ xValueMin = GardenPlotAPI.plots[plotIndex - i].box.minX; break
+ }
+ }
+ for (i in 0..4) {
+ if (isBoundaryPlot(plotIndex + i, Direction.WEST_EAST, Value.MAX)) {
+ xValueMax = GardenPlotAPI.plots[plotIndex + i].box.maxX; break
+ }
+ }
+
+ val a = LorenzVec(xValueMin, current.y, current.z)
+ val b = LorenzVec(xValueMax, current.y, current.z)
+ listOf(a, b)
+ } else if (xVelocity.absoluteValue < zVelocity.absoluteValue) {
+ // i * 5 because going vertically is always 5 plots before or after the current
+ var zValueTop = 0.0
+ var zValueBottom = 0.0
+
+ for (i in 0..4) {
+ if (isBoundaryPlot(plotIndex - (i * 5), Direction.NORTH_SOUTH, Value.TOP)) {
+ zValueTop = GardenPlotAPI.plots[plotIndex - (i * 5)].box.minZ; break
+ }
+ }
+ for (i in 0..4) {
+ if (isBoundaryPlot(plotIndex + (i * 5), Direction.NORTH_SOUTH, Value.BOTTOM)) {
+ zValueBottom = GardenPlotAPI.plots[plotIndex + (i * 5)].box.maxZ; break
+ }
+ }
+
+ val a = LorenzVec(current.x, current.y, zValueTop)
+ val b = LorenzVec(current.x, current.y, zValueBottom)
+ listOf(a, b)
+ } else null
+ }
+
+ private fun isBoundaryPlot(plotIndex: Int, direction: Direction, value: Value): Boolean {
+ if (direction == Direction.WEST_EAST) {
+ val isNextNewRow: Boolean
+ val isNextUnlocked: Boolean
+ val isNextBarn: Boolean
+ if (value == Value.MIN) {
+ if (plotIndex - 1 == -1) return true // check if next plot is out of bounds
+ //Check if the next plot's border is 240 and therefore in the previous row
+ isNextNewRow = GardenPlotAPI.plots[plotIndex - 1].box.maxX.absoluteValue.round(0) == 240.0
+ isNextUnlocked = GardenPlotAPI.plots[plotIndex - 1].unlocked
+ isNextBarn = GardenPlotAPI.plots[plotIndex - 1].isBarn()
+ } else {
+ if (plotIndex + 1 == 25) return true // check if next plot is out of bounds
+ isNextNewRow = (plotIndex + 1) % 5 == 0
+ isNextUnlocked = GardenPlotAPI.plots[plotIndex + 1].unlocked
+ isNextBarn = GardenPlotAPI.plots[plotIndex + 1].isBarn()
+ }
+ return isNextNewRow || !isNextUnlocked || isNextBarn
+ } else if (direction == Direction.NORTH_SOUTH) {
+ val isNextUnlocked: Boolean
+ val isNextBarn: Boolean
+
+ if (value == Value.MAX) {
+ if (plotIndex - 1 == -1 || (plotIndex - 5) < 0) return true // check if next plot is out of bounds
+ isNextUnlocked = GardenPlotAPI.plots[plotIndex - 5].unlocked
+ isNextBarn = GardenPlotAPI.plots[plotIndex - 5].isBarn()
+ } else {
+ if (plotIndex + 5 > 24) return true // check if next plot is out of bounds
+ isNextUnlocked = GardenPlotAPI.plots[plotIndex + 5].unlocked
+ isNextBarn = GardenPlotAPI.plots[plotIndex + 5].isBarn()
+ }
+ return !isNextUnlocked || isNextBarn
+ }
+ return false
+ }
+}