aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/util/skyblock/SackUtil.kt
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()
}