diff options
Diffstat (limited to 'src/main/java/at/hannibal2')
5 files changed, 503 insertions, 47 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index b7e6d7417..a3143ee36 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -64,6 +64,7 @@ import at.hannibal2.skyhanni.data.jsonobjects.local.VisualWordsJson import at.hannibal2.skyhanni.data.mob.MobData import at.hannibal2.skyhanni.data.mob.MobDebug import at.hannibal2.skyhanni.data.mob.MobDetection +import at.hannibal2.skyhanni.data.model.TabWidget import at.hannibal2.skyhanni.data.repo.RepoManager import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.PreInitFinishedEvent @@ -577,6 +578,7 @@ class SkyHanniMod { loadModule(ChatUtils) loadModule(FixedRateTimerManager()) loadModule(ChromaManager) + loadModule(TabWidget) loadModule(HotmData) loadModule(ContributorManager) loadModule(TabComplete) diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt index 166282582..6f9fdd138 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt @@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.ConfigManager.Companion.gson +import at.hannibal2.skyhanni.data.model.TabWidget import at.hannibal2.skyhanni.events.HypixelJoinEvent import at.hannibal2.skyhanni.events.IslandChangeEvent import at.hannibal2.skyhanni.events.LorenzChatEvent @@ -9,6 +10,7 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.ProfileJoinEvent import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.events.WidgetUpdateEvent import at.hannibal2.skyhanni.features.bingo.BingoAPI import at.hannibal2.skyhanni.features.dungeon.DungeonAPI import at.hannibal2.skyhanni.features.rift.RiftAPI @@ -36,8 +38,7 @@ class HypixelData { private val patternGroup = RepoPattern.group("data.hypixeldata") private val islandNamePattern by patternGroup.pattern( - "islandname", - "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)" + "islandname", "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)" ) private var lastLocRaw = SimpleTimeMark.farPast() @@ -45,51 +46,41 @@ class HypixelData { companion object { private val patternGroup = RepoPattern.group("data.hypixeldata") private val serverIdScoreboardPattern by patternGroup.pattern( - "serverid.scoreboard", - "§7\\d+/\\d+/\\d+ §8(?<servertype>[mM])(?<serverid>\\S+).*" + "serverid.scoreboard", "§7\\d+/\\d+/\\d+ §8(?<servertype>[mM])(?<serverid>\\S+).*" ) private val serverIdTablistPattern by patternGroup.pattern( - "serverid.tablist", - " Server: §r§8(?<serverid>\\S+)" + "serverid.tablist", " Server: §r§8(?<serverid>\\S+)" ) private val lobbyTypePattern by patternGroup.pattern( - "lobbytype", - "(?<lobbyType>.*lobby)\\d+" + "lobbytype", "(?<lobbyType>.*lobby)\\d+" ) private val playerAmountPattern by patternGroup.pattern( - "playeramount", - "^\\s*(?:§.)+Players (?:§.)+\\((?<amount>\\d+)\\)\\s*$" + "playeramount", "^\\s*(?:§.)+Players (?:§.)+\\((?<amount>\\d+)\\)\\s*$" ) private val playerAmountCoopPattern by patternGroup.pattern( - "playeramount.coop", - "^\\s*(?:§.)*Coop (?:§.)*\\((?<amount>\\d+)\\)\\s*$" + "playeramount.coop", "^\\s*(?:§.)*Coop (?:§.)*\\((?<amount>\\d+)\\)\\s*$" ) private val playerAmountGuestingPattern by patternGroup.pattern( - "playeramount.guesting", - "^\\s*(?:§.)*Guests (?:§.)*\\((?<amount>\\d+)\\)\\s*$" + "playeramount.guesting", "^\\s*(?:§.)*Guests (?:§.)*\\((?<amount>\\d+)\\)\\s*$" ) + /** * REGEX-TEST: §r§b§lParty §r§f(4) */ private val dungeonPartyAmountPattern by patternGroup.pattern( - "playeramount.dungeonparty", - "^\\s*(?:§.)+Party (?:§.)+\\((?<amount>\\d+)\\)\\s*$" + "playeramount.dungeonparty", "^\\s*(?:§.)+Party (?:§.)+\\((?<amount>\\d+)\\)\\s*$" ) private val soloProfileAmountPattern by patternGroup.pattern( - "solo.profile.amount", - "^\\s*(?:§.)*Island\\s*$" + "solo.profile.amount", "^\\s*(?:§.)*Island\\s*$" ) private val scoreboardVisitingAmoutPattern by patternGroup.pattern( - "scoreboard.visiting.amount", - "\\s+§.✌ §.\\(§.(?<currentamount>\\d+)§./(?<maxamount>\\d+)\\)" + "scoreboard.visiting.amount", "\\s+§.✌ §.\\(§.(?<currentamount>\\d+)§./(?<maxamount>\\d+)\\)" ) private val guestPattern by patternGroup.pattern( - "guesting.scoreboard", - "SKYBLOCK GUEST" + "guesting.scoreboard", "SKYBLOCK GUEST" ) private val scoreboardTitlePattern by patternGroup.pattern( - "scoreboard.title", - "SK[YI]BLOCK(?: CO-OP| GUEST)?" + "scoreboard.title", "SK[YI]BLOCK(?: CO-OP| GUEST)?" ) /** @@ -97,8 +88,7 @@ class HypixelData { * REGEX-TEST: §5ф §dWizard Tower */ private val skyblockAreaPattern by patternGroup.pattern( - "skyblock.area", - "\\s*§(?<symbol>7⏣|5ф) §(?<color>.)(?<area>.*)" + "skyblock.area", "\\s*§(?<symbol>7⏣|5ф) §(?<color>.)(?<area>.*)" ) var hypixelLive = false @@ -125,12 +115,7 @@ class HypixelData { // Data from locraw var locrawData: JsonObject? = null private var locraw: MutableMap<String, String> = mutableMapOf( - "server" to "", - "gametype" to "", - "lobbyname" to "", - "lobbytype" to "", - "mode" to "", - "map" to "" + "server" to "", "gametype" to "", "lobbyname" to "", "lobbytype" to "", "mode" to "", "map" to "" ) val server get() = locraw["server"] ?: "" @@ -158,7 +143,8 @@ class HypixelData { } ErrorManager.logErrorWithData( - Exception("NoServerId"), "Could not find server id", + Exception("NoServerId"), + "Could not find server id", "islandType" to LorenzUtils.skyBlockIsland, "tablist" to TabListData.getTabList(), "scoreboard" to ScoreboardData.sidebarLinesFormatted @@ -302,10 +288,7 @@ class HypixelData { // So, as requested by Hannibal, use locraw from // NEU and have NEU send it. // Remove this when NEU dependency is removed - if (LorenzUtils.onHypixel && - locrawData == null && - lastLocRaw.passedSince() > 15.seconds - ) { + if (LorenzUtils.onHypixel && locrawData == null && lastLocRaw.passedSince() > 15.seconds) { lastLocRaw = SimpleTimeMark.now() thread(start = true) { Thread.sleep(1000) @@ -342,7 +325,6 @@ class HypixelData { val inSkyBlock = checkScoreboard() if (inSkyBlock) { - checkIsland() checkSidebar() checkCurrentServerId() } @@ -351,6 +333,14 @@ class HypixelData { skyBlock = inSkyBlock } + @SubscribeEvent + fun onTabListUpdate(event: WidgetUpdateEvent) { + when (event.widget) { + TabWidget.AREA -> checkIsland(event) + else -> Unit + } + } + private fun checkProfileName() { if (profileName.isNotEmpty()) return @@ -392,18 +382,23 @@ class HypixelData { noTrade = ironman || stranded || bingo } - private fun checkIsland() { - var foundIsland = "" - TabListData.fullyLoaded = false + private fun checkIsland(event: WidgetUpdateEvent) { + val islandType: IslandType + val foundIsland: String + if (event.isClear()) { + + TabListData.fullyLoaded = false + islandType = IslandType.NONE + foundIsland = "" - TabListData.getTabList().matchFirst(islandNamePattern) { - foundIsland = group("island").removeColor() + } else { TabListData.fullyLoaded = true + // Can not use color coding, because of the color effect (§f§lSKYB§6§lL§e§lOCK§A§L GUEST) + val guesting = guestPattern.matches(ScoreboardData.objectiveTitle.removeColor()) + foundIsland = TabWidget.AREA.matchMatcherFirstLine { group("island").removeColor() } ?: "" + islandType = getIslandType(foundIsland, guesting) } - // Can not use color coding, because of the color effect (§f§lSKYB§6§lL§e§lOCK§A§L GUEST) - val guesting = guestPattern.matches(ScoreboardData.objectiveTitle.removeColor()) - val islandType = getIslandType(foundIsland, guesting) if (skyBlockIsland != islandType) { IslandChangeEvent(islandType, skyBlockIsland).postAndCatch() if (islandType == IslandType.UNKNOWN) { @@ -413,6 +408,9 @@ class HypixelData { loggerIslandChange.log(islandType.name) } skyBlockIsland = islandType + if (TabListData.fullyLoaded) { + TabWidget.reSendEvents() + } } } diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt new file mode 100644 index 000000000..d459e2a9e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt @@ -0,0 +1,438 @@ +package at.hannibal2.skyhanni.data.model + +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.events.WidgetUpdateEvent +import at.hannibal2.skyhanni.utils.CollectionUtils.editCopy +import at.hannibal2.skyhanni.utils.CollectionUtils.getOrNull +import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Matcher +import java.util.regex.Pattern + +private val repoGroup by RepoPattern.exclusiveGroup("tab.widget.enum") + +/** + * This class defines various widgets within the tab list, specifically focusing on the reading of the values. + * Each enum value corresponds to a distinct widget in the tab list, ensuring no overlap between them. + * The general info widget is broken up into multiple smaller ones. + * The class facilitates access to the lines associated with each widget and triggers events when a widget undergoes changes or becomes invisible. + */ +enum class TabWidget( + pattern0: String, +) { + PLAYER_LIST( + // language=RegExp + "(?:§.)*Players (?:§.)*\\(\\d+\\)" + ), + + /** This line holds no information, only here because every widget must be present */ + INFO( + // language=RegExp + "(?:§.)*Info" + ), + AREA( + // language=RegExp + "(?:§.)*(Area|Dungeon): (?:§.)*(?<island>.*)" + ), + SERVER( + // language=RegExp + "Server: (?:§.)*(?<serverid>.*)" + ), + GEMS( + // language=RegExp + "Gems: (?:§.)*(?<gems>.*)" + ), + FAIRY_SOULS( + // language=RegExp + "Fairy Souls: (?:§.)*(?<got>\\d+)(?:§.)*\\/(?:§.)*(?<max>\\d+)" + ), + PROFILE( + // language=RegExp + "(?:§.)*Profile: (?:§.)*(?<profile>\\S+).*" + ), + SB_LEVEL( + // language=RegExp + "SB Level(?:§.)*: (?:§.)*\\[(?:§.)*(?<level>\\d+)(?:§.)*\\] (?:§.)*(?<xp>\\d+).*" + ), + BANK( + // language=RegExp + "Bank: (?:§.)*(?<amount>[^§]+)(?:(?:§.)* \\/ (?:§.)*(?<personal>.*))?" + ), + INTEREST( + // language=RegExp + "Interest: (?:§.)*(?<time>[^§]+)(?:§.)* \\((?<amount>[^)]+)\\)" + ), + SOULFLOW( + // language=RegExp + "Soulflow: (?:§.)*(?<amount>.*)" + ), + PET( + // language=RegExp + "(?:§.)*Pet:" + ), + PET_TRANING( + // language=RegExp + "(?:§.)*Pet Training:" + ), + PET_SITTER( + // language=RegExp + "Kat: .*" + ), + FIRE_SALE( + // language=RegExp + "(?:§.)*Fire Sales: .*" + ), + ELECTION( + // language=RegExp + "(?:§.)*Election: (?:§.)*(?<time>.*)" + ), + EVENT( + // language=RegExp + "(?:§.)*Event: (?:§.)*(?<event>.*)" + ), + SKILLS( + // language=RegExp + "(?:§.)*Skills: ?(?:§.)*(?<avg>[\\d.]*)" + ), + STATS( + // language=RegExp + "(?:§.)*Stats:" + ), + GUESTS( + // language=RegExp + "(?:§.)*Guests (?:§.)*.*" + ), + COOP( + // language=RegExp + "(?:§.)*Coop (?:§.)*.*" + ), + MINION( + // language=RegExp + "(?:§.)*Minions: (?:§.)*(?<used>\\d+)(?:§.)*/(?:§.)*(?<max>\\d+)" + ), + JERRY_ISLAND_CLOSING( + // language=RegExp + "Island closes in: (?:§.)*(?<time>.*)" + ), + NORTH_STARS( + // language=RegExp + "North Stars: (?:§.)*(?<amount>\\d+)" + ), + COLLECTION( + // language=RegExp + "(?:§.)*Collection:" + ), + JACOB_CONTEST( + // language=RegExp + "(?:§.)*Jacob's Contest:.*" + ), + SLAYER( + // language=RegExp + "(?:§.)*Slayer:" + ), + DAILY_QUESTS( + // language=RegExp + "(?:§.)*Daily Quests:" + ), + ACTIVE_EFFECTS( + // language=RegExp + "(?:§.)*Active Effects: (?:§.)*\\((?<amount>\\d+)\\)" + ), + BESTIARY( + // language=RegExp + "(?:§.)*Bestiary:" + ), + ESSENCE( + // language=RegExp + "(?:§.)*Essence:.*" + ), + FORGE( + // language=RegExp + "(?:§.)*Forges:" + ), + TIMERS( + // language=RegExp + "(?:§.)*Timers:" + ), + DUNGEON_STATS( + // language=RegExp + "Opened Rooms: (?:§.)*(?<opend>\\d+)" + ), + PARTY( + // language=RegExp + "(?:§.)*Party:.*" + ), + TRAPPER( + // language=RegExp + "(?:§.)*Trapper:" + ), + COMMISSIONS( + // language=RegExp + "(?:§.)*Commissions:" + ), + POWDER( + // language=RegExp + "(?:§.)*Powders:" + ), + CRYSTAL( + // language=RegExp + "(?:§.)*Crystals:" + ), + UNCLAIMED_CHESTS( + // language=RegExp + "Unclaimed chests: (?:§.)*(?<amount>\\d+)" + ), + RAIN( + // language=RegExp + "(?<type>Thunder|Rain): (?:§.)*(?<time>.*)" + ), + BROODMOTHER( + // language=RegExp + "Broodmother: (?:§.)*(?<time>.*)" + ), + EYES_PLACED( + // language=RegExp + "Eyes placed: (?:§.)*(?<amount>\\d).*|(?:§.)*Dragon spawned!|(?:§.)*Egg respawning!" + ), + PROTECTOR( + // language=RegExp + "Protector: (?:§.)*(?<time>.*)" + ), + DRAGON( + // language=RegExp + "(?:§.)*Dragon: (?:§.)*\\((?<type>[^)])\\)" + ), + VOLCANO( + // language=RegExp + "Volcano: (?:§.)*(?<time>.*)" + ), + REPUTATION( + // language=RegExp + "(?:§.)*(Barbarian|Mage) Reputation:" + ), + FACTION_QUESTS( + // language=RegExp + "(?:§.)*Faction Quests:" + ), + TROPHY_FISH( + // language=RegExp + "(?:§.)*Trophy Fish:" + ), + RIFT_INFO( + // language=RegExp + "(?:§.)*Good to know:" + ), + RIFT_SHEN( + // language=RegExp + "(?:§.)*Shen: (?:§.)*\\((?<time>[^)])\\)" + ), + RIFT_BARRY( + // language=RegExp + "(?:§.)*Advertisement:" + ), + COMPOSTER( + // language=RegExp + "(?:§.)*Composter:" + ), + GARDEN_LEVEL( + // language=RegExp + "Garden Level: (?:§.)*(?<level>.*)" + ), + COPPER( + // language=RegExp + "Copper: (?:§.)*(?<amount>\\d+)" + ), + PESTS( + // language=RegExp + "(?:§.)*Pests:" + ), + VISITORS( + // language=RegExp + "(?:§.)*Visitors: (?:§.)*\\((?<count>\\d+)\\)" + ), + CROP_MILESTONE( + // language=RegExp + "(?:§.)*Crop Milestones:" + ), + PRIVATE_ISLAND_CRYSTALS( + // language=RegExp + "Crystals: (?:§.)*(?<count>\\d+)" + ), + OLD_PET_SITTER( + // language=RegExp + "Pet Sitter:.*" + ), + DUNGEON_HUB_PROGRESS( + // language=RegExp + "(?:§.)*Dungeons:" + ), + DUNGEON_PUZZLE( + // language=RegExp + "(?:§.)*Puzzles: (?:§.)*\\((?<amount>\\d+)\\)" + ), + DUNGEON_PARTY( + // language=RegExp + "(?:§.)*Party (?:§.)*\\(\\d+\\)" + ), + DUNGEON_PLAYER_STATS( + // language=RegExp + "(?:§.)*Player Stats" + ), + DUNGEON_SKILLS_AND_STATS( + // language=RegExp + "(?:§.)*Skills: (?:§.)*\\w+ \\d+: (?:§.)*[\\d.]+%" + ), + + /** This line holds no information, only here because every widget must be present */ + DUNGEON_ACCOUNT_INFO_LINE( + // language=RegExp + "(?:§.)*Account Info" + ), + DUNGEON_STATS_LINE( + // language=RegExp + "(?:§.)*Dungeon Stats" + ), + FROZEN_CORPSES( + // language=RegExp + "§b§lFrozen Corpses:" + ), + + ; + + /** The pattern for the first line of the widget*/ + val pattern by repoGroup.pattern(name.replace("_", ".").lowercase(), "\\s*$pattern0") + + /** The current active information from tab list. + * + * When the widget isn't visible it will be empty + * */ + var lines: List<String> = emptyList() + private set + + /** Both are inclusive */ + var boundary = -1 to -1 + + /** Is this widget currently visible in the tab list */ + var isActive: Boolean = false + private set + + /** Internal value for the checking to set [isActive] */ + private var gotChecked = false + + /** A [matchMatcher] for the first line using the pattern from the widget*/ + inline fun <T> matchMatcherFirstLine(consumer: Matcher.() -> T) = + if (isActive) + pattern.matchMatcher(lines.first(), consumer) + else null + + private fun postNewEvent(lines: List<String>) { + // Prevent Post if lines are equal + if (lines == this.lines) return + this.lines = lines + isActive = true + WidgetUpdateEvent(this, lines).postAndCatch() + } + + private fun postClearEvent() { + lines = emptyList() + WidgetUpdateEvent(this, lines).postAndCatch() + } + + /** Update the state of the widget, posts the clear if [isActive] == true && [gotChecked] == false */ + private fun updateIsActive() { + if (isActive == gotChecked) return + isActive = gotChecked + if (!gotChecked) { + postClearEvent() + } + } + + companion object { + + /** The index for the start of each Widget (inclusive) */ + private val separatorIndexes = mutableListOf<Pair<Int, TabWidget?>>() + + /** Patterns that where loaded from a future version*/ + private var extraPatterns: List<Pattern> = emptyList() + + init { + entries.forEach { it.pattern } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onTabListUpdate(event: TabListUpdateEvent) { + if (!LorenzUtils.inSkyBlock) { + if (separatorIndexes.isNotEmpty()) { + separatorIndexes.forEach { it.second?.updateIsActive() } + separatorIndexes.clear() + } + return + } + + val tabList = filterTabList(event.tabList) + + separatorIndexes.clear() + + for ((index, line) in tabList.withIndex()) { + val match = entries.firstOrNull { it.pattern.matches(line) } + ?: if (extraPatterns.any { it.matches(line) }) null else continue + separatorIndexes.add(index to match) + } + separatorIndexes.add(tabList.size to null) + + separatorIndexes.zipWithNext { (firstIndex, widget), (secondIndex, _) -> + widget?.boundary = firstIndex to secondIndex - 1 + widget?.gotChecked = true + widget?.postNewEvent(tabList.subList(firstIndex, secondIndex).filter { it.isNotEmpty() }) + } + + entries.forEach { it.updateIsActive() } + + separatorIndexes.forEach { + it.second?.gotChecked = false + } + } + + @SubscribeEvent(priority = EventPriority.LOW) + fun onRepoReload(event: RepositoryReloadEvent) { + extraPatterns = repoGroup.getUnusedPatterns() + } + + private fun filterTabList(tabList: List<String>): List<String> { + var playerListFound = false + var infoFound = false + + val headers = generateSequence(0) { it + 20 }.take(4).map { it to tabList.getOrNull(it) } + + val removeIndexes = mutableListOf<Int>() + + for ((index, header) in headers) when { + PLAYER_LIST.pattern.matches(header) -> if (playerListFound) removeIndexes.add(index - removeIndexes.size) else playerListFound = + true + + INFO.pattern.matches(header) -> if (infoFound) removeIndexes.add(index - removeIndexes.size) else infoFound = + true + } + + return tabList.transformIf({ size > 81 }, { dropLast(size - 80) }).editCopy { + removeIndexes.forEach { + removeAt(it) + } + } + } + + fun reSendEvents() = entries.forEach { + if (it.isActive) { + it.postNewEvent(it.lines) + } else { + it.postClearEvent() + } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt new file mode 100644 index 000000000..60300628f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/WidgetUpdateEvent.kt @@ -0,0 +1,14 @@ +package at.hannibal2.skyhanni.events + +import at.hannibal2.skyhanni.data.model.TabWidget + +/** The events get send on change of the widget and on island switch */ +open class WidgetUpdateEvent( + val widget: TabWidget, + val lines: List<String>, +) : LorenzEvent() { + + fun isWidget(widgetType: TabWidget) = widget == widgetType + + fun isClear() = lines.isEmpty() +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt index 22032ff84..e3df7086c 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.model.TabWidget import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.PacketEvent import at.hannibal2.skyhanni.events.TabListUpdateEvent @@ -70,7 +71,10 @@ object TabListData { val tabHeader = header.conditionalTransform(noColor, { this.removeColor() }, { this }) val tabFooter = footer.conditionalTransform(noColor, { this.removeColor() }, { this }) - val string = "Header:\n\n$tabHeader\n\nBody:\n\n${resultList.joinToString("\n")}\n\nFooter:\n\n$tabFooter" + val widgets = TabWidget.entries.filter { it.isActive } + .joinToString("\n") { "\n${it.name} : \n${it.lines.joinToString("\n")}" } + val string = + "Header:\n\n$tabHeader\n\nBody:\n\n${resultList.joinToString("\n")}\n\nFooter:\n\n$tabFooter\n\nWidgets:$widgets" OSUtils.copyToClipboard(string) ChatUtils.chat("Tab list copied into the clipboard!") |