aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-12-07 22:30:51 +0100
committerLinnea Gräf <nea@nea.moe>2024-12-07 22:30:51 +0100
commit0ca988c907c7e8e26029f59cc098e6be5e008ee5 (patch)
tree59af4d0e872e9fb53820c3ad2fd7147ae894a5ad /src
parent6955c99b2e241cf7e4070424e8dbf29f80bb63fd (diff)
downloadLocalTransactionLedger-0ca988c907c7e8e26029f59cc098e6be5e008ee5.tar.gz
LocalTransactionLedger-0ca988c907c7e8e26029f59cc098e6be5e008ee5.tar.bz2
LocalTransactionLedger-0ca988c907c7e8e26029f59cc098e6be5e008ee5.zip
feat: Add dungeon chest loot detection
Diffstat (limited to 'src')
-rw-r--r--src/main/java/moe/nea/ledger/events/RegistrationFinishedEvent.kt7
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt29
-rw-r--r--src/main/kotlin/moe/nea/ledger/ItemUtil.kt10
-rw-r--r--src/main/kotlin/moe/nea/ledger/Ledger.kt2
-rw-r--r--src/main/kotlin/moe/nea/ledger/LedgerLogger.kt4
-rw-r--r--src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt12
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt91
-rw-r--r--src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt7
8 files changed, 109 insertions, 53 deletions
diff --git a/src/main/java/moe/nea/ledger/events/RegistrationFinishedEvent.kt b/src/main/java/moe/nea/ledger/events/RegistrationFinishedEvent.kt
new file mode 100644
index 0000000..d36e0c7
--- /dev/null
+++ b/src/main/java/moe/nea/ledger/events/RegistrationFinishedEvent.kt
@@ -0,0 +1,7 @@
+package moe.nea.ledger.events
+
+import net.minecraftforge.fml.common.eventhandler.Event
+
+class RegistrationFinishedEvent : Event() {
+
+}
diff --git a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
index f582015..f4a0232 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemIdProvider.kt
@@ -1,6 +1,8 @@
package moe.nea.ledger
import moe.nea.ledger.events.BeforeGuiAction
+import moe.nea.ledger.events.ExtraSupplyIdEvent
+import moe.nea.ledger.events.RegistrationFinishedEvent
import net.minecraft.client.Minecraft
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@@ -25,6 +27,11 @@ class ItemIdProvider {
private val knownNames = mutableMapOf<String, ItemId>()
+ @SubscribeEvent
+ fun onTick(event: RegistrationFinishedEvent) {
+ MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(knownNames::put))
+ }
+
@SubscribeEvent(priority = EventPriority.HIGH)
fun savePlayerInventoryIds(event: BeforeGuiAction) {
val player = Minecraft.getMinecraft().thePlayer ?: return
@@ -66,14 +73,30 @@ class ItemIdProvider {
}
private val coinRegex = "(?<amount>$SHORT_NUMBER_PATTERN) Coins?".toPattern()
- private val stackedItem = "(?<name>.*) x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
+ private val stackedItemRegex = "(?<name>.*) x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
+ private val essenceRegex = "(?<essence>.*) Essence x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
- fun findFromLore(name: String): Pair<ItemId, Double>? {
+ fun findCostItemsFromSpan(lore: List<String>): List<Pair<ItemId, Double>> {
+ return lore.iterator().asSequence()
+ .dropWhile { it.unformattedString() != "Cost" }.drop(1)
+ .takeWhile { it != "" }
+ .map { findStackableItemByName(it) ?: Pair(ItemId.NIL, 1.0) }
+ .toList()
+ }
+
+ fun findStackableItemByName(name: String): Pair<ItemId, Double>? {
val properName = name.unformattedString()
+ if (properName == "FREE") {
+ return Pair(ItemId.COINS, 0.0)
+ }
coinRegex.useMatcher(properName) {
return Pair(ItemId.COINS, parseShortNumber(group("amount")))
}
- stackedItem.useMatcher(properName) {
+ essenceRegex.useMatcher(properName) {
+ return Pair(ItemId("ESSENCE_${group("essence").uppercase()}"),
+ parseShortNumber(group("count")))
+ }
+ stackedItemRegex.useMatcher(properName) {
val item = findForName(group("name"))
if (item != null) {
val count = parseShortNumber(group("count"))
diff --git a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
index 38c2b50..cadcb66 100644
--- a/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
+++ b/src/main/kotlin/moe/nea/ledger/ItemUtil.kt
@@ -16,9 +16,19 @@ fun ItemStack.getInternalId(): ItemId? {
if (id == "PET") {
id = getPetId() ?: id
}
+ if (id == "ENCHANTED_BOOK") {
+ id = getEnchanments().entries.singleOrNull()?.let {
+ "${it.key};${it.value}".uppercase()
+ }
+ }
return id?.let(::ItemId)
}
+fun ItemStack.getEnchanments(): Map<String, Int> {
+ val enchantments = getExtraAttributes().getCompoundTag("enchantments")
+ return enchantments.keySet.associateWith { enchantments.getInteger(it) }
+}
+
class PetInfo {
var type: String? = null
var tier: String? = null
diff --git a/src/main/kotlin/moe/nea/ledger/Ledger.kt b/src/main/kotlin/moe/nea/ledger/Ledger.kt
index 8a4b850..ee15a83 100644
--- a/src/main/kotlin/moe/nea/ledger/Ledger.kt
+++ b/src/main/kotlin/moe/nea/ledger/Ledger.kt
@@ -6,6 +6,7 @@ import moe.nea.ledger.config.LedgerConfig
import moe.nea.ledger.database.Database
import moe.nea.ledger.events.ChatReceived
import moe.nea.ledger.events.LateWorldLoadEvent
+import moe.nea.ledger.events.RegistrationFinishedEvent
import moe.nea.ledger.modules.AuctionHouseDetection
import moe.nea.ledger.modules.BankDetection
import moe.nea.ledger.modules.BazaarDetection
@@ -110,6 +111,7 @@ class Ledger {
.forEach { ClientCommandHandler.instance.registerCommand(it) }
di.provide<Database>().loadAndUpgrade()
+ MinecraftForge.EVENT_BUS.post(RegistrationFinishedEvent())
}
var lastJoin = -1L
diff --git a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
index 76b8741..e8c15ea 100644
--- a/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
+++ b/src/main/kotlin/moe/nea/ledger/LedgerLogger.kt
@@ -157,6 +157,10 @@ enum class TransactionType {
value class ItemId(
val string: String
) {
+ fun singleItem(): Pair<ItemId, Double> {
+ return Pair(this, 1.0)
+ }
+
companion object {
val COINS = ItemId("SKYBLOCK_COIN")
val BITS = ItemId("SKYBLOCK_BIT")
diff --git a/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt b/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt
new file mode 100644
index 0000000..d040961
--- /dev/null
+++ b/src/main/kotlin/moe/nea/ledger/events/ExtraSupplyIdEvent.kt
@@ -0,0 +1,12 @@
+package moe.nea.ledger.events
+
+import moe.nea.ledger.ItemId
+import net.minecraftforge.fml.common.eventhandler.Event
+
+class ExtraSupplyIdEvent(
+ private val store: (String, ItemId) -> Unit
+) : Event() {
+ fun store(name: String, id: ItemId) {
+ store.invoke(name, id)
+ }
+}
diff --git a/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt
index 5598174..c50e9eb 100644
--- a/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt
+++ b/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt
@@ -1,23 +1,26 @@
package moe.nea.ledger.modules
+import moe.nea.ledger.ExpiringValue
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.SHORT_NUMBER_PATTERN
import moe.nea.ledger.TransactionType
import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.events.ExtraSupplyIdEvent
import moe.nea.ledger.events.GuiClickEvent
import moe.nea.ledger.getDisplayNameU
+import moe.nea.ledger.getInternalId
import moe.nea.ledger.getLore
-import moe.nea.ledger.parseShortNumber
import moe.nea.ledger.unformattedString
import moe.nea.ledger.useMatcher
import moe.nea.ledger.utils.Inject
+import net.minecraft.init.Blocks
+import net.minecraft.item.Item
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
-import net.minecraftforge.fml.common.gameevent.TickEvent
import java.time.Instant
-import java.util.regex.Pattern
+import kotlin.time.Duration.Companion.seconds
class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) {
@@ -49,16 +52,8 @@ class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) {
Damage: 0s
}
*/
- val costPattern = Pattern.compile("(?<cost>$SHORT_NUMBER_PATTERN) Coins")
-
-
- data class ChestCost(
- val cost: Double,
- val openTimestamp: Long,
- val hasKey: Boolean,
- )
-
- var lastOpenedChest: ChestCost? = null
+ @Inject
+ lateinit var itemIdProvider: ItemIdProvider
@SubscribeEvent
fun onKismetClick(event: GuiClickEvent) {
@@ -78,6 +73,19 @@ class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) {
}
}
+ data class ChestCost(
+ val diff: List<ItemChange>,
+ val timestamp: Instant,
+ )
+
+ var lastOpenedChest = ExpiringValue.empty<ChestCost>()
+
+ @SubscribeEvent
+ fun supplyExtraIds(event: ExtraSupplyIdEvent) {
+ event.store("Dungeon Chest Key", ItemId("DUNGEON_CHEST_KEY"))
+ event.store("Kismet Feather", ItemId("KISMET_FEATHER"))
+ }
+
@SubscribeEvent
fun onRewardChestClick(event: GuiClickEvent) {
val slot = event.slotIn ?: return
@@ -86,41 +94,36 @@ class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) {
val name = stack.getDisplayNameU()
if (name != "§aOpen Reward Chest") return
val lore = stack.getLore()
- val costIndex = lore.indexOf("§7Cost")
- if (costIndex < 0 || costIndex + 1 !in lore.indices) return
- val cost = costPattern.useMatcher(lore[costIndex + 1].unformattedString()) {
- parseShortNumber(group("cost"))
- } ?: 0.0 // Free chest!
- val hasKey = lore.contains("§9Dungeon Chest Key")
- lastOpenedChest?.let(::completeTransaction)
- lastOpenedChest = ChestCost(cost, System.currentTimeMillis(), hasKey)
+ val cost = itemIdProvider.findCostItemsFromSpan(lore)
+ val gain = (9..18)
+ .mapNotNull { slot.inventory.getStackInSlot(it) }
+ .filter { it.item != Item.getItemFromBlock(Blocks.stained_glass_pane) }
+ .map {
+ it.getInternalId()?.singleItem()
+ ?: itemIdProvider.findStackableItemByName(it.displayName)
+ ?: Pair(ItemId.NIL, it.stackSize.toDouble())
+ }
+ lastOpenedChest = ExpiringValue(ChestCost(
+ cost.map { ItemChange.lose(it.first, it.second) }
+ + gain.map { ItemChange.gain(it.first, it.second) },
+ Instant.now()
+ ))
}
+ val rewardMessage = " .* CHEST REWARDS".toPattern()
+
@SubscribeEvent
fun onChatMessage(event: ChatReceived) {
- if (event.message == "You don't have that many coins in the bank!")
- lastOpenedChest = null
- }
-
- fun completeTransaction(toOpen: ChestCost) {
- lastOpenedChest = null
- logger.logEntry(
- LedgerEntry(
+ if (event.message == "You don't have that many coins in the bank!") {
+ lastOpenedChest.take()
+ }
+ rewardMessage.useMatcher(event.message) {
+ val chest = lastOpenedChest.consume(3.seconds) ?: return
+ logger.logEntry(LedgerEntry(
TransactionType.DUNGEON_CHEST_OPEN,
- Instant.ofEpochMilli(toOpen.openTimestamp),
- listOfNotNull(
- if (toOpen.hasKey) ItemChange.lose(ItemId.DUNGEON_CHEST_KEY, 1) else null,
- ItemChange.loseCoins(toOpen.cost)
- ),
- )
- )
- }
-
- @SubscribeEvent
- fun onTick(event: TickEvent) {
- val toOpen = lastOpenedChest
- if (toOpen != null && toOpen.openTimestamp + 1000L < System.currentTimeMillis()) {
- completeTransaction(toOpen)
+ chest.timestamp,
+ chest.diff,
+ ))
}
}
}
diff --git a/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt b/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt
index 8a2aa19..4e2e40a 100644
--- a/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt
+++ b/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt
@@ -10,7 +10,6 @@ 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
@@ -61,11 +60,7 @@ class KatDetection {
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()
+ val cost = itemIdProvider.findCostItemsFromSpan(lore)
lastPetUpgradeScheduled = PetUpgrade(beforePetId, cost)
}