aboutsummaryrefslogtreecommitdiff
path: root/mod/src/main/kotlin/moe/nea/ledger/modules
diff options
context:
space:
mode:
Diffstat (limited to 'mod/src/main/kotlin/moe/nea/ledger/modules')
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt34
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt37
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt143
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt49
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt58
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt95
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt62
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt66
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt48
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt47
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt72
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt95
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/ExternalDataProvider.kt43
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/EyedropsDetection.kt35
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/ForgeDetection.kt48
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/GambleDetection.kt62
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionDetection.kt35
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/GodPotionMixinDetection.kt38
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/KatDetection.kt95
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/KuudraChestDetection.kt45
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/MineshaftCorpseDetection.kt81
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/MinionDetection.kt61
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/NpcDetection.kt111
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/UpdateChecker.kt167
-rw-r--r--mod/src/main/kotlin/moe/nea/ledger/modules/VisitorDetection.kt87
25 files changed, 1714 insertions, 0 deletions
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt
new file mode 100644
index 0000000..1c228ff
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AccessorySwapperDetection.kt
@@ -0,0 +1,34 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.LedgerEntry
+import moe.nea.ledger.LedgerLogger
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.gen.ItemIds
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class AccessorySwapperDetection {
+
+ val swapperUsed = "Swapped .* enrichments to .*!".toPattern()
+
+ @Inject
+ lateinit var logger: LedgerLogger
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ swapperUsed.useMatcher(event.message) {
+ logger.logEntry(
+ LedgerEntry(
+ TransactionType.ACCESSORIES_SWAPPING,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(ItemIds.TALISMAN_ENRICHMENT_SWAPPER, 1)
+ )
+ )
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt
new file mode 100644
index 0000000..cd48d45
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AllowanceDetection.kt
@@ -0,0 +1,37 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+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.parseShortNumber
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class AllowanceDetection {
+
+ val allowancePattern =
+ Pattern.compile("ALLOWANCE! You earned (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+
+ @Inject
+ lateinit var logger: LedgerLogger
+
+ @SubscribeEvent
+ fun onAllowanceGain(event: ChatReceived) {
+ allowancePattern.useMatcher(event.message) {
+ logger.logEntry(
+ LedgerEntry(
+ TransactionType.ALLOWANCE_GAIN,
+ event.timestamp,
+ listOf(
+ ItemChange.gainCoins(parseShortNumber(group("coins"))),
+ )
+ )
+ )
+ }
+ }
+}
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt
new file mode 100644
index 0000000..d02095d
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/AuctionHouseDetection.kt
@@ -0,0 +1,143 @@
+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.BeforeGuiAction
+import moe.nea.ledger.events.ChatReceived
+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.di.Inject
+import net.minecraft.client.gui.inventory.GuiChest
+import net.minecraft.inventory.ContainerChest
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+import kotlin.time.Duration.Companion.seconds
+
+class AuctionHouseDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+ data class LastViewedItem(
+ val count: Int,
+ val id: ItemId,
+ )
+ /*
+ You collected 8,712,000 coins from selling Ultimate Carrot Candy Upgrade to [VIP] kodokush in an auction!
+ You collected 60,000 coins from selling Walnut to [MVP++] Alea1337 in an auction!
+ You purchased 2x Walnut for 69 coins!
+ You purchased ◆ Ice Rune I for 4,000 coins!
+ */
+
+ val createAuctionScreen = "Confirm( BIN)? Auction".toPattern()
+ val auctionCreationCostPattern = "Cost: (?<cost>$SHORT_NUMBER_PATTERN) coins?".toPattern()
+
+ val auctionCreatedChatPattern = "(BIN )?Auction started for .*".toPattern()
+
+ var lastCreationCost: ExpiringValue<Double> = ExpiringValue.empty()
+
+ @SubscribeEvent
+ fun onCreateAuctionClick(event: BeforeGuiAction) {
+ val slots = event.chestSlots ?: return
+ if (!createAuctionScreen.asPredicate().test(slots.lowerChestInventory.name)) return
+ val auctionSlot = slots.lowerChestInventory.getStackInSlot(9 + 2) ?: return
+ val creationCost = auctionSlot.getLore().firstNotNullOfOrNull {
+ auctionCreationCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")) }
+ }
+ if (creationCost != null) {
+ lastCreationCost = ExpiringValue(creationCost)
+ }
+ }
+
+ @SubscribeEvent
+ fun onCreateAuctionChat(event: ChatReceived) {
+ auctionCreatedChatPattern.useMatcher(event.message) {
+ lastCreationCost.consume(3.seconds)?.let { cost ->
+ ledger.logEntry(LedgerEntry(
+ TransactionType.AUCTION_LISTING_CHARGE,
+ event.timestamp,
+ listOf(ItemChange.loseCoins(cost))
+ ))
+ }
+ }
+ }
+
+ val collectSold =
+ Pattern.compile("You collected (?<coins>$SHORT_NUMBER_PATTERN) coins? from selling (?<what>.*) to (?<buyer>.*) in an auction!")
+ val purchased =
+ Pattern.compile("You purchased (?:(?<amount>[0-9]+)x )?(?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+ var lastViewedItems: MutableList<LastViewedItem> = mutableListOf()
+
+ @SubscribeEvent
+ fun onEvent(event: ChatReceived) {
+ collectSold.useMatcher(event.message) {
+ val lastViewedItem = lastViewedItems.removeLastOrNull()
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.AUCTION_SOLD,
+ event.timestamp,
+ listOfNotNull(
+ ItemChange.gainCoins(parseShortNumber(group("coins"))),
+ lastViewedItem?.let { ItemChange.lose(it.id, it.count) }
+ ),
+ )
+ )
+ }
+ purchased.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.AUCTION_BOUGHT,
+ event.timestamp,
+ listOf(
+ ItemChange.loseCoins(parseShortNumber(group("coins"))),
+ ItemChange.gain(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ group("amount")?.toInt() ?: 1
+ )
+ )
+ )
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onBeforeAuctionCollected(event: BeforeGuiAction) {
+ val chest = (event.gui as? GuiChest) ?: return
+ val slots = chest.inventorySlots as ContainerChest
+ val name = slots.lowerChestInventory.displayName.unformattedText.unformattedString()
+
+ if (name == "BIN Auction View" || name == "Auction View") {
+ handleCollectSingleAuctionView(slots)
+ }
+ if (name == "Manage Auctions") {
+ handleCollectMultipleAuctionsView(slots)
+ }
+ }
+
+ private fun handleCollectMultipleAuctionsView(slots: ContainerChest) {
+ lastViewedItems =
+ (0 until slots.lowerChestInventory.sizeInventory)
+ .mapNotNull { slots.lowerChestInventory.getStackInSlot(it) }
+ .filter {
+ it.getLore().contains("§7Status: §aSold!") // BINs
+ || it.getLore().contains("§7Status: §aEnded!") // Auctions
+ }
+ .mapNotNull { LastViewedItem(it.stackSize, it.getInternalId() ?: return@mapNotNull null) }
+ .toMutableList()
+ }
+
+
+ fun handleCollectSingleAuctionView(slots: ContainerChest) {
+ val soldItem = slots.lowerChestInventory.getStackInSlot(9 + 4) ?: return
+ val id = soldItem.getInternalId() ?: return
+ val count = soldItem.stackSize
+ lastViewedItems = mutableListOf(LastViewedItem(count, id))
+ }
+
+
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt
new file mode 100644
index 0000000..928d30c
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BankDetection.kt
@@ -0,0 +1,49 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+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.parseShortNumber
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+
+class BankDetection @Inject constructor(val ledger: LedgerLogger) {
+ val withdrawPattern =
+ Pattern.compile("^(You have withdrawn|Withdrew) (?<amount>$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?<newtotal>$SHORT_NUMBER_PATTERN) coins? (?:left in the account!|in your account!)$")
+ val depositPattern =
+ Pattern.compile("^(?:You have deposited|Deposited) (?<amount>$SHORT_NUMBER_PATTERN) coins?! (?:There's now|You now have) (?<newtotal>$SHORT_NUMBER_PATTERN) coins? (?:in your account!|in the account!)$")
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ withdrawPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BANK_WITHDRAW,
+ event.timestamp,
+ listOf(ItemChange(ItemId.COINS,
+ parseShortNumber(group("amount")),
+ ItemChange.ChangeDirection.TRANSFORM)),
+ )
+ )
+ }
+ depositPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BANK_DEPOSIT,
+ event.timestamp,
+ listOf(ItemChange(ItemId.COINS,
+ parseShortNumber(group("amount")),
+ ItemChange.ChangeDirection.TRANSFORM)),
+ )
+ )
+ }
+ }
+
+}
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt
new file mode 100644
index 0000000..0f1fc2c
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarDetection.kt
@@ -0,0 +1,58 @@
+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.SHORT_NUMBER_PATTERN
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.parseShortNumber
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class BazaarDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+
+ val instaBuyPattern =
+ Pattern.compile("\\[Bazaar\\] Bought (?<count>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+ val instaSellPattern =
+ Pattern.compile("\\[Bazaar\\] Sold (?<count>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins!")
+
+
+ @SubscribeEvent
+ fun onInstSellChat(event: ChatReceived) {
+ instaBuyPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BAZAAR_BUY_INSTANT,
+ event.timestamp,
+ listOf(
+ ItemChange.loseCoins(parseShortNumber(group("coins"))),
+ ItemChange.gain(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ parseShortNumber(group("count"))
+ )
+ )
+ )
+ )
+ }
+ instaSellPattern.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BAZAAR_SELL_INSTANT,
+ event.timestamp,
+ listOf(
+ ItemChange.gainCoins(parseShortNumber(group("coins"))),
+ ItemChange.lose(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ parseShortNumber(group("count"))
+ )
+ ),
+ )
+ )
+ }
+ }
+}
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt
new file mode 100644
index 0000000..330ee1d
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BazaarOrderDetection.kt
@@ -0,0 +1,95 @@
+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.SHORT_NUMBER_PATTERN
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.mixin.AccessorGuiEditSign
+import moe.nea.ledger.parseShortNumber
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraft.client.gui.inventory.GuiEditSign
+import net.minecraftforge.client.event.GuiScreenEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class BazaarOrderDetection @Inject constructor(val ledger: LedgerLogger, val ids: ItemIdProvider) {
+
+ val buyOrderClaimed =
+ Pattern.compile("\\[Bazaar] Claimed (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) worth (?<coins>$SHORT_NUMBER_PATTERN) coins? bought for $SHORT_NUMBER_PATTERN each!")
+ val sellOrderClaimed =
+ Pattern.compile("\\[Bazaar] Claimed (?<coins>$SHORT_NUMBER_PATTERN) coins? from selling (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) at $SHORT_NUMBER_PATTERN each!")
+ val orderFlipped =
+ Pattern.compile("\\[Bazaar] Order Flipped! (?<amount>$SHORT_NUMBER_PATTERN)x (?<what>.*) for (?<coins>$SHORT_NUMBER_PATTERN) coins? of total expected profit.")
+ val previousPricePattern =
+ Pattern.compile("(?<price>$SHORT_NUMBER_PATTERN)/u")
+ var lastFlippedPreviousPrice = 0.0
+
+ @SubscribeEvent
+ fun detectSignFlip(event: GuiScreenEvent.InitGuiEvent) {
+ val gui = event.gui
+ if (gui !is GuiEditSign) return
+ gui as AccessorGuiEditSign
+ val text = gui.tileEntity_ledger.signText
+ if (text[2].unformattedText != "Previous price:") return
+ previousPricePattern.useMatcher(text[3].unformattedText) {
+ lastFlippedPreviousPrice = parseShortNumber(group("price"))
+ }
+ }
+
+ @SubscribeEvent
+ fun detectBuyOrders(event: ChatReceived) {
+ orderFlipped.useMatcher(event.message) {
+ val amount = parseShortNumber(group("amount")).toInt()
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BAZAAR_BUY_ORDER,
+ event.timestamp,
+ listOf(
+ ItemChange.loseCoins(lastFlippedPreviousPrice * amount),
+ ItemChange.gain(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ amount,
+ )
+ )
+ )
+ )
+ }
+ buyOrderClaimed.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BAZAAR_BUY_ORDER,
+ event.timestamp,
+ listOf(
+ ItemChange.loseCoins(parseShortNumber(group("coins"))),
+ ItemChange.gain(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ parseShortNumber(group("amount"))
+ )
+ ),
+ )
+ )
+ }
+ sellOrderClaimed.useMatcher(event.message) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BAZAAR_SELL_ORDER,
+ event.timestamp,
+ listOf(
+ ItemChange.gainCoins(
+ parseShortNumber(group("coins"))
+ ),
+ ItemChange.lose(
+ ids.findForName(group("what")) ?: ItemId.NIL,
+ parseShortNumber(group("amount")),
+ )
+ ),
+ )
+ )
+ }
+ }
+}
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt
new file mode 100644
index 0000000..44a0050
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsDetection.kt
@@ -0,0 +1,62 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.events.LateWorldLoadEvent
+import moe.nea.ledger.LedgerEntry
+import moe.nea.ledger.LedgerLogger
+import moe.nea.ledger.SHORT_NUMBER_PATTERN
+import moe.nea.ledger.ScoreboardUtil
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.gen.ItemIds
+import moe.nea.ledger.parseShortNumber
+import moe.nea.ledger.unformattedString
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.time.Instant
+
+class BitsDetection @Inject constructor(val ledger: LedgerLogger) {
+
+ var lastBits = -1
+
+ val bitScoreboardRegex = "Bits: (?<purse>$SHORT_NUMBER_PATTERN)".toPattern()
+
+ @SubscribeEvent
+ fun onWorldSwitch(event: LateWorldLoadEvent) {
+ ScoreboardUtil.getScoreboardStrings().forEach {
+ bitScoreboardRegex.useMatcher<Unit>(it.unformattedString()) {
+ val bits = parseShortNumber(group("purse")).toInt()
+ if (lastBits != bits) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BITS_PURSE_STATUS,
+ Instant.now(),
+ listOf(
+ ItemChange(ItemIds.SKYBLOCK_BIT, bits.toDouble(), ItemChange.ChangeDirection.SYNC)
+ )
+ )
+ )
+ lastBits = bits
+ }
+ return
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onEvent(event: ChatReceived) {
+ if (event.message.startsWith("You consumed a Booster Cookie!")) {
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.BOOSTER_COOKIE_ATE,
+ Instant.now(),
+ listOf(
+ ItemChange.lose(ItemIds.BOOSTER_COOKIE, 1)
+ )
+ )
+ )
+ }
+ }
+}
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt
new file mode 100644
index 0000000..553bebf
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/BitsShopDetection.kt
@@ -0,0 +1,66 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.events.GuiClickEvent
+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.gen.ItemIds
+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.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.time.Instant
+
+class BitsShopDetection @Inject constructor(val ledger: LedgerLogger) {
+
+
+ data class BitShopEntry(
+ val id: ItemId,
+ val stackSize: Int,
+ val bitPrice: Int,
+ val timestamp: Long = System.currentTimeMillis()
+ )
+
+ var lastClickedBitShopItem: BitShopEntry? = null
+ var bitCostPattern = "(?<cost>$SHORT_NUMBER_PATTERN) Bits".toPattern()
+
+ @SubscribeEvent
+ fun recordLastBitPrice(event: GuiClickEvent) {
+ val slot = event.slotIn ?: return
+ val name = slot.inventory.displayName.unformattedText.unformattedString()
+ if (name != "Community Shop" && !name.startsWith("Bits Shop"))
+ return
+ val stack = slot.stack ?: return
+ val id = stack.getInternalId() ?: return
+ val bitPrice = stack.getLore()
+ .firstNotNullOfOrNull { bitCostPattern.useMatcher(it.unformattedString()) { parseShortNumber(group("cost")).toInt() } }
+ ?: return
+ lastClickedBitShopItem = BitShopEntry(id, stack.stackSize, bitPrice)
+ }
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ if (event.message.startsWith("You bought ")) {
+ val lastBit = lastClickedBitShopItem ?: return
+ if (System.currentTimeMillis() - lastBit.timestamp > 5000) return
+ ledger.logEntry(
+ LedgerEntry(
+ TransactionType.COMMUNITY_SHOP_BUY,
+ Instant.now(),
+ listOf(
+ ItemChange.lose(ItemIds.SKYBLOCK_BIT, lastBit.bitPrice.toDouble()),
+ ItemChange.gain(lastBit.id, lastBit.stackSize)
+ )
+ )
+ )
+ }
+ }
+
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt
new file mode 100644
index 0000000..cca02e1
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/ChestDetection.kt
@@ -0,0 +1,48 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+import moe.nea.ledger.ItemIdProvider
+import moe.nea.ledger.getDisplayNameU
+import moe.nea.ledger.getInternalId
+import moe.nea.ledger.getLore
+import moe.nea.ledger.unformattedString
+import moe.nea.ledger.utils.di.Inject
+import net.minecraft.init.Blocks
+import net.minecraft.inventory.Slot
+import net.minecraft.item.Item
+import java.time.Instant
+
+abstract class ChestDetection {
+ data class ChestCost(
+ val diff: List<ItemChange>,
+ val timestamp: Instant,
+ )
+
+ @Inject
+ lateinit var itemIdProvider: ItemIdProvider
+ fun scrapeChestReward(rewardSlot: Slot): ChestCost? {
+ val inventory = rewardSlot.inventory
+ if (!inventory.displayName.unformattedText.unformattedString()
+ .endsWith(" Chest")
+ ) return null
+ val rewardStack = rewardSlot.stack ?: return null
+ val name = rewardStack.getDisplayNameU()
+ if (name != "§aOpen Reward Chest") return null
+ val lore = rewardStack.getLore()
+ val cost = itemIdProvider.findCostItemsFromSpan(lore)
+ val gain = (9..18)
+ .mapNotNull { inventory.getStackInSlot(it) }
+ .filter { it.item != Item.getItemFromBlock(Blocks.stained_glass_pane) }
+ .map {
+ it.getInternalId()?.withStackSize(it.stackSize)
+ ?: itemIdProvider.findStackableItemByName(it.displayName)
+ ?: ItemId.NIL.withStackSize(it.stackSize)
+ }
+ return ChestCost(
+ cost.map { ItemChange.lose(it.first, it.second) } + gain.map { ItemChange.gain(it.first, it.second) },
+ Instant.now()
+ )
+ }
+
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt
new file mode 100644
index 0000000..e389ffb
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonEyePlacementDetection.kt
@@ -0,0 +1,47 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.ItemId
+import moe.nea.ledger.LedgerEntry
+import moe.nea.ledger.LedgerLogger
+import moe.nea.ledger.TransactionType
+import moe.nea.ledger.events.ChatReceived
+import moe.nea.ledger.events.WorldSwitchEvent
+import moe.nea.ledger.gen.ItemIds
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DragonEyePlacementDetection {
+ val eyePlaced = "☬ You placed a Summoning Eye!( Brace yourselves!)? \\(./.\\)".toPattern()
+//☬ You placed a Summoning Eye! Brace yourselves! (8/8)
+ var eyeCount = 0
+
+ @SubscribeEvent
+ fun onWorldSwap(event: WorldSwitchEvent) {
+ eyeCount = 0
+ }
+
+ @SubscribeEvent
+ fun onRetrieveEye(event: ChatReceived) {
+ if (event.message == "You recovered a Summoning Eye!") {
+ eyeCount--
+ }
+ eyePlaced.useMatcher(event.message) {
+ eyeCount++
+ }
+ if (event.message == "Your Sleeping Eyes have been awoken by the magic of the Dragon. They are now Remnants of the Eye!") {
+ logger.logEntry(LedgerEntry(
+ TransactionType.WYRM_EVOKED,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(ItemIds.SUMMONING_EYE, eyeCount)
+ )
+ ))
+ eyeCount = 0
+ }
+ }
+
+ @Inject
+ lateinit var logger: LedgerLogger
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt
new file mode 100644
index 0000000..574cfcf
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DragonSacrificeDetection.kt
@@ -0,0 +1,72 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.DebouncedValue
+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.gen.ItemIds
+import moe.nea.ledger.parseShortNumber
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import kotlin.time.Duration.Companion.seconds
+
+class DragonSacrificeDetection {
+ //SACRIFICE! You turned Holy Dragon Boots into 30 Dragon Essence!
+ //BONUS LOOT! You also received 17x Holy Dragon Fragment from your sacrifice!
+ @Inject
+ lateinit var itemIdProvider: ItemIdProvider
+
+ @Inject
+ lateinit var logger: LedgerLogger
+
+ val sacrificePattern =
+ "SACRIFICE! You turned (?<sacrifice>.*) into (?<amount>$SHORT_NUMBER_PATTERN) Dragon Essence!".toPattern()
+ val bonusLootPattern = "BONUS LOOT! You also received (?<bonus>.*) from your sacrifice!".toPattern()
+
+ var lastSacrifice: DebouncedValue<LedgerEntry> = DebouncedValue.farFuture()
+
+
+ @SubscribeEvent
+ fun onChat(event: ChatReceived) {
+ sacrificePattern.useMatcher(event.message) {
+ val sacrifice = itemIdProvider.findForName(group("sacrifice")) ?: return
+ val lootEssence = parseShortNumber(group("amount"))
+ consume(lastSacrifice.replace())
+ lastSacrifice = DebouncedValue(LedgerEntry(
+ TransactionType.DRACONIC_SACRIFICE,
+ event.timestamp,
+ listOf(
+ ItemChange.lose(sacrifice, 1),
+ ItemChange.gain(ItemIds.ESSENCE_DRAGON, lootEssence)
+ )
+ ))
+ }
+ bonusLootPattern.useMatcher(event.message) {
+ val bonusItem = itemIdProvider.findStackableItemByName(
+ group("bonus"), true
+ ) ?: return
+ lastSacrifice.replace()?.let {
+ consume(
+ it.copy(items = it.items + ItemChange.unpairGain(bonusItem))
+ )
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent) {
+ consume(lastSacrifice.consume(4.seconds))
+ }
+
+ fun consume(entry: LedgerEntry?) {
+ if (entry != null)
+ logger.logEntry(entry)
+ }
+} \ No newline at end of file
diff --git a/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt b/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt
new file mode 100644
index 0000000..e747be9
--- /dev/null
+++ b/mod/src/main/kotlin/moe/nea/ledger/modules/DungeonChestDetection.kt
@@ -0,0 +1,95 @@
+package moe.nea.ledger.modules
+
+import moe.nea.ledger.ExpiringValue
+import moe.nea.ledger.ItemChange
+import moe.nea.ledger.LedgerEntry
+import moe.nea.ledger.LedgerLogger
+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.gen.ItemIds
+import moe.nea.ledger.getDisplayNameU
+import moe.nea.ledger.unformattedString
+import moe.nea.ledger.useMatcher
+import moe.nea.ledger.utils.di.Inject
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.time.Instant
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.time.Duration.Companion.seconds
+
+class DungeonChestDet