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.
 
vore-rpg/src/battlers/Energy.ts

161 lines
6.2 KiB

import {assertion} from "../testing/Assertions";
/** The interface managing a character's Stamina and Energy. */
export interface Energy {
/** The character's maximum Stamina, which they are returned to when they rest. >= 1, <= 99999. */
readonly maxStamina: number
/** The character's current Stamina, which their energy returns to when they complete a battle. >= 0, <= maxStamina. */
readonly currentStamina: number
/** The character's current Energy, used for special abilities. >= 0, <= currentStamina. */
readonly currentEnergy: number
}
/** Formats the given Energy object into a string suitable for logging. Should never throw. */
export function energyToString(energy: Energy): string {
return `{Energy ${energy.currentEnergy}/${energy.currentStamina}/${energy.maxStamina}}`
}
/**
* Returns whether the given Energy object is valid, i.e., it has currentEnergy <= currentStamina <= maxStamina, and
* all three are positive integers or zero.
*/
export function isValidEnergy(energy: Energy): boolean {
return isValidMaxStamina(energy.maxStamina)
&& isValidCurrentEnergy(energy.currentStamina, energy.maxStamina)
&& isValidCurrentEnergy(energy.currentEnergy, energy.currentStamina)
}
/** Returns whether the given value is valid for max stamina, i.e., it's an integer between 1 and 99999. */
export function isValidMaxStamina(maxStamina: number): boolean {
return Number.isSafeInteger(maxStamina) && maxStamina >= 1 && maxStamina <= 99999
}
/** Returns whether the given value is valid for current energy or stamina, i.e., it's an integer between 0 and max. */
export function isValidCurrentEnergy(value: number, max: number): boolean {
return Number.isSafeInteger(value) && value >= 0 && value <= max
}
/** Asserts that the given Energy object is valid (see the documentation for isValidEnergy). */
export function checkValidEnergy(energy: Energy): Energy {
return assertion.check(energy, isValidEnergy, (energy) => `Invalid energy: ${energyToString(energy)}`)
}
/** Asserts that the given value is valid for maxStamina. */
export function checkValidMaxStamina(maxStamina: number): number {
return assertion.check(maxStamina, isValidMaxStamina, (maxStamina) => `Invalid max stamina: ${maxStamina}`)
}
/** Asserts that the given value is valid for currentStamina or currentEnergy. */
export function checkValidCurrentEnergy(value: number, max: number): number {
return assertion.check(value, (value) => isValidCurrentEnergy(value, max), (value) => `Invalid current energy: ${value}`)
}
/**
* Sets the max stamina value on the given Energy. This may result in the currentStamina (and possibly currentEnergy)
* values decreasing to fit under the new ceiling.
*/
export function setMaxStamina(energy: Energy, maxStamina: number): Energy {
checkValidEnergy(energy)
checkValidMaxStamina(maxStamina)
const currentStamina = Math.min(energy.currentStamina, maxStamina)
const currentEnergy = Math.min(energy.currentEnergy, currentStamina)
return checkValidEnergy({...energy, maxStamina, currentStamina, currentEnergy})
}
/**
* Deals damage to the Stamina of the given Energy. currentStamina will be decreased; currentEnergy will also be
* decreased if damageEnergy is true, or if the new Stamina is lower than the Energy. Neither can drop below 0 this way.
*/
export function damageStamina(energy: Energy, damage: number, options: {damageEnergy: boolean}): Energy {
checkValidEnergy(energy)
assertion.checkPositiveIntegerOrZero(damage, () => `Invalid damage: ${damage}`)
const currentStamina = Math.max(0, energy.currentStamina - damage)
const currentEnergy = options.damageEnergy ? Math.max(0, energy.currentEnergy - damage) : Math.min(energy.currentEnergy, currentStamina)
return checkValidEnergy({
...energy,
currentStamina,
currentEnergy,
})
}
/**
* Recovers the Stamina of the given Energy. currentStamina will be increased; currentEnergy will also be increased
* if recoverEnergy is true. Neither can exceed maxStamina this way.
*/
export function recoverStamina(energy: Energy, recovery: number, options: {recoverEnergy: boolean}): Energy {
checkValidEnergy(energy)
assertion.checkPositiveIntegerOrZero(recovery, () => `Invalid healing: ${recovery}`)
const currentStamina = Math.min(energy.maxStamina, energy.currentStamina + recovery)
const currentEnergy = options.recoverEnergy ? energy.currentEnergy + (currentStamina - energy.currentStamina) : energy.currentEnergy
return checkValidEnergy({
...energy,
currentStamina,
currentEnergy,
})
}
/**
* Sets the Stamina of the given Energy. currentEnergy will be decreased if it's lower than the new Stamina.
*/
export function setCurrentStamina(energy: Energy, value: number): Energy {
checkValidEnergy(energy)
checkValidCurrentEnergy(value, energy.maxStamina)
const currentStamina = value
const currentEnergy = Math.min(currentStamina, energy.currentEnergy)
return checkValidEnergy({
...energy,
currentStamina,
currentEnergy
})
}
/**
* Deals damage to the Energy of the given Energy. It can't drop below 0 this way.
*/
export function damageEnergy(energy: Energy, damage: number): Energy {
checkValidEnergy(energy)
assertion.checkPositiveIntegerOrZero(damage, () => `Invalid damage: ${damage}`)
const currentEnergy = Math.max(0, energy.currentEnergy - damage)
return checkValidEnergy({
...energy,
currentEnergy,
})
}
/**
* Recovers the Energy of the given Energy. It can't exceed currentStamina this way.
*/
export function recoverEnergy(energy: Energy, recovery: number): Energy {
checkValidEnergy(energy)
assertion.checkPositiveIntegerOrZero(recovery, () => `Invalid healing: ${recovery}`)
const currentEnergy = Math.min(energy.currentStamina, energy.currentEnergy + recovery)
return checkValidEnergy({
...energy,
currentEnergy
})
}
/**
* Sets the current Energy of the given Energy.
*/
export function setCurrentEnergy(energy: Energy, value: number): Energy {
checkValidEnergy(energy)
checkValidCurrentEnergy(value, energy.currentStamina)
return checkValidEnergy({
...energy,
currentEnergy: value
})
}