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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
package moe.nea.ledger
import moe.nea.ledger.events.BeforeGuiAction
import moe.nea.ledger.events.ExtraSupplyIdEvent
import moe.nea.ledger.events.RegistrationFinishedEvent
import moe.nea.ledger.events.SupplyDebugInfo
import moe.nea.ledger.modules.ExternalDataProvider
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
class ItemIdProvider {
@SubscribeEvent
fun onMouseInput(event: GuiScreenEvent.MouseInputEvent.Pre) {
if (Mouse.getEventButton() == -1) return
MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui))
}
@SubscribeEvent
fun onKeyInput(event: GuiScreenEvent.KeyboardInputEvent.Pre) {
MinecraftForge.EVENT_BUS.post(BeforeGuiAction(event.gui))
}
private val knownNames = mutableMapOf<String, ItemId>()
fun createLookupTagFromDisplayName(itemName: String): String {
return itemName.unformattedString().trim().lowercase()
}
fun saveKnownItem(itemName: String, itemId: ItemId) {
knownNames[createLookupTagFromDisplayName(itemName)] = itemId
}
@SubscribeEvent
fun onDataLoaded(event: ExternalDataProvider.DataLoaded) {
event.provider.itemNames.forEach { (itemId, itemName) ->
saveKnownItem(itemName, ItemId(itemId))
}
}
@SubscribeEvent
fun onRegistrationFinished(event: RegistrationFinishedEvent) {
MinecraftForge.EVENT_BUS.post(ExtraSupplyIdEvent(::saveKnownItem))
}
@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) }
}
@SubscribeEvent
fun onDebugData(event: SupplyDebugInfo) {
event.record("knownItemNames", knownNames.size)
}
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()) {
saveKnownItem(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 {
saveFromSlot(it?.stack, preprocessor)
}
}
// TODO: make use of colour
fun findForName(name: String, fallbackToGenerated: Boolean = true): ItemId? {
var id = knownNames[createLookupTagFromDisplayName(name)]
if (id == null && fallbackToGenerated) {
id = generateName(name)
}
return id
}
fun generateName(name: String): ItemId {
return ItemId(name.uppercase().replace(" ", "_"))
}
private val coinRegex = "(?<amount>$SHORT_NUMBER_PATTERN) Coins?".toPattern()
private val stackedItemRegex = "(?<name>.*) x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
private val essenceRegex = "(?<essence>.*) Essence x(?<count>$SHORT_NUMBER_PATTERN)".toPattern()
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()
}
private val etherialRewardPattern = "\\+(?<amount>${SHORT_NUMBER_PATTERN})x? (?<what>.*)".toPattern()
fun findStackableItemByName(name: String, fallbackToGenerated: Boolean = false): Pair<ItemId, Double>? {
val properName = name.unformattedString().trim()
if (properName == "FREE" || properName == "This Chest is Free!") {
return Pair(ItemId.COINS, 0.0)
}
coinRegex.useMatcher(properName) {
return Pair(ItemId.COINS, parseShortNumber(group("amount")))
}
etherialRewardPattern.useMatcher(properName) {
val id = when (val id = group("what")) {
"Copper" -> ItemId.COPPER
"Bits" -> ItemId.BITS
"Garden Experience" -> ItemId.GARDEN
"Farming XP" -> ItemId.FARMING
"Gold Essence" -> ItemId.GOLD_ESSENCE
"Gemstone Powder" -> ItemId.GEMSTONE_POWDER
"Mithril Powder" -> ItemId.MITHRIL_POWDER
"Pelts" -> ItemId.PELT
"Fine Flour" -> ItemId.FINE_FLOUR
else -> {
id.ifDropLast(" Experience") {
ItemId.skill(generateName(it).string)
} ?: id.ifDropLast(" XP") {
ItemId.skill(generateName(it).string)
} ?: id.ifDropLast(" Powder") {
ItemId("SKYBLOCK_POWDER_${generateName(it).string}")
} ?: id.ifDropLast(" Essence") {
ItemId("ESSENCE_${generateName(it).string}")
} ?: generateName(id)
}
}
return Pair(id, parseShortNumber(group("amount")))
}
essenceRegex.useMatcher(properName) {
return Pair(ItemId("ESSENCE_${group("essence").uppercase()}"),
parseShortNumber(group("count")))
}
stackedItemRegex.useMatcher(properName) {
var item = findForName(group("name"), fallbackToGenerated)
if (item != null) {
val count = parseShortNumber(group("count"))
return Pair(item, count)
}
}
return findForName(properName, fallbackToGenerated)?.let { Pair(it, 1.0) }
}
fun getKnownItemIds(): Collection<ItemId> {
return knownNames.values
}
}
|