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
|
package moe.nea.firmament.features.inventory
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.format.FormatStyle
import java.time.format.TextStyle
import java.time.temporal.ChronoField
import net.minecraft.text.Text
import net.minecraft.util.StringIdentifiable
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.events.ItemTooltipEvent
import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.aqua
import moe.nea.firmament.util.grey
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.tr
import moe.nea.firmament.util.unformattedString
object TimerInLore {
object TConfig : ManagedConfig("lore-timers", Category.INVENTORY) {
val showTimers by toggle("show") { true }
val timerFormat by choice("format") { TimerFormat.SOCIALIST }
}
enum class TimerFormat(val formatter: DateTimeFormatter) : StringIdentifiable {
RFC(DateTimeFormatter.RFC_1123_DATE_TIME),
LOCAL(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)),
SOCIALIST(
{
appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
appendLiteral(" ")
appendValue(ChronoField.DAY_OF_MONTH, 2)
appendLiteral(".")
appendValue(ChronoField.MONTH_OF_YEAR, 2)
appendLiteral(".")
appendValue(ChronoField.YEAR, 4)
appendLiteral(" ")
appendValue(ChronoField.HOUR_OF_DAY, 2)
appendLiteral(":")
appendValue(ChronoField.MINUTE_OF_HOUR, 2)
appendLiteral(":")
appendValue(ChronoField.SECOND_OF_MINUTE, 2)
}),
AMERICAN("EEEE, MMM d h:mm a yyyy"),
;
constructor(block: DateTimeFormatterBuilder.() -> Unit)
: this(DateTimeFormatterBuilder().also(block).toFormatter())
constructor(format: String) : this(DateTimeFormatter.ofPattern(format))
override fun asString(): String {
return name
}
}
enum class CountdownTypes(
val match: String,
val label: String, // TODO: convert to a string
val isRelative: Boolean = false,
) {
STARTING("Starting in:", "Starts at"),
STARTS("Starts in:", "Starts at"),
INTEREST("Interest in:", "Interest at"),
UNTILINTEREST("Until interest:", "Interest at"),
ENDS("Ends in:", "Ends at"),
REMAINING("Remaining:", "Ends at"),
DURATION("Duration:", "Finishes at"),
TIMELEFT("Time left:", "Ends at"),
EVENTTIMELEFT("Event lasts for", "Ends at", isRelative = true),
SHENSUCKS("Auction ends in:", "Auction ends at"),
ENDS_PET_LEVELING(
"Ends:",
"Finishes at"
),
CALENDARDETAILS(" (§e", "Starts at"),
COMMUNITYPROJECTS("Contribute again", "Come back at"),
CHOCOLATEFACTORY("Next Charge", "Available at"),
STONKSAUCTION("Auction ends in", "Ends at"),
LIZSTONKREDEMPTION("Resets in:", "Resets at");
}
val regex =
"(?i)(?:(?<years>[0-9]+) ?(y|years?) )?(?:(?<days>[0-9]+) ?(d|days?))? ?(?:(?<hours>[0-9]+) ?(h|hours?))? ?(?:(?<minutes>[0-9]+) ?(m|minutes?))? ?(?:(?<seconds>[0-9]+) ?(s|seconds?))?\\b".toRegex()
@Subscribe
fun modifyLore(event: ItemTooltipEvent) {
if (!TConfig.showTimers) return
var lastTimer: ZonedDateTime? = null
for (i in event.lines.indices) {
val line = event.lines[i].unformattedString
val countdownType = CountdownTypes.entries.find { it.match in line } ?: continue
if (countdownType == CountdownTypes.CALENDARDETAILS
&& !event.stack.displayNameAccordingToNbt.unformattedString.startsWith("Day ")
) continue
val countdownMatch = regex.findAll(line).filter { it.value.isNotBlank() }.lastOrNull() ?: continue
val (years, days, hours, minutes, seconds) =
listOf("years", "days", "hours", "minutes", "seconds")
.map {
countdownMatch.groups[it]?.value?.toLong() ?: 0L
}
if (years + days + hours + minutes + seconds == 0L) continue
var baseLine = ZonedDateTime.now(SBData.hypixelTimeZone)
if (countdownType.isRelative) {
if (lastTimer == null) {
event.lines.add(i + 1,
tr("firmament.loretimer.missingrelative",
"Found a relative countdown with no baseline (Firmament)").grey())
continue
}
baseLine = lastTimer
}
val timer =
baseLine.plusYears(years).plusDays(days).plusHours(hours).plusMinutes(minutes).plusSeconds(seconds)
lastTimer = timer
val localTimer = timer.withZoneSameInstant(ZoneId.systemDefault())
// TODO: install approximate time stabilization algorithm
event.lines.add(i + 1,
Text.literal("${countdownType.label}: ")
.grey()
.append(Text.literal(TConfig.timerFormat.formatter.format(localTimer)).aqua())
)
}
}
}
|