extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: var manager := _create_manager() var contributor: SimNPC = manager.npcs[0] var observer: SimNPC = manager.npcs[1] var stranger: SimNPC = manager.npcs[2] var relationship: RelationshipStateRecord = manager.relationship_system.get_relationship( observer.id, contributor.id ) _check(relationship != null, "Nearby household NPCs should begin with a directed relationship") if relationship == null: manager.free() _finish() return observer.hunger = 85.0 stranger.hunger = 85.0 contributor.position = Vector3.ZERO observer.position = Vector3(4.0, 0.0, 0.0) stranger.position = Vector3(20.0, 0.0, 0.0) contributor.add_inventory(SimulationIds.RESOURCE_FOOD, 2.0) var deposited: float = manager.economy.deposit_inventory( contributor, SimulationIds.RESOURCE_FOOD ) _check(is_equal_approx(deposited, 2.0), "The real economy path should deposit contributed food") var cause_event: EconomicEventRecord = manager.get_relationship_cause(relationship) var cause_event_id := int(cause_event.data["event_id"]) if cause_event != null else -1 _check( is_equal_approx(relationship.get_trust(), 0.65), "Food aid should raise the hungry familiar NPC's directed trust" ) _check( ( cause_event != null and StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_STORAGE_DEPOSITED and int(cause_event.data["actor_id"]) == contributor.id ), "Trust should reference the exact structured deposit event that caused it" ) _check( ( manager.npc_knows_event(contributor.id, cause_event_id) and manager.npc_knows_event(observer.id, cause_event_id) and not manager.npc_knows_event(stranger.id, cause_event_id) ), "Only the contributor and nearby observer should know the deposit fact" ) var reverse: RelationshipStateRecord = manager.relationship_system.get_relationship( contributor.id, observer.id ) _check( ( reverse != null and is_equal_approx(reverse.get_trust(), RelationshipStateRecord.NEUTRAL_TRUST) ), "Food aid trust should remain directed instead of changing both people implicitly" ) _check( manager.relationship_system.get_relationship(stranger.id, contributor.id) == null, "A distant NPC should not gain trust without an existing familiar relationship" ) var pantry: StorageStateRecord = manager.get_pantry() pantry.withdraw( SimulationIds.RESOURCE_FOOD, maxf( ( pantry.get_amount(SimulationIds.RESOURCE_FOOD) - (ActionSelectionSystem.RELATIONSHIP_AID_PANTRY_THRESHOLD - 5.0) ), 0.0 ) ) manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD) contributor.hunger = 95.0 contributor.is_starving = true observer.profession = SimulationIds.PROFESSION_GUARD observer.hunger = 20.0 observer.energy = 80.0 observer.inventory.clear() observer.last_task = &"" manager.economy.deposit_resource(SimulationIds.RESOURCE_WOOD, 10.0) manager.village.safety = 50.0 manager.village.knowledge = 20.0 manager.village.update_priorities() var neutral_choice: ActionSelectionResult = ActionSelectionSystem.new().select_action( observer, manager.village, 0.5, manager.npcs ) var helping_choice: ActionSelectionResult = manager.action_selector.select_action( observer, manager.village, 0.5, manager.npcs ) _check( neutral_choice.action_id == SimulationIds.ACTION_PATROL, "Without relationship context, the guard should keep doing ordinary patrol work" ) _check( ( helping_choice.action_id == SimulationIds.ACTION_GATHER_FOOD and contributor.npc_name in helping_choice.reason ), "Trust should visibly redirect ordinary work toward helping a starving acquaintance" ) var saved_json: String = manager.serialize_state() var restored := _create_manager(999) _check( restored.restore_state_from_json(saved_json), "Relationship state and its event cause should restore through the world schema" ) var restored_relationship: RelationshipStateRecord = ( restored.relationship_system.get_relationship(observer.id, contributor.id) ) _check( ( restored_relationship != null and is_equal_approx(restored_relationship.get_trust(), relationship.get_trust()) and ( restored_relationship.get_last_trust_cause_event_id() == relationship.get_last_trust_cause_event_id() ) ), "Save/load should preserve directed trust and its causal event identity" ) _check( restored.get_state_checksum() == manager.get_state_checksum(), "Restored relationship state should retain the complete deterministic checksum" ) manager.free() restored.free() _finish() func _finish() -> void: if failures.is_empty(): print("[TEST] Relationship consequence passed: food aid -> trust -> helping choice") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1) func _create_manager(seed_value: int = 801) -> Node: var manager: Node = load("res://simulation/SimulationManager.gd").new() manager.simulation_seed = seed_value manager.debug_logs = false var home_positions: Array[Vector3] = [ Vector3(0.0, 0.0, 0.0), Vector3(1.0, 0.0, 0.0), Vector3(20.0, 0.0, 20.0), Vector3(30.0, 0.0, 30.0), Vector3(40.0, 0.0, 40.0), Vector3(50.0, 0.0, 50.0), ] manager.home_positions = home_positions root.add_child(manager) manager.set_process(false) return manager func _check(condition: bool, message: String) -> void: if not condition: failures.append(message)