class_name EconomicEventRecord extends RefCounted const SCHEMA_VERSION := 3 const POSITION_LEGACY_SCHEMA_VERSION := 2 const LEGACY_SCHEMA_VERSION := 1 var data: Dictionary func _init(record_data: Dictionary = {}) -> void: data = record_data.duplicate(true) static func create( event_id: int, event_type: StringName, tick: int, actor_id: int, source_id: StringName, destination_id: StringName, item_id: StringName, amount: float, world_position: Vector3 = Vector3.ZERO ) -> EconomicEventRecord: return EconomicEventRecord.new( { "schema_version": SCHEMA_VERSION, "event_id": event_id, "event_type": String(event_type), "tick": tick, "actor_id": actor_id, "source_id": String(source_id), "destination_id": String(destination_id), "item_id": String(item_id), "amount": amount, "action_id": "", "required_amount": 0.0, "world_position": [world_position.x, world_position.y, world_position.z] } ) static func create_narrative( event_id: int, event_type: StringName, tick: int, actor_id: int, source_id: StringName, action_display: String = "", world_position: Vector3 = Vector3.ZERO, action_id: StringName = &"", item_id: StringName = &"", required_amount: float = 0.0 ) -> EconomicEventRecord: return EconomicEventRecord.new( { "schema_version": SCHEMA_VERSION, "event_id": event_id, "event_type": String(event_type), "tick": tick, "actor_id": actor_id, "source_id": String(source_id), "destination_id": "", "item_id": String(item_id), "amount": 0.0, "action_name": action_display, "action_id": String(action_id), "required_amount": required_amount, "world_position": [world_position.x, world_position.y, world_position.z] } ) static func from_dictionary(record_data: Dictionary) -> EconomicEventRecord: var version := int(record_data.get("schema_version", -1)) if version not in [LEGACY_SCHEMA_VERSION, POSITION_LEGACY_SCHEMA_VERSION, SCHEMA_VERSION]: return null if not record_data.has_all( [ "event_id", "event_type", "tick", "actor_id", "source_id", "destination_id", "item_id", "amount" ] ): return null if version == SCHEMA_VERSION and not record_data.has("world_position"): return null var normalized := record_data.duplicate(true) normalized["schema_version"] = SCHEMA_VERSION normalized["event_id"] = int(record_data["event_id"]) normalized["event_type"] = String(record_data["event_type"]) normalized["tick"] = int(record_data["tick"]) normalized["actor_id"] = int(record_data["actor_id"]) normalized["source_id"] = String(record_data["source_id"]) normalized["destination_id"] = String(record_data["destination_id"]) normalized["item_id"] = String(record_data["item_id"]) normalized["amount"] = float(record_data["amount"]) normalized["action_id"] = String(record_data.get("action_id", "")) normalized["required_amount"] = float(record_data.get("required_amount", 0.0)) var saved_position = record_data.get("world_position", [0.0, 0.0, 0.0]) if not saved_position is Array or saved_position.size() != 3: return null normalized["world_position"] = [ float(saved_position[0]), float(saved_position[1]), float(saved_position[2]) ] if normalized["event_id"] < 0 or normalized["tick"] < 0 or normalized["event_type"].is_empty(): return null if StringName(normalized["event_type"]) == SimulationIds.EVENT_TASK_BLOCKED: var has_stable_contract: bool = ( not normalized["action_id"].is_empty() and not normalized["item_id"].is_empty() and normalized["required_amount"] > 0.0 ) if version == SCHEMA_VERSION and not has_stable_contract: return null return EconomicEventRecord.new(normalized) func to_dictionary() -> Dictionary: return data.duplicate(true) func get_world_position() -> Vector3: var saved_position: Array = data["world_position"] return Vector3(float(saved_position[0]), float(saved_position[1]), float(saved_position[2])) func description(npc_names: Dictionary = {}) -> String: var actor_name: String = npc_names.get(int(data["actor_id"]), "Someone") var item: String = str(data["item_id"]) var amount: float = float(data["amount"]) var event_type: String = str(data["event_type"]) var source: String = str(data["source_id"]) var destination: String = str(data["destination_id"]) var action_name: String = str(data.get("action_name", "")) match event_type: "resource_extracted": return "%s gathered %.0f %s from %s" % [actor_name, amount, item, source] "storage_deposited": return "%s deposited %.0f %s into %s" % [actor_name, amount, item, destination] "storage_withdrawn": return "%s withdrew %.0f %s from %s" % [actor_name, amount, item, source] "item_consumed": return "%s consumed %.0f %s" % [actor_name, amount, item] "npc_slept": return "%s slept and recovered energy" % actor_name "npc_died": return "%s died from starvation" % actor_name "task_started": return "%s began %s" % [actor_name, action_name if not action_name.is_empty() else item] "task_blocked": return "%s could not complete %s" % [actor_name, action_name] "resource_depleted": return "%s was depleted" % source "animal_fed": return "%s fed %s %.0f %s" % [actor_name, destination, amount, item] _: return "tick %d: %s" % [int(data["tick"]), event_type]