aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features
diff options
context:
space:
mode:
authorJ10a1n15 <45315647+j10a1n15@users.noreply.github.com>2024-03-12 19:59:55 +0100
committerGitHub <noreply@github.com>2024-03-12 19:59:55 +0100
commit4352ffb08d4bfffc06adad2a068f375ab9874333 (patch)
treeb8ce745fca8ab82ffa04f6ab87334139a6b7192b /src/main/java/at/hannibal2/skyhanni/features
parenteb863d60e82b5541a9f42d2608f61cb97ada209b (diff)
downloadskyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.tar.gz
skyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.tar.bz2
skyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.zip
Feature: CustomScoreboard (#893)
Co-authored-by: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Co-authored-by: Thunderblade73 <gaidermarkus@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bingo/BingoAPI.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt130
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt53
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt87
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt635
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardEvents.kt498
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt404
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/UnknownLinesHandler.kt156
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/MaxPurseItems.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ServerRestartTitle.kt21
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListReader.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListRenderer.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt23
17 files changed, 2005 insertions, 32 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoAPI.kt
index fc884c91d..bee916507 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoAPI.kt
@@ -72,6 +72,8 @@ object BingoAPI {
fun getRankFromScoreboard(text: String) = if (detectionPattern.matches(text)) getRank(text) else null
+ fun getIconFromScoreboard(text: String) = getRankFromScoreboard(text)?.let { getIcon(it) }
+
fun getRank(text: String) = ranks.entries.find { text.contains(it.key) }?.value
fun getIcon(searchRank: Int) = ranks.entries.find { it.value == searchRank }?.key
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt
index 7a6fa1d17..e1e420ab4 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt
@@ -2,7 +2,7 @@ package at.hannibal2.skyhanni.features.event.diana
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.data.IslandType
-import at.hannibal2.skyhanni.data.MayorElection
+import at.hannibal2.skyhanni.data.Perk
import at.hannibal2.skyhanni.data.PetAPI
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
@@ -16,8 +16,8 @@ object DianaAPI {
fun hasSpadeInHand() = InventoryUtils.itemInHandId == spade
- private fun isRitualActive() = MayorElection.isPerkActive("Diana", "Mythological Ritual") ||
- MayorElection.isPerkActive("Jerry", "Perkpocalypse") || SkyHanniMod.feature.event.diana.alwaysDiana
+ private fun isRitualActive() = Perk.MYTHOLOGICAL_RITUAL.isActive ||
+ Perk.PERKPOCALYPSE.isActive || SkyHanniMod.feature.event.diana.alwaysDiana
fun hasGriffinPet() = PetAPI.isCurrentPet("Griffin")
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt
index 399967809..f9999fe2c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt
@@ -33,7 +33,7 @@ object FarmingContestAPI {
"crop",
"§8(?<crop>.*) Contest"
)
- private val sidebarCropPattern by patternGroup.pattern(
+ val sidebarCropPattern by patternGroup.pattern(
"sidebarcrop",
"(?:§e○|§6☘) §f(?<crop>.*) §a.*"
)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
index 2da5a21eb..7dc92b8d9 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
@@ -4,7 +4,7 @@ import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.ClickType
import at.hannibal2.skyhanni.data.GardenCropMilestones.getCounter
import at.hannibal2.skyhanni.data.GardenCropMilestones.setCounter
-import at.hannibal2.skyhanni.data.MayorElection
+import at.hannibal2.skyhanni.data.Perk
import at.hannibal2.skyhanni.data.jsonobjects.repo.DicerDropsJson
import at.hannibal2.skyhanni.data.jsonobjects.repo.DicerDropsJson.DicerType
import at.hannibal2.skyhanni.events.CropClickEvent
@@ -175,7 +175,7 @@ object GardenCropSpeed {
fun finneganPerkActive(): Boolean {
val forcefullyEnabledAlwaysFinnegan = config.forcefullyEnabledAlwaysFinnegan
- val perkActive = MayorElection.isPerkActive("Finnegan", "Farming Simulator")
+ val perkActive = Perk.FARMING_SIMULATOR.isActive
return forcefullyEnabledAlwaysFinnegan || perkActive
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt
new file mode 100644
index 000000000..5e15166c0
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt
@@ -0,0 +1,130 @@
+//
+// TODO LIST
+// V2 RELEASE
+// - Soulflow API
+// - Bank API (actually maybe not, I like the current design)
+// - beacon power
+// - skyblock level
+// - more bg options (round, blurr, outline)
+// - countdown events like fishing festival + fiesta when its not on tablist
+// - CookieAPI https://discord.com/channels/997079228510117908/1162844830360146080/1195695210433351821
+// - Rng meter display
+// - shorten time till next mayor https://discord.com/channels/997079228510117908/1162844830360146080/1216440046320746596
+// - option to hide coins earned
+// - color options in the purse etc lines
+// - choose the amount of decimal places in shorten nums
+// - very important bug fix: duplex is weird :(
+//
+
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.GuiPositionMovedEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAlignedWidth
+import at.hannibal2.skyhanni.utils.TabListData
+import net.minecraftforge.client.GuiIngameForge
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+typealias ScoreboardElementType = Pair<String, HorizontalAlignment>
+
+class CustomScoreboard {
+
+ private var display = emptyList<ScoreboardElementType>()
+ private var cache = emptyList<ScoreboardElementType>()
+ private val guiName = "Custom Scoreboard"
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ if (display.isEmpty()) return
+
+ RenderBackground().renderBackground()
+
+ val render =
+ if (!TabListData.fullyLoaded && config.displayConfig.cacheScoreboardOnIslandSwitch && cache.isNotEmpty()) {
+ cache
+ } else {
+ display
+ }
+ config.position.renderStringsAlignedWidth(render, posLabel = guiName)
+ }
+
+ @SubscribeEvent
+ fun onGuiPositionMoved(event: GuiPositionMovedEvent) {
+ if (event.guiName == guiName) {
+ val alignmentConfig = config.displayConfig.alignment
+ if (alignmentConfig.alignRight || alignmentConfig.alignCenterVertically) {
+ alignmentConfig.alignRight = false
+ alignmentConfig.alignCenterVertically = false
+ ChatUtils.chat("Disabled Custom Scoreboard auto-alignment.")
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!isEnabled()) return
+
+ // Creating the lines
+ if (event.isMod(5)) {
+ display = createLines()
+ if (TabListData.fullyLoaded) {
+ cache = display.toList()
+ }
+ }
+
+ // Remove Known Lines, so we can get the unknown ones
+ UnknownLinesHandler.handleUnknownLines()
+ }
+
+ companion object {
+ internal val config get() = SkyHanniMod.feature.gui.customScoreboard
+ internal val displayConfig get() = config.displayConfig
+ internal val informationFilteringConfig get() = config.informationFilteringConfig
+ internal val backgroundConfig get() = config.backgroundConfig
+ }
+
+ private fun createLines() = buildList<ScoreboardElementType> {
+ for (element in config.scoreboardEntries) {
+ val line = element.getVisiblePair()
+
+ // Hide consecutive empty lines
+ if (
+ config.informationFilteringConfig.hideConsecutiveEmptyLines &&
+ line.isNotEmpty() && line[0].first == "<empty>" && lastOrNull()?.first?.isEmpty() == true
+ ) {
+ continue
+ }
+
+ // Adds empty lines
+ if (line[0].first == "<empty>") {
+ add("" to HorizontalAlignment.LEFT)
+ continue
+ }
+
+ // Does not display this line
+ if (line.any { it.first == "<hidden>" }) {
+ continue
+ }
+
+ addAll(line)
+ }
+ }
+
+ // Thank you Apec for showing that the ElementType of the stupid scoreboard is FUCKING HELMET WTF
+ @SubscribeEvent
+ fun onRenderScoreboard(event: RenderGameOverlayEvent.Post) {
+ if (event.type == RenderGameOverlayEvent.ElementType.HELMET) {
+ GuiIngameForge.renderObjective = !isHideVanillaScoreboardEnabled()
+ }
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+ private fun isHideVanillaScoreboardEnabled() = isEnabled() && config.displayConfig.hideVanillaScoreboard
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt
new file mode 100644
index 000000000..74ba89dd6
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt
@@ -0,0 +1,53 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.config.features.gui.customscoreboard.DisplayConfig
+import at.hannibal2.skyhanni.data.HypixelData
+import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.features.bingo.BingoAPI
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.config
+import at.hannibal2.skyhanni.utils.NumberUtil
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.removeResets
+import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace
+import at.hannibal2.skyhanni.utils.TabListData
+import java.util.regex.Pattern
+
+object CustomScoreboardUtils {
+ private val numberFormat get() = config.displayConfig.numberFormat
+
+ internal fun getGroupFromPattern(list: List<String>, pattern: Pattern, group: String) = list.map {
+ it.removeResets().trimWhiteSpace()
+ }.firstNotNullOfOrNull { line ->
+ pattern.matchMatcher(line) {
+ group(group)
+ }
+ } ?: "0"
+
+ fun getProfileTypeSymbol() = when {
+ HypixelData.ironman -> "§7♲ "
+ HypixelData.stranded -> "§a☀ "
+ HypixelData.bingo -> ScoreboardData.sidebarLinesFormatted.firstNotNullOfOrNull {
+ BingoAPI.getIconFromScoreboard(it)?.plus(" ")
+ } ?: "§e❤ "
+
+ else -> "§e"
+ }
+
+ fun getTablistFooter(): String {
+ val tabList = TabListData.getPlayerTabOverlay()
+ if (tabList.footer_skyhanni == null) return ""
+ return tabList.footer_skyhanni.formattedText.replace("§r", "")
+ }
+
+ internal fun Number.formatNum(): String = when (numberFormat) {
+ DisplayConfig.NumberFormat.SHORT -> NumberUtil.format(this)
+ DisplayConfig.NumberFormat.LONG -> this.addSeparators()
+ else -> "0"
+ }
+
+ internal fun String.formatNum() = this.formatDouble().formatNum()
+
+ class UndetectedScoreboardLines(message: String) : Exception(message)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt
new file mode 100644
index 000000000..2a2f009ac
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt
@@ -0,0 +1,87 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.data.GuiEditManager
+import at.hannibal2.skyhanni.data.GuiEditManager.Companion.getAbsX
+import at.hannibal2.skyhanni.data.GuiEditManager.Companion.getAbsY
+import at.hannibal2.skyhanni.data.GuiEditManager.Companion.getDummySize
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.backgroundConfig
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.config
+import at.hannibal2.skyhanni.utils.RenderUtils
+import at.hannibal2.skyhanni.utils.SpecialColour
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.ScaledResolution
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.util.ResourceLocation
+import org.lwjgl.opengl.GL11
+
+class RenderBackground {
+ fun renderBackground() {
+ val position = config.position
+ val border = 5
+
+ val x = position.getAbsX()
+ val y = position.getAbsY()
+
+ val elementWidth = position.getDummySize().x
+ val elementHeight = position.getDummySize().y
+
+ val scaledWidth = ScaledResolution(Minecraft.getMinecraft()).scaledWidth
+ val scaledHeight = ScaledResolution(Minecraft.getMinecraft()).scaledHeight
+
+ // Update the position to the alignment options
+ if (
+ config.displayConfig.alignment.alignRight
+ || config.displayConfig.alignment.alignCenterVertically
+ ) {
+ position.set(
+ Position(
+ if (config.displayConfig.alignment.alignRight)
+ scaledWidth - elementWidth - (backgroundConfig.borderSize * 2)
+ else x,
+ if (config.displayConfig.alignment.alignCenterVertically)
+ scaledHeight / 2 - elementHeight / 2
+ else y,
+ position.getScale(),
+ position.isCenter
+ )
+ )
+ }
+
+ if (GuiEditManager.isInGui()) return
+
+ GlStateManager.pushMatrix()
+ GlStateManager.pushAttrib()
+
+ GlStateManager.color(1f, 1f, 1f, 1f)
+
+
+ if (backgroundConfig.enabled) {
+ if (backgroundConfig.useCustomBackgroundImage) {
+ val textureLocation = ResourceLocation("skyhanni", "scoreboard.png")
+ Minecraft.getMinecraft().textureManager.bindTexture(textureLocation)
+
+ Utils.drawTexturedRect(
+ (x - backgroundConfig.borderSize).toFloat(),
+ (y - backgroundConfig.borderSize).toFloat(),
+ (elementWidth + backgroundConfig.borderSize * 3).toFloat(),
+ (elementHeight + border * 2).toFloat(),
+ GL11.GL_NEAREST
+ )
+ } else {
+ RenderUtils.drawRoundRect(
+ x - backgroundConfig.borderSize,
+ y - backgroundConfig.borderSize,
+ elementWidth + backgroundConfig.borderSize * 3,
+ elementHeight + backgroundConfig.borderSize * 2,
+ SpecialColour.specialToChromaRGB(backgroundConfig.color),
+ backgroundConfig.roundedCornerSmoothness
+ )
+ }
+ }
+
+ GlStateManager.popMatrix()
+ GlStateManager.popAttrib()
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt
new file mode 100644
index 000000000..801ba3249
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt
@@ -0,0 +1,635 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.config.features.gui.customscoreboard.DisplayConfig.ArrowAmountDisplay
+import at.hannibal2.skyhanni.data.BitsAPI
+import at.hannibal2.skyhanni.data.HypixelData
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.data.MaxwellAPI
+import at.hannibal2.skyhanni.data.MayorAPI
+import at.hannibal2.skyhanni.data.PartyAPI
+import at.hannibal2.skyhanni.data.PurseAPI
+import at.hannibal2.skyhanni.data.QuiverAPI
+import at.hannibal2.skyhanni.data.QuiverAPI.NONE_ARROW_TYPE
+import at.hannibal2.skyhanni.data.QuiverAPI.asArrowPercentage
+import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.data.SlayerAPI
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.config
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.displayConfig
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.informationFilteringConfig
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboardUtils.formatNum
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboardUtils.getGroupFromPattern
+import at.hannibal2.skyhanni.mixins.hooks.tryToReplaceScoreboardLine
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter
+import at.hannibal2.skyhanni.utils.LorenzUtils.inAdvancedMiningIsland
+import at.hannibal2.skyhanni.utils.LorenzUtils.inAnyIsland
+import at.hannibal2.skyhanni.utils.LorenzUtils.inDungeons
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.NumberUtil.percentageColor
+import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
+import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.TabListData
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.TimeUtils.formatted
+import io.github.moulberry.notenoughupdates.util.SkyBlockTime
+import java.util.function.Supplier
+
+internal var unknownLines = listOf<String>()
+internal var amountOfUnknownLines = 0
+
+enum class ScoreboardElement(
+ private val displayPair: Supplier<List<ScoreboardElementType>>,
+ private val showWhen: () -> Boolean,
+ private val configLine: String
+) {
+ TITLE(
+ ::getTitleDisplayPair,
+ { true },
+ "§6§lSKYBLOCK"
+ ),
+ PROFILE(
+ ::getProfileDisplayPair,
+ { true },
+ "§7♲ Blueberry"
+ ),
+ PURSE(
+ ::getPurseDisplayPair,
+ ::getPurseShowWhen,
+ "Purse: §652,763,737"
+ ),
+ MOTES(
+ ::getMotesDisplayPair,
+ ::getMotesShowWhen,
+ "Motes: §d64,647"
+ ),
+ BANK(
+ ::getBankDisplayPair,
+ ::getBankShowWhen,
+ "Bank: §6249M"
+ ),
+ BITS(
+ ::getBitsDisplayPair,
+ ::getBitsShowWhen,
+ "Bits: §b59,264"
+ ),
+ COPPER(
+ ::getCopperDisplayPair,
+ ::getCopperShowWhen,
+ "Copper: §c23,495"
+ ),
+ GEMS(
+ ::getGemsDisplayPair,
+ ::getGemsShowWhen,
+ "Gems: §a57,873"
+ ),
+ HEAT(
+ ::getHeatDisplayPair,
+ ::getHeatShowWhen,
+ "Heat: §c♨ 0"
+ ),
+ NORTH_STARS(
+ ::getNorthStarsDisplayPair,
+ ::getNorthStarsShowWhen,
+ "North Stars: §d756"
+ ),
+ EMPTY_LINE(
+ ::getEmptyLineDisplayPair,
+ { true }, ""
+ ),
+ ISLAND(
+ ::getIslandDisplayPair,
+ { true },
+ "§7㋖ §aHub"
+ ),
+ LOCATION(
+ ::getLocationDisplayPair,
+ { true },
+ "§7⏣ §bVillage"
+ ),
+ VISITING(
+ ::getVisitDisplayPair,
+ ::getVisitShowWhen,
+ " §a✌ §7(§a1§7/6)"
+ ),
+ DATE(
+ ::getDateDisplayPair,
+ { true },
+ "Late Summer 11th"
+ ),
+ TIME(
+ ::getTimeDisplayPair,
+ { true },
+ "§710:40pm §b☽"
+ ),
+ LOBBY_CODE(
+ ::getLobbyDisplayPair,
+ { true },
+ "§8m77CK"
+ ),
+ POWER(
+ ::getPowerDisplayPair,
+ ::getPowerShowWhen,
+ "Power: Sighted"
+ ),
+ COOKIE(
+ ::getCookieDisplayPair,
+ ::getCookieShowWhen,
+ "§d§lCookie Buff\n §f3days, 17hours"
+ ),
+ EMPTY_LINE2(
+ ::getEmptyLineDisplayPair,
+ { true }, ""
+ ),
+ OBJECTIVE(
+ ::getObjectiveDisplayPair,
+ ::getObjectiveShowWhen,
+ "Objective:\n§eStar SkyHanni on Github"
+ ),
+ SLAYER(
+ ::getSlayerDisplayPair,
+ ::getSlayerShowWhen,
+ "Slayer Quest\n §7- §cVoidgloom Seraph III\n §7- §e12§7/§c120 §7Kills"
+ ),
+ EMPTY_LINE3(
+ ::getEmptyLineDisplayPair,
+ { true },
+ ""
+ ),
+ QUIVER(
+ ::getQuiverDisplayPair,
+ ::getQuiverShowWhen,
+ "Flint Arrow: §f1,234"
+ ),
+ POWDER(
+ ::getPowderDisplayPair,
+ ::getPowderShowWhen,
+ "§9§lPowder\n §7- §fMithril: §254,646\n §7- §fGemstone: §d51,234"
+ ),
+ EVENTS(
+ ::getEventsDisplayPair,
+ ::getEventsShowWhen,
+ "§7Wide Range of Events\n§7(too much to show all)"
+ ),
+ MAYOR(
+ ::getMayorDisplayPair,
+ ::getMayorShowWhen,
+ "§2Diana:\n §7- §eLucky!\n §7- §eMythological Ritual\n §7- §ePet XP Buff"
+ ),
+ PARTY(
+ ::getPartyDisplayPair,
+ ::getPartyShowWhen,
+ "§9§lParty (4):\n §7- §fhannibal2\n §7- §fMoulberry\n §7- §fVahvl\n §7- §fJ10a1n15"
+ ),
+ FOOTER(
+ ::getFooterDisplayPair,
+ { true },
+ "§ewww.hypixel.net"
+ ),
+ EXTRA(
+ ::getExtraDisplayPair,
+ ::getExtraShowWhen,
+ "§cUnknown lines the mod is not detecting"
+ ),
+ ;
+
+ override fun toString(): String {
+ return configLine
+ }
+
+ fun getVisiblePair() = if (isVisible()) getPair() else listOf("<hidden>" to HorizontalAlignment.LEFT)
+
+ private fun getPair(): List<ScoreboardElementType> {
+ return try {
+ displayPair.get()
+ } catch (e: NoSuchElementException) {
+ listOf("<hidden>" to HorizontalAlignment.LEFT)
+ }
+ }
+
+ private fun isVisible(): Boolean {
+ if (!informationFilteringConfig.hideIrrelevantLines) return true
+ return showWhen()
+ }
+}
+
+
+private fun getTitleDisplayPair() = if (displayConfig.titleAndFooter.useHypixelTitleAnimation) {
+ listOf(ScoreboardData.objectiveTitle to displayConfig.titleAndFooter.alignTitleAndFooter)
+} else {
+ listOf(
+ displayConfig.titleAndFooter.customTitle.get().toString()
+ .replace("&", "§") to displayConfig.titleAndFooter.alignTitleAndFooter
+ )
+}
+
+private fun getProfileDisplayPair() =
+ listOf(CustomScoreboardUtils.getProfileTypeSymbol() + HypixelData.profileName.firstLetterUppercase() to HorizontalAlignment.LEFT)
+
+private fun getPurseDisplayPair(): List<ScoreboardElementType> {
+ var purse = PurseAPI.currentPurse.formatNum()
+
+ val earned = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, PurseAPI.coinsPattern, "earned")
+
+ if (earned != "0") {
+ purse += " §7(§e+$earned§7)§6"
+ }
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && purse == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§6$purse Purse"
+ else -> "Purse: §6$purse"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getPurseShowWhen() = !inAnyIsland(IslandType.THE_RIFT)
+
+private fun getMotesDisplayPair(): List<ScoreboardElementType> {
+ val motes = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.motesPattern, "motes")
+ .formatNum()
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && motes == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§d$motes Motes"
+ else -> "Motes: §d$motes"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getMotesShowWhen() = inAnyIsland(IslandType.THE_RIFT)
+
+private fun getBankDisplayPair(): List<ScoreboardElementType> {
+ val bank = getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.bankPattern, "bank")
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && bank == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§6$bank Bank"
+ else -> "Bank: §6$bank"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getBankShowWhen() = !inAnyIsland(IslandType.THE_RIFT)
+
+private fun getBitsDisplayPair(): List<ScoreboardElementType> {
+ val bits = BitsAPI.bits.coerceAtLeast(0).formatNum()
+ val bitsToClaim = if (BitsAPI.bitsToClaim == -1) {
+ "§cOpen Sbmenu§b"
+ } else {
+ BitsAPI.bitsToClaim.coerceAtLeast(0).formatNum()
+ }
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && bits == "0" && bitsToClaim == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> {
+ if (displayConfig.showUnclaimedBits) {
+ "§b$bits§7/${if (bitsToClaim == "0") "§30" else "§b${bitsToClaim}"} §bBits"
+ } else {
+ "§b$bits Bits"
+ }
+ }
+
+ else -> {
+ if (displayConfig.showUnclaimedBits) {
+ "Bits: §b$bits§7/${if (bitsToClaim == "0") "§30" else "§b${bitsToClaim}"}"
+ } else {
+ "Bits: §b$bits"
+ }
+ }
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getBitsShowWhen() = !HypixelData.bingo && !inAnyIsland(IslandType.CATACOMBS, IslandType.KUUDRA_ARENA)
+
+private fun getCopperDisplayPair(): List<ScoreboardElementType> {
+ val copper = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.copperPattern, "copper")
+ .formatNum()
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && copper == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§c$copper Copper"
+ else -> "Copper: §c$copper"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getCopperShowWhen() = inAnyIsland(IslandType.GARDEN)
+
+private fun getGemsDisplayPair(): List<ScoreboardElementType> {
+ val gems = getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.gemsPattern, "gems")
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && gems == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§a$gems Gems"
+ else -> "Gems: §a$gems"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getGemsShowWhen() = !inAnyIsland(IslandType.THE_RIFT, IslandType.CATACOMBS, IslandType.KUUDRA_ARENA)
+
+private fun getHeatDisplayPair(): List<ScoreboardElementType> {
+ val heat = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.heatPattern, "heat")
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && heat == "§c♨ 0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> if (heat == "0") "§c♨ 0 Heat" else "$heat Heat"
+ else -> if (heat == "0") "Heat: §c♨ 0" else "Heat: $heat"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getHeatShowWhen() = inAnyIsland(IslandType.CRYSTAL_HOLLOWS)
+ && ScoreboardData.sidebarLinesFormatted.any { ScoreboardPattern.heatPattern.matches(it) }
+
+private fun getNorthStarsDisplayPair(): List<ScoreboardElementType> {
+ val northStars =
+ getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.northstarsPattern, "northstars")
+ .formatNum()
+
+ return listOf(
+ when {
+ informationFilteringConfig.hideEmptyLines && northStars == "0" -> "<hidden>"
+ displayConfig.displayNumbersFirst -> "§d$northStars North Stars"
+ else -> "North Stars: §d$northStars"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getNorthStarsShowWhen() = inAnyIsland(IslandType.WINTER)
+
+private fun getEmptyLineDisplayPair() = listOf("<empty>" to HorizontalAlignment.LEFT)
+
+private fun getIslandDisplayPair() =
+ listOf("§7㋖ §a" + HypixelData.skyBlockIsland.displayName to HorizontalAlignment.LEFT)
+
+private fun getLocationDisplayPair() = buildList {
+ add(
+ (tryToReplaceScoreboardLine(
+ getGroupFromPattern(
+ ScoreboardData.sidebarLinesFormatted,
+ ScoreboardPattern.locationPattern,
+ "location"
+ )
+ )?.trim()
+ ?: "<hidden>") to HorizontalAlignment.LEFT
+ )
+
+ val plotLine = ScoreboardData.sidebarLinesFormatted.firstOrNull { ScoreboardPattern.plotPattern.matches(it) }
+ if (plotLine != null) add(plotLine to HorizontalAlignment.LEFT)
+}
+
+private fun getVisitDisplayPair() =
+ listOf(
+ ScoreboardData.sidebarLinesFormatted.first { ScoreboardPattern.visitingPattern.matches(it) } to HorizontalAlignment.LEFT
+ )
+
+private fun getVisitShowWhen() =
+ ScoreboardData.sidebarLinesFormatted.any { ScoreboardPattern.visitingPattern.matches(it) }
+
+private fun getDateDisplayPair() =
+ listOf(
+ SkyBlockTime.now().formatted(yearElement = false, hoursAndMinutesElement = false) to HorizontalAlignment.LEFT
+ )
+
+
+private fun getTimeDisplayPair(): List<ScoreboardElementType> {
+ var symbol = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.timePattern, "symbol")
+ if (symbol == "0") symbol = ""
+ return listOf(
+ "§7" + SkyBlockTime.now()
+ .formatted(dayAndMonthElement = false, yearElement = false) + " $symbol" to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getLobbyDisplayPair(): List<ScoreboardElementType> {
+ val lobbyCode = getGroupFromPattern(
+ ScoreboardData.sidebarLinesFormatted,
+ ScoreboardPattern.lobbyCodePattern,
+ "code"
+ )
+
+ val displayValue = if (lobbyCode == "0") "<hidden>" else "§8$lobbyCode"
+ return listOf(displayValue to HorizontalAlignment.LEFT)
+}
+
+private fun getPowerDisplayPair() = listOf(
+ when (MaxwellAPI.currentPower) {
+ null -> "§cOpen \"Your Bags\"!"
+ else ->
+ if (displayConfig.displayNumbersFirst) {
+ "${MaxwellAPI.currentPower?.replace("Power", "")} Power " +
+ "§7(§6${MaxwellAPI.magicalPower}§7)"
+ } else {
+ "Power: ${MaxwellAPI.currentPower?.replace("Power", "")} " +
+ "§7(§6${MaxwellAPI.magicalPower?.addSeparators()}§7)"
+ }
+ } to HorizontalAlignment.LEFT
+)
+
+private fun getPowerShowWhen() = !inAnyIsland(IslandType.THE_RIFT)
+
+private fun getCookieDisplayPair(): List<ScoreboardElementType> {
+ val timeLine = CustomScoreboardUtils.getTablistFooter().split("\n")
+ .nextAfter("§d§lCookie Buff") ?: "<hidden>"
+
+ return listOf(
+ "§d§lCookie Buff" to HorizontalAlignment.LEFT,
+ if (timeLine.contains("Not active"))
+ " §7- §cNot active" to HorizontalAlignment.LEFT
+ else
+ " §7- §e${timeLine.substringAfter("§d§lCookie Buff").trim()}" to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getCookieShowWhen(): Boolean {
+ if (HypixelData.bingo) return false
+
+ return if (informationFilteringConfig.hideEmptyLines) {
+ CustomScoreboardUtils.getTablistFooter().split("\n").any {
+ CustomScoreboardUtils.getTablistFooter().split("\n").nextAfter("§d§lCookie Buff")?.contains(it)
+ ?: false
+ }
+ } else {
+ true
+ }
+}
+
+private fun getObjectiveDisplayPair() = buildList {
+ val objective = ScoreboardData.sidebarLinesFormatted.first { ScoreboardPattern.objectivePattern.matches(it) }
+
+ add(objective to HorizontalAlignment.LEFT)
+ add((ScoreboardData.sidebarLinesFormatted.nextAfter(objective) ?: "<hidden>") to HorizontalAlignment.LEFT)
+
+ if (ScoreboardData.sidebarLinesFormatted.any { ScoreboardPattern.thirdObjectiveLinePattern.matches(it) }) {
+ add(
+ (ScoreboardData.sidebarLinesFormatted.nextAfter(objective, 2)
+ ?: "Second objective here") to HorizontalAlignment.LEFT
+ )
+ }
+}
+
+private fun getObjectiveShowWhen(): Boolean =
+ !inAnyIsland(IslandType.KUUDRA_ARENA)
+ && ScoreboardData.sidebarLinesFormatted.none { ScoreboardPattern.objectivePattern.matches(it) }
+
+
+private fun getSlayerDisplayPair(): List<ScoreboardElementType> = listOf(
+ (if (SlayerAPI.hasActiveSlayerQuest()) "Slayer Quest" else "<hidden>") to HorizontalAlignment.LEFT,
+ (" §7- §e${SlayerAPI.latestSlayerCategory.trim()}" to HorizontalAlignment.LEFT),
+ (" §7- §e${SlayerAPI.latestSlayerProgress.trim()}" to HorizontalAlignment.LEFT)
+)
+
+// TODO: Redo the Slayer showWhen
+private fun getSlayerShowWhen() = true
+
+private fun getQuiverDisplayPair(): List<ScoreboardElementType> {
+ if (QuiverAPI.currentArrow == null)
+ return listOf("§cChange your Arrow once" to HorizontalAlignment.LEFT)
+ if (QuiverAPI.currentArrow == NONE_ARROW_TYPE)
+ return listOf("No Arrows selected" to HorizontalAlignment.LEFT)
+
+ val amountString = (if (displayConfig.colorArrowAmount) {
+ percentageColor(QuiverAPI.currentAmount.toLong(), QuiverAPI.MAX_ARROW_AMOUNT.toLong()).getChatColor()
+ } else {
+ ""
+ }) + when (displayConfig.arrowAmountDisplay) {
+ ArrowAmountDisplay.NUMBER -> QuiverAPI.currentAmount.addSeparators()
+ ArrowAmountDisplay.PERCENTAGE -> "${QuiverAPI.currentAmount.asArrowPercentage()}%"
+ else -> QuiverAPI.currentAmount.addSeparators()
+ }
+
+ return listOf(
+ if (displayConfig.displayNumbersFirst) {
+ "$amountString ${QuiverAPI.currentArrow?.arrow}s"
+ } else {
+ "${QuiverAPI.currentArrow?.arrow?.replace(" Arrow", "")}: $amountString Arrows"
+ } to HorizontalAlignment.LEFT
+ )
+}
+
+private fun getQuiverShowWhen(): Boolean {
+ if (informationFilteringConfig.hideIrrelevantLines && !QuiverAPI.hasBowInInventory()) return false
+ return !inAnyIsland(IslandType.THE_RIFT)
+}
+
+private fun getPowderDisplayPair() = buildList {
+ val mithrilPowder =
+ getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.mithrilPowderPattern, "mithrilpowder")
+ .formatNum()
+ val gemstonePowder =
+ getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.gemstonePowderPattern, "gemstonepowder")
+ .formatNum()
+
+ add("§9§lPowder" to HorizontalAlignment.LEFT)
+
+ if (informationFilteringConfig.hideEmptyLines && mithrilPowder == "0" && gemstonePowder == "0") {
+ add(0, "<hidden>" to HorizontalAlignment.LEFT)
+ } else {
+ if (displayConfig.displayNumbersFirst) {
+ add(" §7- §2$mithrilPowder Mithril" to HorizontalAlignment.LEFT)
+ add(" §7- §d$gemstonePowder Gemstone" to HorizontalAlignment.LEFT)
+ } else {
+ add(" §7- §fMithril: §2$mithrilPowder" to HorizontalAlignment.LEFT)
+ add(" §7- §fGemstone: §d$gemstonePowder" to HorizontalAlignment.LEFT)
+ }
+ }
+}
+
+private fun getPowderShowWhen() = inAdvancedMiningIsland()
+
+private fun getEventsDisplayPair(): List<ScoreboardElementType> {
+ return ScoreboardEvents.getEvent()
+ .flatMap { it.getLines().map { i -> i to HorizontalAlignment.LEFT } }
+ .takeIf { it.isNotEmpty() } ?: listOf("<hidden>" to HorizontalAlignment.LEFT)
+}
+
+private fun getEventsShowWhen() = ScoreboardEvents.getEvent().isNotEmpty()
+
+private fun getMayorDisplayPair() = buildList {
+ add(
+ ((MayorAPI.currentMayor?.mayorName?.let { MayorAPI.mayorNameWithColorCode(it) }
+ ?: "<hidden>") +
+ (if (config.mayorConfig.showTimeTillNextMayor) {
+ "§7 (§e${MayorAPI.timeTillNextMayor.format()}§7)"
+ } else {
+ ""
+ })) to HorizontalAlignment.LEFT
+ )
+ if (config.mayorConfig.showMayorPerks) {
+ MayorAPI.currentMayor?.activePerks?.forEach {
+ add(" §7- §e${it.perkName}" to HorizontalAlignment.LEFT)
+ }
+ }
+}
+
+private fun getMayorShowWhen() =
+ !inAnyIsland(IslandType.THE_RIFT) && MayorAPI.currentMayor != null
+
+private fun getPartyDisplayPair() =
+ if (PartyAPI.partyMembers.isEmpty() && informationFilteringConfig.hideEmptyLines) {
+ listOf("<hidden>" to HorizontalAlignment.LEFT)
+ } else {
+ val title =
+ if (PartyAPI.partyMembers.isEmpty()) "§9§lParty" else "§9§lParty (${PartyAPI.partyMembers.size})"
+ val partyList = PartyAPI.partyMembers
+ .take(config.partyConfig.maxPartyList.get())
+ .map {
+ " §7- §7$it"
+ }
+ .toTypedArray()
+ listOf(title, *partyList).map { it to HorizontalAlignment.LEFT }
+ }
+
+private fun getPartyShowWhen() = if (inDungeons) {
+ false // Hidden bc the scoreboard lines already exist
+} else {
+ if (config.partyConfig.showPartyEverywhere) {
+ true
+ } else {
+ inAnyIsland(
+ IslandType.DUNGEON_HUB,
+ IslandType.KUUDRA_ARENA,
+ IslandType.CRIMSON_ISLE
+ )
+ }
+}
+
+private fun getFooterDisplayPair() = listOf(
+ displayConfig.titleAndFooter.customFooter.get().toString()
+ .replace("&", "§") to displayConfig.titleAndFooter.alignTitleAndFooter
+)
+
+private fun getExtraDisplayPair(): List<ScoreboardElementType> {
+ if (unknownLines.isEmpty()) return listOf("<hidden>" to HorizontalAlignment.LEFT)
+
+ if (amountOfUnknownLines != unknownLines.size && config.unknownLinesWarning) {
+ ErrorManager.logErrorWithData(
+ CustomScoreboardUtils.UndetectedScoreboardLines("CustomScoreboard detected ${unknownLines.size} unknown line${if (unknownLines.size > 1) "s" else ""}"),
+ "CustomScoreboard detected ${unknownLines.size} unknown line${if (unknownLines.size > 1) "s" else ""}",
+ "Unknown Lines" to unknownLines,
+ "Island" to HypixelData.skyBlockIsland,
+ "Area" to HypixelData.skyBlockArea,
+ noStackTrace = true
+ )
+ amountOfUnknownLines = unknownLines.size
+ }
+
+ return listOf("§cUndetected Lines:" to HorizontalAlignment.LEFT) + unknownLines.map { it to HorizontalAlignment.LEFT }
+}
+
+private fun getExtraShowWhen(): Boolean {
+ if (unknownLines.isEmpty()) {
+ amountOfUnknownLines = 0
+ }
+ return unknownLines.isNotEmpty()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardEvents.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardEvents.kt
new file mode 100644
index 000000000..0ca968a98
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardEvents.kt
@@ -0,0 +1,498 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.data.HypixelData
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.features.garden.contest.FarmingContestAPI.sidebarCropPattern
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.Companion.config
+import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardEvents.VOTING
+import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern
+import at.hannibal2.skyhanni.features.misc.ServerRestartTitle
+import at.hannibal2.skyhanni.features.rift.area.stillgorechateau.RiftBloodEffigies
+import at.hannibal2.skyhanni.utils.CollectionUtils.addIfNotNull
+import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter
+import at.hannibal2.skyhanni.utils.LorenzUtils.inAdvancedMiningIsland
+import at.hannibal2.skyhanni.utils.LorenzUtils.inDungeons
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
+import at.hannibal2.skyhanni.utils.StringUtils.anyMatches
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.TabListData
+import java.util.function.Supplier
+import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern as SbPattern
+
+/**
+ * This enum contains all the lines that either are events or other lines that are so rare/not often seen that they
+ * don't fit in the normal [ScoreboardElement] enum.
+ *
+ * We for example have the [VOTING] Event, while this is clearly not an event, I don't consider them as normal lines
+ * because they are visible for a maximum of like 1 minute every 5 days and ~12 hours.
+ */
+
+private fun getSbLines(): List<String> {
+ return ScoreboardData.sidebarLinesFormatted
+}
+
+enum class ScoreboardEvents(private val displayLine: Supplier<List<String>>, private val showWhen: () -> Boolean) {
+ VOTING(
+ ::getVotingLines,
+ ::getVotingShowWhen
+ ),
+ SERVER_CLOSE(
+ ::getServerCloseLines,
+ ::getServerCloseShowWhen
+ ),
+ DUNGEONS(
+ ::getDungeonsLines,
+ ::getDungeonsShowWhen
+ ),
+ KUUDRA(
+ ::getKuudraLines,
+ ::getKuudraShowWhen
+ ),
+ DOJO(
+ ::getDojoLines,
+ ::getDojoShowWhen
+ ),
+ DARK_AUCTION(
+ ::getDarkAuctionLines,
+ ::getDarkAuctionShowWhen
+ ),
+ JACOB_CONTEST(
+ ::getJacobContestLines,
+ ::getJacobContestShowWhen
+ ),
+ JACOB_MEDALS(
+ ::getJacobMedalsLines,
+ ::getJacobMedalsShowWhen
+ ),
+ TRAPPER(
+ ::getTrapperLines,
+ ::getTrapperShowWhen
+ ),
+ GARDEN_CLEAN_UP(
+ ::getGardenCleanUpLines,
+ ::getGardenCleanUpShowWhen
+ ),
+ GARDEN_PASTING(
+ ::getGardenPastingLines,
+ ::getGardenPastingShowWhen
+ ),
+ FLIGHT_DURATION(
+ ::getFlightDurationLines,
+ ::getFlightDurationShowWhen
+ ),
+ WINTER(
+ ::getWinterLines,
+ ::getWinterShowWhen
+ ),
+ SPOOKY(
+ ::getSpookyLines,
+ ::getSpookyShowWhen
+ ),
+ ACTIVE_TABLIST_EVENTS(
+ ::getActiveEventLine,
+ ::getActiveEventShowWhen
+ ),
+ BROODMOTHER(
+ ::getBroodmotherLines,
+ ::getBroodmotherShowWhen
+ ),
+ NEW_YEAR(
+ ::getNewYearLines,
+ ::getNewYearShowWhen
+ ),
+ ORINGO(
+ ::getOringoLines,
+ ::getOringoShowWhen
+ ),
+ MINING_EVENTS(
+ ::getMiningEventsLines,
+ ::getMiningEventsShowWhen
+ ),
+ DAMAGE(
+ ::getDamageLines,
+ ::getDamageShowWhen
+ ),
+ MAGMA_BOSS(
+ ::getMagmaBossLines,
+ ::getMagmaBossShowWhen
+ ),
+ ESSENCE(
+ ::getEssenceLines,
+ ::getEssenceShowWhen
+ ),
+ EFFIGIES(
+ ::getEffigiesLines,
+ ::getEffigiesShowWhen
+ ),
+ REDSTONE(
+ ::getRedstoneLines,
+ ::getRedstoneShowWhen
+ ),
+
+ // Maybe as a default state, use tablist "Events: ..."
+ NONE(
+ ::getNoneLines,
+ { false }
+ );
+
+ fun getLines(): List<String> {
+ return displayLine.get()
+ }
+
+ companion object {
+ fun getEvent(): List<ScoreboardEvents> {
+ if (config.displayConfig.showAllActiveEvents) {
+ return entries.filter { it.showWhen() }
+ }
+ return listOf(entries.firstOrNull { it.showWhen() } ?: NONE)
+ }
+ }
+}
+
+private fun getVotingLines() = buildList {
+ val sbLines = getSbLines()
+
+ val yearLine = sbLines.firstOrNull { SbPattern.yearVotesPattern.matches(it) } ?: return emptyList<String>()
+ addIfNotNull(yearLine)
+
+ if (sbLines.nextAfter(yearLine) == "§7Waiting for") {
+ add("§7Waiting for")
+ add("§7your vote...")
+ } else {
+ if (SbPattern.votesPattern.anyMatches(sbLines)) {
+ addAll(sbLines.filter { SbPattern.votesPattern.matches(it) })
+ }
+ }
+}
+
+
+private fun getVotingShowWhen(): Boolean {
+ return SbPattern.yearVotesPattern.anyMatches(getSbLines())
+}
+
+private fun getServerCloseLines() = buildList {
+ val matchingLine = getSbLines().first { ServerRestartTitle.restartingGreedyPattern.matches(it) }
+ add(matchingLine.split("§8")[0])
+}
+
+private fun getServerCloseShowWhen(): Boolean {
+ return ServerRestartTitle.restartingGreedyPattern.anyMatches(getSbLines())
+}
+
+private fun getDungeonsLines() = listOf(
+ SbPattern.autoClosingPattern,
+ SbPattern.startingInPattern,
+ SbPattern.keysPattern,
+ SbPattern.timeElapsedPattern,
+ SbPattern.clearedPattern,
+ SbPattern.soloPattern,
+ SbPattern.teammatesPattern,
+ SbPattern.floor3GuardiansPattern
+).let { patterns ->
+ // BetterMap adds a random §r at the start, making it go black
+ getSbLines().filter { line -> patterns.any { it.matches(line.replace("§r", "")) } }
+}
+
+private fun getDungeonsShowWhen(): Boolean {
+ return IslandType.CATACOMBS.isInIsland() || inDungeons
+}
+
+private fun getKuudraLines() = listOf(
+ SbPattern.autoClosingPattern,
+ SbPattern.startingInPattern,
+ SbPattern.timeElapsedPattern,
+ SbPattern.instanceShutdownPattern,
+ SbPattern.wavePattern,
+ SbPattern.tokensPattern,
+ SbPattern.submergesPattern
+)
+ .mapNotNull { pattern ->
+ getSbLines().firstOrNull { pattern.matches(it) }
+ }
+
+private fun getKuudraShowWhen(): Boolean {
+ return IslandType.KUUDRA_ARENA.isInIsland()
+}
+
+private fun getDojoLines() = listOf(
+ SbPattern.dojoChallengePattern,
+ SbPattern.dojoDifficultyPattern,
+ SbPattern.dojoPointsPattern,
+ SbPattern.dojoTimePattern
+)
+ .mapNotNull { pattern ->
+ getSbLines().firstOrNull { pattern.matches(it) }
+ }
+
+private fun getDojoShowWhen(): Boolean {
+ return SbPattern.dojoChallengePattern.anyMatches(getSbLines())
+}
+
+private fun getDarkAuctionLines() = buildList {
+ getSbLines().firstOrNull { SbPattern.startingInPattern.matches(it) }?.let { add(it) }
+ getSbLines().firstOrNull { SbPattern.timeLeftPattern.matches(it) }?.let { add(it) }
+
+ val darkAuctionCurrentItemLine =
+ getSbLines().firstOrNull { SbPattern.darkAuctionCurrentItemPattern.matches(it) }
+
+ if (darkAuctionCurrentItemLine != null) {
+ addIfNotNull(darkAuctionCurrentItemLine)
+ addIfNotNull(getSbLines().nextAfter(darkAuctionCurrentItemLine))
+ }
+}
+
+private fun getDarkAuctionShowWhen(): Boolean {
+ return IslandType.DARK_AUCTION.isInIsland()
+}
+
+private fun getJacobContestLines() = buildList {
+ val jacobsContestLine = getSbLines().firstOrNull { SbPattern.jacobsContestPattern.matches(it) }
+
+ jacobsContestLine?.let {
+ addIfNotNull(it)
+ addIfNotNull(getSbLines().nextAfter(it))
+ addIfNotNull(getSbLines().nextAfter(it, 2))
+ addIfNotNull(getSbLines().nextAfter(it, 3))
+ }
+}
+
+private fun getJacobContestShowWhen(): Boolean {
+ return sidebarCropPattern.anyMatches(getSbLines())
+}
+
+private fun getJacobMedalsLines(): List<String> {
+ return getSbLines().filter { SbPattern.medalsPattern.matches(it) }
+}
+
+private fun getJacobMedalsShowWhen(): Boolean {
+ return SbPattern.medalsPattern.anyMatches(getSbLines())
+}
+
+private fun getTrapperLines() = buildList {
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.peltsPattern.matches(it) })
+
+ val trapperMobLocationLine = getSbLines().firstOrNull { SbPattern.mobLocationPattern.matches(it) }
+ if (trapperMobLocationLine != null) {
+ add("Tracker Mob Location:")
+ addIfNotNull(getSbLines().nextAfter(trapperMobLocationLine))
+ }
+}
+
+private fun getTrapperShowWhen(): Boolean {
+ return getSbLines().any {
+ ScoreboardPattern.peltsPattern.matches(it) || ScoreboardPattern.mobLocationPattern.matches(it)
+ }
+}
+
+private fun getGardenCleanUpLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.cleanUpPattern.matches(it) }.trim())
+}
+
+private fun getGardenCleanUpShowWhen(): Boolean {
+ return SbPattern.cleanUpPattern.anyMatches(getSbLines())
+}
+
+private fun getGardenPastingLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.pastingPattern.matches(it) }.trim())
+}
+
+private fun getGardenPastingShowWhen(): Boolean {
+ return SbPattern.pastingPattern.anyMatches(getSbLines())
+}
+
+private fun getFlightDurationLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.flightDurationPattern.matches(it) }.trim())
+}
+
+private fun getFlightDurationShowWhen(): Boolean {
+ return SbPattern.flightDurationPattern.anyMatches(getSbLines())
+}
+
+private fun getWinterLines() = buildList {
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterEventStartPattern.matches(it) })
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterNextWavePattern.matches(it) && !it.endsWith("Soon!") })
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterWavePattern.matches(it) })
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterMagmaLeftPattern.matches(it) })
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterTotalDmgPattern.matches(it) })
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.winterCubeDmgPattern.matches(it) })
+}
+
+private fun getWinterShowWhen(): Boolean {
+ return getSbLines().any {
+ ScoreboardPattern.winterEventStartPattern.matches(it)
+ || (ScoreboardPattern.winterNextWavePattern.matches(it) && !it.endsWith("Soon!"))
+ || ScoreboardPattern.winterWavePattern.matches(it)
+ }
+}
+
+private fun getSpookyLines() = buildList {
+ addIfNotNull(getSbLines().firstOrNull { SbPattern.spookyPattern.matches(it) }) // Time
+ addIfNotNull("§7Your Candy: ")
+ addIfNotNull(
+ CustomScoreboardUtils.getTablistFooter()
+ .split("\n")
+ .firstOrNull { it.startsWith("§7Your Candy:") }
+ ?.removePrefix("§7Your Candy:") ?: "§cCandy not found"
+ ) // Candy
+}
+
+private fun getSpookyShowWhen(): Boolean {
+ return getSbLines().any { ScoreboardPattern.spookyPattern.matches(it) }
+}
+
+private fun getActiveEventLine(): List<String> {
+ val currentActiveEvent = TabListData.getTabList().firstOrNull { SbPattern.eventNamePattern.matches(it) }
+ ?.let {
+ SbPattern.eventNamePattern.matchMatcher(it) {
+ group("name")
+ }
+ }
+ val currentActiveEventEndsIn = TabListData.getTabList().firstOrNull { SbPattern.eventTimeEndsPattern.matches(it) }
+ ?.let {
+ SbPattern.eventTimeEndsPattern.matchMatcher(it) {
+ group("time")
+ }
+ }
+
+ return listOf("$currentActiveEvent $currentActiveEventEndsIn")
+}
+
+private fun getActiveEventShowWhen(): Boolean {
+ return TabListData.getTabList().any { SbPattern.eventNamePattern.matches(it) } &&
+ TabListData.getTabList().any { SbPattern.eventTimeEndsPattern.matches(it) }
+}
+
+private fun getBroodmotherLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.broodmotherPattern.matches(it) })
+}
+
+private fun getBroodmotherShowWhen(): Boolean {
+ return getSbLines().any { SbPattern.broodmotherPattern.matches(it) }
+}
+
+private fun getNewYearLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.newYearPattern.matches(it) })
+}
+
+private fun getNewYearShowWhen(): Boolean {
+ return getSbLines().any { SbPattern.newYearPattern.matches(it) }
+}
+
+private fun getOringoLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.travelingZooPattern.matches(it) })
+}
+
+private fun getOringoShowWhen(): Boolean {
+ return getSbLines().any { SbPattern.travelingZooPattern.matches(it) }
+}
+
+private fun getMiningEventsLines() = buildList {
+ // Wind
+ if (getSbLines().any { SbPattern.windCompassPattern.matches(it) }
+ && getSbLines().any { SbPattern.windCompassArrowPattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.windCompassPattern.matches(it) })
+ add("| ${getSbLines().first { SbPattern.windCompassArrowPattern.matches(it) }} §f|")
+ }
+
+ // Better Together
+ if (getSbLines().any { SbPattern.nearbyPlayersPattern.matches(it) }) {
+ add("§dBetter Together")
+ add(" ${getSbLines().first { SbPattern.nearbyPlayersPattern.matches(it) }}")
+ }
+
+ // Zone Events
+ if (getSbLines().any { SbPattern.miningEventPattern.matches(it) }
+ && getSbLines().any { SbPattern.miningEventZonePattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.miningEventPattern.matches(it) }.removePrefix("Event: "))
+ add("in ${getSbLines().first { SbPattern.miningEventZonePattern.matches(it) }.removePrefix("Zone: ")}")
+ }
+
+ // Zone Events but no Zone Line
+ if (getSbLines().any { SbPattern.miningEventPattern.matches(it) }
+ && getSbLines().none { SbPattern.miningEventZonePattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.miningEventPattern.matches(it) }
+ .removePrefix("Event: "))
+ }
+
+ // Mithril Gourmand
+ if (getSbLines().any { SbPattern.mithrilRemainingPattern.matches(it) }
+ && getSbLines().any { SbPattern.mithrilYourMithrilPattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.mithrilRemainingPattern.matches(it) })
+ add(getSbLines().first { SbPattern.mithrilYourMithrilPattern.matches(it) })
+ }
+
+ // Raffle
+ if (getSbLines().any { SbPattern.raffleTicketsPattern.matches(it) }
+ && getSbLines().any { SbPattern.rafflePoolPattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.raffleTicketsPattern.matches(it) })
+ add(getSbLines().first { SbPattern.rafflePoolPattern.matches(it) })
+ }
+
+ // Raid
+ if (getSbLines().any { SbPattern.yourGoblinKillsPattern.matches(it) }
+ && getSbLines().any { SbPattern.remainingGoblinPattern.matches(it) }) {
+ add(getSbLines().first { SbPattern.yourGoblinKillsPattern.matches(it) })
+ add(getSbLines().first { SbPattern.remainingGoblinPattern.matches(it) })
+ }
+}
+
+private fun getMiningEventsShowWhen(): Boolean {
+ return inAdvancedMiningIsland()
+}
+
+private fun getDamageLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.bossHPPattern.matches(it) }) +
+ (getSbLines().first { SbPattern.bossDamagePattern.matches(it) })
+}
+
+private fun getDamageShowWhen(): Boolean {
+ return getSbLines().any { SbPattern.bossHPPattern.matches(it) }
+ && getSbLines().any { SbPattern.bossDamagePattern.matches(it) }
+}
+
+private fun getMagmaBossLines() = getSbLines().filter { line ->
+ SbPattern.magmaBossPattern.matches(line)
+ || SbPattern.damageSoakedPattern.matches(line)
+ || SbPattern.killMagmasPattern.matches(line)
+ || SbPattern.killMagmasDamagedSoakedBarPattern.matches(line)
+ || SbPattern.reformingPattern.matches(line)
+ || SbPattern.bossHealthPattern.matches(line)
+ || SbPattern.bossHealthBarPattern.matches(line)
+}
+
+private fun getMagmaBossShowWhen(): Boolean {
+ return SbPattern.magmaChamberPattern.matches(HypixelData.skyBlockArea)
+}
+
+private fun getEssenceLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.essencePattern.matches(it) })
+}
+
+private fun getEssenceShowWhen(): Boolean {
+ return SbPattern.essencePattern.anyMatches(getSbLines())
+}
+
+private fun getEffigiesLines(): List<String> {
+ return listOf(getSbLines().first { RiftBloodEffigies.heartsPattern.matches(it) })
+}
+
+private fun getEffigiesShowWhen(): Boolean {
+ return RiftBloodEffigies.heartsPattern.anyMatches(getSbLines())
+}
+
+private fun getRedstoneLines(): List<String> {
+ return listOf(getSbLines().first { SbPattern.redstonePattern.matches(it) })
+}
+
+private fun getRedstoneShowWhen(): Boolean {
+ return SbPattern.redstonePattern.anyMatches(getSbLines())
+}
+
+private fun getNoneLines(): List<String> {
+ return when {
+ config.informationFilteringConfig.hideEmptyLines -> listOf("<hidden>")
+ else -> listOf("§cNo Event")
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt
new file mode 100644
index 000000000..45c0aa01d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt
@@ -0,0 +1,404 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+
+object ScoreboardPattern {
+ val group = RepoPattern.group("features.gui.customscoreboard")
+
+ // Stats from the scoreboard
+ private val scoreboardGroup = group.group("scoreboard")
+
+ // main scoreboard
+ private val mainSb = scoreboardGroup.group("main")
+ val motesPattern by mainSb.pattern(
+ "motes",
+ "^(§.)*Motes: (§.)*(?<motes>[\\d,]+).*$"
+ )
+ val heatPattern by mainSb.pattern(
+ "heat",
+ "^Heat: (?<heat>.*)$"
+ ) // this line is weird (either text or number), ill leave it as is; it even has different colors?
+ val copperPattern by mainSb.pattern(
+ "copper",
+ "^(§.)*Copper: (§.)*(?<copper>[\\d,]+).*$"
+ )
+ val locationPattern by mainSb.pattern(
+ "location",
+ "^\\s*(?<location>(§7⏣|§5ф) .*)$"
+ )
+ val lobbyCodePattern by mainSb.pattern(
+ "lobbycode",
+ "^\\s*§.((\\d{2}/\\d{2}/\\d{2})|Server closing: [\\d:]+) §8(?<code>.*)\$"
+ )
+ val datePattern by mainSb.pattern(
+ "date",
+ "^\\s*(Late |Early )?(Spring|Summer|Autumn|Winter) \\d{1,2}(st|nd|rd|th)?.*"
+ )
+ val timePattern by mainSb.pattern(
+ "time",
+ "^\\s*§7\\d{1,2}:\\d{2}(?:am|pm) (?<symbol>(§b☽|§e☀|§.⚡|§.☔)).*$"
+ )
+ val footerPattern by mainSb.pattern(
+ "footer",
+ "§e(www|alpha).hypixel.net\$"
+ )
+ val yearVotesPattern by mainSb.pattern(
+ "yearvotes",
+ "(?<yearvotes>^§6Year \\d+ Votes\$)"
+ )
+ val votesPattern by mainSb.pattern(
+ "votes",
+ "(?<votes>§[caebd]\\|+§f\\|+ §(.+)\$)"
+ )
+ val waitingForVotePattern by mainSb.pattern(
+ "waitingforvote",
+ "(§7Waiting for|§7your vote\\.\\.\\.)$"
+ )
+ val northstarsPattern by mainSb.pattern(
+ "northstars",
+ "North Stars: §d(?<northstars>[\\w,]+).*$"
+ )
+ val profileTypePattern by mainSb.pattern(
+ "profiletype",
+ "^\\s*(§7♲ §7Ironman|§a☀ §aStranded|§.Ⓑ §.Bingo).*$"
+ )
+
+ // multi use
+ private val multiUseSb = scoreboardGroup.group("multiuse")
+ val autoClosingPattern by multiUseSb.pattern(
+ "autoclosing",
+ "(§.)*Auto-closing in: §c(\\d{1,2}:)?\\d{1,2}$"
+ )
+ val startingInPattern by multiUseSb.pattern(
+ "startingin",
+ "(§.)*Starting in: §.(\\d{1,2}:)?\\d{1,2}$"
+ )
+ val timeElapsedPattern by multiUseSb.pattern(
+ "timeelapsed",
+ "(§.)*Time Elapsed: (§.)*(?<time>(\\w+[ydhms] ?)+)$"
+ )
+ val instanceShutdownPattern by multiUseSb.pattern(
+ "instanceshutdown",
+ "(§.)*Instance Shutdown: (§.)*(?<time>(\\w+[ydhms] ?)+)$"
+ )
+ val timeLeftPattern by multiUseSb.pattern(
+ "timeleft",
+ "(§.)*Time Left: (§.)*[\\w:,.]*$"
+ )
+
+ // dungeon scoreboard
+ private val dungeonSb = scoreboardGroup.group("dungeon")
+ val keysPattern by dungeonSb.pattern(
+ "keys",
+ "Keys: §.■ §.[✗✓] §.■ §a.x$"
+ )
+ val clearedPattern by dungeonSb.pattern(
+ "cleared",
+ "(§.)*Cleared: (§.)*(?<percent>[\\w,.]+)% (§.)*\\((§.)*(?<score>[\\w,.]+)(§.)*\\)$"
+ )
+ val soloPattern by dungeonSb.pattern(
+ "solo",
+ "§3§lSolo$"
+ )
+ val teammatesPattern by dungeonSb.pattern(
+ "teammates",
+ "(§.)*(?<classAbbv>\\[\\w]) (§.)*(?<username>[a-zA-Z0-9_]{2,16}) ((§.)*(?<classLevel>\\[Lvl?(?<level>[\\w,.]+)?]?)|(§.)*(?<health>[\\w,.]+)(§.)*.?)$"
+ )
+ val floor3GuardiansPattern by dungeonSb.pattern(
+ "floor3guardians",
+ "^§. - §.(Healthy|Reinforced|Laser|Chaos)§a ([\\w,.]?)+§c❤$"
+ )
+
+ // kuudra
+ private val kuudraSb = scoreboardGroup.group("kuudra")
+ val wavePattern by kuudraSb.pattern(
+ "wave",
+ "^(§.)*Wave: (§.)*\\d+(§.)*( §.- §.\\d+:\\d+)?$"
+ )
+ val tokensPattern by kuudraSb.pattern(
+ "tokens",
+ "^(§.)*Tokens: §.[\\w,]+$"
+ )
+ val submergesPattern by kuudraSb.pattern(
+ "submerges",
+ "^(§.)*Submerges In: (§.)*[\\w,?:]+$"
+ )
+
+ // farming
+ private val farmingSb = scoreboardGroup.group("farming")
+ val medalsPattern by farmingSb.pattern(
+ "medals",
+ "§[6fc]§l(GOLD|SILVER|BRONZE) §fmedals: §[6fc]\\d+$"
+ )
+ val lockedPattern by farmingSb.pattern(
+ "locked",
+ "^\\s*§cLocked$"
+ )
+ val cleanUpPattern by farmingSb.pattern(
+ "cleanup",
+ "^\\s*(§.)*Cleanup(§.)*: (§.)*[\\d,.]+%$"
+ )
+ val pastingPattern by farmingSb.pattern(
+ "pasting",
+ "^\\s*§f(Barn )?Pasting§7: (§.)*[\\d,]+%$"
+ )
+ val peltsPattern by farmingSb.pattern(
+ "pelts",
+ "^(§.)*Pelts: (§.)*(?<pelts>[\\d,]+)( (§.)*\\([+-](?<diff>[\\w,.]+)\\))?\$"
+ )
+ val mobLocationPattern by farmingSb.pattern(
+ "moblocation",
+ "^(§.)*Tracker Mob Location:"
+ )
+ val jacobsContestPattern by farmingSb.pattern(
+ "jacobscontest",
+ "^§eJacob's Contest$"
+ )
+ val plotPattern by farmingSb.pattern(
+ "plot",
+ "\\s*§aPlot §7-.*"
+ )
+
+ // mining
+ private val miningSb = scoreboardGroup.group("mining")
+ val powderPattern by miningSb.pattern(
+ "powder",
+ "(§.)*᠅ §f(Gemstone|Mithril)( Powder)?(§.)*:?.*$"
+ )
+ val windCompassPattern by miningSb.pattern(
+ "windcompass",
+ "§9Wind Compass$"
+ )
+ val windCompassArrowPattern by miningSb.pattern(
+ "windcompassarrow",
+ "( )*((§[a-zA-Z0-9]|[⋖⋗≈])+)( )*((§[a-zA-Z0-9]|[⋖⋗≈])+)?( )*"
+ )
+ val miningEventPattern by miningSb.pattern(
+ "miningevent",
+ "^Event: §.§L.*$"
+ )
+ val miningEventZonePattern by miningSb.pattern(
+ "miningeventzone",
+ "^Zone: §.*$"
+ )
+ val raffleUselessPattern by miningSb.pattern(
+ "raffleuseless",
+ "^(Find tickets on the|ground and bring them|to the raffle box)$"
+ )
+ val raffleTicketsPattern by miningSb.pattern(
+ "raffletickets",
+ "^Tickets: §a\\d+ §7\\(\\d{1,3}\\.\\d%\\)$"
+ )
+ val rafflePoolPattern by miningSb.pattern(
+ "rafflepool",
+ "^Pool: §6\\d+§8/500$"
+ )
+ val mithrilUselessPattern by miningSb.pattern(
+ "mithriluseless",
+ "^§7Give Tasty Mithril to Don!$"
+ )
+ val mithrilRemainingPattern by miningSb.pattern(
+ "mithrilremaining",
+ "^Remaining: §a(\\d+ Tasty Mithril|FULL)$"
+ )
+ val mithrilYourMithrilPattern by miningSb.pattern(
+ "mithrilyourmithril",
+ "^Your Tasty Mithril: §c\\d+.*$"
+ )
+ val nearbyPlayersPattern by miningSb.pattern(
+ "nearbyplayers",
+ "^Nearby Players: §.(\\d+|N/A)$"
+ )
+ val uselessGoblinPattern by miningSb.pattern(
+ "uselessgoblin",
+ "^§7Kill goblins!$"
+ )
+ val remainingGoblinPattern by miningSb.pattern(
+ "remaininggoblin", "^Remaining: §a\\d+ goblins$"
+ )
+ val yourGoblinKillsPattern by miningSb.pattern(
+ "yourgoblin", "^Your kills: §c\\d+ ☠( §a\\(\\+\\d+\\))?$"
+ )
+
+ // combat
+ private val combatSb = scoreboardGroup.group("combat")
+ val magmaChamberPattern by combatSb.pattern(
+ "magmachamber",
+ "^Magma Chamber$"
+ )
+ val magmaBossPattern by combatSb.pattern(
+ "magmaboss",
+ "^§7Boss: §[c6e]\\d{1,3}%$"
+ )
+ val damageSoakedPattern by combatSb.pattern(
+ "damagesoaked",
+ "^§7Damage Soaked:"
+ )
+ val killMagmasPattern by combatSb.pattern(
+ "killmagmas",
+ "^§6Kill the Magmas:$"
+ )
+ val killMagmasDamagedSoakedBarPattern by combatSb.pattern(
+ "killmagmasbar",
+ "^((§.)*▎+)+.*$"
+ )
+ val reformingPattern by combatSb.pattern(
+ "magmareforming",
+ "^§cThe boss is reforming!$"
+ )
+ val bossHealthPattern by combatSb.pattern(
+ "magmabosshealth",
+ "^§7Boss Health:$"
+ )
+ val bossHealthBarPattern by combatSb.pattern(
+ "magmabosshealthbar",
+ "^§.(\\d{1,2}(\\.\\d)?M|\\d{1,3}k)§f/§a10M§c❤$"
+ )
+ val broodmotherPattern by combatSb.pattern(
+ "broodmother",
+ "^§4Broodmother§7: §[e64](Slain|Dormant|Soon|Awakening|Imminent|Alive!)$"
+ )
+ val bossHPPattern by combatSb.pattern(
+ "bosshp",
+ "^(Protector|Dragon) HP: §a(,?\\d{1,3})* §c❤$"
+ )
+ val bossDamagePattern by combatSb.pattern(
+ "bossdamage",
+ "^Your Damage: §c(,?\\d{1,3}(\\.\\d)?)*$"
+ )
+ val slayerQuestPattern by combatSb.pattern(
+ "slayerquest",
+ "^Slayer Quest$"
+ )
+
+ // misc
+ private val miscSb = scoreboardGroup.group("misc")
+ val essencePattern by miscSb.pattern(
+ "essence",
+ "^\\s*(.*)?Essence: §.(?<essence>-?\\d+(:?,\\d{3})*(?:\\.\\d+)?)$"
+ )
+ val brokenRedstonePattern by miscSb.pattern(
+ "brokenredstone",
+ "\\s*e: §e§b\\d{1,3}%$"
+ )
+ val redstonePattern by miscSb.pattern(
+ "redstone",
+ "\\s*(§.)*⚡ §cRedstone: (§.)*\\d{1,3}%$"
+ )
+ val visitingPattern by miscSb.pattern(
+ "visiting",
+ "^\\s*§a✌ §7\\(§.\\d+(§.)?/\\d+(§.)?\\)$"
+ )
+ val flightDurationPattern by miscSb.pattern(
+ "flightduration",
+ "^\\s*Flight Duration: §a(:?\\d{1,3})*$"
+ )
+ val dojoChallengePattern by miscSb.pattern(
+ "dojochallenge",
+ "^(§.)*Challenge: (§.)*(?<challenge>[\\w ]+)$"
+ )
+ val dojoDifficultyPattern by miscSb.pattern(
+ "dojodifficulty",
+ "^(§.)*Difficulty: (§.)*(?<difficulty>[\\w ]+)$"
+ )
+ val dojoPointsPattern by miscSb.pattern(
+ "dojopoints",
+ "^(§.)*Points: (§.)*(?<points>[\\w,.]+) ?( (§.)*\\((§.)*[+-](§.)*(?<difference>[\\w,.]+)(§.)*\\))?\$"
+ )
+ val dojoTimePattern by miscSb.pattern(
+ "dojotime",
+ "^(§.)*Time: (§.)*(?<time>(?<seconds>\\w+s))( (§.)*\\((§.)*[+-](§.)*(?<difference>[\\w,.]+)(§.)*\\))?$"
+ )
+ val objectivePattern by miscSb.pattern(
+ "objective",
+ "^(§.)*(Objective|Quest).*"
+ )
+ // this thirdObjectiveLinePattern includes all those weird objective lines that go into a third scoreboard line
+ val thirdObjectiveLinePattern by miscSb.pattern(
+ "thirdobjectiveline",
+ "(\\s*§.\\(§.\\w+§./§.\\w+§.\\)|§f Mages.*|§f Barbarians.*|§edefeat Kuudra|§eand stun him)"
+ )
+ // collection of lines that just randomly exist and I have no clue how on earth to effectively remove them
+ val wtfAreThoseLinesPattern by miscSb.pattern(
+ "wtfarethoselines",
+ "^§eMine 10 Rubies$"
+ )
+ val darkAuctionCurrentItemPattern by miscSb.pattern(
+ "darkauction.currentitem",
+ "^Current Item:$"
+ )
+
+ // events
+ private val eventsSb = scoreboardGroup.group("events")
+ val travelingZooPattern by eventsSb.pattern(
+ "travelingzoo",
+ "§aTraveling Zoo§f \\d{0,2}:\\d{2}$"
+ )
+ val newYearPattern by eventsSb.pattern(
+ "newyear",
+ "§dNew Year Event!§f \\d{0,2}?:?\\d{2}$"
+ )
+ val spookyPattern by eventsSb.pattern(
+ "spooky",
+ "§6Spooky Festival§f \\d{0,2}?:?\\d{2}$"
+ )
+ val winterEventStartPattern by eventsSb.pattern(
+ "wintereventstart",
+ "(§.)*Event Start: §.\\d+:\\d+$"
+ )
+ val winterNextWavePattern by eventsSb.pattern(
+ "wintereventnextwave",
+ "(§.)*Next Wave: (§.)*(\\d+:\\d+|Soon!)$"
+ )
+ val winterWavePattern by eventsSb.pattern(
+ "wintereventwave",
+ "(§.)*Wave \\d+$"
+ )
+ val winterMagmaLeftPattern by eventsSb.pattern(
+ "wintereventmagmaleft",
+ "(§.)*Magma Cubes Left: §.\\d+$"
+ )
+ val winterTotalDmgPattern by eventsSb.pattern(
+ "wintereventtotaldmg",
+ "(§.)*Your Total Damage: §.\\d+( §e\\(#\\d+\\)?)?$"
+ )
+ val winterCubeDmgPattern by eventsSb.pattern(
+ "wintereventcubedmg",
+ "(§.)*Your Cube Damage: §.\\d+$"
+ )
+
+ // rift
+ private val riftSb = scoreboardGroup.group("rift")
+ val riftDimensionPattern by riftSb.pattern(
+ "dimension",
+ "^\\s*§fRift Dimension$"
+ )
+
+
+ // Stats from the tablist
+ private val tablistGroup = group.group("tablist")
+ val gemsPattern by tablistGroup.pattern(
+ "gems",
+ "^\\s*Gems: §a(?<gems>\\d*,?(\\.\\d+)?[a-zA-Z]?)$"
+ )
+ val bankPattern by tablistGroup.pattern(
+ "bank",
+ "^\\s*Bank: §6(?<bank>[\\w.,]+(?:§7 \\/ §6(?<coop>[\\w.,]+))?)$"
+ )
+ val mithrilPowderPattern by tablistGroup.pattern(
+ "mithrilpowder",
+ "^\\s*Gemstone Powder: (?:§.)+(?<mithrilpowder>[\\d,\\.]+)$"
+ )
+ val gemstonePowderPattern by tablistGroup.pattern(
+ "gemstonepowder",
+ "^\\s*Gemstone Powder: (?:§.)+(?<gemstonepowder>[\\d,\\.]+)$"
+ )
+ val eventNamePattern by tablistGroup.pattern(
+ "event",
+ "^\\s*§e§lEvent: §r(?<name>§.*)$"
+ )
+ val eventTimeEndsPattern by tablistGroup.pattern(
+ "eventtime",
+ "^\\s+Ends In: §r§e(?<time>.*)$"
+ )
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/UnknownLinesHandler.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/UnknownLinesHandler.kt
new file mode 100644
index 000000000..5fe788983
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/UnknownLinesHandler.kt
@@ -0,0 +1,156 @@
+package at.hannibal2.skyhanni.features.gui.customscoreboard
+
+import at.hannibal2.skyhanni.data.BitsAPI
+import at.hannibal2.skyhanni.data.PurseAPI
+import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.features.misc.ServerRestartTitle
+import at.hannibal2.skyhanni.features.rift.area.stillgorechateau.RiftBloodEffigies
+import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.StringUtils.removeResets
+import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern as SbPattern
+
+object UnknownLinesHandler {
+ fun handleUnknownLines() {
+ val sidebarLines = ScoreboardData.sidebarLinesFormatted
+
+ unknownLines = sidebarLines.toMutableList().filter { it.isNotBlank() }.map { it.removeResets() }
+
+ /*
+ * remove with pattern
+ */
+ val patternsToExclude = listOf(
+ PurseAPI.coinsPattern,
+ SbPattern.motesPattern,
+ BitsAPI.bitsScoreboardPattern,
+ SbPattern.heatPattern,
+ SbPattern.copperPattern,
+ SbPattern.locationPattern,
+ SbPattern.lobbyCodePattern,
+ SbPattern.datePattern,
+ SbPattern.timePattern,
+ SbPattern.footerPattern,
+ SbPattern.yearVotesPattern,
+ SbPattern.votesPattern,
+ SbPattern.waitingForVotePattern,
+ SbPattern.northstarsPattern,
+ SbPattern.profileTypePattern,
+ SbPattern.autoClosingPattern,
+ SbPattern.startingInPattern,
+ SbPattern.timeElapsedPattern,
+ SbPattern.keysPattern,
+ SbPattern.clearedPattern,
+ SbPattern.soloPattern,
+ SbPattern.teammatesPattern,
+ SbPattern.floor3GuardiansPattern,
+ SbPattern.wavePattern,
+ SbPattern.tokensPattern,
+ SbPattern.submergesPattern,
+ SbPattern.medalsPattern,
+ SbPattern.lockedPattern,
+ SbPattern.cleanUpPattern,
+ SbPattern.pastingPattern,
+ SbPattern.peltsPattern,
+ SbPattern.mobLocationPattern,
+ SbPattern.jacobsContestPattern,
+ SbPattern.plotPattern,
+ SbPattern.powderPattern,
+ SbPattern.windCompassPattern,
+ SbPattern.windCompassArrowPattern,
+ SbPattern.miningEventPattern,
+ SbPattern.miningEventZonePattern,
+ SbPattern.raffleUselessPattern,
+ SbPattern.raffleTicketsPattern,
+ SbPattern.rafflePoolPattern,
+ SbPattern.mithrilUselessPattern,
+ SbPattern.mithrilRemainingPattern,
+ SbPattern.mithrilYourMithrilPattern,
+ SbPattern.nearbyPlayersPattern,
+ SbPattern.uselessGoblinPattern,
+ SbPattern.remainingGoblinPattern,
+ SbPattern.yourGoblinKillsPattern,
+ SbPattern.magmaBossPattern,
+ SbPattern.damageSoakedPattern,
+ SbPattern.killMagmasPattern,
+ SbPattern.killMagmasDamagedSoakedBarPattern,
+ SbPattern.reformingPattern,
+ SbPattern.bossHealthPattern,
+ SbPattern.bossHealthBarPattern,
+ SbPattern.broodmotherPattern,
+ SbPattern.bossHPPattern,
+ SbPattern.bossDamagePattern,
+ SbPattern.slayerQuestPattern,
+ SbPattern.essencePattern,
+ SbPattern.brokenRedstonePattern,
+ SbPattern.redstonePattern,
+ SbPattern.visitingPattern,
+ SbPattern.flightDurationPattern,
+ SbPattern.dojoChallengePattern,
+ SbPattern.dojoDifficultyPattern,
+ SbPattern.dojoPointsPattern,
+ SbPattern.dojoTimePattern,
+ SbPattern.objectivePattern,
+ ServerRestartTitle.restartingGreedyPattern,
+ SbPattern.travelingZooPattern,
+ SbPattern.newYearPattern,
+ SbPattern.spookyPattern,
+ SbPattern.winterEventStartPattern,
+ SbPattern.winterNextWavePattern,
+ SbPattern.winterWavePattern,
+ SbPattern.winterMagmaLeftPattern,
+ SbPattern.winterTotalDmgPattern,
+ SbPattern.winterCubeDmgPattern,
+ SbPattern.riftDimensionPattern,
+ RiftBloodEffigies.heartsPattern,
+ SbPattern.wtfAreThoseLinesPattern,
+ SbPattern.timeLeftPattern,
+ SbPattern.darkAuctionCurrentItemPattern,
+ )
+
+ unknownLines = unknownLines.filterNot { line ->
+ patternsToExclude.any { pattern -> pattern.matches(line) }
+ }
+
+
+ /*
+ * remove known text
+ */
+ // remove objectives
+ val objectiveLine =
+ sidebarLines.firstOrNull { SbPattern.objectivePattern.matches(it) }
+ ?: "Objective"
+ unknownLines = unknownLines.filter { sidebarLines.nextAfter(objectiveLine) != it }
+ unknownLines = unknownLines.filter {
+ sidebarLines.nextAfter(objectiveLine, 2) != it
+ && !SbPattern.thirdObjectiveLinePattern.matches(it)
+ }
+
+ // Remove jacobs contest
+ for (i in 1..3)
+ unknownLines = unknownLines.filter {
+ sidebarLines.nextAfter(sidebarLines.firstOrNull { line ->
+ SbPattern.jacobsContestPattern.matches(line)
+ } ?: "§eJacob's Contest", i) != it
+ }
+
+ // Remove slayer
+ for (i in 1..2)
+ unknownLines = unknownLines.filter {
+ sidebarLines.nextAfter(sidebarLines.firstOrNull { line ->
+ SbPattern.slayerQuestPattern.matches(line)
+ } ?: "Slayer Quest", i) != it
+ }
+
+ // remove trapper mob location
+ unknownLines = unknownLines.filter {
+ sidebarLines.nextAfter(sidebarLines.firstOrNull { line ->
+ SbPattern.mobLocationPattern.matches(line)
+ } ?: "Tracker Mob Location:") != it
+ }
+
+ // da
+ unknownLines = unknownLines.filter { sidebarLines.nextAfter(sidebarLines.firstOrNull { line ->
+ SbPattern.darkAuctionCurrentItemPattern.matches(line)
+ } ?: "Current Item:") != it }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/MaxPurseItems.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/MaxPurseItems.kt
index d835fc5a8..53b74c7f7 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/MaxPurseItems.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/MaxPurseItems.kt
@@ -45,7 +45,7 @@ class MaxPurseItems {
for (info in item.getLore()) {
orderPattern.matchMatcher(info) {
// +0.1 because I expect people to use the gold nugget option
- buyOrderPrice = group("coins").formatDouble()?.let { it + 0.1 } ?: 0.0
+ buyOrderPrice = group("coins").formatDouble() + 0.1
// If we get to this point, we have the instant price because instant is earlier in the list of items
// So we can return
return
@@ -55,7 +55,7 @@ class MaxPurseItems {
createInstantPattern.matchMatcher(name) {
for (info in item.getLore()) {
instantPattern.matchMatcher(info) {
- instantBuyPrice = group("coins").formatDouble() ?: 0.0
+ instantBuyPrice = group("coins").formatDouble()
}
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
index ff27bd43f..2f1c14f53 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
@@ -1,7 +1,7 @@
package at.hannibal2.skyhanni.features.minion
import at.hannibal2.skyhanni.SkyHanniMod
-import at.hannibal2.skyhanni.data.MayorElection
+import at.hannibal2.skyhanni.data.Perk
import at.hannibal2.skyhanni.data.jsonobjects.repo.MinionXPJson
import at.hannibal2.skyhanni.events.IslandChangeEvent
import at.hannibal2.skyhanni.events.LorenzToolTipEvent
@@ -103,7 +103,7 @@ class MinionXp {
// TODO add wisdom and temporary skill exp (Events) to calculation
val baseXp = xp.amount * item.amount
- val xpAmount = if (MayorElection.isPerkActive("Derpy", "MOAR SKILLZ!!!")) {
+ val xpAmount = if (Perk.MOAR_SKILLZ.isActive) {
baseXp * 1.5
} else baseXp
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ServerRestartTitle.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ServerRestartTitle.kt
index 34196acbe..a236daf4e 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/ServerRestartTitle.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ServerRestartTitle.kt
@@ -16,10 +16,17 @@ class ServerRestartTitle {
private val config get() = SkyHanniMod.feature.misc
- private val restartPattern by RepoPattern.pattern(
- "misc.serverrestart.restart",
- "§cServer closing: (?<minutes>\\d+):(?<seconds>\\d+) §8.*"
- )
+ companion object {
+ private val restartingGroup = RepoPattern.group("features.misc.serverrestart")
+ private val restartingPattern by restartingGroup.pattern(
+ "time",
+ "§cServer closing: (?<minutes>\\d+):(?<seconds>\\d+) ?§8.*"
+ )
+ val restartingGreedyPattern by restartingGroup.pattern(
+ "greedy",
+ "§cServer closing:.*"
+ )
+ }
@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
@@ -29,7 +36,7 @@ class ServerRestartTitle {
if (!event.repeatSeconds(1)) return
for (line in ScoreboardData.sidebarLinesFormatted) {
- restartPattern.matchMatcher(line) {
+ restartingPattern.matchMatcher(line) {
try {
val minutes = group("minutes").toInt().minutes
val seconds = group("seconds").toInt().seconds
@@ -39,9 +46,9 @@ class ServerRestartTitle {
LorenzUtils.sendTitle("§cServer Restart in §b$time", 2.seconds)
} catch (e: Throwable) {
ErrorManager.logErrorWithData(
- e, "Error reading server restart time from socreboard",
+ e, "Error reading server restart time from scoreboard",
"line" to line,
- "restartPattern" to restartPattern.pattern(),
+ "restartPattern" to restartingPattern.pattern(),
)
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListReader.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListReader.kt
index 07473f9d9..bba2ff6aa 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListReader.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListReader.kt
@@ -2,14 +2,12 @@ package at.hannibal2.skyhanni.features.misc.compacttablist
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.events.LorenzTickEvent
-import at.hannibal2.skyhanni.mixins.transformers.AccessorGuiPlayerTabOverlay
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.StringUtils.removeResets
import at.hannibal2.skyhanni.utils.StringUtils.removeSFormattingCode
import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpaceAndResets
import at.hannibal2.skyhanni.utils.TabListData
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
-import net.minecraft.client.Minecraft
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
// heavily inspired by SBA code
@@ -99,7 +97,7 @@ object TabListReader {
}
private fun parseFooterAsColumn(): TabColumn? {
- val tabList = Minecraft.getMinecraft().ingameGUI.tabList as AccessorGuiPlayerTabOverlay
+ val tabList = TabListData.getPlayerTabOverlay()
if (tabList.footer_skyhanni == null) {
return null
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListRenderer.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListRenderer.kt
index c6aa9a510..6f2d5d636 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListRenderer.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/compacttablist/TabListRenderer.kt
@@ -3,11 +3,11 @@ package at.hannibal2.skyhanni.features.misc.compacttablist
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.SkipTabListLineEvent
-import at.hannibal2.skyhanni.mixins.transformers.AccessorGuiPlayerTabOverlay
import at.hannibal2.skyhanni.utils.CollectionUtils.filterToMutable
import at.hannibal2.skyhanni.utils.KeyboardManager.isActive
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.TabListData.Companion.getPlayerTabOverlay
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui
@@ -78,7 +78,7 @@ object TabListRenderer {
}
var totalHeight = maxLines * lineHeight
- val tabList = Minecraft.getMinecraft().ingameGUI.tabList as AccessorGuiPlayerTabOverlay
+ val tabList = getPlayerTabOverlay()
var header = listOf<String>()
if (tabList.header_skyhanni != null) {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt
index cd18de35a..be05e11fb 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/rift/area/stillgorechateau/RiftBloodEffigies.kt
@@ -28,15 +28,6 @@ class RiftBloodEffigies {
private val config get() = RiftAPI.config.area.stillgoreChateau.bloodEffigies
- private val patternGroup = RepoPattern.group("rift.area.stillgore.effegies")
- private val effigiesTimerPattern by patternGroup.pattern(
- "respawn",
- "§eRespawn §c(?<time>.*) §7\\(or click!\\)"
- )
- private val effegieHeartPattern by patternGroup.pattern(
- "heart",
- "Effigies: (?<hearts>.*)"
- )
private var locations: List<LorenzVec> = emptyList()
private var effigiesTimes = mapOf(
@@ -48,6 +39,18 @@ class RiftBloodEffigies {
5 to -1L,
)
+ companion object {
+ private val group = RepoPattern.group("rift.area.stillgore.effegies")
+ val effigiesTimerPattern by group.pattern(
+ "respawn",
+ "§eRespawn §c(?<time>.*) §7\\(or click!\\)"
+ )
+ val heartsPattern by group.pattern(
+ "heart",
+ "Effigies: (?<hearts>((§[7c])?⧯)*)"
+ )
+ }
+
@SubscribeEvent
fun onWorldChange(event: LorenzWorldChangeEvent) {
effigiesTimes = mapOf(
@@ -74,7 +77,7 @@ class RiftBloodEffigies {
if (!isEnabled()) return
val line = event.newList.firstOrNull { it.startsWith("Effigies:") } ?: return
- val hearts = effegieHeartPattern.matchMatcher(line) {
+ val hearts = heartsPattern.matchMatcher(line) {
group("hearts")
} ?: return