extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: var manager := _create_manager() var witness: SimNPC = manager.npcs[0] var hungry_witness: SimNPC = manager.npcs[1] var distant: SimNPC = manager.npcs[2] witness.hunger = 20.0 hungry_witness.hunger = 85.0 witness.position = Vector3(4.0, 0.0, 0.0) hungry_witness.position = Vector3(5.0, 0.0, 0.0) distant.position = Vector3(40.0, 0.0, 0.0) var berry := ResourceNode.new() berry.name = "PlayerSocialBerry" berry.node_id = &"player_social_berry" berry.action_id = SimulationIds.ACTION_GATHER_FOOD berry.resource_id = SimulationIds.RESOURCE_FOOD berry.initial_amount = 1.0 berry.yield_per_action = 1.0 berry.debug_label_enabled = false var interaction_point := Marker3D.new() interaction_point.name = "InteractionPoint" interaction_point.position = Vector3.ZERO berry.add_child(interaction_point) root.add_child(berry) await process_frame _check( manager.register_resource_node(berry), "Player supply test should bind a real finite ResourceNode" ) _check( is_equal_approx(manager.harvest_resource_node(berry), 1.0), "Player harvesting should supply the real pantry" ) var supply_event := _latest_event_of_type(manager, SimulationIds.EVENT_RESOURCE_EXTRACTED) var supply_event_id := int(supply_event.data["event_id"]) _check( ( int(supply_event.data["actor_id"]) == SimulationIds.PLAYER_ACTOR_ID and ( StringName(supply_event.data["destination_id"]) == SimulationIds.STORAGE_VILLAGE_PANTRY ) ), "The player supply should be recorded as a pantry food extraction by the player actor" ) _check( ( manager.npc_knows_event(witness.id, supply_event_id) and manager.npc_knows_event(hungry_witness.id, supply_event_id) and not manager.npc_knows_event(distant.id, supply_event_id) ), "Nearby living villagers should witness the player restock while distant ones stay unaware" ) var calm_relationship: RelationshipStateRecord = manager.relationship_system.get_relationship( witness.id, SimulationIds.PLAYER_ACTOR_ID ) _check( calm_relationship == null, "A non-hungry witness should not gain trust toward the player" ) var hungry_relationship: RelationshipStateRecord = manager.relationship_system.get_relationship( hungry_witness.id, SimulationIds.PLAYER_ACTOR_ID ) _check( ( hungry_relationship != null and is_equal_approx(hungry_relationship.get_familiarity(), 0.5) and is_equal_approx(hungry_relationship.get_trust(), 0.65) and hungry_relationship.get_last_trust_cause_event_id() == supply_event_id ), "A hungry witness should gain directed trust toward the player from the real supply event" ) var saved_json: String = manager.serialize_state() var restored := _create_manager(902) _check( restored.restore_state_from_json(saved_json), "A player relationship should survive schema-v12 save and restore" ) _check( ( restored.get_state_checksum() == manager.get_state_checksum() and restored.npc_knows_event(hungry_witness.id, supply_event_id) and ( restored.relationship_system.get_relationship( hungry_witness.id, SimulationIds.PLAYER_ACTOR_ID ) != null ) ), "Restored player trust should preserve checksum, evidence, and the directed relationship" ) restored.free() berry.queue_free() manager.free() var legacy_v11 := _create_v11_legacy() var migrated := SimulationStateRecord.from_dictionary(legacy_v11) _check( ( migrated != null and migrated.simulation.get("next_opportunity_id", -1) >= 0 and not migrated.opportunities.is_empty() ), "World schema v11 should migrate to v12 without dropping opportunity history" ) _finish() func _create_v11_legacy() -> Dictionary: var manager := _create_manager(901) var pantry: StorageStateRecord = manager.get_pantry() pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD)) pantry.deposit(SimulationIds.RESOURCE_FOOD, 1.0) manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD) var actor: SimNPC = manager.npcs[0] actor.position = Vector3.ZERO actor.hunger = 85.0 manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0) manager.economy.deposit_inventory(actor, SimulationIds.RESOURCE_FOOD) var record: Dictionary = manager.create_state_record().to_dictionary() record["schema_version"] = 11 manager.free() return record func _create_manager(seed_value: int = 901) -> 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(2.0, 0.0, 0.0), Vector3(10.0, 0.0, 0.0), Vector3(12.0, 0.0, 0.0), Vector3(30.0, 0.0, 30.0), Vector3(40.0, 0.0, 40.0), ] manager.home_positions = home_positions root.add_child(manager) manager.set_process(false) return manager func _latest_event_of_type(manager: Node, event_type: StringName) -> EconomicEventRecord: var events: Array = manager.economic_events for index in range(events.size() - 1, -1, -1): var event := events[index] as EconomicEventRecord if StringName(event.data["event_type"]) == event_type: return event return null func _finish() -> void: if failures.is_empty(): print("[TEST] Player social consequence passed: supply -> witness -> trust") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1) func _check(condition: bool, message: String) -> void: if not condition: failures.append(message)