extends SceneTree ## Headless playthrough of the whole Phase 0 room. ## Run: godot --headless --path . -s test/integration.gd var fails := 0 func check(cond: bool, msg: String) -> void: if cond: print(" ok: " + msg) else: fails += 1 printerr(" FAIL: " + msg) # exposure accrues on physics ticks (real-time), so waits must count # physics frames — process frames run uncapped-fast in headless func wait(frames: int) -> void: for i in frames: await physics_frame func wait_until(cond: Callable, max_frames: int) -> bool: for i in max_frames: if cond.call(): return true await physics_frame return cond.call() func find_i(kind_or_prompt: String) -> Node3D: for n in get_nodes_in_group("interactable"): if str(n.get("kind")) == kind_or_prompt or str(n.get("prompt_text")) == kind_or_prompt: return n return null func _initialize() -> void: root.add_child(load("res://scenes/main.tscn").instantiate()) _run() func _run() -> void: Engine.time_scale = 3.0 # ponytail: same sim, less wall-clock await wait(10) var main := root.get_node("Main") var droid: Droid = main.droid print("== donation ==") var socket := find_i("DONATE WHEEL TO DOOR BUS") socket.interact(droid) check(main.door1.is_open and droid.wheels == 1, "wheel donated, door1 open, droid slow") print("== be the wire ==") droid.global_position = Vector3(0, 0.2, 15.3) droid.core.force_state("charged") check(await wait_until(func(): return main.door2.is_open, 120), "charged droid in the bus gap closes the circuit") var wheel := find_i("TAKE WHEEL") wheel.interact(droid) check(droid.wheels == 2, "replacement wheel reinstalled") print("== electrified puddle ==") droid.global_position = Vector3(-2.6, 0.2, 24) droid.core.force_state("charged") check(await wait_until(func(): return main.puddle_live.enabled, 120), "puddle goes live from droid charge") check(await wait_until(func(): return main.relay.state == "tripped", 300), "live puddle trips sluice relay") check(main.door3.is_open, "relay opens door3") check(droid.shell.state == "wet" or droid.stun > 0, "droid got wet/shocked in the process") droid.global_position = Vector3(0, 0.2, 25) await wait_until(func(): return droid.stun <= 0 and main.puddle_live.enabled == false, 600) print("== leaky bucket ==") var bucket := find_i("bucket") bucket.interact(droid) check(droid.held == bucket, "bucket taken") var tank := find_i("POUR COOLANT") for trip in 2: droid.global_position = Vector3(-2.7, 0.2, 28) check(await wait_until(func(): return bucket.ex.state == "full", 400), "bucket fills at valve (trip %d)" % (trip + 1)) droid.global_position = Vector3(2.4, 0.2, 32.0) await wait(5) tank.interact(droid) check(tank.count == 2 and main.door4.is_open, "two pours open the sluice door") print("== drum rotation ==") # droid is wet from the valve; wet+charged self-shocks (by design) — dry off first droid.shell.force_state("dry") droid.global_position = Vector3(2.9, 0.2, 18.6) droid.core.force_state("charged") check(await wait_until(func(): return main.drum_turned, 180), "charged touch starts the drum") await wait(200) # tween is 3s var crate := find_i("crate") check(crate != null and crate.global_position.y < 2.0 and crate.global_position.x < 6.5, "rotation brought the crate within reach") droid.global_position = Vector3(4.2, 0.2, 22) crate.interact(droid) check(droid.held == crate, "crate taken (bucket dropped)") var plate := find_i("PLACE CARGO ON PLATE") droid.global_position = Vector3(2.0, 0.2, 24.2) plate.interact(droid) check(main.bonus_door.is_open, "cargo plate opens bonus bay") print("== solar exit ==") main.t_sol = 5.0 # daytime droid.global_position = Vector3(0, 0.2, 37) check(await wait_until(func(): return main.exit_door.is_open, 120), "daylight powers solar cell, exit opens") main.t_sol = 45.0 # nightfall check(await wait_until(func(): return not main.exit_door.is_open, 300), "night kills solar cell, exit closes") main.t_sol = 5.0 print("== win ==") await wait_until(func(): return main.exit_door.is_open, 120) droid.global_position = Vector3(0, 0.2, 42) check(await wait_until(func(): return main.won, 60), "crossing the exit wins Phase 0") if fails == 0: print("INTEGRATION PASS — full room playthrough OK") else: printerr("INTEGRATION: %d FAILURES" % fails) quit(1 if fails > 0 else 0)