merge: integrate remote player quest slices
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
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 journal := main_scene.get_node("QuestJournalLayer/QuestJournal")
|
||||
manager.set_process(false)
|
||||
player.set_physics_process(false)
|
||||
_freeze_visuals(view)
|
||||
|
||||
var actor: SimNPC = manager.npcs[0]
|
||||
var interested: SimNPC = manager.npcs[2]
|
||||
_set_pantry_amount(manager, 1.0)
|
||||
for npc in manager.npcs:
|
||||
_set_loaded_position(manager, view, npc, Vector3(40.0 + npc.id * 10.0, 0.0, 30.0))
|
||||
actor.position = Vector3.ZERO
|
||||
interested.position = Vector3(4.0, 0.0, 0.0)
|
||||
actor.hunger = 60.0
|
||||
interested.hunger = 90.0
|
||||
manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
var quest: PlayerQuestRecord = manager.get_active_player_quest()
|
||||
_check(quest != null, "An unassisted known need should generate a player quest")
|
||||
if quest == null:
|
||||
manager.set_process(true)
|
||||
_finish()
|
||||
return
|
||||
|
||||
_check(
|
||||
(
|
||||
quest.get_requester_npc_id() == interested.id
|
||||
and journal.visible
|
||||
and "Standing" in (journal.get_node("Copy/Standing") as Label).text
|
||||
and "Stranger" in (journal.get_node("Copy/Standing") as Label).text
|
||||
and interested.npc_name in (journal.get_node("Copy/Quest") as Label).text
|
||||
),
|
||||
"The quest journal should surface the standing tier and the named need",
|
||||
)
|
||||
|
||||
var food_node: ResourceNode = _find_food_node(manager)
|
||||
_check(food_node != null, "The runtime proof needs a stocked player-usable food source")
|
||||
if food_node == null:
|
||||
_finish()
|
||||
return
|
||||
var checksum_before: String = manager.get_state_checksum()
|
||||
_check(
|
||||
manager.harvest_resource_node(food_node) > 0.0,
|
||||
"The player should gather the quest resource through the ordinary finite harvest"
|
||||
)
|
||||
_check(
|
||||
manager.player_deposit(SimulationIds.RESOURCE_FOOD) > 0.0,
|
||||
"The player should resolve the quest through the ordinary pantry deposit"
|
||||
)
|
||||
_check(
|
||||
(
|
||||
manager.get_active_player_quest() == null
|
||||
and is_equal_approx(
|
||||
manager.get_player_standing().get_standing(), quest.get_standing_reward()
|
||||
)
|
||||
and is_equal_approx(manager.get_player_standing().get_gratitude(interested.id), 0.1)
|
||||
and manager.get_state_checksum() != checksum_before
|
||||
),
|
||||
"Resolving the need should complete the quest and grant standing + gratitude",
|
||||
)
|
||||
|
||||
var saved_json: String = manager.serialize_state()
|
||||
var restored := _create_restored_manager(saved_json)
|
||||
_check(
|
||||
(
|
||||
restored != null
|
||||
and restored.get_state_checksum() == manager.get_state_checksum()
|
||||
and restored.get_player_standing().get_resolved_needs() == 1
|
||||
and is_equal_approx(
|
||||
restored.get_player_standing().get_standing(), quest.get_standing_reward()
|
||||
)
|
||||
),
|
||||
"Completed quest standing should restore deterministically through the schema",
|
||||
)
|
||||
manager.set_process(true)
|
||||
_finish()
|
||||
|
||||
|
||||
func _create_restored_manager(saved_json: String) -> Node:
|
||||
var restored: Node = load("res://simulation/SimulationManager.gd").new()
|
||||
restored.simulation_seed = 909
|
||||
restored.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),
|
||||
]
|
||||
restored.home_positions = home_positions
|
||||
root.add_child(restored)
|
||||
restored.set_process(false)
|
||||
if not restored.restore_state_from_json(saved_json):
|
||||
return null
|
||||
return restored
|
||||
|
||||
|
||||
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 _prepare_shared_patrol(first: SimNPC, second: SimNPC, position: Vector3) -> void:
|
||||
for npc in [first, second]:
|
||||
npc.set_task(SimulationIds.ACTION_PATROL)
|
||||
npc.target_id = &"guard_post"
|
||||
npc.start_working()
|
||||
first.position = position
|
||||
second.position = position + Vector3(1.0, 0.0, 0.0)
|
||||
|
||||
|
||||
func _set_pantry_amount(manager: Node, amount: float) -> void:
|
||||
var pantry: StorageStateRecord = manager.get_pantry()
|
||||
pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD))
|
||||
pantry.deposit(SimulationIds.RESOURCE_FOOD, amount)
|
||||
manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||
|
||||
|
||||
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] Quest journal runtime surfacing passed")
|
||||
quit(0)
|
||||
return
|
||||
for failure in failures:
|
||||
push_error("[TEST] " + failure)
|
||||
quit(1)
|
||||
Reference in New Issue
Block a user