Files
gamedev-the-steward/tests/field_note_opportunity_surface_test.gd
T

162 lines
4.8 KiB
GDScript

extends SceneTree
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var main_scene: Node = load("res://main.tscn").instantiate()
root.add_child(main_scene)
await process_frame
for _frame in 10:
await physics_frame
var manager: Node = main_scene.get_node("SimulationManager")
var view: Node = main_scene.get_node("WorldViewManager")
var player: CharacterBody3D = main_scene.get_node("Player")
var demo: Node = main_scene.get_node("DemoController")
var field_note := main_scene.get_node("VillagerInspectionLayer/VillagerFieldNote")
manager.set_process(false)
player.set_physics_process(false)
_freeze_visuals(view)
var staged: Dictionary = demo.call("stage_pantry_crisis")
_check(not staged.is_empty(), "The runtime proof should stage a real open pantry need")
if staged.is_empty():
_finish()
return
var interested: SimNPC = staged["interested"]
var pantry_position: Vector3 = (
StorageNode.get_by_id(SimulationIds.STORAGE_VILLAGE_PANTRY).get_interaction_position()
)
player.global_position = pantry_position
_move_population_away(manager, view, interested.id, pantry_position)
_set_loaded_position(manager, view, interested, pantry_position + Vector3(1.0, 0.0, 0.0))
var checksum_before: String = manager.get_state_checksum()
var serialized_before: String = manager.serialize_state()
var events_before: int = manager.economic_events.size()
for _query in 3:
player.call("get_nearby_villager_inspection")
field_note.call("refresh_note", true)
var need_label := field_note.get_node("Copy/Need") as Label
_check(
(
manager.get_state_checksum() == checksum_before
and manager.serialize_state() == serialized_before
and manager.economic_events.size() == events_before
and field_note.visible
and need_label.visible
and "Need" in need_label.text
),
"The inspected interested villager should surface the real need without mutating state",
)
if not need_label.visible:
_finish()
return
var helper: OpportunityHelperResult = manager.get_active_opportunity_helper()
var helper_npc := _find_npc(manager.npcs, helper.helper_npc_id) if helper != null else null
_check(
(
helper != null
and helper_npc != null
and helper_npc.npc_name in need_label.text
and "bringing" in need_label.text.to_lower()
),
"The open need with a capable helper should name that helper on the field note",
)
var checksum_after_helper: String = manager.get_state_checksum()
var food_node: ResourceNode = _find_food_node(manager)
_check(food_node != null, "The resolution proof needs a stocked player-usable food source")
if food_node == null:
_finish()
return
var harvested: float = manager.harvest_resource_node(food_node)
_check(
(
harvested > 0.0
and manager.get_active_opportunity() == null
and (
manager.opportunity_system.get_latest().get_status()
== OpportunityStateRecord.STATUS_RESOLVED
)
),
"An ordinary finite-resource harvest should resolve the same open need",
)
field_note.call("refresh_note", true)
_check(
not need_label.visible and manager.get_state_checksum() != checksum_after_helper,
"Resolving the need should clear the surfaced need segment from the field note",
)
_finish()
func _find_food_node(manager: Node) -> ResourceNode:
for node in ResourceNode.get_all():
var state: ResourceStateRecord = (
manager.get_resource_state(node.node_id) as ResourceStateRecord
)
if (
node.resource_id == SimulationIds.RESOURCE_FOOD
and state != null
and state.can_player_use_resource()
and state.can_extract()
and state.get_amount_remaining() >= 1.0
):
return node
return null
func _find_npc(npcs: Array[SimNPC], npc_id: int) -> SimNPC:
for npc in npcs:
if npc.id == npc_id:
return npc
return null
func _move_population_away(manager: Node, view: Node, keep_id: int, origin: Vector3) -> void:
for npc in manager.npcs:
if npc.id == keep_id:
continue
_set_loaded_position(
manager, view, npc, origin + Vector3(40.0 + float(npc.id) * 2.0, 0.0, 20.0)
)
func _set_loaded_position(manager: Node, view: Node, npc: SimNPC, position: Vector3) -> void:
manager.synchronize_npc_position(npc.id, position)
var visual := view.active_npc_visuals.get(npc.id) as Node3D
if visual != null:
visual.set_physics_process(false)
if visual.has_method("stop_travel"):
visual.stop_travel()
visual.global_position = position
func _freeze_visuals(view: Node) -> void:
for visual in view.active_npc_visuals.values():
visual.set_physics_process(false)
if visual.has_method("stop_travel"):
visual.stop_travel()
func _check(condition: bool, message: String) -> void:
if not condition:
failures.append(message)
func _finish() -> void:
if failures.is_empty():
print("[TEST] Field-note opportunity surfacing passed")
quit(0)
return
for failure in failures:
push_error("[TEST] " + failure)
quit(1)