You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

182 lines
6.3 KiB

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
}
}