blob: 0d7d2b7008f16cfda03a5137f5bafa7e9ab50e04 (
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
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
|
package moe.nea.firmament.util.skyblock
import kotlin.time.Duration
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import moe.nea.firmament.util.ErrorUtil
import moe.nea.firmament.util.directLiteralStringContent
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.parseShortNumber
import moe.nea.firmament.util.parseTimePattern
import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch
object AbilityUtils {
data class ItemAbility(
val name: String,
val hasPowerScroll: Boolean,
val activation: AbilityActivation,
val manaCost: Int?,
val descriptionLines: List<Text>,
val cooldown: Duration?,
)
@JvmInline
value class AbilityActivation(
val label: String
) {
companion object {
val RIGHT_CLICK = AbilityActivation("RIGHT CLICK")
val SNEAK_RIGHT_CLICK = AbilityActivation("SNEAK RIGHT CLICK")
val SNEAK = AbilityActivation("SNEAK")
val EMPTY = AbilityActivation("")
fun of(text: String?): AbilityActivation {
val trimmed = text?.trim()
if (trimmed.isNullOrBlank())
return EMPTY
return AbilityActivation(trimmed)
}
}
}
private val abilityNameRegex = "Ability: (?<name>.*?) *".toPattern()
private fun findAbility(iterator: ListIterator<Text>): ItemAbility? {
if (!iterator.hasNext()) {
return null
}
val line = iterator.next()
// The actual information about abilities is stored in the siblings
if (line.directLiteralStringContent != "") return null
var powerScroll: Boolean = false // This should instead determine the power scroll based on text colour
var abilityName: String? = null
var activation: String? = null
var hasProcessedActivation = false
for (sibling in line.siblings) {
val directContent = sibling.directLiteralStringContent ?: continue
if (directContent == "⦾ ") {
powerScroll = true
continue
}
if (!hasProcessedActivation && abilityName != null) {
hasProcessedActivation = true
activation = directContent
continue
}
abilityNameRegex.useMatch<Nothing>(directContent) {
abilityName = group("name")
continue
}
if (abilityName != null) {
ErrorUtil.softError("Found abilityName $abilityName without finding but encountered unprocessable element in: $line")
}
return null
}
if (abilityName == null) return null
val descriptionLines = mutableListOf<Text>()
var manaCost: Int? = null
var cooldown: Duration? = null
while (iterator.hasNext()) {
val descriptionLine = iterator.next()
if (descriptionLine.unformattedString == "") break
var nextIsManaCost = false
var isSpecialLine = false
var nextIsDuration = false
for (sibling in descriptionLine.siblings) {
val directContent = sibling.directLiteralStringContent ?: continue
if ("Mana Cost: " == directContent) { // TODO: 'Soulflow Cost: ' support (or maybe a generic 'XXX Cost: ')
nextIsManaCost = true
isSpecialLine = true
continue
}
if ("Cooldown: " == directContent) {
nextIsDuration = true
isSpecialLine = true
continue
}
if (nextIsDuration) {
nextIsDuration = false
cooldown = parseTimePattern(directContent)
continue
}
if (nextIsManaCost) {
nextIsManaCost = false
manaCost = parseShortNumber(directContent).toInt()
continue
}
if (isSpecialLine) {
ErrorUtil.softError("Unknown special line segment: '$sibling' in '$descriptionLine'")
}
}
if (!isSpecialLine) {
descriptionLines.add(descriptionLine)
}
}
return ItemAbility(
abilityName,
powerScroll,
AbilityActivation.of(activation),
manaCost,
descriptionLines,
cooldown
)
}
fun getAbilities(lore: List<Text>): List<ItemAbility> {
val iterator = lore.listIterator()
val abilities = mutableListOf<ItemAbility>()
while (iterator.hasNext()) {
findAbility(iterator)?.let(abilities::add)
}
return abilities
}
// TODO: memoize
fun getAbilities(itemStack: ItemStack): List<ItemAbility> {
return getAbilities(itemStack.loreAccordingToNbt)
}
}
|