From e9270b395c0ceeb8a6e5ceb7fca51a8b633e766e Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:47:11 -0500 Subject: [PATCH] Backend: HoppityAPI Improvements (#2899) --- .../features/event/hoppity/HoppityAPI.kt | 113 +++++++++++++----- .../features/event/hoppity/HoppityEggType.kt | 13 +- .../event/hoppity/HoppityEggsCompactChat.kt | 10 +- .../event/hoppity/HoppityEggsManager.kt | 11 +- .../event/hoppity/HoppityEggsShared.kt | 2 +- 5 files changed, 102 insertions(+), 47 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt index 6510172daed8..e146a6e01a8c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt @@ -2,22 +2,24 @@ package at.hannibal2.skyhanni.features.event.hoppity import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.Companion.getEggType import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.STRAY -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPattern -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker.duplicateDoradoStrayPattern import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker.duplicatePseudoStrayPattern import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker.formLoreToSingleLine import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +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 @@ -29,11 +31,11 @@ import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches -import at.hannibal2.skyhanni.utils.SimpleTimeMark -import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getMinecraftId import at.hannibal2.skyhanni.utils.SkyBlockTime import at.hannibal2.skyhanni.utils.SkyblockSeason import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.init.Items +import net.minecraft.inventory.Slot import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.seconds @@ -49,7 +51,8 @@ object HoppityAPI { private var newRabbit = false private var lastMeal: HoppityEggType? = null private var lastDuplicateAmount: Long? = null - private var lastDoradoFire: SimpleTimeMark = SimpleTimeMark.farPast() + private var inMiscProcessInventory = false + private val processedSlots = mutableListOf() val hoppityRarities by lazy { LorenzRarity.entries.filter { it <= DIVINE } } @@ -111,16 +114,51 @@ object HoppityAPI { "§7Spend §6(?[\\d.MBk]*) Chocolate §7in.*", ) + /** + * REGEX-TEST: §eClick to claim! + */ + private val claimableMilestonePattern by ChocolateFactoryAPI.patternGroup.pattern( + "milestone.claimable", + "§eClick to claim!", + ) + + /** + * REGEX-TEST: Chocolate Factory + * REGEX-TEST: Chocolate Shop Milestones + * REGEX-TEST: Chocolate Factory Milestones + * REGEX-TEST: Chocolate Breakfast Egg + * REGEX-TEST: Chocolate Lunch Egg + * REGEX-TEST: Chocolate Dinner Egg + */ + private val miscProcessInvPattern by ChocolateFactoryAPI.patternGroup.pattern( + "inventory.misc", + "(?:§.)*Chocolate (?:Shop |(?:Factory|Breakfast|Lunch|Dinner) ?)(?:Milestones|Egg)?", + ) + + private fun addProcessedSlot(slot: Slot) { + processedSlots.add(slot.slotNumber) + DelayedRun.runDelayed(5.seconds) { // Assume we caught it on the first 'frame', so we can remove it after 5 seconds. + processedSlots.remove(slot.slotNumber) + } + } + + private fun shouldProcessStraySlot(slot: Slot) = + // Strays can only appear in the first 3 rows of the inventory, excluding the middle slot of the middle row. + slot.slotNumber != 13 && slot.slotNumber in 0..26 && + // Don't process the same slot twice. + !processedSlots.contains(slot.slotNumber) && + slot.stack != null && slot.stack.item != null && + // All strays are skulls with a display name, and lore. + slot.stack.hasDisplayName() && slot.stack.item == Items.skull && slot.stack.getLore().isNotEmpty() + @SubscribeEvent(priority = EventPriority.HIGH) fun onTick(event: SecondPassedEvent) { if (!ChocolateFactoryAPI.inChocolateFactory) return - InventoryUtils.getItemsInOpenChest().filter { - it.stack.hasDisplayName() && - it.stack.getMinecraftId().toString() == "minecraft:skull" && - it.stack.getLore().isNotEmpty() - }.forEach { + InventoryUtils.getItemsInOpenChest().filter { shouldProcessStraySlot(it) }.forEach { + var processed = false ChocolateFactoryStrayTracker.strayCaughtPattern.matchMatcher(it.stack.displayName) { ChocolateFactoryStrayTracker.handleStrayClicked(it) + processed = true when (groupOrNull("name") ?: return@matchMatcher) { "Fish the Rabbit" -> { EggFoundEvent(STRAY, it.slotNumber).post() @@ -135,8 +173,7 @@ object HoppityAPI { ChocolateFactoryStrayTracker.strayDoradoPattern.matchMatcher(formLoreToSingleLine(it.stack.getLore())) { // If the lore contains the escape pattern, we don't want to fire the event. // There are also 3 separate messages that can match, which is why we need to check the time since the last fire. - val escaped = ChocolateFactoryStrayTracker.doradoEscapeStrayPattern.anyMatches(it.stack.getLore()) - if (escaped || lastDoradoFire.passedSince() <= 10.seconds) return@matchMatcher + if (ChocolateFactoryStrayTracker.doradoEscapeStrayPattern.anyMatches(it.stack.getLore())) return@matchMatcher // We don't need to do a handleStrayClicked here - the lore from El Dorado is already: // §6§lGolden Rabbit §d§lCAUGHT! @@ -146,17 +183,37 @@ object HoppityAPI { lastMeal = STRAY duplicate = it.stack.getLore().any { line -> duplicateDoradoStrayPattern.matches(line) } attemptFireRabbitFound() - lastDoradoFire = SimpleTimeMark.now() } + if (processed) addProcessedSlot(it) } } + @SubscribeEvent + fun onInventoryOpen(event: InventoryFullyOpenedEvent) { + inMiscProcessInventory = miscProcessInvPattern.matches(event.inventoryName) + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + inMiscProcessInventory = false + } + + private fun shouldProcessMiscSlot(slot: Slot) = + // Don't process the same slot twice. + !processedSlots.contains(slot.slotNumber) && + slot.stack != null && slot.stack.item != null && + // All misc items are skulls with a display name, and lore. + slot.stack.hasDisplayName() && slot.stack.item == Items.skull && slot.stack.getLore().isNotEmpty() + @SubscribeEvent(priority = EventPriority.HIGH) fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { - val index = event.slot?.slotIndex?.takeIf { it != -999 } ?: return + if (!inMiscProcessInventory) return + val slot = event.slot ?: return + val index = slot.slotIndex.takeIf { it != -999 } ?: return + if (!shouldProcessMiscSlot(slot)) return val clickedStack = InventoryUtils.getItemsInOpenChest() - .find { it.slotNumber == event.slot.slotNumber && it.hasStack } + .find { it.slotNumber == slot.slotNumber && it.hasStack } ?.stack ?: return val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName) @@ -165,19 +222,19 @@ object HoppityAPI { lastMeal = SIDE_DISH attemptFireRabbitFound() } + milestoneNamePattern.matchMatcher(nameText) { - clickedStack.getLore().let { - if (!it.any { line -> line == "§eClick to claim!" }) return - allTimeLorePattern.firstMatcher(it) { - EggFoundEvent(CHOCOLATE_FACTORY_MILESTONE, index).post() - lastMeal = CHOCOLATE_FACTORY_MILESTONE - attemptFireRabbitFound() - } - shopLorePattern.firstMatcher(it) { - EggFoundEvent(CHOCOLATE_SHOP_MILESTONE, index).post() - lastMeal = CHOCOLATE_SHOP_MILESTONE - attemptFireRabbitFound() - } + val lore = clickedStack.getLore() + if (!claimableMilestonePattern.anyMatches(lore)) return + allTimeLorePattern.firstMatcher(lore) { + EggFoundEvent(CHOCOLATE_FACTORY_MILESTONE, index).post() + lastMeal = CHOCOLATE_FACTORY_MILESTONE + attemptFireRabbitFound() + } + shopLorePattern.firstMatcher(lore) { + EggFoundEvent(CHOCOLATE_SHOP_MILESTONE, index).post() + lastMeal = CHOCOLATE_SHOP_MILESTONE + attemptFireRabbitFound() } } } @@ -186,7 +243,7 @@ object HoppityAPI { fun onChat(event: LorenzChatEvent) { if (!LorenzUtils.inSkyBlock) return - eggFoundPattern.matchMatcher(event.message) { + HoppityEggsManager.eggFoundPattern.matchMatcher(event.message) { resetRabbitData() lastMeal = getEggType(event) val note = groupOrNull("note")?.removeColor() diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt index 5371816e1319..b56e9ab2041d 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt @@ -1,7 +1,10 @@ package at.hannibal2.skyhanni.features.event.hoppity +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark import at.hannibal2.skyhanni.utils.SkyBlockTime +import java.util.regex.Matcher import kotlin.time.Duration enum class HoppityEggType( @@ -49,7 +52,15 @@ enum class HoppityEggType( fun allFound() = resettingEntries.forEach { it.markClaimed() } - fun getMealByName(mealName: String) = entries.find { it.mealName == mealName } + private fun getMealByName(mealName: String) = entries.find { it.mealName == mealName } + + internal fun Matcher.getEggType(event: LorenzChatEvent): HoppityEggType = + HoppityEggType.getMealByName(group("meal")) ?: run { + ErrorManager.skyHanniError( + "Unknown meal: ${group("meal")}", + "message" to event.message, + ) + } fun checkClaimed() { val currentSbTime = SkyBlockTime.now() diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt index 75b743aa1f37..930be4ac55e3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt @@ -8,10 +8,10 @@ import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.BOUGHT import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.Companion.getEggType import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.STRAY import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPattern -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils @@ -138,12 +138,8 @@ object HoppityEggsCompactChat { CHOCOLATE_SHOP_MILESTONE -> "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d!" - STRAY -> { - "§d§lHOPPITY'S HUNT §r§dYou found a §r§aStray Rabbit§r§d!".also { - // If it was an El Dorado dupe stray, we don't want hanging data - DelayedRun.runDelayed(300.milliseconds) { resetCompactData() } - } - } + STRAY -> + "§d§lHOPPITY'S HUNT §r§dYou found a §r§aStray Rabbit§r§d!" else -> "§d§lHOPPITY'S HUNT §r§7Unknown Egg Type?" diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt index 4b3451514839..f16cf9f6fdbc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt @@ -8,10 +8,10 @@ import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.Companion.getEggType import at.hannibal2.skyhanni.features.fame.ReminderUtils import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.HypixelCommands @@ -26,7 +26,6 @@ import at.hannibal2.skyhanni.utils.SkyBlockTime import at.hannibal2.skyhanni.utils.SoundUtils import at.hannibal2.skyhanni.utils.TimeUtils.format import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import java.util.regex.Matcher import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -171,14 +170,6 @@ object HoppityEggsManager { } } - internal fun Matcher.getEggType(event: LorenzChatEvent): HoppityEggType = - HoppityEggType.getMealByName(group("meal")) ?: run { - ErrorManager.skyHanniError( - "Unknown meal: ${group("meal")}", - "message" to event.message, - ) - } - fun getAndDisposeWaypointOnclick(): () -> Unit { val onClick = latestWaypointOnclick latestWaypointOnclick = {} diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsShared.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsShared.kt index 8c32e897924b..6d95cd9b0069 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsShared.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsShared.kt @@ -1,7 +1,7 @@ package at.hannibal2.skyhanni.features.event.hoppity import at.hannibal2.skyhanni.events.LorenzChatEvent -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.Companion.getEggType import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.HypixelCommands