summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/event/ChocolateFactoryConfig.java90
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java39
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java11
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt19
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryAPI.kt256
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryBarnManager.kt77
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryInventory.kt115
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryStats.kt79
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/HoppityEggLocator.kt240
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/HoppityEggType.kt53
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/HoppityEggsManager.kt123
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/HoppityEggsShared.kt57
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt4
16 files changed, 1184 insertions, 1 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index d1ce70441..374f414b4 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -123,6 +123,13 @@ import at.hannibal2.skyhanni.features.dungeon.DungeonTeammateOutlines
import at.hannibal2.skyhanni.features.dungeon.HighlightDungeonDeathmite
import at.hannibal2.skyhanni.features.dungeon.TerracottaPhase
import at.hannibal2.skyhanni.features.event.UniqueGiftingOpportunitiesFeatures
+import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryAPI
+import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryBarnManager
+import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryInventory
+import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryStats
+import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityEggLocator
+import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityEggsManager
+import at.hannibal2.skyhanni.features.event.chocolatefactory.HoppityEggsShared
import at.hannibal2.skyhanni.features.event.diana.AllBurrowsList
import at.hannibal2.skyhanni.features.event.diana.BurrowWarpHelper
import at.hannibal2.skyhanni.features.event.diana.DianaProfitTracker
@@ -550,6 +557,7 @@ class SkyHanniMod {
loadModule(PestAPI)
loadModule(MiningAPI)
loadModule(FossilExcavatorAPI)
+ loadModule(ChocolateFactoryAPI)
// features
loadModule(BazaarOrderHelper())
@@ -605,6 +613,12 @@ class SkyHanniMod {
loadModule(SkyblockXPInChat())
loadModule(AreaMiniBossFeatures())
loadModule(MobHighlight())
+ loadModule(ChocolateFactoryBarnManager)
+ loadModule(ChocolateFactoryInventory)
+ loadModule(HoppityEggsManager)
+ loadModule(HoppityEggLocator)
+ loadModule(HoppityEggsShared)
+ loadModule(ChocolateFactoryStats)
loadModule(SpawnTimers())
loadModule(MarkedPlayerManager())
loadModule(SlayerMiniBossFeatures())
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/ChocolateFactoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/ChocolateFactoryConfig.java
new file mode 100644
index 000000000..ad129e0e3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/ChocolateFactoryConfig.java
@@ -0,0 +1,90 @@
+package at.hannibal2.skyhanni.config.features.event;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import at.hannibal2.skyhanni.features.event.chocolatefactory.ChocolateFactoryStats.ChocolateFactoryStat;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.Accordion;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ChocolateFactoryConfig {
+
+ @Expose
+ @ConfigOption(name = "Hoppity Eggs", desc = "")
+ @Accordion
+ public HoppityEggsConfig hoppityEggs = new HoppityEggsConfig();
+
+ @Expose
+ @ConfigOption(name = "Chocolate Factory Features", desc = "Global toggle for all chocolate factory features.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Chocolate Factory Stats", desc = "Show general info about your chocolate factory.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean statsDisplay = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Stats List",
+ desc = "Drag text to change what displays in the chocolate factory stats list and what order the text appears in."
+ )
+ @ConfigEditorDraggableList
+ public List<ChocolateFactoryStat> statsDisplayList = new ArrayList<>(Arrays.asList(
+ ChocolateFactoryStat.HEADER,
+ ChocolateFactoryStat.CURRENT,
+ ChocolateFactoryStat.THIS_PRESTIGE,
+ ChocolateFactoryStat.ALL_TIME,
+ ChocolateFactoryStat.EMPTY,
+ ChocolateFactoryStat.PER_SECOND,
+ ChocolateFactoryStat.PER_MINUTE,
+ ChocolateFactoryStat.PER_HOUR,
+ ChocolateFactoryStat.PER_DAY,
+ ChocolateFactoryStat.EMPTY_2,
+ ChocolateFactoryStat.MULTIPLIER,
+ ChocolateFactoryStat.BARN,
+ ChocolateFactoryStat.LEADERBOARD_POS
+ ));
+
+ @Expose
+ @ConfigOption(name = "Show Stack Sizes", desc = "Shows additional info as many items in the chocolate menu as the stack size.")
+ @ConfigEditorBoolean
+ public boolean showStackSizes = true;
+
+ @Expose
+ @ConfigOption(name = "Highlight Upgrades", desc = "Highlight any upgrades that you can afford.")
+ @ConfigEditorBoolean
+ public boolean highlightUpgrades = true;
+
+ @Expose
+ @ConfigOption(name = "Use Middle Click", desc = "Click on slots with middle click to speed up interactions.")
+ @ConfigEditorBoolean
+ public boolean useMiddleClick = true;
+
+ @Expose
+ @ConfigOption(name = "Rabbit Warning", desc = "Warn when the rabbit that needs to be clicked appears.")
+ @ConfigEditorBoolean
+ public boolean rabbitWarning = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Rabbit Crush Threshold",
+ desc = "How close should you be to your barn capacity should you be before being warned about needing to upgrade it."
+ )
+ @ConfigEditorSlider(minValue = 3, maxValue = 20, minStep = 1)
+ public int barnCapacityThreshold = 6;
+
+ @Expose
+ @ConfigLink(owner = ChocolateFactoryConfig.class, field = "statsDisplay")
+ public Position position = new Position(183, 160, false, true);
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
index 149c8c6a4..b68664d79 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
@@ -23,6 +23,10 @@ public class EventConfig {
@Expose
public WinterConfig winter = new WinterConfig();
+ @Expose
+ @Category(name = "Hoppity", desc = "Features for the Hoppity event and the chocolate factory.")
+ public ChocolateFactoryConfig chocolateFactory = new ChocolateFactoryConfig();
+
@ConfigOption(name = "City Project", desc = "")
@Accordion
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java
new file mode 100644
index 000000000..f935cb699
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java
@@ -0,0 +1,39 @@
+package at.hannibal2.skyhanni.config.features.event;
+
+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.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class HoppityEggsConfig {
+
+ @Expose
+ @ConfigOption(name = "Hoppity Waypoints", desc = "Toggle guess waypoints for Hoppity's Hunt.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean waypoints = true;
+
+ @Expose
+ @ConfigOption(name = "Show All Waypoints", desc = "Show all possible egg waypoints for the current lobby. §e" +
+ "Only works when you don't have an Egglocator in your inventory.")
+ @ConfigEditorBoolean
+ public boolean showAllWaypoints = false;
+
+ @Expose
+ @ConfigOption(name = "Show Claimed Eggs", desc = "Displays which eggs have been found in the last SkyBlock day.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean showClaimedEggs = false;
+
+ @Expose
+ @ConfigOption(name = "Shared Hoppity Waypoints", desc = "Enable being able to share and receive egg waypoints in your lobby.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean sharedWaypoints = true;
+
+ @Expose
+ @ConfigLink(owner = HoppityEggsConfig.class, field = "showClaimedEggs")
+ public Position position = new Position(33, 72, 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 3f62f3a1b..b802b2281 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
@@ -49,6 +49,17 @@ public class ProfileSpecificStorage {
public String currentPet = "";
@Expose
+ public ChocolateFactoryStorage chocolateFactory = new ChocolateFactoryStorage();
+
+ public static class ChocolateFactoryStorage {
+ @Expose
+ public int currentRabbits = 0;
+
+ @Expose
+ public int maxRabbits = -1;
+ }
+
+ @Expose
public MaxwellPowerStorage maxwell = new MaxwellPowerStorage();
public static class MaxwellPowerStorage {
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt
new file mode 100644
index 000000000..1aed17041
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt
@@ -0,0 +1,19 @@
+package at.hannibal2.skyhanni.data.jsonobjects.repo
+
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.utils.LorenzVec
+import com.google.gson.annotations.Expose
+
+data class HoppityEggLocationsJson(
+ @Expose val eggLocations: Map<IslandType, List<LorenzVec>>,
+ @Expose val rabbitSlots: Map<Int, Int>,
+ @Expose val otherUpgradeSlots: Set<Int>,
+ @Expose val noPickblockSlots: Set<Int>,
+ @Expose val barnIndex: Int,
+ @Expose val infoIndex: Int,
+ @Expose val productionInfoIndex: Int,
+ @Expose val prestigeIndex: Int,
+ @Expose val milestoneIndex: Int,
+ @Expose val leaderboardIndex: Int,
+ @Expose val maxRabbits: Int,
+)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryAPI.kt
new file mode 100644
index 000000000..9f77ea0cf
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryAPI.kt
@@ -0,0 +1,256 @@
+package at.hannibal2.skyhanni.features.event.chocolatefactory
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.features.event.ChocolateFactoryConfig
+import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.ChocolateFactoryStorage
+import at.hannibal2.skyhanni.data.ProfileStorageData
+import at.hannibal2.skyhanni.data.jsonobjects.repo.DisabledFeaturesJson
+import at.hannibal2.skyhanni.data.jsonobjects.repo.HoppityEggLocationsJson
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.InventoryUpdatedEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter
+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.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble
+import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
+import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
+import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal
+import at.hannibal2.skyhanni.utils.SkyblockSeason
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.StringUtils.matchFirst
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.UtilsPatterns
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object ChocolateFactoryAPI {
+
+ val config: ChocolateFactoryConfig get() = SkyHanniMod.feature.event.chocolateFactory
+ val profileStorage: ChocolateFactoryStorage? get() = ProfileStorageData.profileSpecific?.chocolateFactory
+
+ val patternGroup = RepoPattern.group("misc.chocolatefactory")
+ private val chocolateAmountPattern by patternGroup.pattern(
+ "chocolate.amount",
+ "(?<amount>[\\d,]+) Chocolate"
+ )
+ private val chocolatePerSecondPattern by patternGroup.pattern(
+ "chocolate.persecond",
+ "§6(?<amount>[\\d.,]+) §8per second"
+ )
+ private val chocolateAllTimePattern by patternGroup.pattern(
+ "chocolate.alltime",
+ "§7All-time Chocolate: §6(?<amount>[\\d,]+)"
+ )
+ private val chocolateThisPrestigePattern by patternGroup.pattern(
+ "chocolate.thisprestige",
+ "§7Chocolate this Prestige: §6(?<amount>[\\d,]+)"
+ )
+ private val chocolateMultiplierPattern by patternGroup.pattern(
+ "chocolate.multiplier",
+ "§7Total Multiplier: §6(?<amount>[\\d.]+)x"
+ )
+ private val barnAmountPattern by patternGroup.pattern(
+ "barn.amount",
+ "§7Your Barn: §.(?<rabbits>\\d+)§7/§.(?<max>\\d+) Rabbits"
+ )
+ private val prestigeLevelPattern by patternGroup.pattern(
+ "prestige.level",
+ "'§6Chocolate Factory (?<prestige>[IVX]+)"
+ )
+ private val clickMeRabbitPattern by patternGroup.pattern(
+ "rabbit.clickme",
+ "§e§lCLICK ME!"
+ )
+ private val leaderboardPlacePattern by patternGroup.pattern(
+ "leaderboard.place",
+ "§7You are §8#§b(?<position>[\\d,]+)"
+ )
+
+ var rabbitSlots = mapOf<Int, Int>()
+ var otherUpgradeSlots = setOf<Int>()
+ var noPickblockSlots = setOf<Int>()
+ var barnIndex = 34
+ private var infoIndex = 13
+ private var productionInfoIndex = 45
+ private var prestigeIndex = 28
+ var milestoneIndex = 53
+ private var leaderboardIndex = 51
+ var maxRabbits = 395
+
+ var inChocolateFactory = false
+
+ var currentPrestige = 0
+ var chocolateCurrent = 0L
+ var chocolateAllTime = 0L
+ var chocolatePerSecond = 0.0
+ var chocolateThisPrestige = 0L
+ var chocolateMultiplier = 1.0
+ var leaderboardPosition: Int? = null
+
+ val upgradeableSlots: MutableSet<Int> = mutableSetOf()
+ var bestUpgrade: Int? = null
+ var bestRabbitUpgrade: String? = null
+ var clickRabbitSlot: Int? = null
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
+ if (!isEnabled()) return
+ if (event.inventoryName != "Chocolate Factory") return
+ inChocolateFactory = true
+
+ DelayedRun.runNextTick {
+ updateInventoryItems(event.inventoryItems)
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryUpdated(event: InventoryUpdatedEvent) {
+ if (!inChocolateFactory) return
+
+ updateInventoryItems(event.inventoryItems)
+ }
+
+ private fun updateInventoryItems(inventory: Map<Int, ItemStack>) {
+ val profileStorage = profileStorage ?: return
+
+ val infoItem = InventoryUtils.getItemAtSlotIndex(infoIndex) ?: return
+ val prestigeItem = InventoryUtils.getItemAtSlotIndex(prestigeIndex) ?: return
+ val productionInfoItem = InventoryUtils.getItemAtSlotIndex(productionInfoIndex) ?: return
+ val leaderboardItem = InventoryUtils.getItemAtSlotIndex(leaderboardIndex) ?: return
+
+ processInfoItems(infoItem, prestigeItem, productionInfoItem, leaderboardItem)
+
+ bestUpgrade = null
+ upgradeableSlots.clear()
+ var bestAffordableUpgradeRatio = Double.MAX_VALUE
+ var bestPossibleUpgradeRatio = Double.MAX_VALUE
+ clickRabbitSlot = null
+
+ for ((slotIndex, item) in inventory) {
+ if (config.rabbitWarning && clickMeRabbitPattern.matches(item.name)) {
+ SoundUtils.playBeepSound()
+ clickRabbitSlot = slotIndex
+ }
+
+ val lore = item.getLore()
+ val upgradeCost = lore.getUpgradeCost() ?: continue
+
+ if (slotIndex == barnIndex) {
+ lore.matchFirst(barnAmountPattern) {
+ profileStorage.currentRabbits = group("rabbits").formatInt()
+ profileStorage.maxRabbits = group("max").formatInt()
+
+ ChocolateFactoryBarnManager.trySendBarnFullMessage()
+ }
+ }
+
+ val canAfford = upgradeCost <= chocolateCurrent
+ if (canAfford) upgradeableSlots.add(slotIndex)
+
+ if (slotIndex in rabbitSlots) {
+ val chocolateIncrease = rabbitSlots[slotIndex] ?: 0
+ val upgradeRatio = upgradeCost.toDouble() / chocolateIncrease
+
+ if (canAfford && upgradeRatio < bestAffordableUpgradeRatio) {
+ bestUpgrade = slotIndex
+ bestAffordableUpgradeRatio = upgradeRatio
+ }
+ if (upgradeRatio < bestPossibleUpgradeRatio) {
+ bestPossibleUpgradeRatio = upgradeRatio
+ bestRabbitUpgrade = item.name
+ }
+ }
+ }
+ }
+
+ private fun processInfoItems(
+ chocolateItem: ItemStack,
+ prestigeItem: ItemStack,
+ productionItem: ItemStack,
+ leaderboardItem: ItemStack,
+ ) {
+ chocolateAmountPattern.matchMatcher(chocolateItem.name.removeColor()) {
+ chocolateCurrent = group("amount").formatLong()
+ }
+ for (line in chocolateItem.getLore()) {
+ chocolatePerSecondPattern.matchMatcher(line) {
+ chocolatePerSecond = group("amount").formatDouble()
+ }
+ chocolateAllTimePattern.matchMatcher(line) {
+ chocolateAllTime = group("amount").formatLong()
+ }
+ }
+ prestigeLevelPattern.matchMatcher(prestigeItem.name) {
+ currentPrestige = group("prestige").romanToDecimal()
+ }
+ prestigeItem.getLore().matchFirst(chocolateThisPrestigePattern) {
+ chocolateThisPrestige = group("amount").formatLong()
+ }
+ productionItem.getLore().matchFirst(chocolateMultiplierPattern) {
+ chocolateMultiplier = group("amount").formatDouble()
+ }
+ leaderboardItem.getLore().matchFirst(leaderboardPlacePattern) {
+ leaderboardPosition = group("position").formatInt()
+ }
+ if (!config.statsDisplay) return
+ ChocolateFactoryStats.updateDisplay()
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ clearData()
+ }
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ clearData()
+ }
+
+ private fun clearData() {
+ inChocolateFactory = false
+ }
+
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ val data = event.getConstant<HoppityEggLocationsJson>("HoppityEggLocations")
+
+ HoppityEggLocator.eggLocations = data.eggLocations
+
+ rabbitSlots = data.rabbitSlots
+ otherUpgradeSlots = data.otherUpgradeSlots
+ noPickblockSlots = data.noPickblockSlots
+ barnIndex = data.barnIndex
+ infoIndex = data.infoIndex
+ productionInfoIndex = data.productionInfoIndex
+ prestigeIndex = data.prestigeIndex
+ milestoneIndex = data.milestoneIndex
+ leaderboardIndex = data.leaderboardIndex
+ maxRabbits = data.maxRabbits
+
+ val disabledFeatures = event.getConstant<DisabledFeaturesJson>("DisabledFeatures")
+ HOPPITY_EVENT_DISABLED = disabledFeatures.features["HOPPITY_EVENT_DISABLED"] ?: false
+ }
+
+ private var HOPPITY_EVENT_DISABLED = false
+
+ private fun List<String>.getUpgradeCost(): Long? {
+ val nextLine = this.nextAfter({ UtilsPatterns.costLinePattern.matches(it) }) ?: return null
+ return chocolateAmountPattern.matchMatcher(nextLine.removeColor()) {
+ group("amount").formatLong()
+ }
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+
+ fun isHoppityEvent() = SkyblockSeason.getCurrentSeason() == SkyblockSeason.SPRING &&
+ (LorenzUtils.isOnAlphaServer || !HOPPITY_EVENT_DISABLED)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryBarnManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryBarnManager.kt
new file mode 100644
index 000000000..353e0dfe3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryBarnManager.kt
@@ -0,0 +1,77 @@
+package at.hannibal2.skyhanni.features.event.chocolatefactory
+
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.seconds
+
+object ChocolateFactoryBarnManager {
+
+ private val config get() = ChocolateFactoryAPI.config
+ private val profileStorage get() = ChocolateFactoryAPI.profileStorage
+
+ private val newRabbitPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "rabbit.new",
+ "§d§lNEW RABBIT! §6\\+\\d Chocolate §7and §6\\+0.\\d+x Chocolate §7per second!"
+ )
+ private val rabbitDuplicatePattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "rabbit.duplicate",
+ "§7§lDUPLICATE RABBIT! §6\\+[\\d,]+ Chocolate"
+ )
+
+ var barnFull = false
+ private var lastBarnFullWarning = SimpleTimeMark.farPast()
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+
+ newRabbitPattern.matchMatcher(event.message) {
+ val profileStorage = profileStorage ?: return
+ profileStorage.currentRabbits += 1
+ trySendBarnFullMessage()
+ HoppityEggsManager.shareWaypointPrompt()
+ }
+
+ rabbitDuplicatePattern.matchMatcher(event.message) {
+ HoppityEggsManager.shareWaypointPrompt()
+ }
+ }
+
+ fun trySendBarnFullMessage() {
+ if (!ChocolateFactoryAPI.isEnabled()) return
+ val profileStorage = profileStorage ?: return
+
+ val remainingSpace = profileStorage.maxRabbits - profileStorage.currentRabbits
+ barnFull =
+ remainingSpace <= config.barnCapacityThreshold && profileStorage.maxRabbits < ChocolateFactoryAPI.maxRabbits
+ if (!barnFull) return
+
+ if (lastBarnFullWarning.passedSince() < 30.seconds) return
+
+ if (profileStorage.maxRabbits == -1) {
+ ChatUtils.clickableChat(
+ "Open your chocolate factory to see your barn's capacity status!",
+ "cf"
+ )
+ return
+ }
+
+ ChatUtils.clickableChat(
+ "§cYour barn is almost full! " +
+ "§7(${barnStatus()}). §cUpgrade it so they don't get crushed",
+ "cf"
+ )
+ SoundUtils.playBeepSound()
+ lastBarnFullWarning = SimpleTimeMark.now()
+ }
+
+ fun barnStatus(): String {
+ val profileStorage = profileStorage ?: return "Unknown"
+ return "${profileStorage.currentRabbits}/${profileStorage.maxRabbits} Rabbits"
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryInventory.kt b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryInventory.kt
new file mode 100644
index 000000000..bad268f96
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryInventory.kt
@@ -0,0 +1,115 @@
+package at.hannibal2.skyhanni.features.event.chocolatefactory
+
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.GuiRenderItemEvent
+import at.hannibal2.skyhanni.events.RenderInventoryItemTipEvent
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
+import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal
+import at.hannibal2.skyhanni.utils.RenderUtils.drawSlotText
+import at.hannibal2.skyhanni.utils.RenderUtils.highlight
+import at.hannibal2.skyhanni.utils.StringUtils.matchFirst
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object ChocolateFactoryInventory {
+
+ private val config get() = ChocolateFactoryAPI.config
+
+ private val rabbitAmountPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "rabbit.amount",
+ "Rabbit \\S+ - \\[(?<amount>\\d+)].*"
+ )
+ private val upgradeTierPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "upgradetier",
+ ".*\\s(?<tier>[IVXLC]+)"
+ )
+ private val unclaimedRewardsPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "unclaimedrewards",
+ "§7§aYou have \\d+ unclaimed rewards?!"
+ )
+
+ @SubscribeEvent
+ fun onRenderItemOverlayPost(event: GuiRenderItemEvent.RenderOverlayEvent.GuiRenderItemPost) {
+ if (!ChocolateFactoryAPI.inChocolateFactory) return
+ if (!config.highlightUpgrades) return
+
+ val item = event.stack ?: return
+ val itemName = item.name
+ if (itemName != ChocolateFactoryAPI.bestRabbitUpgrade) return
+
+ event.drawSlotText(event.x + 18, event.y, "§6✦", .8f)
+ }
+
+ @SubscribeEvent
+ fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) {
+ if (!ChocolateFactoryAPI.inChocolateFactory) return
+ if (!config.highlightUpgrades) return
+
+ for (slot in InventoryUtils.getItemsInOpenChest()) {
+ if (slot.slotIndex in ChocolateFactoryAPI.upgradeableSlots) {
+ if (slot.slotIndex == ChocolateFactoryAPI.bestUpgrade) {
+ slot highlight LorenzColor.GREEN.addOpacity(200)
+ } else {
+ slot highlight LorenzColor.GREEN.addOpacity(75)
+ }
+ }
+ if (slot.slotIndex == ChocolateFactoryAPI.barnIndex && ChocolateFactoryBarnManager.barnFull) {
+ slot highlight LorenzColor.RED
+ }
+ if (slot.slotIndex == ChocolateFactoryAPI.clickRabbitSlot) {
+ slot highlight LorenzColor.RED
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderItemTip(event: RenderInventoryItemTipEvent) {
+ if (!ChocolateFactoryAPI.inChocolateFactory) return
+ if (!config.showStackSizes) return
+
+ val item = event.stack
+ val itemName = item.name.removeColor()
+ val slotNumber = event.slot.slotNumber
+
+ if (slotNumber in ChocolateFactoryAPI.rabbitSlots) {
+ rabbitAmountPattern.matchMatcher(itemName) {
+ val rabbitTip = when (val rabbitAmount = group("amount").formatInt()) {
+ in (0..9) -> "$rabbitAmount"
+ in (10..74) -> "§a$rabbitAmount"
+ in (75..124) -> "§9$rabbitAmount"
+ in (125..174) -> "§5$rabbitAmount"
+ in (175..199) -> "§6$rabbitAmount"
+ 200 -> "§d$rabbitAmount"
+ else -> "§c$rabbitAmount"
+ }
+
+ event.stackTip = rabbitTip
+ }
+ }
+ if (slotNumber in ChocolateFactoryAPI.otherUpgradeSlots) {
+ upgradeTierPattern.matchMatcher(itemName) {
+ event.stackTip = group("tier").romanToDecimal().toString()
+ }
+ }
+ if (slotNumber == ChocolateFactoryAPI.milestoneIndex) {
+ item.getLore().matchFirst(unclaimedRewardsPattern) {
+ event.stackTip = "§c!!!"
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) {
+ if (!ChocolateFactoryAPI.inChocolateFactory) return
+ val slot = event.slot ?: return
+ if (!config.useMiddleClick) return
+ if (slot.slotNumber in ChocolateFactoryAPI.noPickblockSlots) return
+
+ event.makePickblock()
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryStats.kt
new file mode 100644
index 000000000..e1df1672d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/chocolatefactory/ChocolateFactoryStats.kt
@@ -0,0 +1,79 @@
+package at.hannibal2.skyhanni.features.event.chocolatefactory
+
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object ChocolateFactoryStats {
+
+ private val config get() = ChocolateFactoryAPI.config
+
+ private var displayList = listOf<String>()
+
+ @SubscribeEvent
+ fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
+ if (!ChocolateFactoryAPI.inChocolateFactory) return
+ if (!config.statsDisplay) return
+
+ config.position.renderStrings(displayList, posLabel = "Chocolate Factory Stats")
+ }
+
+ fun updateDisplay() {
+ val perSecond = ChocolateFactoryAPI.chocolatePerSecond
+ val perMinute = perSecond * 60
+ val perHour = perMinute * 60
+ val perDay = perHour * 24
+ val position = ChocolateFactoryAPI.leaderboardPosition?.addSeparators() ?: "???"
+
+ displayList = formatList(buildList {
+ add("§6§lChocolate Factory Stats")
+
+ add("§eCurrent Chocolate: §6${ChocolateFactoryAPI.chocolateCurrent.addSeparators()}")
+ add("§eThis Prestige: §6${ChocolateFactoryAPI.chocolateThisPrestige.addSeparators()}")
+ add("§eAll-time: §6${ChocolateFactoryAPI.chocolateAllTime.addSeparators()}")
+
+ add("§ePer Second: §6${perSecond.addSeparators()}")
+ add("§ePer Minute: §6${perMinute.addSeparators()}")
+ add("§ePer Hour: §6${perHour.addSeparators()}")
+ add("§ePer Day: §6${perDay.addSeparators()}")
+
+ add("§eChocolate Multiplier: §6${ChocolateFactoryAPI.chocolateMultiplier}")
+ add("§eBarn: §6${ChocolateFactoryBarnManager.barnStatus()}")
+
+ add("§ePosition: §7#§b$position")
+
+ add("")
+ add("")
+ add("")
+ })
+ }
+
+ private fun formatList(list: List<String>): List<String> {
+ return config.statsDisplayList
+ .filter { ChocolateFactoryAPI.currentPrestige != 1 || it != ChocolateFactoryStat.THIS_PRESTIGE }
+ .map { list[it.ordinal] }
+ }
+
+ enum class ChocolateFactoryStat(private val display: String) {
+ HEADER("§6§lChocolate Facto