From b8d4c50958d91d3d85737c7bcfb9ae507ad62fab Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:31:37 +0100 Subject: [PATCH] Feature: SkyblockXP Bar (#2886) Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Co-authored-by: Cal Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../hannibal2/skyhanni/api/SkyBlockXPAPI.kt | 91 +++++++++++++++++++ .../features/inventory/InventoryConfig.java | 3 +- .../config/features/misc/MiscConfig.java | 7 ++ .../config/storage/ProfileSpecificStorage.kt | 3 + .../at/hannibal2/skyhanni/data/BitsAPI.kt | 8 +- .../features/inventory/SkyBlockXPBar.kt | 24 +++++ .../hannibal2/skyhanni/utils/UtilsPatterns.kt | 5 + 7 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/api/SkyBlockXPAPI.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockXPBar.kt diff --git a/src/main/java/at/hannibal2/skyhanni/api/SkyBlockXPAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/SkyBlockXPAPI.kt new file mode 100644 index 000000000000..a5270ed3271f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/SkyBlockXPAPI.kt @@ -0,0 +1,91 @@ +package at.hannibal2.skyhanni.api + +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.data.model.TabWidget +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.events.WidgetUpdateEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.UtilsPatterns +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern + +@SkyHanniModule +object SkyBlockXPAPI { + + private val group = RepoPattern.group("skyblockxpapi.inventory") + + private val itemNamePattern by group.pattern("itemname", "§aSkyBlock Leveling") + + /** + * REGEX-TEST: §7Your SkyBlock Level: §8[§9287§8] + */ + private val levelPattern by group.pattern("level", "§7Your SkyBlock Level: §8\\[§.(?\\d+)§8\\]") + + /** + * REGEX-TEST: §3§l§m §f§l§m §r §b24§3/§b100 §bXP + */ + private val xpPattern by group.pattern("xp", "[§\\w\\s]+§b(?\\d+)§3\\/§b100 §bXP") + + val levelXpPair get() = storage?.toLevelXpPair() + + // Stored as 12345, 123 is the level, 45 is the xp + private var storage + get() = ProfileStorageData.profileSpecific?.totalSkyBlockXP + set(value) { + ProfileStorageData.profileSpecific?.totalSkyBlockXP = value + } + + private fun Int.toLevelXpPair() = this / 100 to this % 100 + + @HandleEvent + fun onWidgetUpdate(event: WidgetUpdateEvent) { + if (!event.isWidget(TabWidget.SB_LEVEL)) return + + TabWidget.SB_LEVEL.matchMatcherFirstLine { + val level = group("level")?.toIntOrNull() + val xp = group("xp")?.toIntOrNull() + + updateStorage(level, xp) + } + } + + @HandleEvent + fun onInventoryFullyOpened(event: InventoryFullyOpenedEvent) { + if (!UtilsPatterns.skyblockMenuGuiPattern.matches(event.inventoryName)) return + + val stack = event.inventoryItems.values.find { itemNamePattern.matches(it.displayName) } ?: return + + var level: Int? = null + var xp: Int? = null + + loop@ for (line in stack.getLore()) { + if (level != null && xp != null) break@loop + + if (level == null) { + levelPattern.matchMatcher(line) { + level = group("level")?.toIntOrNull() + continue@loop + } + } + + if (xp == null) { + xpPattern.matchMatcher(line) { + xp = group("xp")?.toIntOrNull() + continue@loop + } + } + } + + updateStorage(level, xp) + } + + private fun updateStorage(level: Int?, xp: Int?) { + storage = calculateTotalXp(level ?: return, xp ?: return) + } + + fun calculateTotalXp(level: Int, xp: Int): Int = level * 100 + xp + +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index 8df3d69672cd..5cbb44effd27 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -220,8 +220,7 @@ public String toString() { @Expose @ConfigOption(name = "Quick Craft Confirmation", desc = "Require Ctrl+Click to craft items that aren't often quick crafted " + - "(e.g. armor, weapons, accessories). " + - "Sack items can be crafted normally." + "(e.g. armor, weapons, accessories). Sack items can be crafted normally." ) @ConfigEditorBoolean @FeatureToggle diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java index 8ed46ff297ae..9926d89832ef 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java @@ -169,6 +169,13 @@ public class MiscConfig { @ConfigEditorBoolean public boolean crashOnDeath = false; + @Expose + @ConfigOption(name = "SkyBlock XP Bar", desc = "Replaces the vanilla XP bar with a SkyBlock XP bar.") + @SearchTag("skyblockxp") + @ConfigEditorBoolean + @FeatureToggle + public boolean skyblockXpBar = false; + // TODO move into scoreboard accordion @Expose @ConfigOption(name = "Red Scoreboard Numbers", desc = "Hide the red scoreboard numbers on the right side of the screen.") diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt index 55cfe8d30489..dfb0227d9a3e 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt @@ -730,6 +730,9 @@ class ProfileSpecificStorage { var currentSlot: Int? = null } + @Expose + var totalSkyBlockXP: Int? = null + @Expose var draconicSacrificeTracker: DraconicSacrificeTracker.Data = DraconicSacrificeTracker.Data() diff --git a/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt index 434b30df51bf..a5dc72ff9595 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt @@ -22,6 +22,7 @@ import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.StringUtils.removeResets import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace import at.hannibal2.skyhanni.utils.TimeUtils +import at.hannibal2.skyhanni.utils.UtilsPatterns import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.days @@ -155,11 +156,6 @@ object BitsAPI { "§7Fame Rank: §e(?.*)", ) - private val bitsGuiNamePattern by bitsGuiGroup.pattern( - "mainmenuname", - "^SkyBlock Menu$", - ) - private val cookieGuiStackPattern by bitsGuiGroup.pattern( "mainmenustack", "^§6Booster Cookie$", @@ -258,7 +254,7 @@ object BitsAPI { val stacks = event.inventoryItems - if (bitsGuiNamePattern.matches(event.inventoryName)) { + if (UtilsPatterns.skyblockMenuGuiPattern.matches(event.inventoryName)) { val cookieStack = stacks.values.lastOrNull { cookieGuiStackPattern.matches(it.displayName) } // If the cookie stack is null, then the player should not have any bits to claim diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockXPBar.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockXPBar.kt new file mode 100644 index 000000000000..c26b01059b03 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockXPBar.kt @@ -0,0 +1,24 @@ +package at.hannibal2.skyhanni.features.inventory + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.SkyBlockXPAPI +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.client.Minecraft +import net.minecraftforge.client.event.RenderGameOverlayEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object SkyBlockXPBar { + private val config get() = SkyHanniMod.feature.misc + + @SubscribeEvent + fun onRenderScoreboard(event: RenderGameOverlayEvent.Pre) { + if (!isEnabled()) return + if (event.type != RenderGameOverlayEvent.ElementType.EXPERIENCE) return + val (level, xp) = SkyBlockXPAPI.levelXpPair ?: return + Minecraft.getMinecraft().thePlayer.setXPStats(xp / 100f, 100, level) + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.skyblockXpBar +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt b/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt index 0f5d195b82be..6923bef1fb36 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt @@ -157,4 +157,9 @@ object UtilsPatterns { "inventory.shopoptions", "Shop Trading Options", ) + + val skyblockMenuGuiPattern by patternGroup.pattern( + "inventory.skyblockmenu", + "SkyBlock Menu", + ) }