extends SceneTree # Headless test: run with # godot --headless -s res://tests/test_desktop_controls.gd # Forgejo #118 and #121: two raw-touch controls that a mouse could not # reach. # # Both read InputEventScreenTouch/ScreenDrag, and the project no longer turns # clicks into them - the same routing gap #93 found in the research tree. # The boost was unreachable on a desktop and the formation could not be # rearranged at all. # # Driven through the REAL routing in each case (GameController for the # fader, BuildController for the editor) rather than by calling the controls # directly, because the routing is what was missing - a test that called # handle_mouse_button() itself would have passed against both reports. # # Covers: # A. #118 - a click on the fader takes hold of it, dragging moves the # boost, and releasing lets go # B. the fader claims the press, so dragging the boost does not also zoom # the world behind it # C. #121 - a click grabs a formation slot, moving drags it, and the # formation really changes # D. releasing drops the slot, and moving afterwards does not keep dragging # E. the wheel zooms the formation panel, which is what replaces the pinch var game_state: Node var scene: Node var controller: Node var fleet: Node var fader: Control var editor: Control var formation_menu: Control var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: # GameState is an autoload: a bare GameState. does not # compile in a SceneTree script, so the node is fetched by name instead. game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.reset_progression() game_state.reset_waves() game_state.reset_formation() game_state.intro_active = false game_state.vessel_levels["unlock_distorder"] = 1 scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) current_scene = scene fleet = scene.get_node("Fleet") controller = scene.get_node("BuildController") fader = scene.get_node("UI/SpeedFader") editor = scene.get_node("UI/FormationMenu/Editor") formation_menu = scene.get_node("UI/FormationMenu") func _fail(message: String) -> bool: print("TEST_DESKTOP_CONTROLS: FAIL (%s)" % message) quit(1) return true func _click(at: Vector2, pressed: bool, target: Node) -> void: var event: InputEventMouseButton = InputEventMouseButton.new() event.button_index = MOUSE_BUTTON_LEFT event.pressed = pressed event.position = at target._unhandled_input(event) func _move(to: Vector2, target: Node) -> void: var event: InputEventMouseMotion = InputEventMouseMotion.new() event.position = to target._unhandled_input(event) func _wheel(button: int, at: Vector2) -> void: var event: InputEventMouseButton = InputEventMouseButton.new() event.button_index = button event.pressed = true event.position = at controller._unhandled_input(event) func _process(delta: float) -> bool: elapsed += delta match stage: 0: if elapsed < 0.3: return false scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) # The fader only exists where the fleet can actually boost. fleet.spawn_ships(2, Vector2.ZERO, PackedStringArray([ GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_DISTORDER])) elapsed = 0.0 stage = 1 return false 1: if elapsed < 0.3: return false if not fader.visible: return _fail("the fleet has an Energy Propulser and the fader is still hidden") if game_state.max_fleet_speed_multiplier <= 1.0: return _fail("the boost has no range to move through") # --- A. click, drag, release ----------------------------- var top: Vector2 = fader.global_position + Vector2(fader.size.x * 0.5, fader.track_margin + 2.0) var bottom: Vector2 = fader.global_position + Vector2(fader.size.x * 0.5, fader.size.y - fader.track_margin - 2.0) _click(bottom, true, scene) if not fader.is_active(): return _fail("a click on the fader did not take hold of it") var at_bottom: float = game_state.fleet_speed_multiplier _move(top, scene) var at_top: float = game_state.fleet_speed_multiplier if at_top <= at_bottom: return _fail("dragging the fader from bottom to top left the boost at %.2f" % at_top) _click(top, false, scene) if fader.is_active(): return _fail("the fader is still held after the button came up") _move(bottom, scene) if not is_equal_approx(game_state.fleet_speed_multiplier, at_top): return _fail("the fader kept following the pointer after release") print("fader: %.2fx at the bottom, %.2fx at the top, released cleanly" \ % [at_bottom, at_top]) # --- B. it claims the press ------------------------------ var zoom_before: float = scene.get_node("Camera2D").zoom.x _click(bottom, true, scene) _click(bottom, false, scene) if not is_equal_approx(scene.get_node("Camera2D").zoom.x, zoom_before): return _fail("pressing the fader also moved the camera behind it") print("the press stops at the fader; the world behind it is untouched") # --- C/D/E. the formation editor ------------------------- formation_menu.visible = true editor.queue_redraw() elapsed = 0.0 stage = 2 return false 2: if elapsed < 0.2: return false var slot_before: Vector2 = game_state.formation[0] var grab: Vector2 = editor.global_position + editor._slot_screen_pos(0) _click(grab, true, controller) if editor.dragging_index != 0: return _fail("a click on the first formation slot grabbed %d" % editor.dragging_index) _move(grab + Vector2(70.0, 40.0), controller) if game_state.formation[0] == slot_before: return _fail("dragging the slot did not move it") var dragged: Vector2 = game_state.formation[0] print("slot 0 moved from %s to %s under a mouse" % [str(slot_before), str(dragged)]) # --- D. release lets go ---------------------------------- _click(grab + Vector2(70.0, 40.0), false, controller) if editor.dragging_index != -1: return _fail("the slot is still held after the button came up") _move(grab + Vector2(200.0, 200.0), controller) if game_state.formation[0] != dragged: return _fail("the slot kept following the pointer after release") print("released, and the slot stopped following") # --- E. the wheel zooms ---------------------------------- var scale_before: float = editor.scale_factor _wheel(MOUSE_BUTTON_WHEEL_UP, editor.global_position + editor.size * 0.5) if editor.scale_factor <= scale_before: return _fail("the wheel did not zoom the formation panel in") _wheel(MOUSE_BUTTON_WHEEL_DOWN, editor.global_position + editor.size * 0.5) _wheel(MOUSE_BUTTON_WHEEL_DOWN, editor.global_position + editor.size * 0.5) if editor.scale_factor >= scale_before: return _fail("the wheel did not zoom back out") print("the wheel zooms the panel both ways (%.2f -> %.2f)" \ % [scale_before, editor.scale_factor]) print("TEST_DESKTOP_CONTROLS: PASS") quit(0) return true return false