feat: integrate regional caravan persistence

This commit is contained in:
Rijad Zuzo
2026-08-23 17:20:41 +02:00
parent 49c5bda651
commit 52307f1306
24 changed files with 1939 additions and 92 deletions
+116 -1
View File
@@ -12,6 +12,9 @@ const NpcTickDebugLog := preload("res://simulation/debug/npc_tick_debug_log.gd")
const RelationshipSystemScript := preload("res://simulation/relationships/RelationshipSystem.gd")
const EventKnowledgeSystemScript := preload("res://simulation/knowledge/EventKnowledgeSystem.gd")
const ConflictSystemScript := preload("res://simulation/conflict/ConflictSystem.gd")
const RegionalSimulationFacadeScript := preload(
"res://simulation/regional/RegionalSimulationFacade.gd"
)
signal npc_task_changed(npc: SimNPC, old_task: StringName, new_task: StringName)
signal village_changed(village: SimVillage)
@@ -84,6 +87,8 @@ var conflict_system := ConflictSystemScript.new()
var player_quest_system := PlayerQuestSystem.new()
var player_needs := PlayerNeedsSystem.new()
var player_negotiation := PlayerNegotiationSystem.new()
var _regional_simulation: RegionalSimulationFacade
var _restoring_save_manifest := false
var _last_player_tier := PlayerStandingRecord.TIER_STRANGER
var storage_states: Dictionary:
get:
@@ -165,6 +170,9 @@ func _ready() -> void:
clock = SimulationClock.new(tick_interval)
clock.cycle_duration_seconds = cycle_duration_seconds
clock.elapsed_ticks = int(0.25 * cycle_duration_seconds / tick_interval)
if not _initialize_regional_simulation(tick_count):
set_process(false)
return
village.debug_logs = debug_logs
economy.configure(village, debug_logs)
animal_care.configure(economy, active_world_adapter)
@@ -248,7 +256,17 @@ func get_wander_offset(npc_id: int) -> Vector3:
func simulate_tick() -> void:
tick_count += 1
var next_tick := tick_count + 1
if _regional_simulation == null or not _regional_simulation.advance_to_tick(next_tick):
var regional_error := (
_regional_simulation.get_last_error()
if _regional_simulation != null
else "regional facade is missing"
)
push_error("SimulationManager: regional tick failed: " + regional_error)
set_process(false)
return
tick_count = next_tick
if debug_logs:
print("--- Tick ", tick_count, " ---")
animal_care.advance(tick_count)
@@ -2408,6 +2426,34 @@ func get_state_checksum() -> String:
return create_state_record().to_json().sha256_text()
func get_regional_state_checksum() -> String:
return _regional_simulation.checksum() if _regional_simulation != null else ""
func get_world_state_checksum() -> String:
var manifest := create_save_manifest()
return manifest.checksum() if manifest != null else ""
func get_regional_snapshot() -> Dictionary:
return (
_regional_simulation.to_dictionary().duplicate(true) if _regional_simulation != null else {}
)
func request_regional_jajce_delivery(
amount: float = 1.0, presentation_mode: StringName = &"never"
) -> bool:
if (
not uses_combined_save_manifest()
or _regional_simulation == null
or not is_finite(amount)
or amount <= 0.0
):
return false
return _regional_simulation.depart_jajce_delivery(amount, presentation_mode)
func get_latest_decision(npc_id: int) -> ActionSelectionResult:
return latest_decisions.get(npc_id)
@@ -2484,6 +2530,59 @@ func serialize_state() -> String:
return create_state_record().to_json()
func uses_combined_save_manifest() -> bool:
var scope := event_log.get_scope()
return (
StringName(scope.get("world_id", &"")) == SimulationIds.REGIONAL_WORLD_BOSNIA
and (StringName(scope.get("location_id", &"")) == SimulationIds.REGIONAL_LOCATION_JAJCE)
)
func can_serialize_local_state_only() -> bool:
if uses_combined_save_manifest() or _regional_simulation == null:
return false
var idle_facade := RegionalSimulationFacadeScript.create_jajce_route(
simulation_seed, tick_count
)
return idle_facade != null and idle_facade.checksum() == _regional_simulation.checksum()
func create_save_manifest() -> SimulationSaveManifest:
if _regional_simulation == null:
return null
return SimulationSaveManifest.create(create_state_record(), _regional_simulation)
func serialize_save_manifest() -> String:
var manifest := create_save_manifest()
return manifest.to_json() if manifest != null else ""
func restore_save_manifest(manifest: SimulationSaveManifest) -> bool:
if manifest == null:
return false
var local_candidate := manifest.get_local_state()
var regional_candidate := manifest.get_regional_facade()
if local_candidate == null or regional_candidate == null:
return false
var previous_local := create_state_record()
var previous_regional := _regional_simulation
_regional_simulation = regional_candidate
_restoring_save_manifest = true
var restored := restore_state(local_candidate)
_restoring_save_manifest = false
if restored:
_publish_restored_state()
return true
_regional_simulation = previous_regional
_restoring_save_manifest = true
var rolled_back := restore_state(previous_local)
_restoring_save_manifest = false
if not rolled_back:
push_error("SimulationManager: failed to roll back rejected save manifest")
return false
func restore_state_from_json(json_text: String) -> bool:
var record := SimulationStateRecord.from_json(json_text)
if record == null:
@@ -2578,8 +2677,24 @@ func restore_state(record: SimulationStateRecord) -> bool:
conflict_system.register_npc_combatants(npcs)
conflict_system.configure(economy, tick_interval)
_sync_player_combatant()
if not _restoring_save_manifest and not _initialize_regional_simulation(tick_count):
return false
if not _restoring_save_manifest:
_publish_restored_state()
return true
func _publish_restored_state() -> void:
village_changed.emit(village)
state_restored.emit()
func _initialize_regional_simulation(current_tick: int) -> bool:
var facade := RegionalSimulationFacadeScript.create_jajce_route(simulation_seed, current_tick)
if facade == null:
push_error("SimulationManager: could not initialize the Jajce regional facade")
return false
_regional_simulation = facade
return true