extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_prompt_debounce.gd # Verifies the enter-atmosphere prompt is debounced: rapid flicker in the # underlying landing-detection (planet_field.get_planet_fleet_is_on()) # must NOT cause the prompt to flicker on/off - only a raw signal that # stays consistent for prompt_debounce_time in a row should commit a # visible change. Simulates the worst-case flicker by teleporting the # whole fleet back and forth across the planet's landing_radius boundary # every single frame (bypassing actual ship steering, for a fully # deterministic repro). var main_scene: Node var fleet: Node var planet_field: Node var enter_button: IconButton var target_chunk: Vector2i var planet_pos: Vector2 var landing_radius: float var flicker_frames: int = 0 var visible_toggle_count: int = 0 var last_visible_state: bool = false var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: var found: bool = false for x in range(-15, 15): for y in range(-15, 15): var chunk: Vector2i = Vector2i(x, y) if WorldGen.has_planet_at_chunk(chunk): target_chunk = chunk found = true break if found: break if not found: print("FAIL: could not find any chunk with a planet to test with") quit(1) return var packed: PackedScene = load("res://Main.tscn") main_scene = packed.instantiate() root.add_child(main_scene) fleet = main_scene.get_node("Fleet") planet_field = main_scene.get_node("PlanetField") enter_button = main_scene.get_node("UI/BuildActionButton") main_scene.get_node("AsteroidField").set_process(false) main_scene.get_node("EnemySpawner").set_process(false) func _process(delta: float) -> bool: elapsed += delta match stage: 0: var vicinity_pos: Vector2 = WorldGen.chunk_origin(target_chunk) + Vector2.ONE * WorldGen.CHUNK_SIZE * 0.5 for ship in fleet.ships: ship.position = vicinity_pos fleet.move_to(vicinity_pos) elapsed = 0.0 stage = 1 return false 1: if elapsed < planet_field.check_interval + 0.1: return false if not planet_field.populated_chunks.has(target_chunk): print("TEST_PROMPT_DEBOUNCE: FAIL (planet never spawned)") quit(1) return true var planet: Node2D = planet_field.populated_chunks[target_chunk][0] planet_pos = planet.position landing_radius = planet.landing_radius last_visible_state = enter_button.visible elapsed = 0.0 stage = 2 return false 2: # Teleport the fleet's centroid just inside, then just outside, # the landing radius every single frame - the raw detection # flips every frame, so the debounced prompt must never react # to this at all. var inside_pos: Vector2 = planet_pos + Vector2(landing_radius - 5.0, 0.0) var outside_pos: Vector2 = planet_pos + Vector2(landing_radius + 5.0, 0.0) var target_pos: Vector2 = inside_pos if (flicker_frames % 2 == 0) else outside_pos for ship in fleet.ships: ship.position = target_pos flicker_frames += 1 if enter_button.visible != last_visible_state: visible_toggle_count += 1 last_visible_state = enter_button.visible if flicker_frames < 20: return false print("visible_toggle_count=%d (should be 0)" % visible_toggle_count) if visible_toggle_count == 0: print("TEST_PROMPT_DEBOUNCE: PASS") quit(0) else: print("TEST_PROMPT_DEBOUNCE: FAIL (prompt flickered despite debounce)") quit(1) return true return false