blob: 2679949678c827537389322ac2539dd0c3dd282a (
plain)
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
|
package moe.nea.firmament.util.skyblock
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen
import net.minecraft.text.HoverEvent
import net.minecraft.text.Text
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ChestInventoryUpdateEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.repo.ItemNameLookup
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.iterableView
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.parseShortNumber
import moe.nea.firmament.util.skyBlockId
import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch
object SackUtil {
@Serializable
data class SackContents(
// TODO: store the certainty of knowledge for each item.
val contents: MutableMap<SkyblockId, Long> = mutableMapOf(),
// val sackTypes:
)
object Store : ProfileSpecificDataHolder<SackContents>(serializer(), "Sacks", ::SackContents)
val items get() = Store.data?.contents ?: mutableMapOf()
val storedRegex = "^Stored: (?<stored>$SHORT_NUMBER_FORMAT)/(?<max>$SHORT_NUMBER_FORMAT)$".toPattern()
@Subscribe
fun storeDataFromInventory(event: ChestInventoryUpdateEvent) {
val screen = event.inventory as? GenericContainerScreen ?: return
if (!screen.title.unformattedString.endsWith(" Sack")) return
val inv = screen.screenHandler?.inventory ?: return
if (inv.size() < 18) return
val backSlot = inv.getStack(inv.size() - 5)
if (backSlot.displayNameAccordingToNbt.unformattedString != "Go Back") return
if (backSlot.loreAccordingToNbt.map { it.unformattedString } != listOf("To Sack of Sacks")) return
for (itemStack in inv.iterableView) {
// TODO: handle runes and gemstones
val stored = itemStack.loreAccordingToNbt.firstNotNullOfOrNull {
storedRegex.useMatch(it.unformattedString) {
val stored = parseShortNumber(group("stored")).toLong()
val max = parseShortNumber(group("max")).toLong()
stored
}
} ?: continue
val itemId = itemStack.skyBlockId ?: continue
items[itemId] = stored
}
Store.markDirty()
}
@Subscribe
fun updateFromChat(event: ProcessChatEvent) {
if (!event.unformattedString.startsWith("[Sacks]")) return
val update = ChatUpdate()
event.text.siblings.forEach(update::updateFromHoverText)
}
data class SackUpdate(
val itemId: SkyblockId?,
val itemName: String,
val changeAmount: Long,
)
private class ChatUpdate {
val updates = mutableListOf<SackUpdate>()
var foundAdded = false
var foundRemoved = false
fun updateFromCleanText(cleanedText: String) {
cleanedText.split("\n").forEach { line ->
changePattern.useMatch(line) {
val amount = parseShortNumber(group("amount")).toLong()
val itemName = group("itemName")
val itemId = ItemNameLookup.guessItemByName(itemName, false)
updates.add(SackUpdate(itemId, itemName, amount))
}
}
}
fun updateFromHoverText(text: Text) {
text.siblings.forEach(::updateFromHoverText)
val hoverText = text.style.hoverEvent?.getValue(HoverEvent.Action.SHOW_TEXT) ?: return
val cleanedText = hoverText.unformattedString
if (cleanedText.startsWith("Added items:\n")) {
if (!foundAdded) {
updateFromCleanText(cleanedText)
foundAdded = true
}
}
if (cleanedText.startsWith("Removed items:\n")) {
if (!foundRemoved) {
updateFromCleanText(cleanedText)
foundRemoved = true
}
}
}
}
val changePattern = " (?<amount>[+\\-]$SHORT_NUMBER_FORMAT) (?<itemName>[^(]+) \\(.*\\)".toPattern()
}
|