aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni
diff options
context:
space:
mode:
authorDavid Cole <40234707+DavidArthurCole@users.noreply.github.com>2024-07-30 05:06:16 -0400
committerGitHub <noreply@github.com>2024-07-30 11:06:16 +0200
commit47c059081aa202b45d84f02e0b85a3579142fdac (patch)
treede940ab3d73eb51ef27462990997cb78d3a37a10 /src/main/java/at/hannibal2/skyhanni
parentbacc6a3a845311e0e079957423cede7544d298b8 (diff)
downloadskyhanni-47c059081aa202b45d84f02e0b85a3579142fdac.tar.gz
skyhanni-47c059081aa202b45d84f02e0b85a3579142fdac.tar.bz2
skyhanni-47c059081aa202b45d84f02e0b85a3579142fdac.zip
Feature: Stray Rabbit Tracker (#2210)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt315
4 files changed, 334 insertions, 0 deletions
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 c08595b5b..6dd8db8ad 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
@@ -51,6 +51,7 @@ import at.hannibal2.skyhanni.features.garden.fortuneguide.FFGuideGUI
import at.hannibal2.skyhanni.features.garden.pests.PestFinder
import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorDropStatistics
+import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker
import at.hannibal2.skyhanni.features.mining.KingTalismanHelper
import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay
import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker
@@ -273,6 +274,10 @@ object Commands {
"Resets the Sea Creature Tracker",
) { SeaCreatureTracker.resetCommand() }
registerCommand(
+ "shresetstrayrabbittracker",
+ "Resets the Stray Rabbit Tracker",
+ ) { ChocolateFactoryStrayTracker.resetCommand() }
+ registerCommand(
"shfandomwiki",
"Searches the fandom wiki with SkyHanni's own method.",
) { WikiManager.otherWikiCommands(it, true) }
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java
index c381444df..e35bc4d7e 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java
@@ -221,4 +221,14 @@ public class ChocolateFactoryConfig {
@ConfigEditorBoolean
@FeatureToggle
public boolean mythicRabbitRequirement = false;
+
+ @Expose
+ @ConfigOption(name = "Stray Tracker", desc = "Track stray rabbits found in the Chocolate Factory menu.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean strayRabbitTracker = true;
+
+ @Expose
+ @ConfigLink(owner = ChocolateFactoryConfig.class, field = "strayRabbitTracker")
+ public Position strayRabbitTrackerPosition = new Position(300, 300, false, true);
}
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 b49a9c612..3410d9b4d 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
@@ -28,6 +28,7 @@ import at.hannibal2.skyhanni.features.garden.fortuneguide.FarmingItems;
import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker;
import at.hannibal2.skyhanni.features.garden.pests.VinylType;
import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward;
+import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker;
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryUpgrade;
import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI;
import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay;
@@ -151,6 +152,9 @@ public class ProfileSpecificStorage {
@Expose
public Integer hoppityShopYearOpened = null;
+
+ @Expose
+ public ChocolateFactoryStrayTracker.Data strayTracker = new ChocolateFactoryStrayTracker.Data();
}
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt
new file mode 100644
index 000000000..5dd90a82b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt
@@ -0,0 +1,315 @@
+package at.hannibal2.skyhanni.features.inventory.chocolatefactory
+
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.SecondPassedEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.itemName
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
+import at.hannibal2.skyhanni.utils.tracker.TrackerData
+import com.google.gson.annotations.Expose
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+@SkyHanniModule
+object ChocolateFactoryStrayTracker {
+
+ private val config get() = ChocolateFactoryAPI.config
+ private var claimedStraysSlots = mutableListOf<Int>()
+
+ /**
+ * REGEX-TEST: §9Zero §d§lCAUGHT!
+ * REGEX-TEST: §6§lGolden Rabbit §d§lCAUGHT!
+ * REGEX-TEST: §fAudi §d§lCAUGHT!
+ */
+ private val strayCaughtPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.caught",
+ "^§[a-f0-9].* §d§lCAUGHT!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §fMandy §7and §7gained §6+283,574 Chocolate§7!
+ * REGEX-TEST: §7You caught a stray §aSven §7and gained §7§6+397,004 Chocolate§7!'
+ */
+ private val strayLorePattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.loreinfo",
+ "§7You caught a stray (?<rabbit>(?<name>§[^§]*)) .* (?:§7)?§6\\+(?<amount>[\\d,]*) Chocolate§7!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §6§lGolden Rabbit§7! §7You gained §6+13,566,571 Chocolate§7!
+ */
+ private val goldenStrayJackpotMountainPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.goldenrawchoc",
+ "§7You caught a stray §6§lGolden Rabbit§7! §7You gained §6\\+(?<amount>[\\d,]*) Chocolate§7!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §6§lGolden Rabbit§7! §7You caught a glimpse of §6El Dorado§7, §7but he escaped and left behind §7§6313,780 Chocolate§7!
+ */
+ private val goldenStrayDoradoEscape by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.goldendoradoescape",
+ "§7You caught a stray §6§lGolden Rabbit§7! §7You caught a glimpse of §6El Dorado§7, §7but he escaped and left behind §7§6\\+(?<amount>[\\d,]*) Chocolate§7!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §6§lGolden Rabbit§7! §7You caught §6El Dorado §7- quite the elusive rabbit!
+ */
+ private val goldenStrayDoradoCaught by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.goldendoradocaught",
+ "§7You caught a stray §6§lGolden Rabbit§7! §7You caught §6El Dorado §7- quite the elusive rabbit!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §6§lGolden Rabbit§7! §7You caught §6El Dorado§7! Since you §7already have captured him before, §7you gained §6+324,364,585 Chocolate§7.
+ */
+ private val goldenStrayDoradoDuplicate by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.goldendoradoduplicate",
+ "§7You caught a stray §6§lGolden Rabbit§7! §7You caught §6El Dorado§7! Since you §7already have captured him before, §7you gained §6\\+(?<amount>[\\d,]*) Chocolate§7.",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §6§lGolden Rabbit§7! §7You gained §6+5 Chocolate §7until the §7end of the SkyBlock year!
+ *
+ */
+ private val goldenStrayClick by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.goldenclick",
+ "§7You caught a stray §6§lGolden Rabbit§7! §7You gained §6\\+5 Chocolate §7until the §7end of the SkyBlock year!",
+ )
+
+ /**
+ * REGEX-TEST: §7You caught a stray §9Fish the Rabbit§7! §7You have already found §9Fish the §9Rabbit§7, so you received §655,935,257 §6Chocolate§7!
+ */
+ private val fishTheRabbitPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "stray.fish",
+ "§7You caught a stray (?<color>§.)Fish the Rabbit§7! §7You have already found (?:§.)?Fish the (?:§.)?Rabbit§7, so you received §6(?<amount>[\\d,]*) (?:§6)?Chocolate§7!",
+ )
+
+ private val rarityFormatMap = mapOf(
+ "common" to "§f",
+ "uncommon" to "§a",
+ "rare" to "§9",
+ "epic" to "§5",
+ "legendary" to "§6",
+ )
+
+ private val tracker = SkyHanniTracker("Stray Tracker", { Data() }, { it.chocolateFactory.strayTracker })
+ { drawDisplay(it) }
+
+ class Data : TrackerData() {
+ override fun reset() {
+ straysCaught.clear()
+ straysExtraChocMs.clear()
+ goldenTypesCaught.clear()
+ }
+
+ @Expose
+ var straysCaught: MutableMap<String, Int> = mutableMapOf()
+
+ @Expose
+ var straysExtraChocMs: MutableMap<String, Long> = mutableMapOf()
+
+ @Expose
+ var goldenTypesCaught: MutableMap<String, Int> = mutableMapOf()
+ }
+
+ private fun formLoreToSingleLine(lore: List<String>): String {
+ val notEmptyLines = lore.filter { it.isNotEmpty() }
+ return notEmptyLines.joinToString(" ")
+ }
+
+ private fun incrementRarity(rarity: String, chocAmount: Long = 0) {
+ tracker.modify { it.straysCaught.addOrPut(rarity, 1) }
+ val extraTime = ChocolateFactoryAPI.timeUntilNeed(chocAmount + 1)
+ tracker.modify { it.straysExtraChocMs.addOrPut(rarity, extraTime.inWholeMilliseconds) }
+ }
+
+ private fun incrementGoldenType(typeCaught: String, amount: Int = 1) {
+ tracker.modify { it.goldenTypesCaught.addOrPut(typeCaught, amount) }
+ }
+
+ private fun drawDisplay(data: Data): List<List<Any>> = buildList {
+ val extraChocMs = data.straysExtraChocMs.values.sum().milliseconds
+ val formattedExtraTime = extraChocMs.let { if (it == 0.milliseconds) "0s" else it.format() }
+
+ addAsSingletonList(
+ Renderable.hoverTips(
+ "§6§lStray Tracker",
+ tips = listOf("§a+§b${formattedExtraTime} §afrom strays§7"),
+ ),
+ )
+ rarityFormatMap.keys.forEach { rarity ->
+ extractHoverableOfRarity(rarity, data)?.let { addAsSingletonList(it) }
+ }
+ }
+
+ private fun extractHoverableOfRarity(rarity: String, data: Data): Renderable? {
+ val caughtOfRarity = data.straysCaught[rarity]
+ val caughtString = caughtOfRarity?.toString() ?: return null
+
+ val rarityExtraChocMs = data.straysExtraChocMs[rarity]?.milliseconds
+ val extraChocFormat = rarityExtraChocMs?.format() ?: ""
+
+ val colorCode = rarityFormatMap[rarity] ?: ""
+ val lineHeader = "$colorCode${rarity.substring(0, 1).uppercase()}${rarity.substring(1)}§7: §r$colorCode"
+ val lineFormat = "${lineHeader}${caughtString}"
+
+ return rarityExtraChocMs?.let {
+ val tip =
+ "§a+§b$extraChocFormat §afrom $colorCode$rarity strays§7${if (rarity == "legendary") extractGoldenTypesCaught(data) else ""}"
+ Renderable.hoverTips(Renderable.string(lineFormat), tips = tip.split("\n"))
+ } ?: Renderable.string(lineFormat)
+ }
+
+ private fun extractGoldenTypesCaught(data: Data): String {
+ val goldenList = mutableListOf<String>()
+ data.goldenTypesCaught["sidedish"]?.let {
+ goldenList.add("§b$it §6Side Dish" + if (it > 1) "es" else "")
+ }
+ data.goldenTypesCaught["jackpot"]?.let {
+ goldenList.add("§b$it §6Chocolate Jackpot" + if (it > 1) "s" else "")
+ }
+ data.goldenTypesCaught["mountain"]?.let {
+ goldenList.add("§b$it §6Chocolate Mountain" + if (it > 1) "s" else "")
+ }
+ data.goldenTypesCaught["dorado"]?.let {
+ goldenList.add((if (it >= 3) "§a" else "§b") + "$it§7/§a3 §6El Dorado §7Sighting" + if (it > 1) "s" else "")
+ }
+ data.goldenTypesCaught["stampede"]?.let {
+ goldenList.add("§b$it §6Stampede" + if (it > 1) "s" else "")
+ }
+ data.goldenTypesCaught["goldenclick"]?.let {
+ goldenList.add("§b$it §6Golden Click" + if (it > 1) "s" else "")
+ }
+ return if (goldenList.size == 0) "" else ("\n" + goldenList.joinToString("\n"))
+ }
+
+ @SubscribeEvent
+ fun onTick(event: SecondPassedEvent) {
+ if (!isEnabled()) return
+ InventoryUtils.getItemsInOpenChest().filter {
+ !claimedStraysSlots.contains(it.slotIndex)
+ }.forEach {
+ strayCaughtPattern.matchMatcher(it.stack.name) {
+ if (it.stack.getLore().isEmpty()) return
+ claimedStraysSlots.add(it.slotIndex)
+ val loreLine = formLoreToSingleLine(it.stack.getLore())
+
+ // "Base" strays - Common -> Epic, raw choc only reward.
+ strayLorePattern.matchMatcher(loreLine) {
+ //Pretty sure base strays max at Epic, but...
+ val rarity = rarityFormatMap.entries.find { e -> e.value == group("rabbit").substring(0, 2) }?.key ?: "common"
+ incrementRarity(rarity, group("amount").formatLong())
+ }
+
+ // Fish the Rabbit
+ fishTheRabbitPattern.matchMatcher(loreLine) {
+ //Also fairly sure that Fish maxes out at Rare, but...
+ val rarity = rarityFormatMap.entries.find { e -> e.value == group("color").substring(0, 2) }?.key ?: "common"
+ incrementRarity(rarity, group("amount").formatLong())
+ }
+
+ // Golden Strays, Jackpot and Mountain, raw choc only reward.
+ goldenStrayJackpotMountainPattern.matchMatcher(loreLine) {
+ val amount = group("amount").formatLong().also { am -> incrementRarity("legendary", am) }
+ val multiplier = amount / ChocolateFactoryAPI.chocolatePerSecond
+ when (multiplier) {
+ in 479.0..481.0 -> incrementGoldenType("jackpot")
+ in 1499.0..1501.0 -> incrementGoldenType("mountain")
+ }
+ }
+
+ // Golden Strays, "Golden Click"
+ goldenStrayClick.matchMatcher(loreLine) {
+ incrementGoldenType("goldenclick")
+ }
+
+ // Golden Strays, hoard/stampede
+ if (loreLine == "§7You caught a stray §6§lGolden Rabbit§7! §7A hoard of §aStray Rabbits §7has appeared!") {
+ incrementGoldenType("stampede")
+ }
+
+ // Golden Strays, El Dorado "glimpse" - 1/3 before capture
+ goldenStrayDoradoEscape.matchMatcher(loreLine) {
+ incrementRarity("legendary", group("amount").formatLong())
+ incrementGoldenType("dorado")
+ }
+
+ // Golden Strays, El Dorado caught - 3/3
+ if (goldenStrayDoradoCaught.matches(loreLine)) {
+ incrementRarity("legendary")
+ tracker.modify { t -> t.goldenTypesCaught["dorado"] = 3 }
+ }
+
+ // Golden Strays, El Dorado (duplicate catch)
+ goldenStrayDoradoDuplicate.matchMatcher(loreLine) {
+ incrementRarity("legendary", group("amount").formatLong())
+ tracker.modify { t ->
+ t.goldenTypesCaught["dorado"] = t.goldenTypesCaught["dorado"]?.plus(1) ?: 4
+ }
+ }
+ }
+ }
+ InventoryUtils.getItemsInOpenChest().filter {
+ claimedStraysSlots.contains(it.slotIndex)
+ }.forEach {
+ if (!strayCaughtPattern.matches(it.stack.name)) {
+ claimedStraysSlots.removeAt(claimedStraysSlots.indexOf(it.slotIndex))
+ }
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) {
+ if (!isEnabled()) return
+ val index = event.slot?.slotIndex ?: return
+ if (index == -999) return
+ if (claimedStraysSlots.contains(index)) return
+
+ val clickedStack = InventoryUtils.getItemsInOpenChest()
+ .find { it.slotNumber == event.slot.slotNumber && it.hasStack }
+ ?.stack ?: return
+ val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName)
+ if (!nameText.equals("§6§lGolden Rabbit §8- §aSide Dish")) return
+
+ claimedStraysSlots.add(index)
+ incrementGoldenType("sidedish")
+ incrementRarity("legendary", 0)
+ DelayedRun.runDelayed(1.seconds) {
+ claimedStraysSlots.remove(claimedStraysSlots.indexOf(index))
+ }
+ }
+
+ @SubscribeEvent
+ fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ tracker.renderDisplay(config.strayRabbitTrackerPosition)
+ }
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
+ if (!isEnabled()) return
+ tracker.firstUpdate()
+ }
+
+ fun resetCommand() {
+ tracker.resetCommand()
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.strayRabbitTracker && ChocolateFactoryAPI.inChocolateFactory
+}