Skip to content

Commit

Permalink
Implement thresholds, cooldown and pause menu pausing shocking
Browse files Browse the repository at this point in the history
  • Loading branch information
LucHeart committed Feb 28, 2024
1 parent 853eaba commit 2e396cf
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ object ConfigGuiFactory : ConfigScreenFactory<Screen> {
)
}

return yacl.generateScreen(MinecraftClient.getInstance().currentScreen)
return yacl.generateScreen(parent)
}

fun createBuilder(
private fun createBuilder(
defaults: ShockCraftConfig,
config: ShockCraftConfig,
builder: YetAnotherConfigLib.Builder
Expand Down Expand Up @@ -97,6 +97,36 @@ object ConfigGuiFactory : ConfigScreenFactory<Screen> {
.build()
)

.option(Option.createBuilder<Int>()
.name(Text.literal("Damage Threshold"))
.description(OptionDescription.of(Text.literal("How much damage you need to take, or have until a shock is sent")))
.controller { option: Option<Int> ->
IntegerSliderControllerBuilder.create(option)
.range(1, 20)
.step(1)
}
.binding(
defaults.damageThreshold.toInt(),
{ config.damageThreshold.toInt() },
{ config.damageThreshold = it.toUInt() })
.build()
)

.option(Option.createBuilder<Int>()
.name(Text.literal("Cooldown"))
.description(OptionDescription.of(Text.literal("Cooldown between on damage shocks")))
.controller { option ->
IntegerSliderControllerBuilder.create(option)
.range(300, 60_000)
.step(100).formatValue { Text.literal((it / 1000f).toString() + " seconds") }
}
.binding(
defaults.cooldown.toInt(),
{ config.cooldown.toInt() },
{ config.cooldown = it.toUShort() })
.build()
)

.build()
)

Expand Down
84 changes: 60 additions & 24 deletions src/main/kotlin/openshock/integrations/minecraft/ShockCraft.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.EndTick
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.screen.GameMenuScreen
import net.minecraft.client.network.ClientPlayerEntity
import net.minecraft.command.CommandRegistryAccess
import net.minecraft.entity.damage.DamageSource
Expand All @@ -23,6 +24,8 @@ import openshock.integrations.minecraft.openshock.OpenShockApi
import openshock.integrations.minecraft.utils.MathUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.Calendar
import kotlin.math.log

object ShockCraft : ModInitializer {
public val logger: Logger = LoggerFactory.getLogger("shockcraft")
Expand Down Expand Up @@ -52,6 +55,7 @@ object ShockCraft : ModInitializer {

var lastTickHealth: Float = 20f
var lastTickReset: Boolean = false
var pauseMenuOpen: Boolean = true

private fun reset() {
lastTickReset = true
Expand All @@ -67,6 +71,25 @@ object ShockCraft : ModInitializer {

@OptIn(DelicateCoroutinesApi::class)
private fun clientTickLoopFun() {
val currentScreen = MinecraftClient.getInstance().currentScreen;

// Cursed if logic to see if pause menu was opened, might not work with all mods
if (currentScreen != null) {
if (!pauseMenuOpen && currentScreen is GameMenuScreen) {
pauseMenuOpen = true
logger.debug("Game menu opened")
}
} else if (pauseMenuOpen) {
pauseMenuOpen = false
logger.debug("Game menu closed")
}

// Pause menu is open or one of its childs. Reset so we dont shock when you close it again and have taken damage
if(pauseMenuOpen) {
reset()
return
}

val player = MinecraftClient.getInstance().player

// Player does not exist, reset and return
Expand All @@ -75,9 +98,6 @@ object ShockCraft : ModInitializer {
return
}

// No need to run if we are paused or in escape screen at least
if (MinecraftClient.getInstance().isPaused) return

val creativeOrSpectator = player.isCreative || player.isSpectator

// We usually cannot take damage in creative or spectator, reset and return
Expand All @@ -99,7 +119,7 @@ object ShockCraft : ModInitializer {

// Did we take damage?
if (damageSinceLastTick > 0) {
logger.info(player.recentDamageSource?.name + " - " + damageSinceLastTick.toString())
logger.debug(player.recentDamageSource?.name + " - " + damageSinceLastTick.toString())

if (player.isDead) {
logger.debug("Player died")
Expand All @@ -121,51 +141,67 @@ object ShockCraft : ModInitializer {
ControlType.Shock,
config.onDeathIntensity,
config.onDeathDuration,
getName(player.recentDamageSource)
getName(player.recentDamageSource)
)
}

private var lastShock: Long = -1

private suspend fun onDamage(player: ClientPlayerEntity, damage: Float) {
val config = ShockCraftConfig.HANDLER.instance()
if (!config.onDamage) return

val currentTime = Calendar.getInstance().timeInMillis
if (lastShock + config.cooldown.toLong() > currentTime) {
logger.info("OnDamage is on cooldown")
return
}

lastShock = currentTime

var intensity: Byte
var duration: UShort
val percentageThreshold = config.damageThreshold.toFloat() / 20f

when (config.damageMode) {
DamageShockMode.LowHp -> {
val intensity: Byte
val duration: UShort

when (config.damageMode) {
DamageShockMode.LowHp -> {
val percentageDamage = 1 - (player.health / player.maxHealth).coerceAtLeast(0f).coerceAtMost(1f)
if (percentageDamage < percentageThreshold) {
logger.debug("Damage percentage is below threshold")
return
}

intensity = MathUtils.lerp(config.intensityMin, config.intensityMax, percentageDamage)
duration = MathUtils.lerp(config.durationMin, config.durationMax, percentageDamage)
}
DamageShockMode.DamageAmount -> {
}

DamageShockMode.DamageAmount -> {
val percentageDamage = (damage / player.maxHealth).coerceAtLeast(0f).coerceAtMost(1f)
if (percentageDamage < percentageThreshold) {
logger.debug("Damage percentage is below threshold")
return
}

intensity = MathUtils.lerp(config.intensityMin, config.intensityMax, percentageDamage)
duration = MathUtils.lerp(config.durationMin, config.durationMax, percentageDamage)

}
}
}
}

OpenShockApi.control(
ControlType.Shock,
intensity,
duration,
getName(player.recentDamageSource)
getName(player.recentDamageSource)
)
}

private const val SUFFIX: String = " (Integrations.Minecraft)"

private fun getName(damageSource: DamageSource?): String {
if (damageSource != null) {
return if (damageSource.attacker != null) damageSource.attacker!!.name.literalString + SUFFIX
else damageSource.name + SUFFIX
}
return "Unknown$SUFFIX"
}
private fun getName(damageSource: DamageSource?): String {
if (damageSource != null) {
return if (damageSource.attacker != null && damageSource.attacker!!.name.literalString != null) damageSource.attacker!!.name.literalString!!
else damageSource.name
}
return "Unknown"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ class ShockCraftConfig {
@SerialEntry
var durationMax: UShort = 2500u

@SerialEntry
var damageThreshold: UInt = 0u

@SerialEntry
var cooldown: UShort = 500u


// <--- On Death --->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ru.gildor.coroutines.okhttp.await

object OpenShockApi {

private const val SUFFIX: String = " (Integrations.Minecraft)"
private val JSON: MediaType = "application/json".toMediaType()

private val client: OkHttpClient = OkHttpClient()
Expand All @@ -28,7 +29,7 @@ object OpenShockApi {
shocks.add(ControlItem(it, type, intensity, duration))
}

val requestObject = ControlRequest(shocks, name)
val requestObject = ControlRequest(shocks, name + SUFFIX)
val json = Gson().toJson(requestObject)

val url = ShockCraftConfig.HANDLER.instance().apiBaseUrl.toHttpUrl()
Expand Down

0 comments on commit 2e396cf

Please sign in to comment.