From 6955c99b2e241cf7e4070424e8dbf29f80bb63fd Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Sat, 7 Dec 2024 21:18:21 +0100 Subject: feat: Add kat upgrade and flower detection --- src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt | 67 ++++++++++---- src/main/kotlin/moe/nea/ledger/ItemUtil.kt | 53 ++++++++--- src/main/kotlin/moe/nea/ledger/Ledger.kt | 19 ++-- src/main/kotlin/moe/nea/ledger/LedgerLogger.kt | 2 + .../moe/nea/ledger/events/BeforeGuiAction.kt | 7 +- .../kotlin/moe/nea/ledger/modules/KatDetection.kt | 100 +++++++++++++++++++++ 6 files changed, 212 insertions(+), 36 deletions(-) create mode 100644 src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt (limited to 'src/main') diff --git a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt index 43f2cb7..f582015 100644 --- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt +++ b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt @@ -1,11 +1,12 @@ package moe.nea.ledger import moe.nea.ledger.events.BeforeGuiAction -import net.minecraft.client.gui.inventory.GuiChest -import net.minecraft.inventory.ContainerChest +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.client.event.GuiScreenEvent import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import org.lwjgl.input.Mouse @@ -24,24 +25,39 @@ class ItemIdProvider { private val knownNames = mutableMapOf() - @SubscribeEvent - fun saveInventoryIds(event: BeforeGuiAction) { - val chest = (event.gui as? GuiChest) ?: return - val slots = chest.inventorySlots as ContainerChest + @SubscribeEvent(priority = EventPriority.HIGH) + fun savePlayerInventoryIds(event: BeforeGuiAction) { + val player = Minecraft.getMinecraft().thePlayer ?: return + val inventory = player.inventory ?: return + inventory.mainInventory?.forEach { saveFromSlot(it) } + inventory.armorInventory?.forEach { saveFromSlot(it) } + } + + fun saveFromSlot(stack: ItemStack?, preprocessName: (String) -> String = { it }) { + if (stack == null) return + val nbt = stack.tagCompound ?: NBTTagCompound() + val display = nbt.getCompoundTag("display") + var name = display.getString("Name").unformattedString() + name = preprocessName(name) + name = name.trim() + val id = stack.getInternalId() + if (id != null && name.isNotBlank()) { + knownNames[name] = id + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun saveChestInventoryIds(event: BeforeGuiAction) { + val slots = event.chestSlots ?: return val chestName = slots.lowerChestInventory.name.unformattedString() val isOrderMenu = chestName == "Your Bazaar Orders" || chestName == "Co-op Bazaar Orders" + val preprocessor: (String) -> String = if (isOrderMenu) { + { it.removePrefix("BUY ").removePrefix("SELL ") } + } else { + { it } + } slots.inventorySlots.forEach { - val stack = it.stack ?: return@forEach - val nbt = stack.tagCompound ?: NBTTagCompound() - val display = nbt.getCompoundTag("display") - var name = display.getString("Name").unformattedString() - if (isOrderMenu) - name = name.removePrefix("BUY ").removePrefix("SELL ") - name = name.trim() - val id = stack.getInternalId() - if (id != null && name.isNotBlank()) { - knownNames[name] = id - } + saveFromSlot(it?.stack, preprocessor) } } @@ -49,4 +65,21 @@ class ItemIdProvider { return knownNames[name] } + private val coinRegex = "(?$SHORT_NUMBER_PATTERN) Coins?".toPattern() + private val stackedItem = "(?.*) x(?$SHORT_NUMBER_PATTERN)".toPattern() + + fun findFromLore(name: String): Pair? { + val properName = name.unformattedString() + coinRegex.useMatcher(properName) { + return Pair(ItemId.COINS, parseShortNumber(group("amount"))) + } + stackedItem.useMatcher(properName) { + val item = findForName(group("name")) + if (item != null) { + val count = parseShortNumber(group("count")) + return Pair(item, count) + } + } + return findForName(properName)?.let { Pair(it, 1.0) } + } } \ No newline at end of file diff --git a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt index b82c97f..38c2b50 100644 --- a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt +++ b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt @@ -4,25 +4,56 @@ import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound +fun ItemStack.getExtraAttributes(): NBTTagCompound { + val nbt = this.tagCompound ?: return NBTTagCompound() + return nbt.getCompoundTag("ExtraAttributes") +} + fun ItemStack.getInternalId(): ItemId? { - val nbt = this.tagCompound ?: NBTTagCompound() - val extraAttributes = nbt.getCompoundTag("ExtraAttributes") - val id = extraAttributes.getString("id") - return id.takeIf { it.isNotBlank() }?.let(::ItemId) + val extraAttributes = getExtraAttributes() + var id = extraAttributes.getString("id") + id = id.takeIf { it.isNotBlank() } + if (id == "PET") { + id = getPetId() ?: id + } + return id?.let(::ItemId) +} + +class PetInfo { + var type: String? = null + var tier: String? = null } +fun ItemStack.getPetId(): String? { + val petInfoStr = getExtraAttributes().getString("petInfo") + val petInfo = Ledger.gson.fromJson(petInfoStr, PetInfo::class.java) + if (petInfo.type == null || petInfo.tier == null) return null + return petInfo.type + ";" + rarityToIndex(petInfo.tier ?: "") +} + +fun rarityToIndex(rarity: String): Int { + return when (rarity) { + "COMMON" -> 0 + "UNCOMMON" -> 1 + "RARE" -> 2 + "EPIC" -> 3 + "LEGENDARY" -> 4 + "MYTHIC" -> 5 + else -> -1 + } +} fun ItemStack.getLore(): List { - val nbt = this.tagCompound ?: NBTTagCompound() - val extraAttributes = nbt.getCompoundTag("display") - val lore = extraAttributes.getTagList("Lore", 8) - return (0 until lore.tagCount()).map { lore.getStringTagAt(it) } + val nbt = this.tagCompound ?: NBTTagCompound() + val extraAttributes = nbt.getCompoundTag("display") + val lore = extraAttributes.getTagList("Lore", 8) + return (0 until lore.tagCount()).map { lore.getStringTagAt(it) } } fun ItemStack.getDisplayNameU(): String { - val nbt = this.tagCompound ?: NBTTagCompound() - val extraAttributes = nbt.getCompoundTag("display") - return extraAttributes.getString("Name") + val nbt = this.tagCompound ?: NBTTagCompound() + val extraAttributes = nbt.getCompoundTag("display") + return extraAttributes.getString("Name") } diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt index cfbab1c..8a4b850 100644 --- a/src/main/kotlin/moe/nea/ledger/Ledger.kt +++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt @@ -1,5 +1,6 @@ package moe.nea.ledger +import com.google.gson.Gson import io.github.notenoughupdates.moulconfig.managed.ManagedConfig import moe.nea.ledger.config.LedgerConfig import moe.nea.ledger.database.Database @@ -12,6 +13,7 @@ import moe.nea.ledger.modules.BazaarOrderDetection import moe.nea.ledger.modules.BitsDetection import moe.nea.ledger.modules.BitsShopDetection import moe.nea.ledger.modules.DungeonChestDetection +import moe.nea.ledger.modules.KatDetection import moe.nea.ledger.modules.MinionDetection import moe.nea.ledger.modules.NpcDetection import moe.nea.ledger.utils.DI @@ -71,6 +73,7 @@ class Ledger { val managedConfig = ManagedConfig.create(File("config/money-ledger/config.json"), LedgerConfig::class.java) { checkExpose = false } + val gson = Gson() private val tickQueue = ConcurrentLinkedQueue() fun runLater(runnable: Runnable) { tickQueue.add(runnable) @@ -84,21 +87,23 @@ class Ledger { val di = DI() di.registerSingleton(this) di.registerInjectableClasses( - LedgerLogger::class.java, - ItemIdProvider::class.java, + AuctionHouseDetection::class.java, BankDetection::class.java, BazaarDetection::class.java, - DungeonChestDetection::class.java, BazaarOrderDetection::class.java, - AuctionHouseDetection::class.java, BitsDetection::class.java, BitsShopDetection::class.java, + ConfigCommand::class.java, + Database::class.java, + DungeonChestDetection::class.java, + ItemIdProvider::class.java, + KatDetection::class.java, + LedgerLogger::class.java, + LogChatCommand::class.java, MinionDetection::class.java, NpcDetection::class.java, - LogChatCommand::class.java, - ConfigCommand::class.java, - Database::class.java ) + di.registerSingleton(gson) di.instantiateAll() di.getAllInstances().forEach(MinecraftForge.EVENT_BUS::register) di.getAllInstances().filterIsInstance() diff --git a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt index 045c6b1..76b8741 100644 --- a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt +++ b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt @@ -146,6 +146,8 @@ enum class TransactionType { BOOSTER_COOKIE_ATE, COMMUNITY_SHOP_BUY, DUNGEON_CHEST_OPEN, + KAT_TIMESKIP, + KAT_UPGRADE, KISMET_REROLL, NPC_BUY, NPC_SELL, diff --git a/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt b/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt index 69362bd..098912a 100644 --- a/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt +++ b/src/main/kotlin/moe/nea/ledger/events/BeforeGuiAction.kt @@ -1,6 +1,11 @@ package moe.nea.ledger.events import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest import net.minecraftforge.fml.common.eventhandler.Event -data class BeforeGuiAction(val gui: GuiScreen) : Event() +data class BeforeGuiAction(val gui: GuiScreen) : Event() { + val chest = gui as? GuiChest + val chestSlots = chest?.inventorySlots as ContainerChest? +} diff --git a/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt new file mode 100644 index 0000000..8a2aa19 --- /dev/null +++ b/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt @@ -0,0 +1,100 @@ +package moe.nea.ledger.modules + +import moe.nea.ledger.ItemChange +import moe.nea.ledger.ItemId +import moe.nea.ledger.ItemIdProvider +import moe.nea.ledger.LedgerEntry +import moe.nea.ledger.LedgerLogger +import moe.nea.ledger.TransactionType +import moe.nea.ledger.events.BeforeGuiAction +import moe.nea.ledger.events.ChatReceived +import moe.nea.ledger.getInternalId +import moe.nea.ledger.getLore +import moe.nea.ledger.unformattedString +import moe.nea.ledger.useMatcher +import moe.nea.ledger.utils.Inject +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + + +class KatDetection { + @Inject + lateinit var log: LedgerLogger + + @Inject + lateinit var itemIdProvider: ItemIdProvider + + val giftNameToIdMap = mapOf( + "flower" to ItemId("KAT_FLOWER"), + "bouquet" to ItemId("KAT_BOUQUET"), + ) + val katGift = "\\[NPC\\] Kat: A (?.*)\\? For me\\? How sweet!".toPattern() + + @SubscribeEvent + fun onChat(event: ChatReceived) { + katGift.useMatcher(event.message) { + val giftName = group("gift") + val giftId = giftNameToIdMap[giftName] + log.logEntry(LedgerEntry( + TransactionType.KAT_TIMESKIP, + event.timestamp, + listOf( + ItemChange.lose(giftId ?: ItemId.NIL, 1) + ) + )) + } + } + + val confirmSlot = 9 + 9 + 4 + val petSlot = 9 + 4 + + data class PetUpgrade( + val beforePetId: ItemId, + val cost: List> + ) + + var lastPetUpgradeScheduled: PetUpgrade? = null + + @SubscribeEvent + fun onClick(event: BeforeGuiAction) { + val slots = event.chestSlots ?: return + val petItem = slots.lowerChestInventory.getStackInSlot(petSlot) ?: return + val beforePetId = petItem.getInternalId() ?: return + val confirmItem = slots.lowerChestInventory.getStackInSlot(confirmSlot) ?: return + val lore = confirmItem.getLore() + val cost = lore.iterator().asSequence() + .dropWhile { it.unformattedString() != "Cost" }.drop(1) + .takeWhile { it != "" } + .map { itemIdProvider.findFromLore(it) ?: Pair(ItemId.NIL, 1.0) } + .toList() + lastPetUpgradeScheduled = PetUpgrade(beforePetId, cost) + } + + val petUpgradeDialogue = "\\[NPC\\] Kat: I'll get your (?.*) upgraded to (?.*) in no time!".toPattern() + fun upgradePetTier(itemId: ItemId): ItemId { + val str = itemId.string.split(";", limit = 2) + if (str.size == 2) { + val (type, tier) = str + val tierT = tier.toIntOrNull() + if (tierT != null) + return ItemId(type + ";" + (tierT + 1)) + } + return itemId + } + + @SubscribeEvent + fun onPetUpgrade(event: ChatReceived) { + petUpgradeDialogue.useMatcher(event.message) { + val upgrade = lastPetUpgradeScheduled ?: return + lastPetUpgradeScheduled = null + log.logEntry(LedgerEntry( + TransactionType.KAT_UPGRADE, + event.timestamp, + listOf( + ItemChange.lose(upgrade.beforePetId, 1), + ItemChange.gain(upgradePetTier(upgrade.beforePetId), 1), + ) + upgrade.cost.map { ItemChange.lose(it.first, it.second) }, + )) + } + } + +} \ No newline at end of file -- cgit