package model import korlibs.math.* import korlibs.time.* import kotlin.math.* data class Battler( var name: String, var composure: Double, var health: Double, var maxHealth: Double, var energy: Double, var stamina: Double, var maxStamina: Double, var shaken: Boolean = false, var shakenTimes: Int = 0, var composurePausedUntil: TimeSpan = TimeSpan.ZERO, var energyPausedUntil: TimeSpan = TimeSpan.ZERO, var currentUpdate: TimeSpan = TimeSpan.ZERO, private var lastUpdate: TimeSpan = TimeSpan.ZERO, private var lastShaken: Boolean = shaken, private var lastComposure: Double = composure, private var lastEnergy: Double = energy, private var lastStamina: Double = stamina, ) { constructor(name: String, health: Double, stamina: Double) : this(name = name, composure = health, health = health, maxHealth = health, energy = stamina, stamina = stamina, maxStamina = stamina) private val composureChargeTime: TimeSpan get() = TimeSpan(45_000.0 * 1.2.pow(shakenTimes - 1)) private val composureChargeDelay: TimeSpan get() = TimeSpan(2_000.0 * 1.2.pow(shakenTimes - 1)) private val composureRecoveryTime: TimeSpan get() = TimeSpan(7_000.0 * 1.2.pow(shakenTimes - 1)) private val composureRecoveryDelay: TimeSpan get() = TimeSpan(3_000.0 * 1.2.pow(shakenTimes - 1)) private val composureChargeCoefficient: Double get() = health / composureChargeTime.milliseconds.squared() private val energyChargeTime: TimeSpan get() = TimeSpan(30_000.0) private val energyChargeDelay: TimeSpan get() = TimeSpan(1_000.0) private val staminaChargeTime: TimeSpan get() = TimeSpan(300_000.0) private fun advanceComposureTo(t: TimeSpan) { var dtComposure = if (lastUpdate < composurePausedUntil) { if (t > composurePausedUntil) { t - composurePausedUntil } else { TimeSpan.ZERO } } else { t - lastUpdate } if (lastShaken && dtComposure.isPositive() && health > 0) { val halfHealth = health/2.0 val dtRecover = composureRecoveryTime * (halfHealth - lastComposure.coerceAtLeast(0.0)) / halfHealth if (dtComposure > dtRecover) { composure = halfHealth shaken = false dtComposure -= dtRecover } else { composure = ( lastComposure.coerceAtLeast(0.0) + dtComposure.milliseconds * halfHealth / composureRecoveryTime.milliseconds) shaken = lastShaken dtComposure = TimeSpan.ZERO } } else { composure = lastComposure shaken = lastShaken } if (dtComposure.isPositive()) { if (health > 0) { val lastChargeTime = TimeSpan( sqrt(4 * composureChargeCoefficient * composure) / (2 * composureChargeCoefficient) ) composure = if (dtComposure + lastChargeTime >= composureChargeTime) { health } else { (composureChargeCoefficient * (lastChargeTime + dtComposure).milliseconds.squared()) } } else { composure = health } } } private fun advanceEnergyAndStaminaTo(t: TimeSpan) { var dtEnergy = if (lastUpdate < energyPausedUntil) { if (t > energyPausedUntil) { t - energyPausedUntil } else { TimeSpan.ZERO } } else { t - lastUpdate } if (dtEnergy.isPositive()) { if (lastEnergy < lastStamina) { if (lastStamina > 0) { val dtToFull = energyChargeTime * (lastStamina - lastEnergy) / lastStamina if (dtEnergy >= dtToFull) { energy = lastStamina dtEnergy -= dtToFull } else { energy = lastEnergy + (lastStamina * dtEnergy.milliseconds) / energyChargeTime.milliseconds dtEnergy = TimeSpan.ZERO } } else { energy = lastStamina } } if (dtEnergy.isPositive() && lastStamina < maxStamina) { stamina = min( maxStamina, lastStamina + (maxStamina * dtEnergy.milliseconds) / staminaChargeTime.milliseconds) energy = stamina } } } fun advanceTo(t: TimeSpan) { currentUpdate = t if (composure < health) { advanceComposureTo(t) } if (energy < stamina || stamina < maxStamina) { advanceEnergyAndStaminaTo(t) } } fun becomeShaken() { composure = health.coerceAtMost(0.0) if (shaken) { return } composurePausedUntil = (currentUpdate + composureRecoveryDelay).coerceAtLeast(composurePausedUntil) shaken = true shakenTimes += 1 } fun inflictComposureDamage(damage: Double) { if (shaken) { return } composurePausedUntil = (currentUpdate + composureChargeDelay).coerceAtLeast(composurePausedUntil) composure -= damage if (composure <= 0) { becomeShaken() } } fun inflictHealthDamage(damage: Double) { composurePausedUntil = (currentUpdate + composureRecoveryDelay).coerceAtLeast(composurePausedUntil) health -= damage composure -= damage if (composure <= 0) { becomeShaken() } } fun spendEnergy(cost: Double) { energyPausedUntil = (currentUpdate + energyChargeDelay).coerceAtLeast(energyPausedUntil) energy = (energy - cost).coerceAtLeast(0.0) } fun spendStamina(cost: Double) { energyPausedUntil = (currentUpdate + energyChargeDelay).coerceAtLeast(energyPausedUntil) stamina = (stamina - cost).coerceAtLeast(0.0) energy = (energy - cost).coerceAtLeast(0.0) } fun updateAt(t: TimeSpan, block: Battler.() -> Unit) { advanceTo(t) block(this) lastUpdate = currentUpdate lastShaken = shaken lastComposure = composure lastEnergy = energy lastStamina = stamina } }