diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/BarnTimerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/BarnTimerConfig.java index 42e36fe508ab..d62d066e590f 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/BarnTimerConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/BarnTimerConfig.java @@ -8,17 +8,18 @@ import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; +import io.github.notenoughupdates.moulconfig.observer.Property; import org.lwjgl.input.Keyboard; public class BarnTimerConfig { @Expose @ConfigOption( name = "Barn Fishing Timer", - desc = "Show the time and amount of sea creatures while fishing on the barn via hub." + desc = "Show the time and amount of own sea creatures nearby while barn fishing." ) @ConfigEditorBoolean @FeatureToggle - public boolean enabled = true; + public Property enabled = Property.of(true); @Expose @ConfigOption( @@ -26,7 +27,7 @@ public class BarnTimerConfig { desc = "Show the Barn Fishing Timer in the Crystal Hollows." ) @ConfigEditorBoolean - public boolean crystalHollows = true; + public Property crystalHollows = Property.of(true); @Expose @ConfigOption( @@ -34,7 +35,7 @@ public class BarnTimerConfig { desc = "Show the Barn Fishing Timer in the Crimson Isle." ) @ConfigEditorBoolean - public boolean crimsonIsle = true; + public Property crimsonIsle = Property.of(true); @Expose @ConfigOption( @@ -42,7 +43,7 @@ public class BarnTimerConfig { desc = "Show the Barn Fishing Timer on the Jerry's Workshop island." ) @ConfigEditorBoolean - public boolean winterIsland = true; + public Property winterIsland = Property.of(true); @Expose @ConfigOption( @@ -50,12 +51,12 @@ public class BarnTimerConfig { desc = "Show the Barn Fishing Timer on all the different islands that Stranded players can visit." ) @ConfigEditorBoolean - public boolean forStranded = true; + public Property forStranded = Property.of(true); @Expose @ConfigOption( name = "Worm Cap Alert", - desc = "Alerts you with title and sound if you hit the Worm Sea Creature limit of 60." + desc = "Alerts you with title and sound if you hit the Worm Sea Creature limit of 20." ) @ConfigEditorBoolean public boolean wormLimitAlert = true; @@ -74,6 +75,16 @@ public class BarnTimerConfig { ) public int alertTime = 330; + @Expose + @ConfigOption(name = "Fishing Cap Alert", desc = "Gives a warning when you reach a certain amount of mobs.") + @ConfigEditorBoolean + public boolean fishingCapAlert = true; + + @Expose + @ConfigOption(name = "Fishing Cap Amount", desc = "Amount of mobs at which to trigger the Fishing Cap Alert.") + @ConfigEditorSlider(minValue = 10, maxValue = 60, minStep = 1) + public int fishingCapAmount = 30; + @Expose @ConfigLink(owner = BarnTimerConfig.class, field = "enabled") public Position pos = new Position(10, 10, false, true); diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt index 26470fd9e5a9..74cf9294da64 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt @@ -3,25 +3,32 @@ package at.hannibal2.skyhanni.features.fishing import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.mob.Mob import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzKeyPressEvent import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.events.MobEvent +import at.hannibal2.skyhanni.events.SeaCreatureFishEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.EntityUtils -import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld -import at.hannibal2.skyhanni.utils.LocationUtils +import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked +import at.hannibal2.skyhanni.utils.LocationUtils.distanceTo +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RecalculatingValue import at.hannibal2.skyhanni.utils.RenderUtils.renderString import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.SoundUtils import at.hannibal2.skyhanni.utils.StringUtils +import at.hannibal2.skyhanni.utils.TimeLimitedSet import at.hannibal2.skyhanni.utils.TimeUnit import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.getLorenzVec import net.minecraft.client.Minecraft -import net.minecraft.entity.item.EntityArmorStand import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @SkyHanniModule @@ -29,100 +36,199 @@ object FishingTimer { private val config get() = SkyHanniMod.feature.fishing.barnTimer private val barnLocation = LorenzVec(108, 89, -252) + private val mobDespawnTime = mutableMapOf() + + private var lastSeaCreatureFished = SimpleTimeMark.farPast() + private var display: String? = null + private var lastNameFished: String? = null + + private var babyMagmaSlugsToFind = 0 + private var lastMagmaSlugLocation: LorenzVec? = null + private var lastMagmaSlugTime = SimpleTimeMark.farPast() + private var recentBabyMagmaSlugs = TimeLimitedSet(2.seconds) + + private var mobsToFind = 0 + + private val recentMobs = TimeLimitedSet(2.seconds) + private val currentCap by RecalculatingValue(1.seconds) { + when (LorenzUtils.skyBlockIsland) { + IslandType.CRYSTAL_HOLLOWS -> 20 + IslandType.CRIMSON_ISLE -> 5 + else -> config.fishingCapAmount + } + } private var rightLocation = false private var currentCount = 0 private var startTime = SimpleTimeMark.farPast() - private var inHollows = false @SubscribeEvent - fun onTick(event: LorenzTickEvent) { - if (!LorenzUtils.inSkyBlock) return - if (!config.enabled) return - - if (event.repeatSeconds(3)) { - rightLocation = isRightLocation() + fun onSecondPassed(event: SecondPassedEvent) { + if (!isEnabled()) return + updateLocation() + if (startTime.passedSince().inWholeSeconds - config.alertTime in 0..3) { + playSound() + } + if (config.wormLimitAlert && IslandType.CRYSTAL_HOLLOWS.isInIsland()) { + if (currentCount >= 20) { + playSound() + LorenzUtils.sendTitle("§cWORM CAP FULL!!!", 2.seconds) + } + } else if (config.fishingCapAlert && currentCount >= currentCap) { + playSound() } + } - if (!rightLocation) return + private fun playSound() = SoundUtils.repeatSound(250, 4, SoundUtils.plingSound) - if (event.isMod(5)) checkMobs() - if (event.isMod(7)) tryPlaySound() - if (config.manualResetTimer.isKeyHeld() && Minecraft.getMinecraft().currentScreen == null) { - startTime = SimpleTimeMark.now() + @SubscribeEvent + fun onMobSpawn(event: MobEvent.Spawn.SkyblockMob) { + if (!isEnabled()) return + val mob = event.mob + if (babyMagmaSlugsToFind != 0 && mob.name == "Baby Magma Slug") { + recentBabyMagmaSlugs += mob + handleBabySlugs() + return } + if (mob.name !in SeaCreatureManager.allFishingMobs) return + recentMobs += mob + handle() } - private fun tryPlaySound() { - if (currentCount == 0) return - - val passedSince = startTime.passedSince() - val barnTimerAlertTime = (config.alertTime * 1_000).milliseconds - if (passedSince in barnTimerAlertTime..(barnTimerAlertTime + 3.seconds)) { - SoundUtils.playBeepSound() + @SubscribeEvent + fun onMobDeSpawn(event: MobEvent.DeSpawn.SkyblockMob) { + if (!isEnabled()) return + val mob = event.mob + if (mob in mobDespawnTime) { + mobDespawnTime -= mob + if (mob.name == "Magma Slug") { + lastMagmaSlugLocation = mob.baseEntity.getLorenzVec() + babyMagmaSlugsToFind += 3 + lastMagmaSlugTime = SimpleTimeMark.now() + handleBabySlugs() + } } + recentMobs -= mob + updateInfo() } - private fun checkMobs() { - val newCount = countMobs() + @SubscribeEvent + fun onSeaCreatureFish(event: SeaCreatureFishEvent) { + if (!isEnabled()) return + if (!rightLocation) return + lastSeaCreatureFished = SimpleTimeMark.now() + lastNameFished = event.seaCreature.name + mobsToFind = if (event.doubleHook) 2 else 1 + handle() + } - if (currentCount == 0 && newCount > 0) { - startTime = SimpleTimeMark.now() + private fun handle() { + if (lastSeaCreatureFished.passedSince() > 2.seconds) return + val name = lastNameFished ?: return + val mobs = recentMobs.filter { it.name == name && it !in mobDespawnTime } + .sortedBy { it.baseEntity.distanceToPlayer() } + .take(mobsToFind).ifEmpty { return } + mobsToFind -= mobs.size + mobs.forEach { mobDespawnTime[it] = SimpleTimeMark.now() } + if (mobsToFind == 0) { + recentMobs.clear() + lastNameFished = null } + updateInfo() + } - currentCount = newCount - if (newCount == 0) { - startTime = SimpleTimeMark.farPast() + private fun handleBabySlugs() { + if (lastMagmaSlugTime.passedSince() > 2.seconds) return + if (babyMagmaSlugsToFind == 0) return + val location = lastMagmaSlugLocation ?: return + val slugs = recentBabyMagmaSlugs.filter { it !in mobDespawnTime } + .sortedBy { it.baseEntity.distanceTo(location) } + .take(babyMagmaSlugsToFind).ifEmpty { return } + babyMagmaSlugsToFind -= slugs.size + slugs.forEach { mobDespawnTime[it] = SimpleTimeMark.now() } + if (babyMagmaSlugsToFind == 0) { + recentBabyMagmaSlugs.clear() + lastMagmaSlugLocation = null } + updateInfo() + } - if (inHollows && newCount >= 60 && config.wormLimitAlert) { - SoundUtils.playBeepSound() - LorenzUtils.sendTitle("§cWORM CAP FULL!!!", 2.seconds) + @SubscribeEvent + fun onKeyPress(event: LorenzKeyPressEvent) { + if (!isEnabled()) return + if (Minecraft.getMinecraft().currentScreen != null) return + if (config.manualResetTimer.isKeyClicked()) { + mobDespawnTime.replaceAll { _, _ -> + SimpleTimeMark.now() + } } } - private fun countMobs() = - EntityUtils.getEntities().map { entity -> FishingAPI.seaCreatureCount(entity) }.sum() - - private fun isRightLocation(): Boolean { - inHollows = false - - if (config.forStranded && LorenzUtils.isStrandedProfile) return true - - if (config.crystalHollows && IslandType.CRYSTAL_HOLLOWS.isInIsland()) { - inHollows = true - return true + private fun updateInfo() { + currentCount = mobDespawnTime.entries.sumOf { + 1 + it.key.extraEntities.size } + startTime = mobDespawnTime.maxByOrNull { it.value.passedSince() }?.value ?: SimpleTimeMark.farPast() + display = createDisplay() + } - if (config.crimsonIsle && IslandType.CRIMSON_ISLE.isInIsland()) return true - - if (config.winterIsland && IslandType.WINTER.isInIsland()) return true - - if (!IslandType.THE_FARMING_ISLANDS.isInIsland()) { - return LocationUtils.playerLocation().distance(barnLocation) < 50 + private fun updateLocation() { + rightLocation = when (LorenzUtils.skyBlockIsland) { + IslandType.CRYSTAL_HOLLOWS -> config.crystalHollows.get() + IslandType.CRIMSON_ISLE -> config.crimsonIsle.get() + IslandType.WINTER -> config.winterIsland.get() + IslandType.HUB -> barnLocation.distanceToPlayer() < 50 + IslandType.PRIVATE_ISLAND -> config.forStranded.get() && LorenzUtils.isStrandedProfile + else -> false } + } - return false + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) return + if (!rightLocation) return + if (currentCount == 0) return + if (!FishingAPI.isFishing()) return + + display = createDisplay() } @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { - if (!LorenzUtils.inSkyBlock) return - if (!config.enabled) return + if (!isEnabled()) return if (!rightLocation) return if (currentCount == 0) return if (!FishingAPI.isFishing()) return + val text = display ?: return + config.pos.renderString(text, posLabel = "BarnTimer") + } + + private fun createDisplay(): String { val passedSince = startTime.passedSince() - val barnTimerAlertTime = (config.alertTime * 1_000).milliseconds - val color = if (passedSince > barnTimerAlertTime) "§c" else "§e" + val timeColor = if (passedSince > config.alertTime.seconds) "§c" else "§e" val timeFormat = passedSince.format(TimeUnit.MINUTE) + val countColor = if (config.fishingCapAlert && currentCount >= currentCap) "§c" else "§e" val name = StringUtils.pluralize(currentCount, "sea creature") - val text = "$color$timeFormat §8(§e$currentCount §b$name§8)" + return "$timeColor$timeFormat §8($countColor$currentCount §b$name§8)" + } - config.pos.renderString(text, posLabel = "BarnTimer") + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + mobDespawnTime.clear() + recentMobs.clear() + babyMagmaSlugsToFind = 0 + display = null + lastMagmaSlugLocation = null + lastMagmaSlugTime = SimpleTimeMark.farPast() + recentBabyMagmaSlugs.clear() + mobsToFind = 0 + currentCount = 0 + startTime = SimpleTimeMark.farPast() } + private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled.get() + @SubscribeEvent fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { event.move(3, "fishing.barnTimer", "fishing.barnTimer.enabled")