aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmpa <42304516+ItsEmpa@users.noreply.github.com>2024-09-10 23:23:42 +0200
committerGitHub <noreply@github.com>2024-09-10 23:23:42 +0200
commitd6c9b63136cae3a8abf7d4ce91424dfc648dc59c (patch)
treec8e0df4cfeabe04c711b00e32df7ef1c8eab6549
parent9acb6e88fb868ce9d591fb98cfa80730681653f6 (diff)
downloadskyhanni-d6c9b63136cae3a8abf7d4ce91424dfc648dc59c.tar.gz
skyhanni-d6c9b63136cae3a8abf7d4ce91424dfc648dc59c.tar.bz2
skyhanni-d6c9b63136cae3a8abf7d4ce91424dfc648dc59c.zip
Feature: Golden Fish Timer (#1941)
Co-authored-by: Cal <cwolfson58@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: ItsEmpa <itsempa@users.noreply.github.com>
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/GoldenFishTimerConfig.java47
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/TrophyFishingConfig.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/FishingBobberInLiquidEvent.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/FishingBobberInWaterEvent.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/FishingBaitWarnings.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/IsFishingDetection.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/GoldenFishTimer.kt343
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt7
11 files changed, 437 insertions, 28 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/GoldenFishTimerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/GoldenFishTimerConfig.java
new file mode 100644
index 000000000..ca803483b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/GoldenFishTimerConfig.java
@@ -0,0 +1,47 @@
+package at.hannibal2.skyhanni.config.features.fishing.trophyfishing;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class GoldenFishTimerConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Enable the Golden Fish Timer.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Nametag", desc = "Show a nametag on the Golden Fish showing how weak it is and when it will despawn.")
+ @ConfigEditorBoolean
+ public boolean nametag = true;
+
+ @Expose
+ @ConfigOption(name = "Highlight when ready", desc = "Highlight the Golden Fish when it is ready to be caught.")
+ @ConfigEditorBoolean
+ public boolean highlight = true;
+
+ @Expose
+ @ConfigOption(name = "Throw Rod Warning", desc = "Show a warning when you are close to the time limit of throwing your rod.")
+ @ConfigEditorBoolean
+ public boolean throwRodWarning = false;
+
+ @Expose
+ @ConfigOption(name = "Show Head", desc = "Show the Golden Fish head in the Golden Fish Timer GUI.")
+ @ConfigEditorBoolean
+ public boolean showHead = true;
+
+ @Expose
+ @ConfigOption(name = "Throw Rod Warning Time", desc = "The time in seconds before the throw rod warning appears.")
+ @ConfigEditorSlider(minValue = 1, maxValue = 60, minStep = 1)
+ public int throwRodWarningTime = 20;
+
+ @Expose
+ @ConfigLink(owner = GoldenFishTimerConfig.class, field = "enabled")
+ public Position position = new Position(50, 80);
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/TrophyFishingConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/TrophyFishingConfig.java
index d6b5effcb..a8df74950 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/TrophyFishingConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/TrophyFishingConfig.java
@@ -30,6 +30,11 @@ public class TrophyFishingConfig {
public SulphurSkitterBoxConfig sulphurSkitterBox = new SulphurSkitterBoxConfig();
@Expose
+ @ConfigOption(name = "Golden Fish Timer", desc = "")
+ @Accordion
+ public GoldenFishTimerConfig goldenFishTimer = new GoldenFishTimerConfig();
+
+ @Expose
@ConfigOption(name = "Fillet Tooltip", desc = "Show fillet value of Trophy Fish in tooltip.")
@ConfigEditorBoolean
@FeatureToggle
diff --git a/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInLiquidEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInLiquidEvent.kt
new file mode 100644
index 000000000..a2cf6d270
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInLiquidEvent.kt
@@ -0,0 +1,5 @@
+package at.hannibal2.skyhanni.events
+
+import net.minecraft.entity.projectile.EntityFishHook
+
+class FishingBobberInLiquidEvent(val bobber: EntityFishHook, val onWater: Boolean) : LorenzEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInWaterEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInWaterEvent.kt
deleted file mode 100644
index 9423ea2d9..000000000
--- a/src/main/java/at/hannibal2/skyhanni/events/FishingBobberInWaterEvent.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package at.hannibal2.skyhanni.events
-
-class FishingBobberInWaterEvent : LorenzEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
index 800f01cfe..596d6f49c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
@@ -3,7 +3,7 @@ package at.hannibal2.skyhanni.features.fishing
import at.hannibal2.skyhanni.api.event.HandleEvent
import at.hannibal2.skyhanni.data.jsonobjects.repo.ItemsJson
import at.hannibal2.skyhanni.events.FishingBobberCastEvent
-import at.hannibal2.skyhanni.events.FishingBobberInWaterEvent
+import at.hannibal2.skyhanni.events.FishingBobberInLiquidEvent
import at.hannibal2.skyhanni.events.ItemInHandChangeEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
@@ -13,7 +13,6 @@ import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager
import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager.getFilletValue
import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemCategory
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
@@ -21,8 +20,8 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzVec
import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.SimpleTimeMark
-import at.hannibal2.skyhanni.utils.StringUtils.matches
import at.hannibal2.skyhanni.utils.getLorenzVec
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.client.Minecraft
@@ -52,7 +51,7 @@ object FishingAPI {
private var waterRods = listOf<NEUInternalName>()
var bobber: EntityFishHook? = null
- var bobberHasTouchedWater = false
+ var bobberHasTouchedLiquid = false
var wearingTrophyArmor = false
@@ -63,13 +62,13 @@ object FishingAPI {
lastCastTime = SimpleTimeMark.now()
bobber = event.entity
- bobberHasTouchedWater = false
+ bobberHasTouchedLiquid = false
FishingBobberCastEvent(event.entity).postAndCatch()
}
private fun resetBobber() {
bobber = null
- bobberHasTouchedWater = false
+ bobberHasTouchedLiquid = false
}
@SubscribeEvent
@@ -89,12 +88,15 @@ object FishingAPI {
if (bobber.isDead) {
resetBobber()
} else {
- if (!bobberHasTouchedWater) {
- val block = bobber.getLorenzVec().getBlockAt()
- if (block in getAllowedBlocks()) {
- bobberHasTouchedWater = true
- FishingBobberInWaterEvent().postAndCatch()
+ if (!bobberHasTouchedLiquid) {
+ val isWater = when {
+ bobber.isInLava && holdingLavaRod -> false
+ bobber.isInWater && holdingWaterRod -> true
+ else -> return
}
+
+ bobberHasTouchedLiquid = true
+ FishingBobberInLiquidEvent(bobber, isWater).postAndCatch()
}
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingBaitWarnings.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingBaitWarnings.kt
index 6773f1341..7d11f91fd 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingBaitWarnings.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingBaitWarnings.kt
@@ -2,7 +2,7 @@ package at.hannibal2.skyhanni.features.fishing
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.api.event.HandleEvent
-import at.hannibal2.skyhanni.events.FishingBobberInWaterEvent
+import at.hannibal2.skyhanni.events.FishingBobberInLiquidEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.entity.EntityEnterWorldEvent
import at.hannibal2.skyhanni.features.fishing.FishingAPI.isBait
@@ -48,7 +48,7 @@ object FishingBaitWarnings {
}
@SubscribeEvent
- fun onBobberInWater(event: FishingBobberInWaterEvent) {
+ fun onBobber(event: FishingBobberInLiquidEvent) {
if (!isEnabled()) return
DelayedRun.runDelayed(500.milliseconds) {
checkBait()
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/IsFishingDetection.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/IsFishingDetection.kt
index 74bc567c1..4bd6d170c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/IsFishingDetection.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/IsFishingDetection.kt
@@ -1,6 +1,6 @@
package at.hannibal2.skyhanni.features.fishing
-import at.hannibal2.skyhanni.events.FishingBobberInWaterEvent
+import at.hannibal2.skyhanni.events.FishingBobberInLiquidEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.EntityUtils
@@ -26,7 +26,7 @@ object IsFishingDetection {
private var lastSeaCreatureKillAreaTime = SimpleTimeMark.farPast()
@SubscribeEvent
- fun onBobberInWater(event: FishingBobberInWaterEvent) {
+ fun onBobber(event: FishingBobberInLiquidEvent) {
lastRodCastLocation = LocationUtils.playerLocation()
lastRodCastTime = SimpleTimeMark.now()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/GoldenFishTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/GoldenFishTimer.kt
new file mode 100644
index 000000000..7a0e2d65f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/GoldenFishTimer.kt
@@ -0,0 +1,343 @@
+package at.hannibal2.skyhanni.features.fishing.trophy
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.data.TitleManager
+import at.hannibal2.skyhanni.events.DebugDataCollectEvent
+import at.hannibal2.skyhanni.events.EntityMaxHealthUpdateEvent
+import at.hannibal2.skyhanni.events.FishingBobberCastEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.events.SecondPassedEvent
+import at.hannibal2.skyhanni.features.fishing.FishingAPI
+import at.hannibal2.skyhanni.features.fishing.FishingAPI.isLavaRod
+import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ColorUtils.withAlpha
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.getSkullTexture
+import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.RenderUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.drawString
+import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.TimeLimitedSet
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.getLorenzVec
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraft.entity.EntityLivingBase
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
+
+@SkyHanniModule
+object GoldenFishTimer {
+
+ private val config get() = SkyHanniMod.feature.fishing.trophyFishing.goldenFishTimer
+
+ private val patternGroup = RepoPattern.group("fishing.goldenfish")
+ private val spawnPattern by patternGroup.pattern(
+ "spawn",
+ "§9You spot a §r§6Golden Fish §r§9surface from beneath the lava!",
+ )
+ private val interactPattern by patternGroup.pattern(
+ "interact",
+ "§9The §r§6Golden Fish §r§9escapes your hook but looks weakened\\.",
+ )
+ private val weakPattern by patternGroup.pattern(
+ "weak",
+ "§9The §r§6Golden Fish §r§9is weak!",
+ )
+ private val despawnPattern by patternGroup.pattern(
+ "despawn",
+ "§9The §r§6Golden Fish §r§9swims back beneath the lava\\.\\.\\.",
+ )
+
+ private val timeOut = 10.seconds
+ private val despawnTime = 1.minutes
+ private val maxRodTime = 3.minutes
+ private val minimumSpawnTime = 15.minutes
+ private const val MAX_INTERACTIONS = 3
+
+ private var lastFishEntity = SimpleTimeMark.farPast()
+ private var lastChatMessage = SimpleTimeMark.farPast()
+
+ private var lastGoldenFishTime = SimpleTimeMark.farPast()
+
+ private var lastRodThrowTime = SimpleTimeMark.farPast()
+ private var goldenFishDespawnTimer = SimpleTimeMark.farFuture()
+ private var timePossibleSpawn = SimpleTimeMark.farFuture()
+
+ private val isFishing get() = FishingAPI.isFishing() || lastRodThrowTime.passedSince() < maxRodTime
+ private var hasLavaRodInInventory = false
+
+ private fun checkGoldenFish(entity: EntityLivingBase) {
+ if (entity.inventory.none { it?.getSkullTexture() == GOLDEN_FISH_SKULL_TEXTURE }) return
+ possibleGoldenFishEntity = entity
+ lastFishEntity = SimpleTimeMark.now()
+ handle()
+ }
+
+ private const val GOLDEN_FISH_SKULL_TEXTURE =
+ "ewogICJ0aW1lc3RhbXAiIDogMTY0MzgzMTA2MDE5OCwKICAicHJvZmlsZUlkIiA6ICJiN2ZkYmU2N2NkMDA0NjgzYjlmYTllM2UxNzczODI1NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJDVUNGTDE0IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzEyMGNmM2MwYTQwZmM2N2UwZTVmZTBjNDZiMGFlNDA5YWM3MTAzMGE3NjU2ZGExN2IxMWVkMDAxNjQ1ODg4ZmUiCiAgICB9CiAgfQp9"
+ private val goldenFishSkullItem by lazy {
+ ItemUtils.createSkull(
+ displayName = "§6Golden Fish",
+ uuid = "b7fdbe67cd004683b9fa9e3e17738254",
+ value = GOLDEN_FISH_SKULL_TEXTURE,
+ )
+ }
+ private var interactions = 0
+ private var goingDownInit = true
+ private var goingDownPost = false
+ private var hasWarnedRod = false
+
+ private var possibleGoldenFishEntity: EntityLivingBase? = null
+ private var confirmedGoldenFishEntity: EntityLivingBase? = null
+
+ private var display = listOf<Renderable>()
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isActive()) return
+ if (spawnPattern.matches(event.message)) {
+ lastChatMessage = SimpleTimeMark.now()
+ handle()
+ return
+ }
+ if (interactPattern.matches(event.message)) {
+ goldenFishDespawnTimer = SimpleTimeMark.now() + despawnTime
+ interactions++
+ return
+ }
+ if (weakPattern.matches(event.message)) {
+ goldenFishDespawnTimer = SimpleTimeMark.now() + despawnTime
+ val entity = confirmedGoldenFishEntity ?: return
+ if (config.highlight) RenderLivingEntityHelper.setEntityColorWithNoHurtTime(
+ entity,
+ LorenzColor.GREEN.toColor().withAlpha(100)
+ ) { true }
+ return
+ }
+ if (despawnPattern.matches(event.message)) {
+ timePossibleSpawn = SimpleTimeMark.now() + minimumSpawnTime
+ removeGoldenFish()
+ return
+ }
+ TrophyFishMessages.trophyFishPattern.matchMatcher(event.message) {
+ val internalName = TrophyFishMessages.getInternalName(group("displayName"))
+ if (internalName != "goldenfish") return@matchMatcher
+ timePossibleSpawn = SimpleTimeMark.now() + minimumSpawnTime
+ removeGoldenFish()
+ return
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: LorenzRenderWorldEvent) {
+ if (!isActive()) return
+ if (!config.nametag) return
+ val entity = confirmedGoldenFishEntity ?: return
+
+ val location = event.exactLocation(entity).add(y = 2.5)
+ if (location.distanceToPlayer() > 20) return
+ event.drawString(location.add(y = 0.5), "§b${(goldenFishDespawnTimer + 1.seconds).timeUntil().format()}", false)
+ if (interactions >= MAX_INTERACTIONS) event.drawString(location.add(y = 0.25), "§cPULL", false)
+ event.drawString(location, "§6Golden Fish §a($interactions/$MAX_INTERACTIONS)", false)
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isActive()) return
+ val list = display.takeIf { it.isNotEmpty() } ?: return
+ val renderable = Renderable.horizontalContainer(list, verticalAlign = RenderUtils.VerticalAlignment.CENTER)
+ config.position.renderRenderables(listOf(renderable), posLabel = "Golden Fish Timer")
+ }
+
+ private fun updateDisplay() {
+ display = drawDisplay()
+ }
+
+ private fun drawDisplay() = buildList {
+ if (config.showHead) add(
+ Renderable.itemStack(
+ goldenFishSkullItem,
+ 2.5,
+ verticalAlign = RenderUtils.VerticalAlignment.CENTER,
+ ),
+ )
+ val text = buildList {
+ add("§6§lGolden Fish Timer")
+ if (!isGoldenFishActive()) {
+ if (lastGoldenFishTime.isFarPast()) add("§7Last Golden Fish: §cNone this session")
+ else add("§7Last Golden Fish: §b${lastGoldenFishTime.passedSince().formatTime()}")
+ if (lastRodThrowTime.isFarPast()) add("§7Last Row Throw: §cNone yet")
+ else add(
+ "§7Last Rod Throw: §b${lastRodThrowTime.passedSince().formatTime()} " +
+ "§3(${(lastRodThrowTime + maxRodTime + 1.seconds).timeUntil().formatTime()})",
+ )
+ if (timePossibleSpawn.isFarFuture()) add("§7Can spawn in: §cUnknown")
+ else if (timePossibleSpawn.isInFuture()) add(
+ "§7Can spawn in: §b${
+ (timePossibleSpawn + 1.seconds).timeUntil().formatTime()
+ }",
+ )
+ else {
+ add("§7Can spawn since: §b${timePossibleSpawn.passedSince().formatTime()}")
+ val chance = timePossibleSpawn.passedSince().inWholeSeconds.toDouble() / 5.minutes.inWholeSeconds
+ add("§7Chance: §b${LorenzUtils.formatPercentage(chance.coerceAtMost(1.0))}")
+ }
+ } else {
+ add("§7Interactions: §b$interactions/$MAX_INTERACTIONS")
+ add("§7Despawn in: §b${(goldenFishDespawnTimer + 1.seconds).timeUntil().formatTime()}")
+ }
+ }
+
+ add(
+ Renderable.verticalContainer(
+ text.map { Renderable.string(it) },
+ spacing = 1,
+ verticalAlign = RenderUtils.VerticalAlignment.CENTER,
+ ),
+ )
+ }
+
+ @SubscribeEvent
+ fun onSecondPassed(event: SecondPassedEvent) {
+ if (!isEnabled()) return
+ hasLavaRodInInventory = InventoryUtils.containsInLowerInventory { it.getInternalNameOrNull()?.isLavaRod() == true }
+
+ if (!isActive()) return
+
+ if (lastRodThrowTime.passedSince() > maxRodTime) {
+ timePossibleSpawn = SimpleTimeMark.farFuture()
+ lastRodThrowTime = SimpleTimeMark.farPast()
+ }
+ if (!lastRodThrowTime.isFarPast() && (lastRodThrowTime + maxRodTime).timeUntil() < config.throwRodWarningTime.seconds) {
+ rodWarning()
+ }
+
+ updateDisplay()
+ }
+
+ private fun rodWarning() {
+ if (!config.throwRodWarning || hasWarnedRod) return
+ hasWarnedRod = true
+ TitleManager.sendTitle("§cThrow your rod!", 5.seconds, 3.6, 7.0f)
+ SoundUtils.repeatSound(100, 10, SoundUtils.plingSound)
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!isActive()) return
+ // This makes it only count as the rod being throw into lava if the rod goes down, up, and down again.
+ // Not confirmed that this is correct, but it's the best solution found.
+ val bobber = FishingAPI.bobber ?: return
+ if (!bobber.isInLava || bobber.ticksExisted < 5) return
+ if (bobber.motionY > 0 && goingDownInit) goingDownInit = false
+ else if (bobber.motionY < 0 && !goingDownInit && !goingDownPost) {
+ hasWarnedRod = false
+ goingDownPost = true
+ lastRodThrowTime = SimpleTimeMark.now()
+ if (timePossibleSpawn.isFarFuture()) timePossibleSpawn = SimpleTimeMark.now() + minimumSpawnTime
+ }
+ }
+
+ @SubscribeEvent
+ fun onBobberThrow(event: FishingBobberCastEvent) {
+ if (!isActive()) return
+ goingDownInit = true
+ goingDownPost = false
+ }
+
+ @SubscribeEvent
+ fun onEntityHealthUpdate(event: EntityMaxHealthUpdateEvent) {
+ if (!isActive()) return
+ if (isGoldenFishActive()) return
+ val entity = event.entity as? EntityArmorStand ?: return
+ entity.inventory.forEach { it?.getSkullTexture()?.let { texture -> println(texture) } }
+
+ DelayedRun.runDelayed(1.seconds) { checkGoldenFish(entity) }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ lastChatMessage = SimpleTimeMark.farPast()
+ lastFishEntity = SimpleTimeMark.farPast()
+ lastGoldenFishTime = SimpleTimeMark.farPast()
+ possibleGoldenFishEntity = null
+ lastRodThrowTime = SimpleTimeMark.farPast()
+ timePossibleSpawn = SimpleTimeMark.farFuture()
+ interactions = 0
+ display = listOf()
+ removeGoldenFish()
+ }
+
+ @SubscribeEvent
+ fun onDebugData(event: DebugDataCollectEvent) {
+ event.title("Golden Fish Timer")
+ if (!isEnabled()) {
+ event.addIrrelevant("Not Enabled")
+ } else {
+ event.addIrrelevant {
+ add("lastChatMessage: ${lastChatMessage.passedSince().format()}")
+ add("lastFishEntity: ${lastFishEntity.passedSince().format()}")
+ add("lastGoldenFishTime: ${lastGoldenFishTime.passedSince().format()}")
+ add("lastRodThrowTime: ${lastRodThrowTime.passedSince().format()}")
+ add("goldenFishDespawnTimer: ${goldenFishDespawnTimer.timeUntil().format()}")
+ add("timePossibleSpawn: ${timePossibleSpawn.timeUntil().format()}")
+ add("interactions: $interactions")
+ add("goingDownInit: $goingDownInit")
+ add("goingDownPost: $goingDownPost")
+ add("hasWarnedRod: $hasWarnedRod")
+ add("possibleGoldenFishEntity: $possibleGoldenFishEntity")
+ add("confirmedGoldenFishEntity: $confirmedGoldenFishEntity")
+ }
+ }
+ }
+
+ private fun removeGoldenFish() {
+ goldenFishDespawnTimer = SimpleTimeMark.farFuture()
+ confirmedGoldenFishEntity?.let {
+ confirmedGoldenFishEntity = null
+ RenderLivingEntityHelper.removeEntityColor(it)
+ }
+ }
+
+ private fun handle() {
+ if (lastChatMessage.passedSince() > timeOut || lastFishEntity.passedSince() > timeOut) return
+ lastFishEntity = SimpleTimeMark.farPast()
+ lastChatMessage = SimpleTimeMark.farPast()
+ lastGoldenFishTime = SimpleTimeMark.now()
+ interactions = 0
+ ChatUtils.debug("Found Golden Fish!")
+ confirmedGoldenFishEntity = possibleGoldenFishEntity
+ possibleGoldenFishEntity = null
+ goldenFishDespawnTimer = SimpleTimeMark.now() + despawnTime
+ }
+
+ private fun Duration.formatTime() = format(showMilliSeconds = false, showSmallerUnits = true)
+
+ private fun isGoldenFishActive() = confirmedGoldenFishEntity != null
+
+ private fun isEnabled() = config.enabled && (IslandType.CRIMSON_ISLE.isInIsland() || LorenzUtils.isStrandedProfile)
+ private fun isActive() = isEnabled() && isFishing && hasLavaRodInInventory
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt
index 606f78041..7068ef8aa 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishMessages.kt
@@ -23,7 +23,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
object TrophyFishMessages {
private val config get() = SkyHanniMod.feature.fishing.trophyFishing.chatMessages
- private val trophyFishPattern by RepoPattern.pattern(
+ val trophyFishPattern by RepoPattern.pattern(
"fishing.trophy.trophyfish",
"§6§lTROPHY FISH! §r§bYou caught an? §r(?<displayName>§[0-9a-f](?:§k)?[\\w -]+) §r(?<displayRarity>§[0-9a-f]§l\\w+)§r§b\\."
)
@@ -31,16 +31,13 @@ object TrophyFishMessages {
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
if (!LorenzUtils.inSkyBlock) return
- var displayName = ""
- var displayRarity = ""
- trophyFishPattern.matchMatcher(event.message) {
- displayName = group("displayName").replace("§k", "")
- displayRarity = group("displayRarity")
+ val (displayName, displayRarity) = trophyFishPattern.matchMatcher(event.message) {
+ group("displayName").replace("§k", "") to
+ group("displayRarity")
} ?: return
- val internalName = displayName.replace("Obfuscated", "Obfuscated Fish")
- .replace("[- ]".toRegex(), "").lowercase().removeColor()
+ val internalName = getInternalName(displayName)
val rawRarity = displayRarity.lowercase().removeColor()
val rarity = TrophyRarity.getByName(rawRarity) ?: return
@@ -88,6 +85,11 @@ object TrophyFishMessages {
}
}
+ fun getInternalName(displayName: String): String {
+ return displayName.replace("Obfuscated", "Obfuscated Fish")
+ .replace("[- ]".toRegex(), "").lowercase().removeColor()
+ }
+
private fun shouldBlockTrophyFish(rarity: TrophyRarity, amount: Int) =
config.bronzeHider && rarity == TrophyRarity.BRONZE && amount != 1
|| config.silverHider && rarity == TrophyRarity.SILVER && amount != 1
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
index c8492086a..fd240b145 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
@@ -54,7 +54,10 @@ object InventoryUtils {
fun getItemsInHotbar() =
getItemsInOwnInventoryWithNull()?.sliceArray(0..8)?.filterNotNull() ?: emptyList()
- fun countItemsInLowerInventory(predicate: (ItemStack) -> Boolean) =
+ fun containsInLowerInventory(predicate: (ItemStack) -> Boolean): Boolean =
+ countItemsInLowerInventory(predicate) > 0
+
+ fun countItemsInLowerInventory(predicate: (ItemStack) -> Boolean): Int =
getItemsInOwnInventory().filter { predicate(it) }.sumOf { it.stackSize }
fun inStorage() = openInventoryName().let {
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt
index b31bb828b..5cf3ec2e5 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt
@@ -19,6 +19,7 @@ object TimeUtils {
showMilliSeconds: Boolean = this in -1.seconds..1.seconds,
longName: Boolean = false,
maxUnits: Int = -1,
+ showSmallerUnits: Boolean = false,
): String {
var millis = inWholeMilliseconds
val parts = mutableMapOf<TimeUnit, Int>()
@@ -31,10 +32,14 @@ object TimeUtils {
}
}
+ val largestNonZeroUnit = parts.firstNotNullOfOrNull { if (it.value != 0) it.key else null } ?: TimeUnit.SECOND
+
var currentUnits = 0
val result = buildString {
for ((unit, value) in parts) {
- if (value != 0 || (unit == TimeUnit.SECOND && showMilliSeconds)) {
+ val showUnit = value != 0 || (showSmallerUnits && unit.factor <= largestNonZeroUnit.factor)
+
+ if (showUnit) {
val formatted = value.addSeparators()
val text = if (unit == TimeUnit.SECOND && showMilliSeconds) {
val formattedMillis = (millis / 100).toInt()