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 3ec8dc2e92d2..25cca9caf0f9 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 @@ -4,8 +4,9 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.api.event.HandleEvent.Companion.HIGHEST import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryUpdatedEvent 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.BOUGHT @@ -23,11 +24,8 @@ import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactor 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.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.LorenzRarity import at.hannibal2.skyhanni.utils.LorenzRarity.DIVINE import at.hannibal2.skyhanni.utils.LorenzRarity.LEGENDARY @@ -46,11 +44,11 @@ import net.minecraft.init.Blocks import net.minecraft.init.Items import net.minecraft.inventory.Slot import net.minecraft.item.Item +import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.reflect.KMutableProperty1 import kotlin.reflect.full.memberProperties -import kotlin.time.Duration.Companion.seconds @SkyHanniModule object HoppityAPI { @@ -104,13 +102,10 @@ object HoppityAPI { * 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)?", + "(?:§.)*Chocolate (?:Shop |Factory ?)(?:Milestones)?", ) /** @@ -141,13 +136,12 @@ object HoppityAPI { } } - private val hoppityDataSet = HoppityStateDataSet() - private val processedStraySlots = mutableListOf() - private val straySlotIterations = mutableMapOf() - private val S_GLASS_PANE_ITEM by lazy { Item.getItemFromBlock(Blocks.stained_glass_pane) } - private val CHEST_ITEM by lazy { Item.getItemFromBlock(Blocks.chest) } - val hoppityRarities by lazy { LorenzRarity.entries.filter { it <= DIVINE } } + private val hoppityDataSet = HoppityStateDataSet() + private val processedStraySlots = mutableMapOf() + private val miscProcessableItemTypes by lazy { + listOf(Items.skull, Item.getItemFromBlock(Blocks.stained_glass_pane)) + } fun isHoppityEvent() = (SkyblockSeason.currentSeason == SkyblockSeason.SPRING || SkyHanniMod.feature.dev.debug.alwaysHoppitys) fun getEventEndMark(): SimpleTimeMark? = if (isHoppityEvent()) { @@ -168,30 +162,22 @@ object HoppityAPI { return (month % 2 == 1) == (day % 2 == 0) } - private fun addProcessedStraySlot(slot: Slot) { - processedStraySlots.add(slot.slotNumber) - if (straySlotIterations.addOrPut(slot.slotNumber, 1) > 20) return - DelayedRun.runDelayed(1.seconds) { - val noLongerExists = InventoryUtils.getItemsInOpenChest().filter { - shouldProcessStraySlot(it, true) - }.none { - it.slotNumber == slot.slotNumber && - it.stack?.displayName == slot.stack?.displayName - } - if (noLongerExists) { - processedStraySlots.remove(slot.slotNumber) - straySlotIterations.remove(slot.slotNumber) - } else addProcessedStraySlot(slot) - } - } - private fun shouldProcessStraySlot(slot: Slot, containsBypass: Boolean = false) = + private fun Map.filterStrayProcessable() = filter { (slotNumber, stack) -> // 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 && + slotNumber != 13 && slotNumber in 0..26 && // Don't process the same slot twice. - (!processedStraySlots.contains(slot.slotNumber) || containsBypass) && - slot.stack != null && slot.stack.item != null && + !processedStraySlots.contains(slotNumber) && + // Stack must not be null, and must be a skull. + stack.item != null && stack.item == Items.skull && // All strays are skulls with a display name, and lore. - slot.stack.hasDisplayName() && slot.stack.item == Items.skull && slot.stack.getLore().isNotEmpty() + stack.hasDisplayName() && stack.getLore().isNotEmpty() + } + + + private fun Slot.isMiscProcessable() = + // All misc items are skulls or panes, with a display name, and lore. + stack != null && stack.item != null && stack.item in miscProcessableItemTypes && + stack.hasDisplayName() && stack.getLore().isNotEmpty() private fun postApiEggFoundEvent(type: HoppityEggType, event: LorenzChatEvent, note: String? = null) { EggFoundEvent( @@ -200,77 +186,67 @@ object HoppityAPI { note = note ).post() } - private fun getRarityFromHoppityRarity(rarity: String) = hoppityRarities.firstOrNull { - it.chatColorCode == rarity.substring(0, 2) || it.rawName == rarity.removeColor() + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + processedStraySlots.clear() } - fun LorenzRarity.toHoppityRarity() = "$chatColorCode§l$rawName" - @SubscribeEvent(priority = EventPriority.HIGH) - fun onTick(event: SecondPassedEvent) { + @SubscribeEvent + fun onInventoryUpdated(event: InventoryUpdatedEvent) { + // Remove any processed stray slots that are no longer in the inventory. + processedStraySlots.entries.removeIf { + it.key !in event.inventoryItems || event.inventoryItems[it.key]?.displayName != it.value + } + + // Only process if we're in the Chocolate Factory. if (!ChocolateFactoryAPI.inChocolateFactory) return - InventoryUtils.getItemsInOpenChest().filter { shouldProcessStraySlot(it) }.forEach { + + event.inventoryItems.filterStrayProcessable().forEach { (slotNumber, itemStack) -> var processed = false - ChocolateFactoryStrayTracker.strayCaughtPattern.matchMatcher(it.stack.displayName) { - ChocolateFactoryStrayTracker.handleStrayClicked(it) - processed = true + ChocolateFactoryStrayTracker.strayCaughtPattern.matchMatcher(itemStack.displayName) { + processed = ChocolateFactoryStrayTracker.handleStrayClicked(slotNumber, itemStack) when (groupOrNull("name") ?: return@matchMatcher) { "Fish the Rabbit" -> { hoppityDataSet.lastName = "§9Fish the Rabbit" hoppityDataSet.lastRarity = RARE - hoppityDataSet.duplicate = it.stack.getLore().any { line -> duplicatePseudoStrayPattern.matches(line) } - EggFoundEvent(STRAY, it.slotNumber).post() + hoppityDataSet.duplicate = itemStack.getLore().any { line -> duplicatePseudoStrayPattern.matches(line) } + EggFoundEvent(STRAY, slotNumber).post() } else -> return@matchMatcher } } - ChocolateFactoryStrayTracker.strayDoradoPattern.matchMatcher(formLoreToSingleLine(it.stack.getLore())) { + ChocolateFactoryStrayTracker.strayDoradoPattern.matchMatcher(formLoreToSingleLine(itemStack.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. - if (ChocolateFactoryStrayTracker.doradoEscapeStrayPattern.anyMatches(it.stack.getLore())) return@matchMatcher + if (ChocolateFactoryStrayTracker.doradoEscapeStrayPattern.anyMatches(itemStack.getLore())) return@matchMatcher // We don't need to do a handleStrayClicked here - the lore from El Dorado is already: // §6§lGolden Rabbit §d§lCAUGHT! // Which will trigger the above matcher. We only need to check name here to fire the found event for Dorado. hoppityDataSet.lastName = "§6El Dorado" hoppityDataSet.lastRarity = LEGENDARY - hoppityDataSet.duplicate = it.stack.getLore().any { line -> duplicateDoradoStrayPattern.matches(line) } - EggFoundEvent(STRAY, it.slotNumber).post() + hoppityDataSet.duplicate = itemStack.getLore().any { line -> duplicateDoradoStrayPattern.matches(line) } + EggFoundEvent(STRAY, slotNumber).post() } - if (processed) addProcessedStraySlot(it) + if (processed) processedStraySlots[slotNumber] = itemStack.displayName } } - private fun shouldProcessMiscSlot(slot: Slot) = - // Don't process the same slot twice. - !processedStraySlots.contains(slot.slotNumber) && - slot.stack != null && slot.stack.item != null && - // All misc items are skulls, panes, or chests with a display name, and lore. - shouldProcessMiscSlotItem(slot.stack.item) && - slot.stack.hasDisplayName() && slot.stack.getLore().isNotEmpty() - - private fun shouldProcessMiscSlotItem(item: Item) = - item == Items.skull || item == S_GLASS_PANE_ITEM || item == CHEST_ITEM @SubscribeEvent(priority = EventPriority.HIGH) fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { if (!miscProcessInvPattern.matches(InventoryUtils.openInventoryName())) 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 == slot.slotNumber && it.hasStack } - ?.stack ?: return - val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName) + val slot = event.slot?.takeIf { it.isMiscProcessable() } ?: return - if (sideDishNamePattern.matches(nameText)) EggFoundEvent(SIDE_DISH, index).post() + if (sideDishNamePattern.matches(slot.stack.displayName)) EggFoundEvent(SIDE_DISH, event.slotId).post() - milestoneNamePattern.matchMatcher(nameText) { - val lore = clickedStack.getLore() + milestoneNamePattern.matchMatcher(slot.stack.displayName) { + val lore = slot.stack.getLore() if (!claimableMilestonePattern.anyMatches(lore)) return - if (allTimeLorePattern.anyMatches(lore)) EggFoundEvent(CHOCOLATE_FACTORY_MILESTONE, index).post() - if (shopLorePattern.anyMatches(lore)) EggFoundEvent(CHOCOLATE_SHOP_MILESTONE, index).post() + if (allTimeLorePattern.anyMatches(lore)) EggFoundEvent(CHOCOLATE_FACTORY_MILESTONE, event.slotId).post() + if (shopLorePattern.anyMatches(lore)) EggFoundEvent(CHOCOLATE_SHOP_MILESTONE, event.slotId).post() } } @@ -321,7 +297,7 @@ object HoppityAPI { HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) { hoppityDataSet.lastName = group("name") ChocolateFactoryBarnManager.processDataSet(hoppityDataSet) - hoppityDataSet.lastRarity = getRarityFromHoppityRarity(group("rarity")) + hoppityDataSet.lastRarity = LorenzRarity.getByName(group("rarity")) attemptFireRabbitFound(event) } 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 576f2879af09..edc92f9643e0 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 @@ -4,7 +4,6 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEggsConfig import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI.HoppityStateDataSet -import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI.toHoppityRarity import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.Companion.resettingEntries import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -50,7 +49,7 @@ object HoppityEggsCompactChat { in resettingEntries -> "${hoppityDataSet.lastMeal?.coloredName.orEmpty()} Egg" else -> "${hoppityDataSet.lastMeal?.coloredName.orEmpty()} Rabbit" } - val rarityString = hoppityDataSet.lastRarity?.toHoppityRarity() ?: "§C§L???" + val rarityString = hoppityDataSet.lastRarity?.let { "${it.chatColorCode}§l${it.rawName}" } ?: "§C§L???" val rarityFormat = when { hoppityDataSet.duplicate && rarityConfig in listOf(RarityType.BOTH, RarityType.DUPE) -> "$rarityString " !hoppityDataSet.duplicate && rarityConfig in listOf(RarityType.BOTH, RarityType.NEW) -> "$rarityString " 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 de0087126de7..5bf9e30dc0dd 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 @@ -70,7 +70,7 @@ object HoppityEggsManager { */ val rabbitFoundPattern by ChocolateFactoryAPI.patternGroup.pattern( "rabbit.found", - "§D§LHOPPITY'S HUNT §7You found (?.*) §7\\((?.*)§7\\)!", + "§D§LHOPPITY'S HUNT §7You found (?.*) §7\\(§.§L(?.*)§7\\)!", ) /** diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTimer.kt index f263077837c5..6ff8c704721e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTimer.kt @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI @@ -29,8 +30,8 @@ object ChocolateFactoryStrayTimer { private var lastPingTime = SimpleTimeMark.farPast() @HandleEvent - fun onEggFound(eggFoundEvent: EggFoundEvent) { - val type = eggFoundEvent.type + fun onEggFound(event: EggFoundEvent) { + val type = event.type // Only reset the timer for meal entries and hitman eggs if (type !in resettingEntries && type != HoppityEggType.HITMAN) return timer = 30.seconds @@ -38,13 +39,15 @@ object ChocolateFactoryStrayTimer { } @SubscribeEvent - fun onInventoryClose(event: InventoryCloseEvent) { - if (timer == Duration.ZERO || timer == 30.seconds) return - // Reset the timer if the inventory is closed and the timer is not at 30 seconds - // The 30s stray timer only counts if you stay in the inventory for the full duration + fun onIslandChange(event: IslandChangeEvent) { timer = Duration.ZERO } + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + if (timer > Duration.ZERO) timer = 30.seconds + } + @SubscribeEvent fun onTick(event: LorenzTickEvent) { if (!HoppityAPI.isHoppityEvent() || !ChocolateFactoryAPI.inChocolateFactory) return 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 index 9d59ae35a1e1..6bcd7b3fc5c9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt @@ -34,7 +34,7 @@ import at.hannibal2.skyhanni.utils.tracker.TrackerData import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.annotations.Expose -import net.minecraft.inventory.Slot +import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -45,6 +45,7 @@ object ChocolateFactoryStrayTracker { private val config get() = ChocolateFactoryAPI.config private val claimedStraysSlots = mutableListOf() + // /** * REGEX-TEST: §9Zero §d§lCAUGHT! * REGEX-TEST: §6§lGolden Rabbit §d§lCAUGHT! @@ -134,6 +135,7 @@ object ChocolateFactoryStrayTracker { "stray.doradoescape", ".*(?:§.)*(?:but he escaped and left behind|Legend of (?:§.)*El Dorado (?:§.)*grows!).*" ) + // private val tracker = SkyHanniTracker("Stray Tracker", { Data() }, { it.chocolateFactory.strayTracker }) { drawDisplay(it) @@ -224,11 +226,11 @@ object ChocolateFactoryStrayTracker { return if (goldenList.isEmpty()) "" else ("\n" + goldenList.joinToString("\n")) } - fun handleStrayClicked(slot: Slot) { - if (!isEnabled() || claimedStraysSlots.contains(slot.slotNumber)) return + fun handleStrayClicked(slotNumber: Int, itemStack: ItemStack): Boolean { + if (!isEnabled() || claimedStraysSlots.contains(slotNumber)) return false - claimedStraysSlots.add(slot.slotIndex) - val loreLine = formLoreToSingleLine(slot.stack.getLore()) + claimedStraysSlots.add(slotNumber) + val loreLine = formLoreToSingleLine(itemStack.getLore()) // "Base" strays - Common -> Epic, raw choc only reward. strayLorePattern.matchMatcher(loreLine) { @@ -273,6 +275,8 @@ object ChocolateFactoryStrayTracker { } incrementGoldenType("dorado") } + + return true } @SubscribeEvent