diff options
Diffstat (limited to 'src/main/kotlin/util/skyblock')
| -rw-r--r-- | src/main/kotlin/util/skyblock/AbilityUtils.kt | 12 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/DungeonUtil.kt | 4 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/ItemType.kt | 8 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/PartyUtil.kt | 210 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/Rarity.kt | 34 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/SBItemUtil.kt | 4 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/SackUtil.kt | 33 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/ScreenIdentification.kt | 52 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/SkyBlockItems.kt | 11 | ||||
| -rw-r--r-- | src/main/kotlin/util/skyblock/TabListAPI.kt | 41 |
10 files changed, 369 insertions, 40 deletions
diff --git a/src/main/kotlin/util/skyblock/AbilityUtils.kt b/src/main/kotlin/util/skyblock/AbilityUtils.kt index 0d7d2b7..9ba182d 100644 --- a/src/main/kotlin/util/skyblock/AbilityUtils.kt +++ b/src/main/kotlin/util/skyblock/AbilityUtils.kt @@ -1,8 +1,8 @@ package moe.nea.firmament.util.skyblock import kotlin.time.Duration -import net.minecraft.item.ItemStack -import net.minecraft.text.Text +import net.minecraft.world.item.ItemStack +import net.minecraft.network.chat.Component import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.directLiteralStringContent import moe.nea.firmament.util.mc.loreAccordingToNbt @@ -17,7 +17,7 @@ object AbilityUtils { val hasPowerScroll: Boolean, val activation: AbilityActivation, val manaCost: Int?, - val descriptionLines: List<Text>, + val descriptionLines: List<Component>, val cooldown: Duration?, ) @@ -40,7 +40,7 @@ object AbilityUtils { } private val abilityNameRegex = "Ability: (?<name>.*?) *".toPattern() - private fun findAbility(iterator: ListIterator<Text>): ItemAbility? { + private fun findAbility(iterator: ListIterator<Component>): ItemAbility? { if (!iterator.hasNext()) { return null } @@ -72,7 +72,7 @@ object AbilityUtils { return null } if (abilityName == null) return null - val descriptionLines = mutableListOf<Text>() + val descriptionLines = mutableListOf<Component>() var manaCost: Int? = null var cooldown: Duration? = null while (iterator.hasNext()) { @@ -121,7 +121,7 @@ object AbilityUtils { ) } - fun getAbilities(lore: List<Text>): List<ItemAbility> { + fun getAbilities(lore: List<Component>): List<ItemAbility> { val iterator = lore.listIterator() val abilities = mutableListOf<ItemAbility>() while (iterator.hasNext()) { diff --git a/src/main/kotlin/util/skyblock/DungeonUtil.kt b/src/main/kotlin/util/skyblock/DungeonUtil.kt index 488b158..c4eb169 100644 --- a/src/main/kotlin/util/skyblock/DungeonUtil.kt +++ b/src/main/kotlin/util/skyblock/DungeonUtil.kt @@ -7,7 +7,7 @@ import moe.nea.firmament.util.TIME_PATTERN object DungeonUtil { val isInDungeonIsland get() = SBData.skyblockLocation == SkyBlockIsland.DUNGEON - private val timeElapsedRegex = "Time Elapsed: $TIME_PATTERN".toRegex() + private val timeElapsedRegex = "Time Elapsed: (?:$TIME_PATTERN\\s*)+".toRegex() val isInActiveDungeon get() = isInDungeonIsland && ScoreboardUtil.simplifiedScoreboardLines.any { it.matches( timeElapsedRegex) } @@ -21,7 +21,7 @@ object DungeonUtil { ' §7♲ §7Ironman' ' ' 'Keys: §c■ §c✗ §8■ §a1x' -'Time Elapsed: §a46s' +'Time Elapsed: §a1m 46s' 'Cleared: §660% §8(105)' ' ' '§e[B] §b151_Dragon §e2,062§c❤' diff --git a/src/main/kotlin/util/skyblock/ItemType.kt b/src/main/kotlin/util/skyblock/ItemType.kt index 7a776b5..887edef 100644 --- a/src/main/kotlin/util/skyblock/ItemType.kt +++ b/src/main/kotlin/util/skyblock/ItemType.kt @@ -1,13 +1,12 @@ package moe.nea.firmament.util.skyblock -import net.minecraft.item.ItemStack +import net.minecraft.world.item.ItemStack import moe.nea.firmament.util.directLiteralStringContent import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.petData -@JvmInline -value class ItemType private constructor(val name: String) { +data class ItemType private constructor(val name: String) { companion object { fun ofName(name: String): ItemType { return ItemType(name) @@ -41,6 +40,7 @@ value class ItemType private constructor(val name: String) { val SWORD = ofName("SWORD") val DRILL = ofName("DRILL") val PICKAXE = ofName("PICKAXE") + val AXE = ofName("AXE") val GAUNTLET = ofName("GAUNTLET") val LONGSWORD = ofName("LONG SWORD") val EQUIPMENT = ofName("EQUIPMENT") @@ -57,6 +57,8 @@ value class ItemType private constructor(val name: String) { val LEGGINGS = ofName("LEGGINGS") val HELMET = ofName("HELMET") val BOOTS = ofName("BOOTS") + val SHOVEL = ofName("SHOVEL") + val NIL = ofName("__NIL") /** diff --git a/src/main/kotlin/util/skyblock/PartyUtil.kt b/src/main/kotlin/util/skyblock/PartyUtil.kt new file mode 100644 index 0000000..46e1aa3 --- /dev/null +++ b/src/main/kotlin/util/skyblock/PartyUtil.kt @@ -0,0 +1,210 @@ +package moe.nea.firmament.util.skyblock + +import java.util.UUID +import net.hypixel.modapi.HypixelModAPI +import net.hypixel.modapi.packet.impl.clientbound.ClientboundPartyInfoPacket +import net.hypixel.modapi.packet.impl.clientbound.ClientboundPartyInfoPacket.PartyRole +import net.hypixel.modapi.packet.impl.serverbound.ServerboundPartyInfoPacket +import org.intellij.lang.annotations.Language +import kotlinx.coroutines.launch +import net.minecraft.network.chat.Component +import moe.nea.firmament.Firmament +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.apis.Routes +import moe.nea.firmament.commands.thenExecute +import moe.nea.firmament.commands.thenLiteral +import moe.nea.firmament.events.CommandEvent +import moe.nea.firmament.events.ProcessChatEvent +import moe.nea.firmament.events.WorldReadyEvent +import moe.nea.firmament.features.debug.DeveloperFeatures +import moe.nea.firmament.util.ErrorUtil +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.bold +import moe.nea.firmament.util.boolColour +import moe.nea.firmament.util.grey +import moe.nea.firmament.util.tr +import moe.nea.firmament.util.useMatch + +object PartyUtil { + object Internal { + val hma = HypixelModAPI.getInstance() + + val handler = hma.createHandler(ClientboundPartyInfoPacket::class.java) { clientboundPartyInfoPacket -> + Firmament.coroutineScope.launch { + party = Party(clientboundPartyInfoPacket.memberMap.values.map { + PartyMember.fromUuid(it.uuid, it.role) + }) + } + } + + fun sendSyncPacket() { + hma.sendPacket(ServerboundPartyInfoPacket()) + } + + @Subscribe + fun onDevCommand(event: CommandEvent.SubCommand) { + event.subcommand(DeveloperFeatures.DEVELOPER_SUBCOMMAND) { + thenLiteral("party") { + thenLiteral("refresh") { + thenExecute { + sendSyncPacket() + source.sendFeedback(tr("firmament.dev.partyinfo.refresh", "Refreshing party info")) + } + } + thenExecute { + val p = party + val text = Component.empty() + text.append( + tr("firmament.dev.partyinfo", "Party Info: ") + .boolColour(p != null) + ) + if (p == null) { + text.append(tr("firmament.dev.partyinfo.empty", "Empty Party").grey()) + } else { + text.append(tr("firmament.dev.partyinfo.count", "${p.members.size} members").grey()) + p.members.forEach { + text.append("\n") + .append(Component.literal(" - ${it.name}")) + .append(" (") + .append( + when (it.role) { + PartyRole.LEADER -> tr("firmament.dev.partyinfo.leader", "Leader").bold() + PartyRole.MOD -> tr("firmament.dev.partyinfo.mod", "Moderator") + PartyRole.MEMBER -> tr("firmament.dev.partyinfo.member", "Member") + } + ) + .append(")") + } + } + source.sendFeedback(text) + } + } + } + } + + object Regexes { + @Language("RegExp") + val NAME = "(\\[[^\\]]+\\] )?(?<name>[a-zA-Z0-9_]{2,16})" + val NAME_SECONDARY = NAME.replace("name", "name2") + val joinSelf = "You have joined $NAME's? party!".toPattern() + val joinOther = "$NAME joined the party\\.".toPattern() + val leaveSelf = "You left the party\\.".toPattern() + val disbandedEmpty = + "The party was disbanded because all invites expired and the party was empty\\.".toPattern() + val leaveOther = "$NAME has left the party\\.".toPattern() + val kickedOther = "$NAME has been removed from the party\\.".toPattern() + val kickedOtherOffline = "Kicked $NAME because they were offline\\.".toPattern() + val disconnectedOther = "$NAME was removed from your party because they disconnected\\.".toPattern() + val transferLeave = "The party was transferred to $NAME because $NAME_SECONDARY left\\.?".toPattern() + val transferVoluntary = "The party was transferred to $NAME by $NAME_SECONDARY\\.?".toPattern() + val disbanded = "$NAME has disbanded the party!".toPattern() + val kickedSelf = "You have been kicked from the party by $NAME ?\\.?".toPattern() + val partyFinderJoin = "Party Finder > $NAME joined the .* group!.*".toPattern() + } + + fun modifyParty( + allowEmpty: Boolean = false, + modifier: (MutableList<PartyMember>) -> Unit + ) { + val oldList = party?.members ?: emptyList() + if (oldList.isEmpty() && !allowEmpty) return + party = Party(oldList.toMutableList().also(modifier)) + } + + fun MutableList<PartyMember>.modifyMember(name: String, mod: (PartyMember) -> PartyMember) { + val idx = indexOfFirst { it.name == name } + val member = if (idx < 0) { + PartyMember(name, PartyRole.MEMBER) + } else { + removeAt(idx) + } + add(mod(member)) + } + + fun addMemberToParty(name: String) { + modifyParty(true) { + if (it.isEmpty()) + it.add(PartyMember(MC.playerName, PartyRole.LEADER)) + it.add(PartyMember(name, PartyRole.MEMBER)) + } + } + + @Subscribe + fun onJoinServer(event: WorldReadyEvent) { // This event isn't perfect... Hypixel isn't ready yet when we join the server. We should probably just listen to the mod api hello packet and go from there, but this works (since you join and leave servers quite often). + if (party == null) + sendSyncPacket() + } + + @Subscribe + fun onPartyRelatedMessage(event: ProcessChatEvent) { + Regexes.joinSelf.useMatch(event.unformattedString) { + sendSyncPacket() + } + Regexes.joinOther.useMatch(event.unformattedString) { + addMemberToParty(group("name")) + } + Regexes.leaveOther.useMatch(event.unformattedString) { + modifyParty { it.removeIf { it.name == group("name") } } + } + Regexes.leaveSelf.useMatch(event.unformattedString) { + modifyParty { it.clear() } + } + Regexes.disbandedEmpty.useMatch(event.unformattedString) { + modifyParty { it.clear() } + } + Regexes.kickedOther.useMatch(event.unformattedString) { + modifyParty { it.removeIf { it.name == group("name") } } + } + Regexes.kickedOtherOffline.useMatch(event.unformattedString) { + modifyParty { it.removeIf { it.name == group("name") } } + } + Regexes.disconnectedOther.useMatch(event.unformattedString) { + modifyParty { it.removeIf { it.name == group("name") } } + } + Regexes.transferLeave.useMatch(event.unformattedString) { + modifyParty { + it.modifyMember(group("name")) { it.copy(role = PartyRole.LEADER) } + it.removeIf { it.name == group("name2") } + } + } + Regexes.transferVoluntary.useMatch(event.unformattedString) { + modifyParty { + it.modifyMember(group("name")) { it.copy(role = PartyRole.LEADER) } + it.modifyMember(group("name2")) { it.copy(role = PartyRole.MOD) } + } + } + Regexes.disbanded.useMatch(event.unformattedString) { + modifyParty { it.clear() } + } + Regexes.kickedSelf.useMatch(event.unformattedString) { + modifyParty { it.clear() } + } + Regexes.partyFinderJoin.useMatch(event.unformattedString) { + addMemberToParty(group("name")) + } + } + } + + data class Party( + val members: List<PartyMember> + ) + + data class PartyMember( + val name: String, + val role: PartyRole + ) { + companion object { + suspend fun fromUuid(uuid: UUID, role: PartyRole = PartyRole.MEMBER): PartyMember { + return PartyMember( + ErrorUtil.notNullOr( + Routes.getPlayerNameForUUID(uuid), + "Could not find username for player $uuid" + ) { "Ghost" }, + role + ) + } + } + } + + var party: Party? = null +} diff --git a/src/main/kotlin/util/skyblock/Rarity.kt b/src/main/kotlin/util/skyblock/Rarity.kt index b19f371..95e5d87 100644 --- a/src/main/kotlin/util/skyblock/Rarity.kt +++ b/src/main/kotlin/util/skyblock/Rarity.kt @@ -7,10 +7,10 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import net.minecraft.item.ItemStack -import net.minecraft.text.Style -import net.minecraft.text.Text -import net.minecraft.util.Formatting +import net.minecraft.world.item.ItemStack +import net.minecraft.network.chat.Style +import net.minecraft.network.chat.Component +import net.minecraft.ChatFormatting import moe.nea.firmament.util.StringUtil.words import moe.nea.firmament.util.collections.lastNotNullOfOrNull import moe.nea.firmament.util.mc.loreAccordingToNbt @@ -31,6 +31,7 @@ enum class Rarity(vararg altNames: String) { SUPREME, SPECIAL, VERY_SPECIAL, + ULTIMATE, UNKNOWN ; @@ -48,22 +49,23 @@ enum class Rarity(vararg altNames: String) { } val names = setOf(name) + altNames - val text: Text get() = Text.literal(name).setStyle(Style.EMPTY.withColor(colourMap[this])) + val text: Component get() = Component.literal(name).setStyle(Style.EMPTY.withColor(colourMap[this])) val neuRepoRarity: RepoRarity? = RepoRarity.entries.find { it.name == name } companion object { // TODO: inline those formattings as fields val colourMap = mapOf( - Rarity.COMMON to Formatting.WHITE, - Rarity.UNCOMMON to Formatting.GREEN, - Rarity.RARE to Formatting.BLUE, - Rarity.EPIC to Formatting.DARK_PURPLE, - Rarity.LEGENDARY to Formatting.GOLD, - Rarity.MYTHIC to Formatting.LIGHT_PURPLE, - Rarity.DIVINE to Formatting.AQUA, - Rarity.SPECIAL to Formatting.RED, - Rarity.VERY_SPECIAL to Formatting.RED, - Rarity.SUPREME to Formatting.DARK_RED, + Rarity.COMMON to ChatFormatting.WHITE, + Rarity.UNCOMMON to ChatFormatting.GREEN, + Rarity.RARE to ChatFormatting.BLUE, + Rarity.EPIC to ChatFormatting.DARK_PURPLE, + Rarity.LEGENDARY to ChatFormatting.GOLD, + Rarity.MYTHIC to ChatFormatting.LIGHT_PURPLE, + Rarity.DIVINE to ChatFormatting.AQUA, + Rarity.SPECIAL to ChatFormatting.RED, + Rarity.VERY_SPECIAL to ChatFormatting.RED, + Rarity.SUPREME to ChatFormatting.DARK_RED, + Rarity.ULTIMATE to ChatFormatting.DARK_RED, ) val byName = entries.flatMap { en -> en.names.map { it to en } }.toMap() val fromNeuRepo = entries.associateBy { it.neuRepoRarity } @@ -87,7 +89,7 @@ enum class Rarity(vararg altNames: String) { fun fromPetItem(itemStack: ItemStack): Rarity? = itemStack.petData?.tier?.let(::fromNeuRepo) - fun fromLore(lore: List<Text>): Rarity? = + fun fromLore(lore: List<Component>): Rarity? = lore.lastNotNullOfOrNull { it.unformattedString.words() .firstNotNullOfOrNull(::fromString) diff --git a/src/main/kotlin/util/skyblock/SBItemUtil.kt b/src/main/kotlin/util/skyblock/SBItemUtil.kt index 3901b60..619a10b 100644 --- a/src/main/kotlin/util/skyblock/SBItemUtil.kt +++ b/src/main/kotlin/util/skyblock/SBItemUtil.kt @@ -1,12 +1,12 @@ package moe.nea.firmament.util.skyblock -import net.minecraft.item.ItemStack +import net.minecraft.world.item.ItemStack import moe.nea.firmament.util.mc.loreAccordingToNbt import moe.nea.firmament.util.unformattedString object SBItemUtil { fun ItemStack.getSearchName(): String { - val name = this.name.unformattedString + val name = this.hoverName.unformattedString if (name.contains("Enchanted Book")) { val enchant = loreAccordingToNbt.firstOrNull()?.unformattedString if (enchant != null) return enchant diff --git a/src/main/kotlin/util/skyblock/SackUtil.kt b/src/main/kotlin/util/skyblock/SackUtil.kt index fd67c44..a69309b 100644 --- a/src/main/kotlin/util/skyblock/SackUtil.kt +++ b/src/main/kotlin/util/skyblock/SackUtil.kt @@ -2,15 +2,18 @@ package moe.nea.firmament.util.skyblock import kotlinx.serialization.Serializable import kotlinx.serialization.serializer -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen -import net.minecraft.text.HoverEvent -import net.minecraft.text.Text +import net.minecraft.client.gui.screens.inventory.ContainerScreen +import net.minecraft.network.chat.HoverEvent +import net.minecraft.network.chat.Component import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.events.ChestInventoryUpdateEvent import moe.nea.firmament.events.ProcessChatEvent +import moe.nea.firmament.gui.config.storage.ConfigFixEvent +import moe.nea.firmament.gui.config.storage.ConfigStorageClass import moe.nea.firmament.repo.ItemNameLookup import moe.nea.firmament.util.SHORT_NUMBER_FORMAT import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.data.Config import moe.nea.firmament.util.data.ProfileSpecificDataHolder import moe.nea.firmament.util.mc.displayNameAccordingToNbt import moe.nea.firmament.util.mc.iterableView @@ -28,18 +31,26 @@ object SackUtil { // val sackTypes: ) - object Store : ProfileSpecificDataHolder<SackContents>(serializer(), "Sacks", ::SackContents) + @Config + object Store : ProfileSpecificDataHolder<SackContents>(serializer(), "sacks", ::SackContents) + + @Subscribe + fun onConfigFix(event: ConfigFixEvent) { + event.on(996, ConfigStorageClass.PROFILE) { + move("Sacks", "sacks") + } + } val items get() = Store.data?.contents ?: mutableMapOf() val storedRegex = "^Stored: (?<stored>$SHORT_NUMBER_FORMAT)/(?<max>$SHORT_NUMBER_FORMAT)$".toPattern() @Subscribe fun storeDataFromInventory(event: ChestInventoryUpdateEvent) { - val screen = event.inventory as? GenericContainerScreen ?: return + val screen = event.inventory as? ContainerScreen ?: return if (!screen.title.unformattedString.endsWith(" Sack")) return - val inv = screen.screenHandler?.inventory ?: return - if (inv.size() < 18) return - val backSlot = inv.getStack(inv.size() - 5) + val inv = screen.menu?.container ?: return + if (inv.containerSize < 18) return + val backSlot = inv.getItem(inv.containerSize - 5) if (backSlot.displayNameAccordingToNbt.unformattedString != "Go Back") return if (backSlot.loreAccordingToNbt.map { it.unformattedString } != listOf("To Sack of Sacks")) return for (itemStack in inv.iterableView) { @@ -63,7 +74,7 @@ object SackUtil { getUpdatesFromMessage(event.text) } - fun getUpdatesFromMessage(text: Text): List<SackUpdate> { + fun getUpdatesFromMessage(text: Component): List<SackUpdate> { val update = ChatUpdate() text.siblings.forEach(update::updateFromHoverText) return update.updates @@ -91,9 +102,9 @@ object SackUtil { } } - fun updateFromHoverText(text: Text) { + fun updateFromHoverText(text: Component) { text.siblings.forEach(::updateFromHoverText) - val hoverText = text.style.hoverEvent?.getValue(HoverEvent.Action.SHOW_TEXT) ?: return + val hoverText = (text.style.hoverEvent as? HoverEvent.ShowText)?.value ?: return val cleanedText = hoverText.unformattedString if (cleanedText.startsWith("Added items:\n")) { if (!foundAdded) { diff --git a/src/main/kotlin/util/skyblock/ScreenIdentification.kt b/src/main/kotlin/util/skyblock/ScreenIdentification.kt new file mode 100644 index 0000000..ff725fa --- /dev/null +++ b/src/main/kotlin/util/skyblock/ScreenIdentification.kt @@ -0,0 +1,52 @@ +package moe.nea.firmament.util.skyblock + +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.ContainerScreen +import moe.nea.firmament.util.mc.displayNameAccordingToNbt +import moe.nea.firmament.util.mc.loreAccordingToNbt +import moe.nea.firmament.util.unformattedString + + +object ScreenIdentification { + private var lastScreen: Screen? = null + private var lastScreenType: ScreenType? = null + + fun getType(screen: Screen?): ScreenType? { + if (screen == null) return null + if (screen !== lastScreen) { + lastScreenType = ScreenType.entries + .find { it.detector(screen) } + lastScreen = screen + } + return lastScreenType + } +} + +enum class ScreenType(val detector: (Screen) -> Boolean) { + BAZAAR_ANY({ + it is ContainerScreen && ( + it.menu.getSlot(it.menu.rowCount * 9 - 4) + .item + .displayNameAccordingToNbt + .unformattedString == "Manage Orders" + || it.menu.getSlot(it.menu.rowCount * 9 - 5) + .item + .loreAccordingToNbt + .any { + it.unformattedString == "To Bazaar" + }) + }), + ENCHANTMENT_GUIDE({ + it.title.unformattedString.endsWith("Enchantments Guide") + }), + SUPER_PAIRS({ + it.title.unformattedString.startsWith("Superpairs") + }), + EXPERIMENTATION_RNG_METER({ + it.title.unformattedString.contains("Experimentation Table RNG") + }), + DYE_COMPENDIUM({ + it.title.unformattedString.contains("Dye Compendium") + }) +} + diff --git a/src/main/kotlin/util/skyblock/SkyBlockItems.kt b/src/main/kotlin/util/skyblock/SkyBlockItems.kt index ca2b17b..5f4acd4 100644 --- a/src/main/kotlin/util/skyblock/SkyBlockItems.kt +++ b/src/main/kotlin/util/skyblock/SkyBlockItems.kt @@ -3,14 +3,25 @@ package moe.nea.firmament.util.skyblock import moe.nea.firmament.util.SkyblockId object SkyBlockItems { + val COINS = SkyblockId("SKYBLOCK_COIN") val ROTTEN_FLESH = SkyblockId("ROTTEN_FLESH") val ENCHANTED_DIAMOND = SkyblockId("ENCHANTED_DIAMOND") val DIAMOND = SkyblockId("DIAMOND") val ANCESTRAL_SPADE = SkyblockId("ANCESTRAL_SPADE") + val ARCHAIC_SPADE = SkyblockId("ARCHAIC_SPADE") + val DEIFIC_SPADE = SkyblockId("DEIFIC_SPADE") val REFORGE_ANVIL = SkyblockId("REFORGE_ANVIL") val SLICE_OF_BLUEBERRY_CAKE = SkyblockId("SLICE_OF_BLUEBERRY_CAKE") val SLICE_OF_CHEESECAKE = SkyblockId("SLICE_OF_CHEESECAKE") val SLICE_OF_GREEN_VELVET_CAKE = SkyblockId("SLICE_OF_GREEN_VELVET_CAKE") val SLICE_OF_RED_VELVET_CAKE = SkyblockId("SLICE_OF_RED_VELVET_CAKE") val SLICE_OF_STRAWBERRY_SHORTCAKE = SkyblockId("SLICE_OF_STRAWBERRY_SHORTCAKE") + val ASPECT_OF_THE_VOID = SkyblockId("ASPECT_OF_THE_VOID") + val ASPECT_OF_THE_END = SkyblockId("ASPECT_OF_THE_END") + val BONE_BOOMERANG = SkyblockId("BONE_BOOMERANG") + val STARRED_BONE_BOOMERANG = SkyblockId("STARRED_BONE_BOOMERANG") + val TRIBAL_SPEAR = SkyblockId("TRIBAL_SPEAR") + val BLOCK_ZAPPER = SkyblockId("BLOCK_ZAPPER") + val HUNTING_TOOLKIT = SkyblockId("HUNTING_TOOLKIT") + val ETHERWARP_CONDUIT = SkyblockId("ETHERWARP_CONDUIT") } diff --git a/src/main/kotlin/util/skyblock/TabListAPI.kt b/src/main/kotlin/util/skyblock/TabListAPI.kt new file mode 100644 index 0000000..43722e0 --- /dev/null +++ b/src/main/kotlin/util/skyblock/TabListAPI.kt @@ -0,0 +1,41 @@ +package moe.nea.firmament.util.skyblock + +import org.intellij.lang.annotations.Language +import net.minecraft.network.chat.Component +import moe.nea.firmament.util.StringUtil.title +import moe.nea.firmament.util.StringUtil.unwords +import moe.nea.firmament.util.mc.MCTabListAPI +import moe.nea.firmament.util.unformattedString + +object TabListAPI { + + fun getWidgetLines(widgetName: WidgetName, includeTitle: Boolean = false, from: MCTabListAPI.CurrentTabList = MCTabListAPI.currentTabList): List<Component> { + return from.body + .dropWhile { !widgetName.matchesTitle(it) } + .takeWhile { it.string.isNotBlank() && !it.string.startsWith(" ") } + .let { if (includeTitle) it else it.drop(1) } + } + + enum class WidgetName(regex: Regex?) { + COMMISSIONS, + SKILLS("Skills:( .*)?"), + PROFILE("Profile: (.*)"), + COLLECTION, + ESSENCE, + PET + ; + + fun matchesTitle(it: Component): Boolean { + return regex.matches(it.unformattedString) + } + + constructor() : this(null) + constructor(@Language("RegExp") regex: String) : this(Regex(regex)) + + val label = + name.split("_").map { it.lowercase().title() }.unwords() + val regex = regex ?: Regex.fromLiteral("$label:") + + } + +} |
