class_name PlayerStateRecord extends RefCounted const SCHEMA_VERSION := 1 const HUNGER_DEATH_THRESHOLD := 100.0 const STACK_THRESHOLD := 90.0 const DEATH_STANDING_PENALTY := 0.5 const RESET_HUNGER := 20.0 const RESET_ENERGY := 70.0 var data: Dictionary func _init(record_data: Dictionary = {}) -> void: data = record_data.duplicate(true) static func create() -> PlayerStateRecord: return ( PlayerStateRecord . new( { "schema_version": SCHEMA_VERSION, "hunger": 20.0, "energy": 90.0, "is_starving": false, "starvation_ticks": 0, "starvation_death_threshold": 600, "is_dead": false, "deaths": 0, } ) ) static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord: if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION: return null if not ( record_data . has_all( [ "hunger", "energy", "is_starving", "starvation_ticks", "starvation_death_threshold", "is_dead", "deaths", ] ) ): return null var hunger := float(record_data["hunger"]) var energy := float(record_data["energy"]) var starvation_ticks := int(record_data["starvation_ticks"]) var death_threshold := int(record_data["starvation_death_threshold"]) var deaths := int(record_data["deaths"]) if ( not is_finite(hunger) or hunger < 0.0 or hunger > HUNGER_DEATH_THRESHOLD or not is_finite(energy) or energy < 0.0 or energy > 100.0 or starvation_ticks < 0 or death_threshold < 1 or deaths < 0 ): return null return ( PlayerStateRecord . new( { "schema_version": SCHEMA_VERSION, "hunger": hunger, "energy": energy, "is_starving": bool(record_data["is_starving"]), "starvation_ticks": starvation_ticks, "starvation_death_threshold": death_threshold, "is_dead": bool(record_data["is_dead"]), "deaths": deaths, } ) ) func get_hunger() -> float: return float(data["hunger"]) func get_energy() -> float: return float(data["energy"]) func is_starving() -> bool: return bool(data["is_starving"]) func is_dead() -> bool: return bool(data["is_dead"]) func get_starvation_ticks() -> int: return int(data["starvation_ticks"]) func get_deaths() -> int: return int(data["deaths"]) func eat(amount: float) -> float: if amount <= 0.0 or is_dead(): return 0.0 var previous := get_hunger() data["hunger"] = clampf(previous - amount, 0.0, HUNGER_DEATH_THRESHOLD) var consumed := previous - get_hunger() if consumed > 0.0: data["energy"] = minf(get_energy() + consumed * 0.5, 100.0) return consumed func advance(hunger_rate: float, energy_rate: float) -> bool: if is_dead(): return false data["hunger"] = minf(get_hunger() + maxf(hunger_rate, 0.0), HUNGER_DEATH_THRESHOLD) data["energy"] = clampf(get_energy() - maxf(energy_rate, 0.0), 0.0, 100.0) data["is_starving"] = get_hunger() >= STACK_THRESHOLD if is_starving(): data["starvation_ticks"] = int(data["starvation_ticks"]) + 1 if get_starvation_ticks() >= int(data["starvation_death_threshold"]): return true else: data["starvation_ticks"] = 0 return false func die() -> void: data["is_dead"] = true data["deaths"] = int(data["deaths"]) + 1 func respawn() -> void: data["hunger"] = RESET_HUNGER data["energy"] = RESET_ENERGY data["is_starving"] = false data["starvation_ticks"] = 0 data["is_dead"] = false func to_dictionary() -> Dictionary: return data.duplicate(true)