From d6c9b63136cae3a8abf7d4ce91424dfc648dc59c Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:23:42 +0200 Subject: Feature: Golden Fish Timer (#1941) Co-authored-by: Cal Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: ItsEmpa --- .../trophyfishing/GoldenFishTimerConfig.java | 47 +++ .../fishing/trophyfishing/TrophyFishingConfig.java | 5 + .../skyhanni/events/FishingBobberInLiquidEvent.kt | 5 + .../skyhanni/events/FishingBobberInWaterEvent.kt | 3 - .../skyhanni/features/fishing/FishingAPI.kt | 24 +- .../features/fishing/FishingBaitWarnings.kt | 4 +- .../features/fishing/IsFishingDetection.kt | 4 +- .../features/fishing/trophy/GoldenFishTimer.kt | 343 +++++++++++++++++++++ .../features/fishing/trophy/TrophyFishMessages.kt | 18 +- .../at/hannibal2/skyhanni/utils/InventoryUtils.kt | 5 +- .../java/at/hannibal2/skyhanni/utils/TimeUtils.kt | 7 +- 11 files changed, 437 insertions(+), 28 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/fishing/trophyfishing/GoldenFishTimerConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/events/FishingBobberInLiquidEvent.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/events/FishingBobberInWaterEvent.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/GoldenFishTimer.kt (limited to 'src/main/java/at/hannibal2') 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 @@ -29,6 +29,11 @@ public class TrophyFishingConfig { @Expose 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 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() 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() + + @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(?§[0-9a-f](?:§k)?[\\w -]+) §r(?§[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() @@ -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() -- cgit