extends SceneTree ## Movement smoke (windowed): walks the guard to the entrance to check step-up, ## measures precision-walk speed, and checks footstep/landing cues. The guard ## reads captured-mouse input, so run it windowed: ## ## godot --path . --script res://src/test/smoke_movement.gd ## ## Exit code 0 = pass, 1 = failure. const MAX_FRAMES := 1600 var frame := 0 var gm: Node = null var started := false var step := 0 var step_frame := 0 var failures: Array[String] = [] var start_pos := Vector3.ZERO var normal_distance := 0.0 var footsteps_before := 0 func _initialize() -> void: root.size = Vector2i(1280, 720) root.add_child(load("res://scenes/main.tscn").instantiate()) func _process(_delta: float) -> bool: frame += 1 if gm == null: gm = root.find_child("GameManager", true, false) return false if not started: gm.call("_on_venue_selected", "hollywood_building") _check(not gm.guard.input_locked, "venue selection releases the guard") _check(Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED, "venue selection recaptures the mouse") started = true return false if frame > MAX_FRAMES: _fail("timeout before the movement checks finished") return true step_frame += 1 match step: 0: if step_frame < 20: return false _place_guard(8.5) footsteps_before = gm.audio.footsteps start_pos = gm.guard.global_position Input.action_press("move_forward") step = 1 step_frame = 0 1: if step_frame >= 280: Input.action_release("move_forward") var moved := start_pos.distance_to(gm.guard.global_position) _check(moved > 2.0, "guard walked toward the entrance (%.1f m)" % moved) _check(gm.guard.global_position.y > 0.35, "guard climbed the steps (y=%.2f)" % gm.guard.global_position.y) _check(gm.guard.global_position.z < 4.2, "guard reached the steps (z=%.1f)" % gm.guard.global_position.z) _check(gm.audio.footsteps > footsteps_before, "footsteps are emitted while walking") step = 2 step_frame = 0 2: if step_frame < 45: return false _place_guard(8.5) normal_distance = 0.0 start_pos = gm.guard.global_position Input.action_press("move_forward") Input.action_press("walk_slow") step = 3 step_frame = 0 3: if step_frame >= 120: Input.action_release("walk_slow") Input.action_release("move_forward") var moved := start_pos.distance_to(gm.guard.global_position) _check(moved > 1.0 and moved < 4.6, "precision walk covers less ground (%.1f m in 2 s)" % moved) var chip = gm.hud.precision_label _check(chip != null and chip.visible, "precision walk shows its HUD chip") step = 4 step_frame = 0 4: if gm.audio.landings == 0: gm.guard.global_position += Vector3(0, 1.2, 0) if step_frame >= 90: _check(gm.audio.landings > 0, "landing cue fires after a drop") _finish() return true return false func _place_guard(z: float) -> void: gm.guard.global_position = Vector3(0, 0.1, z) gm.guard.rotation.y = 0.0 gm.guard.velocity = Vector3.ZERO func _check(condition: bool, message: String) -> void: if condition: print("ok: " + message) else: _fail(message) func _fail(message: String) -> void: failures.append(message) _finish() func _finish() -> void: Input.action_release("move_forward") Input.action_release("walk_slow") if failures.is_empty(): print("SMOKE OK: movement feel (steps, precision walk, footsteps)") quit(0) else: for message in failures: printerr("SMOKE FAIL: " + message) quit(1)