Skip to content

Commit a4e0d95

Browse files
authored
feat: implement XP level up and chat message triggering (#5)
1 parent a86960c commit a4e0d95

3 files changed

Lines changed: 168 additions & 3 deletions

File tree

‎src/main/kotlin/openshock/integrations/minecraft/ConfigGuiFactory.kt‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,95 @@ object ConfigGuiFactory : ConfigScreenFactory<Screen> {
180180
.build()
181181
)
182182

183+
.group(OptionGroup.createBuilder()
184+
.name(Text.literal("On Level Up"))
185+
.description(OptionDescription.of(Text.literal("Defines what happens when you gain an XP level")))
186+
187+
.option(Option.createBuilder<Boolean>()
188+
.name(Text.literal("Enabled"))
189+
.description(OptionDescription.of(Text.literal("Enable shocking on level up")))
190+
.controller { TickBoxControllerBuilder.create(it) }
191+
.binding(defaults.onLevelUp, { config.onLevelUp }, { config.onLevelUp = it })
192+
.build()
193+
)
194+
.option(Option.createBuilder<Int>()
195+
.name(Text.literal("Intensity"))
196+
.controller { option ->
197+
IntegerSliderControllerBuilder.create(option)
198+
.range(1, 100)
199+
.step(1)
200+
}
201+
.binding(
202+
defaults.onLevelUpIntensity.toInt(),
203+
{ config.onLevelUpIntensity.toInt() },
204+
{ config.onLevelUpIntensity = it.toByte() })
205+
.build()
206+
)
207+
.option(Option.createBuilder<Int>()
208+
.name(Text.literal("Duration"))
209+
.controller { option ->
210+
IntegerSliderControllerBuilder.create(option)
211+
.range(300, 30_000)
212+
.step(100).formatValue { Text.literal((it / 1000f).toString() + " seconds") }
213+
}
214+
.binding(
215+
defaults.onLevelUpDuration.toInt(),
216+
{ config.onLevelUpDuration.toInt() },
217+
{ config.onLevelUpDuration = it.toUShort() })
218+
.build()
219+
)
220+
221+
.build()
222+
)
223+
224+
.group(OptionGroup.createBuilder()
225+
.name(Text.literal("On Chat Message"))
226+
.description(OptionDescription.of(Text.literal("Defines what happens when a specific chat phrase is sent or received")))
227+
228+
.option(Option.createBuilder<Boolean>()
229+
.name(Text.literal("Enable for Chat Messages"))
230+
.description(OptionDescription.of(Text.literal("Enable shocking when a message with the key phrase is sent/received")))
231+
.controller { TickBoxControllerBuilder.create(it) }
232+
.binding(defaults.onChatEvent, { config.onChatEvent }, { config.onChatEvent = it })
233+
.build()
234+
)
235+
.option(Option.createBuilder<String>()
236+
.name(Text.literal("Key Phrase"))
237+
.description(OptionDescription.of(Text.literal("The phrase to trigger the shock")))
238+
.controller { StringControllerBuilder.create(it) }
239+
.binding(defaults.chatMessagePhrase, { config.chatMessagePhrase }, { config.chatMessagePhrase = it })
240+
.build()
241+
)
242+
.option(Option.createBuilder<Int>()
243+
.name(Text.literal("Intensity"))
244+
.controller { option ->
245+
IntegerSliderControllerBuilder.create(option)
246+
.range(1, 100)
247+
.step(1)
248+
}
249+
.binding(
250+
defaults.onChatMessageIntensity.toInt(),
251+
{ config.onChatMessageIntensity.toInt() },
252+
{ config.onChatMessageIntensity = it.toByte() })
253+
.build()
254+
)
255+
.option(Option.createBuilder<Int>()
256+
.name(Text.literal("Duration"))
257+
.controller { option ->
258+
IntegerSliderControllerBuilder.create(option)
259+
.range(300, 30_000)
260+
.step(100).formatValue { Text.literal((it / 1000f).toString() + " seconds") }
261+
}
262+
.binding(
263+
defaults.onChatMessageDuration.toInt(),
264+
{ config.onChatMessageDuration.toInt() },
265+
{ config.onChatMessageDuration = it.toUShort() })
266+
.build()
267+
)
268+
269+
.build()
270+
)
271+
183272
.build()
184273
)
185274

‎src/main/kotlin/openshock/integrations/minecraft/ShockCraft.kt‎

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import kotlinx.coroutines.DelicateCoroutinesApi
44
import kotlinx.coroutines.GlobalScope
55
import kotlinx.coroutines.launch
66
import net.fabricmc.api.ClientModInitializer
7-
import net.fabricmc.api.ModInitializer
87
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
98
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.EndTick
9+
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents
1010
import net.minecraft.client.MinecraftClient
1111
import net.minecraft.client.gui.screen.GameMenuScreen
1212
import net.minecraft.client.network.ClientPlayerEntity
@@ -29,12 +29,21 @@ object ShockCraft : ClientModInitializer {
2929
ShockCraftConfig.HANDLER.load()
3030

3131
ClientTickEvents.END_CLIENT_TICK.register(EndTick { clientTickLoopFun() })
32+
33+
ClientReceiveMessageEvents.CHAT.register { message, _, _, _, _ ->
34+
val config = ShockCraftConfig.HANDLER.instance()
35+
if (config.onChatEvent) {
36+
onChatMessage(message.string)
37+
}
38+
}
3239
}
3340

3441
var lastTickHealth: Float = 20f
3542
var lastTickReset: Boolean = false
3643
var pauseMenuOpen: Boolean = true
3744

45+
var lastTickXpLevel: Int = 0
46+
3847
val DamageSource?.attackerName: String
3948
get() = this?.attacker?.stringifiedName ?: "Unknown"
4049

@@ -44,10 +53,12 @@ object ShockCraft : ClientModInitializer {
4453

4554
if (player == null) {
4655
lastTickHealth = 20f
56+
lastTickXpLevel = 0
4757
return
4858
}
4959

5060
lastTickHealth = player.maxHealth
61+
lastTickXpLevel = player.experienceLevel
5162
}
5263

5364
@OptIn(DelicateCoroutinesApi::class)
@@ -91,12 +102,15 @@ object ShockCraft : ClientModInitializer {
91102
if (lastTickReset) {
92103
lastTickReset = false
93104
lastTickHealth = player.health
105+
lastTickXpLevel = player.experienceLevel
94106
}
95107

96108
val damageSinceLastTick = (lastTickHealth - player.health).coerceAtLeast(0f)
109+
val xpLevelChange = player.experienceLevel - lastTickXpLevel
97110

98-
// Set last tick health, we already calculated what we need
111+
// Set last tick health and experience level, we already calculated what we need
99112
lastTickHealth = player.health
113+
lastTickXpLevel = player.experienceLevel
100114

101115
// Did we take damage?
102116
if (damageSinceLastTick > 0) {
@@ -112,6 +126,43 @@ object ShockCraft : ClientModInitializer {
112126

113127
GlobalScope.launch { onDamage(player, damageSinceLastTick) }
114128
}
129+
130+
if (xpLevelChange > 0) {
131+
logger.debug("Player leveled up by $xpLevelChange levels")
132+
GlobalScope.launch {
133+
onLevelUp(xpLevelChange)
134+
}
135+
}
136+
}
137+
138+
private suspend fun onLevelUp(levels: Int) {
139+
val config = ShockCraftConfig.HANDLER.instance()
140+
if (!config.onLevelUp) return
141+
142+
OpenShockApi.control(
143+
ControlType.Shock,
144+
config.onLevelUpIntensity,
145+
config.onLevelUpDuration,
146+
"XP Level Up ($levels)",
147+
)
148+
}
149+
150+
@OptIn(DelicateCoroutinesApi::class)
151+
private fun onChatMessage(message: String) {
152+
val config = ShockCraftConfig.HANDLER.instance()
153+
if (config.chatMessagePhrase.isBlank()) return
154+
155+
if (message.contains(config.chatMessagePhrase, ignoreCase = true)) {
156+
logger.debug("Chat message triggered shock: $message")
157+
GlobalScope.launch {
158+
OpenShockApi.control(
159+
ControlType.Shock,
160+
config.onChatMessageIntensity,
161+
config.onChatMessageDuration,
162+
"Chat Message Event",
163+
)
164+
}
165+
}
115166
}
116167

117168
private suspend fun onDeath(player: ClientPlayerEntity) {
@@ -176,4 +227,4 @@ object ShockCraft : ClientModInitializer {
176227
player.recentDamageSource.attackerName,
177228
)
178229
}
179-
}
230+
}

‎src/main/kotlin/openshock/integrations/minecraft/config/ShockCraftConfig.kt‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,31 @@ class ShockCraftConfig {
6262

6363
@SerialEntry
6464
var onDeathDuration: UShort = 2500u
65+
66+
// <--- On Level Up --->
67+
68+
@SerialEntry(comment = "Shock on level up?")
69+
var onLevelUp: Boolean = false
70+
71+
@SerialEntry
72+
var onLevelUpIntensity: Byte = 20
73+
74+
@SerialEntry
75+
var onLevelUpDuration: UShort = 500u
76+
77+
// <--- On Chat Message --->
78+
79+
@SerialEntry(comment = "Shock when a specific phrase is sent/received in chat?")
80+
var onChatEvent: Boolean = false
81+
82+
@SerialEntry(comment = "The phrase to trigger the shock")
83+
var chatMessagePhrase: String = "shock me"
84+
85+
@SerialEntry
86+
var onChatMessageIntensity: Byte = 30
87+
88+
@SerialEntry
89+
var onChatMessageDuration: UShort = 1000u
6590

6691
// <--- General --->
6792

0 commit comments

Comments
 (0)