1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
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.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.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 java.time.Instant
import kotlin.time.Duration.Companion.seconds
class DungeonChestDetection @Inject constructor(val logger: LedgerLogger) {
/*{
id: "minecraft:chest",
Count: 1b,
tag: {
display: {
Lore: ["§7Purchase this chest to receive the", "§7rewards above. You can only open", "§7one chest per Dungeons run -", "§7choose wisely!", "", "§7Cost", "§625,000 Coins", "§9Dungeon Chest Key", "", "§7§cNOTE: Coins are withdrawn from your", "§cbank if you don't have enough in", "§cyour purse."],
Name: "§aOpen Reward Chest"
}
},
Damage: 0s
}
{
id: "minecraft:feather",
Count: 1b,
tag: {
overrideMeta: 1b,
ench: [],
HideFlags: 254,
display: {
Lore: ["§7Consume a §9Kismet Feather §7to reroll", "§7the loot within this chest.", "", "§7You may only use a feather once", "§7per dungeon run.", "", "§eClick to reroll this chest!"],
Name: "§aReroll Chest"
},
AttributeModifiers: []
},
Damage: 0s
}
*/
@Inject
lateinit var itemIdProvider: ItemIdProvider
@SubscribeEvent
fun onKismetClick(event: GuiClickEvent) {
val slot = event.slotIn ?: return
if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return
val stack = slot.stack ?: return
if (stack.getDisplayNameU() == "§aReroll Chest") {
logger.logEntry(
LedgerEntry(
TransactionType.KISMET_REROLL,
Instant.now(),
listOf(
ItemChange.lose(ItemId.KISMET_FEATHER, 1)
)
)
)
}
}
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
if (!slot.inventory.displayName.unformattedText.unformattedString().endsWith(" Chest")) return
val stack = slot.stack ?: return
val name = stack.getDisplayNameU()
if (name != "§aOpen Reward Chest") return
val lore = stack.getLore()
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.take()
}
rewardMessage.useMatcher(event.message) {
val chest = lastOpenedChest.consume(3.seconds) ?: return
logger.logEntry(LedgerEntry(
TransactionType.DUNGEON_CHEST_OPEN,
chest.timestamp,
chest.diff,
))
}
}
}
|