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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
package moe.nea.firmament.features.events.anniversity
import io.github.notenoughupdates.moulconfig.observer.ObservableList
import io.github.notenoughupdates.moulconfig.xml.Bind
import moe.nea.jarvis.api.Point
import kotlin.time.Duration.Companion.seconds
import net.minecraft.entity.passive.PigEntity
import net.minecraft.util.math.BlockPos
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.EntityInteractionEvent
import moe.nea.firmament.events.ProcessChatEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.repo.ItemNameLookup
import moe.nea.firmament.repo.SBItemStack
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.parseShortNumber
import moe.nea.firmament.util.useMatch
object AnniversaryFeatures : FirmamentFeature {
override val identifier: String
get() = "anniversary"
object TConfig : ManagedConfig(identifier, Category.EVENTS) {
val enableShinyPigTracker by toggle("shiny-pigs") {true}
val trackPigCooldown by position("pig-hud", 200, 300) { Point(0.1, 0.2) }
}
override val config: ManagedConfig?
get() = TConfig
data class ClickedPig(
val clickedAt: TimeMark,
val startLocation: BlockPos,
val pigEntity: PigEntity
) {
@Bind("timeLeft")
fun getTimeLeft(): Double = 1 - clickedAt.passedTime() / pigDuration
}
val clickedPigs = ObservableList<ClickedPig>(mutableListOf())
var lastClickedPig: PigEntity? = null
val pigDuration = 90.seconds
@Subscribe
fun onTick(event: TickEvent) {
clickedPigs.removeIf { it.clickedAt.passedTime() > pigDuration }
}
val pattern = "SHINY! You extracted (?<reward>.*) from the piglet's orb!".toPattern()
@Subscribe
fun onChat(event: ProcessChatEvent) {
if(!TConfig.enableShinyPigTracker)return
if (event.unformattedString == "Oink! Bring the pig back to the Shiny Orb!") {
val pig = lastClickedPig ?: return
// TODO: store proper location based on the orb location, maybe
val startLocation = pig.blockPos ?: return
clickedPigs.add(ClickedPig(TimeMark.now(), startLocation, pig))
lastClickedPig = null
}
if (event.unformattedString == "SHINY! The orb is charged! Click on it for loot!") {
val player = MC.player ?: return
val lowest =
clickedPigs.minByOrNull { it.startLocation.getSquaredDistance(player.pos) } ?: return
clickedPigs.remove(lowest)
}
pattern.useMatch(event.unformattedString) {
val reward = group("reward")
val parsedReward = parseReward(reward)
addReward(parsedReward)
PigCooldown.rewards.atOnce {
PigCooldown.rewards.clear()
rewards.mapTo(PigCooldown.rewards) { PigCooldown.DisplayReward(it) }
}
}
}
fun addReward(reward: Reward) {
val it = rewards.listIterator()
while (it.hasNext()) {
val merged = reward.mergeWith(it.next()) ?: continue
it.set(merged)
return
}
rewards.add(reward)
}
val rewards = mutableListOf<Reward>()
fun <T> ObservableList<T>.atOnce(block: () -> Unit) {
val oldObserver = observer
observer = null
block()
observer = oldObserver
update()
}
sealed interface Reward {
fun mergeWith(other: Reward): Reward?
data class EXP(val amount: Double, val skill: String) : Reward {
override fun mergeWith(other: Reward): Reward? {
if (other is EXP && other.skill == skill)
return EXP(amount + other.amount, skill)
return null
}
}
data class Coins(val amount: Double) : Reward {
override fun mergeWith(other: Reward): Reward? {
if (other is Coins)
return Coins(other.amount + amount)
return null
}
}
data class Items(val amount: Int, val item: SkyblockId) : Reward {
override fun mergeWith(other: Reward): Reward? {
if (other is Items && other.item == item)
return Items(amount + other.amount, item)
return null
}
}
data class Unknown(val text: String) : Reward {
override fun mergeWith(other: Reward): Reward? {
return null
}
}
}
val expReward = "\\+(?<exp>$SHORT_NUMBER_FORMAT) (?<kind>[^ ]+) XP".toPattern()
val coinReward = "\\+(?<amount>$SHORT_NUMBER_FORMAT) coins".toPattern()
val itemReward = "(?:(?<amount>[0-9]+)x )?(?<name>.*)".toPattern()
fun parseReward(string: String): Reward {
expReward.useMatch<Unit>(string) {
val exp = parseShortNumber(group("exp"))
val kind = group("kind")
return Reward.EXP(exp, kind)
}
coinReward.useMatch<Unit>(string) {
val coins = parseShortNumber(group("amount"))
return Reward.Coins(coins)
}
itemReward.useMatch(string) {
val amount = group("amount")?.toIntOrNull() ?: 1
val name = group("name")
val item = ItemNameLookup.guessItemByName(name, false) ?: return@useMatch
return Reward.Items(amount, item)
}
return Reward.Unknown(string)
}
@Subscribe
fun onWorldClear(event: WorldReadyEvent) {
lastClickedPig = null
clickedPigs.clear()
}
@Subscribe
fun onEntityClick(event: EntityInteractionEvent) {
if (event.entity is PigEntity) {
lastClickedPig = event.entity
}
}
@Subscribe
fun init(event: WorldReadyEvent) {
PigCooldown.forceInit()
}
object PigCooldown : MoulConfigHud("anniversary_pig", TConfig.trackPigCooldown) {
override fun shouldRender(): Boolean {
return clickedPigs.isNotEmpty() && TConfig.enableShinyPigTracker
}
@Bind("pigs")
fun getPigs() = clickedPigs
class DisplayReward(val backedBy: Reward) {
@Bind
fun count(): String {
return when (backedBy) {
is Reward.Coins -> backedBy.amount
is Reward.EXP -> backedBy.amount
is Reward.Items -> backedBy.amount
is Reward.Unknown -> 0
}.toString()
}
val itemStack = if (backedBy is Reward.Items) {
SBItemStack(backedBy.item, backedBy.amount)
} else {
SBItemStack(SkyblockId.NULL)
}
@Bind
fun name(): String {
return when (backedBy) {
is Reward.Coins -> "Coins"
is Reward.EXP -> backedBy.skill
is Reward.Items -> itemStack.asImmutableItemStack().name.string
is Reward.Unknown -> backedBy.text
}
}
@Bind
fun isKnown() = backedBy !is Reward.Unknown
}
@get:Bind("rewards")
val rewards = ObservableList<DisplayReward>(mutableListOf())
}
}
|