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
|
package moe.nea.firmament.features.misc
import com.mojang.brigadier.arguments.IntegerArgumentType
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import moe.nea.firmament.Firmament
import moe.nea.firmament.annotations.Subscribe
import moe.nea.firmament.commands.DurationArgumentType
import moe.nea.firmament.commands.RestArgumentType
import moe.nea.firmament.commands.get
import moe.nea.firmament.commands.thenArgument
import moe.nea.firmament.commands.thenExecute
import moe.nea.firmament.events.CommandEvent
import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.util.CommonSoundEffects
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.clickCommand
import moe.nea.firmament.util.lime
import moe.nea.firmament.util.red
import moe.nea.firmament.util.tr
import moe.nea.firmament.util.yellow
object TimerFeature {
data class Timer(
val start: TimeMark,
val duration: Duration,
val message: String,
val timerId: Int,
) {
fun timeLeft() = (duration - start.passedTime()).coerceAtLeast(0.seconds)
fun isDone() = start.passedTime() >= duration
}
// Theoretically for optimal performance this could be a treeset keyed to the end time
val timers = mutableListOf<Timer>()
@Subscribe
fun tick(event: TickEvent) {
timers.removeAll {
if (it.isDone()) {
MC.sendChat(tr("firmament.timer.finished",
"The timer you set ${FirmFormatters.formatTimespan(it.duration)} ago just went off: ${it.message}")
.yellow())
Firmament.coroutineScope.launch {
withContext(MinecraftDispatcher) {
repeat(5) {
CommonSoundEffects.playSuccess()
delay(0.2.seconds)
}
}
}
true
} else {
false
}
}
}
fun startTimer(duration: Duration, message: String) {
val timerId = createTimerId++
timers.add(Timer(TimeMark.now(), duration, message, timerId))
MC.sendChat(
tr("firmament.timer.start",
"Timer started for $message in ${FirmFormatters.formatTimespan(duration)}.").lime()
.append(" ")
.append(
tr("firmament.timer.cancelbutton",
"Click here to cancel the timer."
).clickCommand("/firm timer clear $timerId").red()
)
)
}
fun clearTimer(timerId: Int) {
val timer = timers.indexOfFirst { it.timerId == timerId }
if (timer < 0) {
MC.sendChat(tr("firmament.timer.cancel.fail",
"Could not cancel that timer. Maybe it was already cancelled?").red())
} else {
val timerData = timers[timer]
timers.removeAt(timer)
MC.sendChat(tr("firmament.timer.cancel.done",
"Cancelled timer ${timerData.message}. It would have been done in ${
FirmFormatters.formatTimespan(timerData.timeLeft())
}.").lime())
}
}
var createTimerId = 0
@Subscribe
fun onCommands(event: CommandEvent.SubCommand) {
event.subcommand("cleartimer") {
thenArgument("timerId", IntegerArgumentType.integer(0)) { timerId ->
thenExecute {
clearTimer(this[timerId])
}
}
thenExecute {
timers.map { it.timerId }.forEach {
clearTimer(it)
}
}
}
event.subcommand("timer") {
thenArgument("time", DurationArgumentType) { duration ->
thenExecute {
startTimer(this[duration], "no message")
}
thenArgument("message", RestArgumentType) { message ->
thenExecute {
startTimer(this[duration], this[message])
}
}
}
}
}
}
|