feat: let villagers witness and trust the player restock
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfl0hnlgwvu8a
|
||||
Reference in New Issue
Block a user