diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index 89f314135541..92f7d7735fc5 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -60,8 +60,9 @@ import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactor import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentsProfitTracker import at.hannibal2.skyhanni.features.mining.KingTalismanHelper import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay -import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker +import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalNucleusTracker import at.hannibal2.skyhanni.features.mining.glacitemineshaft.CorpseTracker +import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.MinionFeatures import at.hannibal2.skyhanni.features.misc.CarryTracker @@ -396,6 +397,11 @@ object Commands { category = CommandCategory.USERS_RESET callback { ExcavatorProfitTracker.resetCommand() } } + event.register("shresetcrystalnucleustracker") { + description = "Resets the Crystal Nucleus Tracker" + category = CommandCategory.USERS_RESET + callback { CrystalNucleusTracker.resetCommand() } + } // non trackers event.register("shresetghostcounter") { diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/CrystalNucleusConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/CrystalNucleusConfig.java new file mode 100644 index 000000000000..4a33127b2d04 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/CrystalNucleusConfig.java @@ -0,0 +1,55 @@ +package at.hannibal2.skyhanni.config.features.chat; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CrystalNucleusConfig { + + @Expose + @ConfigOption(name = "Enabled", desc = "Hide or compact messages relating to Crystal Nucleus runs in the Crystal Hollows.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + public enum CrystalNucleusMessageTypes { + RUN_COMPLETED("§5Run Completed Summary"), + CRYSTAL_COLLECTED("§aC§6r§dy§bs§et§aa§6l §7Collected"), + CRYSTAL_PLACED("§aC§6r§dy§bs§et§aa§6l §7Placed"), + NPC_DIVAN_KEEPERS("§e[NPC] §6Keepers §7(§2Mines of Divan§7)"), + NPC_PROF_ROBOT("§e[NPC] Professor Robot"), + NPC_KING_YOLKAR("§e[NPC] §6King Yolkar"), + NON_TOOL_SCAVENGE("§7Non-Tool §cMetal Detector §7loot") + ; + + private final String name; + + CrystalNucleusMessageTypes(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + @Expose + @ConfigOption(name = "Modified Messages", desc = "Messages that should be hidden or compacted.\n§cImportant information will still appear§7.") + @ConfigEditorDraggableList + public List modifiedMessages = new ArrayList<>(Arrays.asList( + CrystalNucleusMessageTypes.CRYSTAL_COLLECTED, + CrystalNucleusMessageTypes.CRYSTAL_PLACED, + CrystalNucleusMessageTypes.RUN_COMPLETED, + CrystalNucleusMessageTypes.NPC_DIVAN_KEEPERS, + CrystalNucleusMessageTypes.NPC_PROF_ROBOT, + CrystalNucleusMessageTypes.NPC_KING_YOLKAR, + CrystalNucleusMessageTypes.NON_TOOL_SCAVENGE + )); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java index c50a04bae2b6..695dc2ddbd53 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java @@ -11,7 +11,12 @@ public class FilterTypesConfig { @Expose @ConfigOption(name = "Powder Mining", desc = "") @Accordion - public PowderMiningFilterConfig powderMiningFilter = new PowderMiningFilterConfig(); + public PowderMiningConfig powderMining = new PowderMiningConfig(); + + @Expose + @ConfigOption(name = "Crystal Nucleus", desc = "") + @Accordion + public CrystalNucleusConfig crystalNucleus = new CrystalNucleusConfig(); @Expose @ConfigOption(name = "Hypixel Lobbies", desc = "Hide announcements in Hypixel lobbies " + diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningFilterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningConfig.java similarity index 80% rename from src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningFilterConfig.java rename to src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningConfig.java index cb1aae0e5e9c..b2dc68039981 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningFilterConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningConfig.java @@ -13,15 +13,15 @@ import java.util.Arrays; import java.util.List; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.ASCENSION_ROPE; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.JUNGLE_HEART; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.OIL_BARREL; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.SLUDGE_JUICE; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.TREASURITE; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.WISHING_COMPASS; -import static at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.YOGGIE; - -public class PowderMiningFilterConfig { +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.ASCENSION_ROPE; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.JUNGLE_HEART; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.OIL_BARREL; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.SLUDGE_JUICE; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.TREASURITE; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.WISHING_COMPASS; +import static at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.YOGGIE; + +public class PowderMiningConfig { @Expose @ConfigOption(name = "Enabled", desc = "Hide messages while opening chests in the Crystal Hollows.") @ConfigEditorBoolean @@ -34,7 +34,7 @@ public class PowderMiningFilterConfig { "\n§a0§7: §aShow all\n§c60000§7: §cHide all" ) @ConfigEditorSlider(minValue = 0, maxValue = 60000, minStep = 500) - public int powderFilterThreshold = 1000; + public int powderThreshold = 1000; @Expose @ConfigOption( @@ -42,7 +42,7 @@ public class PowderMiningFilterConfig { "\n§a0§7: §aShow all\n§c20§7: §cHide all" ) @ConfigEditorSlider(minValue = 0, maxValue = 20, minStep = 1) - public int essenceFilterThreshold = 5; + public int essenceThreshold = 5; public enum SimplePowderMiningRewardTypes { @@ -86,9 +86,9 @@ public String toString() { @Expose @ConfigOption(name = "Goblin Egg", desc = "Hide Goblin Egg rewards that are below a certain rarity.") @ConfigEditorDropdown - public GoblinEggFilterEntry goblinEggs = GoblinEggFilterEntry.YELLOW_UP; + public GoblinEggEntry goblinEggs = GoblinEggEntry.YELLOW_UP; - public enum GoblinEggFilterEntry { + public enum GoblinEggEntry { SHOW_ALL("Show all"), HIDE_ALL("Hide all"), GREEN_UP("Show §aGreen §7and up"), @@ -98,7 +98,7 @@ public enum GoblinEggFilterEntry { private final String name; - GoblinEggFilterEntry(String name) { + GoblinEggEntry(String name) { this.name = name; } @@ -111,7 +111,6 @@ public String toString() { @Expose @ConfigOption(name = "Gemstones", desc = "") @Accordion - // TODO rename to "gemstoneFilter" - public PowderMiningGemstoneFilterConfig gemstoneFilterConfig = new PowderMiningGemstoneFilterConfig(); + public PowderMiningGemstoneConfig gemstone = new PowderMiningGemstoneConfig(); } diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneFilterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneConfig.java similarity index 98% rename from src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneFilterConfig.java rename to src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneConfig.java index e2b1c5d18143..dcff055b661c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneFilterConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/PowderMiningGemstoneConfig.java @@ -5,7 +5,7 @@ import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown; import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; -public class PowderMiningGemstoneFilterConfig { +public class PowderMiningGemstoneConfig { @Expose @ConfigOption(name = "Stronger Tool Messages", desc = "Hide 'You need a stronger tool..' messages.") diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/CrystalNucleusTrackerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/CrystalNucleusTrackerConfig.java new file mode 100644 index 000000000000..d9d84a63f305 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/CrystalNucleusTrackerConfig.java @@ -0,0 +1,41 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class CrystalNucleusTrackerConfig { + @Expose + @ConfigOption(name = "Enabled", desc = "Enable the Crystal Nucleus Tracker overlay.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigLink(owner = CrystalNucleusTrackerConfig.class, field = "enabled") + public Position position = new Position(20, 20, false, true); + + @Expose + @ConfigOption(name = "Hide in Chocolate Factory", desc = "Hide tracker while the Chocolate Factory is open.") + @ConfigEditorBoolean + public boolean hideInCf = true; + + @Expose + @ConfigOption(name = "Show Outside of Nucleus", desc = "Show the tracker anywhere in the Crystal Hollows.") + @ConfigEditorBoolean + public boolean showOutsideNucleus = false; + + @Expose + @ConfigOption(name = "Profit Per", desc = "Show profit summary message for the completed nucleus run.") + @ConfigEditorBoolean + public boolean profitPer = true; + + @Expose + @ConfigOption(name = "Profit Per Minimum", desc = "Only show items above this coin amount in the summary message hover.") + @ConfigEditorSlider(minValue = 0, maxValue = 1000000, minStep = 5000) + public int profitPerMinimum = 20000; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index f6a5a83baca6..efac465c9078 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -37,6 +37,11 @@ public class MiningConfig { @Accordion public AreaWallsConfig crystalHollowsAreaWalls = new AreaWallsConfig(); + @Expose + @ConfigOption(name = "Crystal Nucleus Tracker", desc = "") + @Accordion + public CrystalNucleusTrackerConfig crystalNucleusTracker = new CrystalNucleusTrackerConfig(); + @Expose @ConfigOption(name = "Cold Overlay", desc = "") @Accordion diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java index 4ad2c8eb300a..6c9dae1eba48 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java @@ -35,6 +35,7 @@ import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentsProfitTracker; import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI; import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay; +import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalNucleusTracker; import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker; import at.hannibal2.skyhanni.features.mining.glacitemineshaft.CorpseTracker; import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker; @@ -617,6 +618,9 @@ public static class MineshaftStorage { @Expose public CorpseTracker.BucketData corpseProfitTracker = new CorpseTracker.BucketData(); } + + @Expose + public CrystalNucleusTracker.Data crystalNucleusTracker = new CrystalNucleusTracker.Data(); } @Expose diff --git a/src/main/java/at/hannibal2/skyhanni/events/mining/CrystalNucleusLootEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/mining/CrystalNucleusLootEvent.kt new file mode 100644 index 000000000000..98a97e2ea964 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/mining/CrystalNucleusLootEvent.kt @@ -0,0 +1,5 @@ +package at.hannibal2.skyhanni.events.mining + +import at.hannibal2.skyhanni.api.event.SkyHanniEvent + +class CrystalNucleusLootEvent(val loot: List>) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt index e32769cf615f..4f592201292a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt @@ -18,6 +18,7 @@ import at.hannibal2.skyhanni.utils.StringUtils import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.util.ChatComponentText import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import tv.twitch.chat.Chat import java.util.regex.Pattern @SkyHanniModule @@ -513,7 +514,8 @@ object ChatFilter { @SubscribeEvent fun onChat(event: LorenzChatEvent) { var blockReason = block(event.message) - if (blockReason == null && config.powderMiningFilter.enabled) blockReason = powderMiningBlock(event) + if (blockReason == null && config.powderMining.enabled) blockReason = powderMiningBlock(event) + if (blockReason == null && config.crystalNucleus.enabled) blockReason = crystalNucleusBlock(event) event.blockedReason = blockReason ?: return } @@ -580,6 +582,20 @@ object ChatFilter { return powderMiningMatchResult } + /** + * Checks if the message is a blocked Crystal Nucleus Run message, as defined in CrystalNucleusChatFilter. + * Will conditionally modify/compact messages in some cases, or return a blocking code + * @param event The event to check + * @return Block reason if applicable + * @see block + */ + private fun crystalNucleusBlock(event: LorenzChatEvent): String? { + val (blockCode, newMessage) = CrystalNucleusChatFilter.block(event.message)?.getPair() ?: Pair(null, null) + newMessage?.let { event.chatComponent = ChatComponentText(it) } + blockCode?.let { return it } + return null + } + private var othersMsg: String? = null /** @@ -644,5 +660,7 @@ object ChatFilter { } } } + event.move(61, "chat.filterType.powderMiningFilter", "chat.filterType.powderMining") + event.move(61, "chat.filterType.gemstoneFilterConfig", "chat.filterType.powderMining.gemstone") } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/CrystalNucleusChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/CrystalNucleusChatFilter.kt new file mode 100644 index 000000000000..a8fcc6db4f13 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/CrystalNucleusChatFilter.kt @@ -0,0 +1,227 @@ +package at.hannibal2.skyhanni.features.chat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.features.chat.CrystalNucleusConfig.CrystalNucleusMessageTypes +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern + +@SkyHanniModule +object CrystalNucleusChatFilter { + + class NucleusChatFilterRes(private var blockMessage: String? = null, private var newMessage: String? = null) { + fun getPair(): Pair { + return Pair(blockMessage, newMessage) + } + } + + private val config get() = SkyHanniMod.feature.chat.filterType.crystalNucleus + private val patternGroup = RepoPattern.group("filter.crystalnucleus") + + private var unclosedRunCompleted = false + private var unclosedCrystalCollected = false + private var crystalCount = 0 + private var crystalCollected = "" + + /** + * REGEX-TEST: §3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ + */ + private val runCompletedWrapperPattern by patternGroup.pattern( + "run.completed", + "§3§l▬{64}", + ) + + /** + * REGEX-TEST: §5§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ + */ + private val crystalCollectedWrapperPattern by patternGroup.pattern( + "crystal.collected.wrapper", + "§5§l▬{64}", + ) + + /** + * REGEX-TEST: §f §r§5§l✦ CRYSTAL FOUND §r§7(1§r§7/5§r§7) + */ + private val crystalCollectedCountPattern by patternGroup.pattern( + "crystal.collected.count", + "§f *§r§5§l✦ CRYSTAL FOUND §r§7\\((?\\d)§r§7/5§r§7\\)", + ) + + /** + * REGEX-TEST: §f §r§5Amethyst Crystal + * REGEX-TEST: §f §r§bSapphire Crystal + * REGEX-TEST: §f §r§6Amber Crystal + * REGEX-TEST: §f §r§eTopaz Crystal + * REGEX-TEST: §f §r§aJade Crystal + */ + private val crystalCollectedIdentifierPattern by patternGroup.pattern( + "crystal.collected.id", + "§f *§r(?(.* Crystal)) *", + ) + + /** + * REGEX-TEST: §5§l✦ §r§dYou placed the §r§bSapphire Crystal§r§d! + */ + private val crystalPlacedPattern by patternGroup.pattern( + "crystal.placed", + "§5§l✦ §r§dYou placed the §r(?.* Crystal)§r§d!", + ) + + /** + * REGEX-TEST: §aYou found §r§cScavenged Diamond Axe §r§awith your §r§cMetal Detector§r§a! + * REGEX-TEST: §aYou found §r§cScavenged Emerald Hammer §r§awith your §r§cMetal Detector§r§a! + * REGEX-TEST: §aYou found §r§a☘ Flawed Jade Gemstone §r§8x2 §r§awith your §r§cMetal Detector§r§a! + */ + private val scavengeLootPattern by patternGroup.pattern( + "divan.scavenge", + "§aYou found §r(?.*) §r§awith your §r§cMetal Detector§r§a!", + ) + + /** + * REGEX-TEST: Thanks for bringing me the §9Synthetic Heart§r! Bring me 5 more components to fix the giant! + * REGEX-TEST: Thanks for bringing me the §9Robotron Reflector§r! Bring me 5 more components to fix the giant! + * REGEX-TEST: Thanks for bringing me the §9Superlite Motor§r! Bring me 4 more components to fix the giant! + * REGEX-TEST: Thanks for bringing me the §9Synthetic Heart§r! Bring me 3 more components to fix the giant! + * REGEX-TEST: Thanks for bringing me the §9FTX 3070§r! Bring me 2 more components to fix the giant! + * REGEX-TEST: Thanks for bringing me the §9Electron Transmitter§r! Bring me one more component to fix the giant! + * REGEX-TEST: §rYou've brought me all of the components! + * REGEX-TEST: §rYou've brought me all of the components... I think? To be honest, I kind of lost count... + */ + private val componentSubmittedPattern by patternGroup.pattern( + "precursor.submitted", + ".*(You've brought me all|me the (?.*)§r! Bring me (?(\\d|one)) more).*", + ) + + fun block(message: String): NucleusChatFilterRes? { + if (!isEnabled()) return null + + blockCrystalCollected(message)?.let { return it } + blockCrystalPlaced(message)?.let { return it } + blockRunCompleted(message)?.let { return it } + blockNonToolScavenge(message)?.let { return it } + blockNPC(message)?.let { return it } + + return null + } + + private fun blockCrystalCollected(message: String): NucleusChatFilterRes? { + if (!shouldBlock(CrystalNucleusMessageTypes.CRYSTAL_COLLECTED)) return null + if (crystalCollectedWrapperPattern.matches(message)) { + unclosedCrystalCollected = !unclosedCrystalCollected + return NucleusChatFilterRes("crystal_collected") + } + + if (!unclosedCrystalCollected) return null + + crystalCollectedCountPattern.matchMatcher(message) { + crystalCount = group("count").toInt() + } + + crystalCollectedIdentifierPattern.matchMatcher(message) { + crystalCollected = group("crystal") + return NucleusChatFilterRes("", "§5§l✦ $crystalCollected §5found§d! §7(§a$crystalCount§7/§a5§7)") + } + + return NucleusChatFilterRes("crystal_collected") + } + + private fun blockCrystalPlaced(message: String): NucleusChatFilterRes? { + if (!inNucleus()) return null + if (!shouldBlock(CrystalNucleusMessageTypes.CRYSTAL_PLACED)) return null + + if (message == " §r§dKeep exploring the §r§5Crystal Hollows §r§dto find the rest!") return NucleusChatFilterRes("crystal_placed") + crystalPlacedPattern.matchMatcher(message) { + return NucleusChatFilterRes("", "§5§l✦ ${group("crystal")} §5placed§d!") + } + return null + } + + private fun blockRunCompleted(message: String): NucleusChatFilterRes? { + if (!inNucleus()) return null + if (!shouldBlock(CrystalNucleusMessageTypes.RUN_COMPLETED)) return null + + if (runCompletedWrapperPattern.matches(message)) { + unclosedRunCompleted = !unclosedRunCompleted + return NucleusChatFilterRes("run_completed") + } + + if (message == "§7Pick it up near the §r§5Nucleus Vault§r§7!") return NucleusChatFilterRes("", "§5Crystal Nucleus Run complete§d!") + if (!unclosedRunCompleted) return null + + return NucleusChatFilterRes("run_completed") + } + + private fun blockNonToolScavenge(message: String): NucleusChatFilterRes? { + if (!shouldBlock(CrystalNucleusMessageTypes.NON_TOOL_SCAVENGE)) return null + + scavengeLootPattern.matchMatcher(message) { + if (!group("loot").startsWith("§cScavenged")) return NucleusChatFilterRes("non_tool_scavenge") + } + + return null + } + + private fun blockNPC(message: String): NucleusChatFilterRes? { + if (!message.startsWith("§e[NPC]")) return null + + blockProfessorRobot(message)?.let { return it } + blockKingYolkar(message)?.let { return it } + blockKeepers(message)?.let { return it } + + return null + } + + private fun blockProfessorRobot(message: String): NucleusChatFilterRes? { + if (!shouldBlock(CrystalNucleusMessageTypes.NPC_PROF_ROBOT)) return null + if (!message.startsWith("§e[NPC] Professor Robot")) return null + + componentSubmittedPattern.matchMatcher(message) { + if (message.contains("brought me all")) { + return NucleusChatFilterRes("", "§e[NPC] Professor Robot§f: §rAll components submitted.") + } else { + return NucleusChatFilterRes( + "", + "§e[NPC] Professor Robot§f: ${group("component")} submitted. ${group("remaining")} components left.", + ) + } + } + + return NucleusChatFilterRes("npc_prof_robot") + } + + private fun blockKingYolkar(message: String): NucleusChatFilterRes? { + if (!shouldBlock(CrystalNucleusMessageTypes.NPC_KING_YOLKAR)) return null + if (!message.startsWith("§e[NPC] §6King Yolkar")) return null + + if (message.contains("*rumble* *rumble*")) { + return NucleusChatFilterRes("", "§e[NPC] §6King Yolkar§f: ...") + } + if (message.contains("Bring me back §a3 §9Goblin Egg")) { + return NucleusChatFilterRes("", "§e[NPC] §6King Yolkar§f: §rBring me §a3 §9Goblin Egg §rof any type.") + } + if (message.contains("These eggs will help me stomach my pain.")) { + return NucleusChatFilterRes("", "§e[NPC] §6King Yolkar§f: §2King's Scent§r applied.") + } + + return NucleusChatFilterRes("npc_king_yolkar") + } + + private fun blockKeepers(message: String): NucleusChatFilterRes? { + if (!shouldBlock(CrystalNucleusMessageTypes.NPC_DIVAN_KEEPERS)) return null + if (!message.startsWith("§e[NPC] §6Keeper of ")) return null + + if (message.contains("You found all of the items!")) { + return NucleusChatFilterRes("", "§e[NPC] §6Keeper of §k§6Gold§f: §rAll tools submitted.") + } + + return NucleusChatFilterRes("npc_divan_keeper") + } + + private fun shouldBlock(type: CrystalNucleusMessageTypes) = config.modifiedMessages.contains(type) + private fun inNucleus() = LorenzUtils.skyBlockArea == "Crystal Nucleus" + private fun isEnabled() = config.enabled && IslandType.CRYSTAL_HOLLOWS.isInIsland() +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/PowderMiningChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/PowderMiningChatFilter.kt index 22db96d30882..6bccc98c5a32 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/PowderMiningChatFilter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/PowderMiningChatFilter.kt @@ -1,18 +1,18 @@ package at.hannibal2.skyhanni.features.chat import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.ASCENSION_ROPE -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.JUNGLE_HEART -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.OIL_BARREL -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.PICKONIMBUS -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.PREHISTORIC_EGG -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.ROBOT_PARTS -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.SLUDGE_JUICE -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.TREASURITE -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.WISHING_COMPASS -import at.hannibal2.skyhanni.config.features.chat.PowderMiningFilterConfig.SimplePowderMiningRewardTypes.YOGGIE -import at.hannibal2.skyhanni.config.features.chat.PowderMiningGemstoneFilterConfig.GemstoneFilterEntry +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.ASCENSION_ROPE +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.JUNGLE_HEART +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.OIL_BARREL +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.PICKONIMBUS +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.PREHISTORIC_EGG +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.ROBOT_PARTS +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.SLUDGE_JUICE +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.TREASURITE +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.WISHING_COMPASS +import at.hannibal2.skyhanni.config.features.chat.PowderMiningConfig.SimplePowderMiningRewardTypes.YOGGIE +import at.hannibal2.skyhanni.config.features.chat.PowderMiningGemstoneConfig.GemstoneFilterEntry import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager @@ -28,8 +28,8 @@ import java.util.regex.Pattern @SkyHanniModule object PowderMiningChatFilter { - private val config get() = SkyHanniMod.feature.chat.filterType.powderMiningFilter - private val gemstoneConfig get() = config.gemstoneFilterConfig + private val config get() = SkyHanniMod.feature.chat.filterType.powderMining + private val gemstoneConfig get() = config.gemstone val patternGroup = RepoPattern.group("filter.powdermining") @@ -285,22 +285,22 @@ object PowderMiningChatFilter { // Powder powderRewardPattern.matchMatcher(ssMessage) { - if (config.powderFilterThreshold == 60000) return "powder_mining_powder" + if (config.powderThreshold == 60000) return "powder_mining_powder" val amountStr = groupOrNull("amount") ?: "1" - if (amountStr.isNotEmpty() && config.powderFilterThreshold > 0) { + if (amountStr.isNotEmpty() && config.powderThreshold > 0) { val amountParsed = amountStr.replace(",", "").toInt() - return if (amountParsed < config.powderFilterThreshold) "powder_mining_powder" + return if (amountParsed < config.powderThreshold) "powder_mining_powder" else "no_filter" } } // Essence essenceRewardPattern.matchMatcher(ssMessage) { - if (config.essenceFilterThreshold == 20) return "powder_mining_essence" + if (config.essenceThreshold == 20) return "powder_mining_essence" val amountStr = groupOrNull("amount") ?: "1" - if (amountStr.isNotEmpty() && config.essenceFilterThreshold > 0) { + if (amountStr.isNotEmpty() && config.essenceThreshold > 0) { val amountParsed = amountStr.toInt() - return if (amountParsed < config.essenceFilterThreshold) "powder_mining_essence" + return if (amountParsed < config.essenceThreshold) "powder_mining_essence" else "no_filter" } } @@ -313,7 +313,7 @@ object PowderMiningChatFilter { return null } - private var rewardPatterns: Map, String> = + private var rewardPatterns: Map, String> = emptyMap() @SubscribeEvent(priority = EventPriority.HIGHEST) @@ -344,21 +344,21 @@ object PowderMiningChatFilter { private fun blockGoblinEggs(ssMessage: String): String? { goblinEggPattern.matchMatcher(ssMessage) { - if (config.goblinEggs == PowderMiningFilterConfig.GoblinEggFilterEntry.SHOW_ALL) return "no_filter" - if (config.goblinEggs == PowderMiningFilterConfig.GoblinEggFilterEntry.HIDE_ALL) return "powder_mining_goblin_eggs" + if (config.goblinEggs == PowderMiningConfig.GoblinEggEntry.SHOW_ALL) return "no_filter" + if (config.goblinEggs == PowderMiningConfig.GoblinEggEntry.HIDE_ALL) return "powder_mining_goblin_eggs" return when (val colorStr = groupOrNull("color")?.lowercase()) { // 'Colorless', base goblin eggs will never be shown in this code path null -> "powder_mining_goblin_eggs" - "green" -> if (config.goblinEggs > PowderMiningFilterConfig.GoblinEggFilterEntry.GREEN_UP) { + "green" -> if (config.goblinEggs > PowderMiningConfig.GoblinEggEntry.GREEN_UP) { "powder_mining_goblin_eggs" } else "no_filter" - "yellow" -> if (config.goblinEggs > PowderMiningFilterConfig.GoblinEggFilterEntry.YELLOW_UP) { + "yellow" -> if (config.goblinEggs > PowderMiningConfig.GoblinEggEntry.YELLOW_UP) { "powder_mining_goblin_eggs" } else "no_filter" - "red" -> if (config.goblinEggs > PowderMiningFilterConfig.GoblinEggFilterEntry.RED_UP) { + "red" -> if (config.goblinEggs > PowderMiningConfig.GoblinEggEntry.RED_UP) { "powder_mining_goblin_eggs" } else "no_filter" // BLUE_ONLY enum not explicitly used in comparison, as the only diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusAPI.kt new file mode 100644 index 000000000000..262889300ba6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusAPI.kt @@ -0,0 +1,72 @@ +package at.hannibal2.skyhanni.features.mining.crystalhollows + +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.mining.CrystalNucleusLootEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object CrystalNucleusAPI { + + private val patternGroup = RepoPattern.group("mining.crystalnucleus") + + /** + * REGEX-TEST: §r§5§lCRYSTAL NUCLEUS LOOT BUNDLE + */ + private val startPattern by patternGroup.pattern( + "loot.start", + " {2}§r§5§lCRYSTAL NUCLEUS LOOT BUNDLE.*", + ) + + /** + * REGEX-TEST: §3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ + */ + private val endPattern by patternGroup.pattern( + "loot.end", + "§3§l▬{64}", + ) + + private var inLoot = false + private val loot = mutableListOf>() + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!IslandType.CRYSTAL_HOLLOWS.isInIsland()) return + + val message = event.message + + if (startPattern.matches(message)) { + inLoot = true + return + } + if (!inLoot) return + + if (endPattern.matches(message)) { + CrystalNucleusLootEvent(loot).post() + loot.clear() + inLoot = false + return + } + + // All loot rewards start with 4 spaces. + // To simplify regex statements, this check is done outside the main logic. + // This also nerfs the "§r§a§lREWARDS" message. + message.takeIf { it.startsWith(" ") }?.substring(4)?.let { lootMessage -> + ItemUtils.readItemAmount(lootMessage)?.let { pair -> + loot.add( + when(pair.first) { + // Assume enchanted books are Fortune IV books + "§fEnchanted" -> "§9Fortune IV" to pair.second + "§fEnchanted Book" -> "§9Fortune IV" to pair.second + else -> pair + } + ) + } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusProfitPer.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusProfitPer.kt new file mode 100644 index 000000000000..cfdbbc9727c6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusProfitPer.kt @@ -0,0 +1,71 @@ +package at.hannibal2.skyhanni.features.mining.crystalhollows + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.api.event.HandleEvent.Companion.HIGH +import at.hannibal2.skyhanni.events.mining.CrystalNucleusLootEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc +import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat + +@SkyHanniModule +object CrystalNucleusProfitPer { + private val config get() = SkyHanniMod.feature.mining.crystalNucleusTracker + + val jungleKeyItem = "JUNGLE_KEY".asInternalName() + val robotPartItems = listOf( + "CONTROL_SWITCH", + "ELECTRON_TRANSMITTER", + "FTX_3070", + "ROBOTRON_REFLECTOR", + "SUPERLITE_MOTOR", + "SYNTHETIC_HEART", + ).map { it.asInternalName()} + + @HandleEvent(priority = HIGH) + fun onCrystalNucleusLoot(event: CrystalNucleusLootEvent) { + if (!config.profitPer) return + val loot = event.loot + + var totalProfit = 0.0 + val map = mutableMapOf() + for ((name, amount) in loot) { + // Gemstone and Mithril Powder + if (name.contains(" Powder")) continue + NEUInternalName.fromItemNameOrNull(name)?.let { + it.getPrice().takeIf { price -> price != -1.0 }?.let { pricePer -> + val profit = amount * pricePer + val text = "§eFound $name §8${amount.addSeparators()}x §7(§6${profit.shortFormat()}§7)" + map[text] = profit + totalProfit += profit + } + } + } + + val jungleKeyPrice = jungleKeyItem.getPrice() + map["§cUsed §5Jungle Key§7: §c-${jungleKeyPrice.shortFormat()}"] = -jungleKeyPrice + totalProfit -= jungleKeyPrice + + var robotPartsPrice = 0.0 + robotPartItems.forEach { robotPartsPrice += it.getPrice() } + map["§cUsed §9Robot Parts§7: §c-${robotPartsPrice.shortFormat()}"] = -robotPartsPrice + totalProfit -= robotPartsPrice + + val hover = map.sortedDesc().filter { + it.value >= config.profitPerMinimum || it.value < 0 + }.keys.toMutableList() + if (hover.size != map.size) hover.add("§7${map.size - hover.size} cheap items are hidden.") + val profitPrefix = + if (totalProfit < 0) "§c" + else "§6" + val totalMessage = "Profit for Crystal Nucleus Run§e: $profitPrefix${totalProfit.shortFormat()}" + hover.add("") + hover.add("§e$totalMessage") + ChatUtils.hoverableChat(totalMessage, hover) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusTracker.kt new file mode 100644 index 000000000000..d7cd4e18f762 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/crystalhollows/CrystalNucleusTracker.kt @@ -0,0 +1,146 @@ +package at.hannibal2.skyhanni.features.mining.crystalhollows + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.api.event.HandleEvent.Companion.HIGH +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent +import at.hannibal2.skyhanni.events.mining.CrystalNucleusLootEvent +import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI +import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalNucleusProfitPer.jungleKeyItem +import at.hannibal2.skyhanni.features.mining.crystalhollows.CrystalNucleusProfitPer.robotPartItems +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString +import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.Searchable +import at.hannibal2.skyhanni.utils.renderables.toSearchable +import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData +import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker +import com.google.gson.annotations.Expose +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object CrystalNucleusTracker { + private val config get() = SkyHanniMod.feature.mining.crystalNucleusTracker + + private val tracker = SkyHanniItemTracker( + "Crystal Nucleus Tracker", + { Data() }, + { it.mining.crystalNucleusTracker }, + ) { drawDisplay(it) } + + class Data : ItemTrackerData() { + override fun resetItems() { + runsCompleted = 0L + } + + override fun getDescription(timesGained: Long): List { + val percentage = timesGained.toDouble() / runsCompleted + val dropRate = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0)) + return listOf( + "§7Dropped §e${timesGained.addSeparators()} §7times.", + "§7Your drop rate: §c$dropRate.", + ) + } + + // No direct coin drops from nuc runs + override fun getCoinName(item: TrackedItem) = "" + override fun getCoinDescription(item: TrackedItem) = mutableListOf() + + @Expose + var runsCompleted = 0L + } + + @HandleEvent(priority = HIGH) + fun onCrystalNucleusLoot(event: CrystalNucleusLootEvent) { + addCompletedRun() + for ((itemName, amount) in event.loot) { + // Gemstone and Mithril Powder + if (itemName.contains(" Powder")) continue + NEUInternalName.fromItemNameOrNull(itemName)?.let { + tracker.addItem(it, amount, false) + } + } + } + + private fun addCompletedRun() { + tracker.modify { + it.runsCompleted++ + } + } + + private fun drawDisplay(data: Data): List = buildList { + addSearchString("§e§lCrystal Nucleus Profit Tracker") + var profit = tracker.drawItems(data, { true }, this) + + val runsCompleted = data.runsCompleted + + if (runsCompleted > 0) { + val jungleKeyCost = jungleKeyItem.getPrice() * runsCompleted + profit -= jungleKeyCost + val jungleKeyCostFormat = jungleKeyCost.shortFormat() + add( + Renderable.hoverTips( + " §7${runsCompleted}x §5Jungle Key§7: §c-$jungleKeyCostFormat", + listOf( + "§7You lost §c$jungleKeyCostFormat §7of total profit", + "§7due to §5Jungle Keys§7." + ), + ).toSearchable(), + ) + + val robotPartsCost = robotPartItems.sumOf { it.getPrice() } * runsCompleted + profit -= robotPartsCost + val robotPartsCostFormat = robotPartsCost.shortFormat() + add( + Renderable.hoverTips( + " §7${runsCompleted * 6}x §9Robot Parts§7: §c-$robotPartsCostFormat", + listOf( + "§7You lost §c$robotPartsCostFormat §7of total profit", + "§7due to §9Robot Parts§7." + ), + ).toSearchable(), + ) + + add( + Renderable.hoverTips( + "§7Runs completed: §e${runsCompleted.addSeparators()}", + listOf("§7You completed §e${runsCompleted.addSeparators()} §7Crystal Nucleus Runs."), + ).toSearchable(), + ) + + add(tracker.addTotalProfit(profit, data.runsCompleted, "run")) + } + + tracker.addPriceFromButton(this) + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent) { + if (!isEnabled()) return + tracker.renderDisplay(config.position) + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + if (event.newIsland == IslandType.CRYSTAL_HOLLOWS) { + tracker.firstUpdate() + } + } + + fun resetCommand() { + tracker.resetCommand() + } + + private fun isCfEnabled() = !config.hideInCf || !ChocolateFactoryAPI.inChocolateFactory + private fun isNucEnabled() = config.showOutsideNucleus || LorenzUtils.skyBlockArea == "Crystal Nucleus" + private fun isEnabled() = config.enabled && IslandType.CRYSTAL_HOLLOWS.isInIsland() && isNucEnabled() && isCfEnabled() +}