174 lines
5.3 KiB
GDScript
174 lines
5.3 KiB
GDScript
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)
|