Files

180 lines
4.1 KiB
GDScript

class_name FactionStateRecord
extends RefCounted
signal changed(state: FactionStateRecord)
const SCHEMA_VERSION := 1
var data: Dictionary
func _init(record_data: Dictionary = {}) -> void:
data = record_data.duplicate(true)
static func _snap_food(value: float) -> float:
return snappedf(value, 1.0 / 64.0)
static func create(
faction_id: StringName,
display_name: String,
food: float,
warriors: int,
aggression: float,
morale: float
) -> FactionStateRecord:
return (
FactionStateRecord
. new(
{
"schema_version": SCHEMA_VERSION,
"faction_id": String(faction_id),
"display_name": display_name,
"food": _snap_food(food),
"warriors": warriors,
"aggression": clampf(aggression, 0.0, 1.0),
"morale": clampf(morale, 0.0, 1.0),
"stance": String(SimulationIds.STANCE_NEUTRAL),
"war_plan": String(SimulationIds.WAR_PLAN_NONE),
"plan_tick": -1,
"confidence": 0.0,
}
)
)
static func from_dictionary(record_data: Dictionary) -> FactionStateRecord:
if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION:
return null
if not (
record_data
. has_all(
[
"faction_id",
"display_name",
"food",
"warriors",
"aggression",
"morale",
"stance",
"war_plan",
"plan_tick",
"confidence",
]
)
):
return null
var faction_id := String(record_data["faction_id"])
var display_name := String(record_data["display_name"])
if faction_id.is_empty() or display_name.is_empty():
return null
var food := float(record_data["food"])
var warriors := int(record_data["warriors"])
var aggression := float(record_data["aggression"])
var morale := float(record_data["morale"])
var confidence := float(record_data["confidence"])
if (
not is_finite(food)
or not is_finite(aggression)
or not is_finite(morale)
or not is_finite(confidence)
or food < 0.0
or warriors < 0
or aggression < 0.0
or aggression > 1.0
or morale < 0.0
or morale > 1.0
or confidence < 0.0
or confidence > 1.0
):
return null
var normalized := record_data.duplicate(true)
normalized["schema_version"] = SCHEMA_VERSION
normalized["faction_id"] = faction_id
normalized["display_name"] = display_name
normalized["food"] = _snap_food(food)
normalized["warriors"] = warriors
normalized["aggression"] = aggression
normalized["morale"] = morale
normalized["stance"] = String(record_data["stance"])
normalized["war_plan"] = String(record_data["war_plan"])
normalized["plan_tick"] = int(record_data["plan_tick"])
normalized["confidence"] = confidence
return FactionStateRecord.new(normalized)
func get_faction_id() -> StringName:
return StringName(data["faction_id"])
func get_display_name() -> String:
return String(data["display_name"])
func get_food() -> float:
return maxf(float(data["food"]), 0.0)
func get_warriors() -> int:
return int(data["warriors"])
func get_aggression() -> float:
return clampf(float(data["aggression"]), 0.0, 1.0)
func get_morale() -> float:
return clampf(float(data["morale"]), 0.0, 1.0)
func get_stance() -> StringName:
return StringName(data["stance"])
func get_war_plan() -> StringName:
return StringName(data["war_plan"])
func get_plan_tick() -> int:
return int(data["plan_tick"])
func get_confidence() -> float:
return clampf(float(data["confidence"]), 0.0, 1.0)
func set_food(value: float) -> void:
var next := _snap_food(maxf(value, 0.0))
if is_equal_approx(next, get_food()):
return
data["food"] = next
changed.emit(self)
func set_morale(value: float) -> void:
var next := clampf(value, 0.0, 1.0)
if is_equal_approx(next, get_morale()):
return
data["morale"] = next
changed.emit(self)
func set_stance(value: StringName) -> void:
if String(value) == String(get_stance()):
return
data["stance"] = String(value)
changed.emit(self)
func set_war_plan(value: StringName, current_tick: int, confidence: float = -1.0) -> void:
data["war_plan"] = String(value)
data["plan_tick"] = current_tick
if confidence >= 0.0:
data["confidence"] = clampf(confidence, 0.0, 1.0)
changed.emit(self)
func to_dictionary() -> Dictionary:
return data.duplicate(true)