From 78c293ef181e0d7f08e8426e5043e3d8bc078514 Mon Sep 17 00:00:00 2001 From: martimavocado <39881008+martimavocado@users.noreply.github.com> Date: Sun, 13 Oct 2024 21:21:17 +0100 Subject: [PATCH] Feature: Flowstate helper (#2561) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../mining/FlowstateHelperConfig.java | 46 ++++ .../config/features/mining/MiningConfig.java | 5 + .../features/mining/FlowstateHelper.kt | 215 ++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/mining/FlowstateHelperConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/FlowstateHelper.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FlowstateHelperConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FlowstateHelperConfig.java new file mode 100644 index 000000000000..37a0f08aadd4 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FlowstateHelperConfig.java @@ -0,0 +1,46 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import at.hannibal2.skyhanni.features.mining.FlowstateElements; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +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.annotations.SearchTag; + +import java.util.List; + +public class FlowstateHelperConfig { + @Expose + @ConfigOption(name = "Enabled", desc = "Shows stats for the Flowstate enchantment on Mining Tools.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Appearance", desc = "Drag text to change the appearance.") + @ConfigEditorDraggableList() + public List appearance = FlowstateElements.defaultOption; + + @Expose + @ConfigOption(name = "Dynamic Color", desc = "Makes the timer's color dynamic.") + @ConfigEditorBoolean + public boolean colorfulTimer = false; + + @Expose + @ConfigOption(name = "Auto Hide", desc = "Automatically hides the GUI after being idle, in seconds.") + @SearchTag("autohide") + @ConfigEditorSlider( + minValue = -1, + maxValue = 30, + minStep = 1 + ) + public int autoHide = 10; + + @Expose + @ConfigLink(owner = FlowstateHelperConfig.class, field = "enabled") + public Position position = new Position(-110 , 9); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index 08d942077b6b..7879eb7816bd 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -78,6 +78,11 @@ public class MiningConfig { @Accordion public CrystalHighlighterConfig crystalHighlighter = new CrystalHighlighterConfig(); + @Expose + @ConfigOption(name = "Flowstate Helper", desc = "") + @Accordion + public FlowstateHelperConfig flowstateHelper = new FlowstateHelperConfig(); + @Expose @ConfigOption(name = "Highlight Commission Mobs", desc = "Highlight mobs that are part of active commissions.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/FlowstateHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/FlowstateHelper.kt new file mode 100644 index 000000000000..3ef46c5b6437 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/FlowstateHelper.kt @@ -0,0 +1,215 @@ +package at.hannibal2.skyhanni.features.mining + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.data.MiningAPI +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent +import at.hannibal2.skyhanni.events.ItemInHandChangeEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.mining.OreMinedEvent +import at.hannibal2.skyhanni.features.mining.FlowstateHelper.blockBreakStreak +import at.hannibal2.skyhanni.features.mining.FlowstateHelper.getSpeedBonus +import at.hannibal2.skyhanni.features.mining.FlowstateHelper.getStreakColor +import at.hannibal2.skyhanni.features.mining.FlowstateHelper.getTimerColor +import at.hannibal2.skyhanni.features.mining.FlowstateHelper.streakEndTimer +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.fromNow +import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getEnchantments +import at.hannibal2.skyhanni.utils.TimeUnit +import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.renderables.Renderable +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object FlowstateHelper { + private val config get() = SkyHanniMod.feature.mining.flowstateHelper + + var streakEndTimer = SimpleTimeMark.farPast() + private set + var blockBreakStreak = 0 + private set + + private var display: List = emptyList() + private var displayDirty = false + private var displayHibernating = true + private var timeSinceHibernation = SimpleTimeMark.farPast() + private var timeSinceMax = SimpleTimeMark.farPast() + private var displayMaxed = false + + private var flowstateCache: Int? = null + + @HandleEvent(onlyOnSkyblock = true) + fun onBlockMined(event: OreMinedEvent) { + if (!MiningAPI.inCustomMiningIsland()) return + if (flowstateCache == null) return + + displayHibernating = false + streakEndTimer = 10.seconds.fromNow() + blockBreakStreak += event.extraBlocks.values.sum() + displayDirty = true + createDisplay() + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!MiningAPI.inCustomMiningIsland()) return + + attemptClearDisplay() + } + + private fun attemptClearDisplay() { + if (streakEndTimer.isInFuture()) return + blockBreakStreak = 0 + timeSinceMax = SimpleTimeMark.farPast() + displayMaxed = false + displayDirty = true + if (!displayHibernating) timeSinceHibernation = SimpleTimeMark.now() + displayHibernating = true + createDisplay() + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!MiningAPI.inCustomMiningIsland() || !config.enabled) return + if (flowstateCache == null && !streakEndTimer.isInFuture()) return + + if (shouldAutoHide()) return + if (display.isEmpty() || streakEndTimer.isInFuture()) { + createDisplay() + } + + config.position.renderRenderables(display, extraSpace = 1, "Flowstate Helper") + } + + private fun shouldAutoHide(): Boolean { + if (config.autoHide < 0) return false + val time = 10.seconds - config.autoHide.seconds + return (streakEndTimer - time).isInPast() + } + + private fun createDisplay() { + if (displayDirty) { + displayDirty = false + FlowstateElements.STREAK.create() + FlowstateElements.SPEED.create() + } + if (!displayHibernating) { + FlowstateElements.TIMER.create() + FlowstateElements.COMPACT.create() + } + display = config.appearance.map { it.renderable } + } + + fun getSpeedBonus(): Int { + val flowstateLevel = flowstateCache ?: 0 + + return if (blockBreakStreak >= 200) { + if (!displayMaxed) { + displayMaxed = true + timeSinceMax = SimpleTimeMark.now() + } + 200 * flowstateLevel + } else blockBreakStreak * flowstateLevel + } + + @SubscribeEvent + fun onChangeItem(event: ItemInHandChangeEvent) { + hasFlowstate() + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + streakEndTimer = SimpleTimeMark.farPast() + attemptClearDisplay() + } + + fun getTimerColor(timeRemaining: Duration): String { + if (!config.colorfulTimer) return "§b" + return when (timeRemaining) { + in 0.seconds..2.seconds -> "§c" + in 2.seconds..4.seconds -> "§#§e§c§7§b§3§6§/" + in 4.seconds..6.seconds -> "§e" + in 6.seconds..8.seconds -> "§a" + in 8.seconds..10.seconds -> "§2" + else -> "§6" + } + } + + fun getStreakColor(streak: Int = blockBreakStreak): String = if (streak < 200) "§e" else "§a" + + private fun hasFlowstate() { + val enchantList = InventoryUtils.getItemInHand()?.getEnchantments() ?: run { + flowstateCache = null + return + } + if ("ultimate_flowstate" !in enchantList) { + flowstateCache = null + return + } + flowstateCache = enchantList.getValue("ultimate_flowstate") + } +} + +enum class FlowstateElements(val label: String, var renderable: Renderable = Renderable.string("")) { + TITLE("§d§lFlowstate Helper", Renderable.string("§d§lFlowstate Helper")), + TIMER("§fTime Remaining: §b9.71"), + STREAK("§7Streak: §f123/200"), + SPEED("§6+600⸕"), + COMPACT("§7x40 §6+120⸕ §b(9.71)"), + ; + + override fun toString() = label + + fun create() { + if (this !in config.appearance) return + + renderable = when (this) { + TIMER -> { + val timeRemaining = streakEndTimer.timeUntil().coerceAtLeast(0.seconds) + + Renderable.string("§7Time Remaining: ${timeRemaining.formatTime()}") + } + + STREAK -> { + val textColor = getStreakColor() + val string = "§7Streak: $textColor$blockBreakStreak" + Renderable.string(string + if (blockBreakStreak < 200) "§8/200" else "") + } + + SPEED -> { + Renderable.string("§6+${getSpeedBonus()}⸕") + } + + COMPACT -> { + val timeRemaining = streakEndTimer.timeUntil().coerceAtLeast(0.seconds) + + Renderable.string( + "§7x${getStreakColor()}$blockBreakStreak " + + "§6+${getSpeedBonus()}⸕ " + + timeRemaining.formatTime(), + ) + } + + else -> return + } + } + + companion object { + private val config get() = SkyHanniMod.feature.mining.flowstateHelper + + private fun Duration.formatTime(): String { + return getTimerColor(this) + format(TimeUnit.SECOND, true, maxUnits = 2, showSmallerUnits = true) + } + + @JvmField + val defaultOption = listOf( + TITLE, TIMER, STREAK, SPEED, + ) + } +}