153 lines
5.5 KiB
GDScript3
153 lines
5.5 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless gameplay test: run with
|
||
|
|
# godot --headless -s res://tests/test_pivot_controls.gd
|
||
|
|
# Covers the fleet-pivot buttons:
|
||
|
|
# 1. HOLDING one turns the formation continuously, not once per press
|
||
|
|
# 2. a brief tap still produces a clear minimum step
|
||
|
|
# 3. pivoting works while the fleet is under way, i.e. the new layout
|
||
|
|
# survives the joystick's per-frame steering updates instead of being
|
||
|
|
# overwritten by the old offsets
|
||
|
|
|
||
|
|
var scene: Node
|
||
|
|
var fleet: Node
|
||
|
|
var controller: Node
|
||
|
|
var cw: IconButton
|
||
|
|
var stage: int = 0
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
var angle_at_hold_start: float = 0.0
|
||
|
|
var angle_after_hold: float = 0.0
|
||
|
|
# GameState (an autoload) is not available as a bare identifier from a
|
||
|
|
# script that extends SceneTree - fetch it via the tree root instead.
|
||
|
|
var game_state: Node
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
game_state = root.get_node("GameState")
|
||
|
|
game_state.reset_formation()
|
||
|
|
scene = load("res://Main.tscn").instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
fleet = scene.get_node("Fleet")
|
||
|
|
controller = scene
|
||
|
|
cw = scene.get_node("UI/PivotCWButton")
|
||
|
|
|
||
|
|
|
||
|
|
func _angle() -> float:
|
||
|
|
return game_state.formation[1].angle()
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
match stage:
|
||
|
|
0:
|
||
|
|
scene.get_node("AsteroidField").set_process(false)
|
||
|
|
scene.get_node("EnemySpawner").set_process(false)
|
||
|
|
# Hold the button down without releasing it.
|
||
|
|
angle_at_hold_start = _angle()
|
||
|
|
cw.pressed_down = true
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 1
|
||
|
|
return false
|
||
|
|
1:
|
||
|
|
if elapsed < 0.6:
|
||
|
|
return false
|
||
|
|
angle_after_hold = _angle()
|
||
|
|
var turned: float = angle_difference(angle_at_hold_start, angle_after_hold)
|
||
|
|
# 0.6s at PIVOT_SPEED should be far beyond a single tap step.
|
||
|
|
var held_ok: bool = absf(turned) > controller.PIVOT_MIN_STEP * 1.5
|
||
|
|
print("held 0.6s -> turned %.3f rad (min step %.3f) held_ok=%s" % [
|
||
|
|
absf(turned), controller.PIVOT_MIN_STEP, held_ok
|
||
|
|
])
|
||
|
|
if not held_ok:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (holding did not turn continuously)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
cw.pressed_down = false
|
||
|
|
controller.pivot_hold_amount = 0.0
|
||
|
|
|
||
|
|
# --- brief tap ------------------------------------------------
|
||
|
|
var before_tap: float = _angle()
|
||
|
|
controller._finish_pivot_tap(1.0)
|
||
|
|
var tapped: float = absf(angle_difference(before_tap, _angle()))
|
||
|
|
var tap_ok: bool = tapped >= controller.PIVOT_MIN_STEP - 0.001
|
||
|
|
print("tap -> turned %.3f rad tap_ok=%s" % [tapped, tap_ok])
|
||
|
|
if not tap_ok:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (tap did not produce the minimum step)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
# --- pivot WHILE the joystick is held -------------------------
|
||
|
|
# Two fingers at once. This is routed through the same raw-touch
|
||
|
|
# path the real device uses, because Godot's Control GUI treats
|
||
|
|
# touch as one emulated pointer and would not deliver a second
|
||
|
|
# simultaneous finger to a button - the exact reason pivoting
|
||
|
|
# while steering used to do nothing.
|
||
|
|
var joy: TouchJoystick = scene.get_node("UI/Joystick")
|
||
|
|
var stick_down: InputEventScreenTouch = InputEventScreenTouch.new()
|
||
|
|
stick_down.index = 0
|
||
|
|
stick_down.pressed = true
|
||
|
|
stick_down.position = joy.global_position + joy.size / 2.0
|
||
|
|
controller._handle_touch(stick_down)
|
||
|
|
var stick_drag: InputEventScreenDrag = InputEventScreenDrag.new()
|
||
|
|
stick_drag.index = 0
|
||
|
|
stick_drag.position = joy.global_position + joy.size / 2.0 + Vector2(joy.max_radius, 0.0)
|
||
|
|
controller._handle_drag(stick_drag)
|
||
|
|
|
||
|
|
if joy.direction.x < 0.9:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (stick did not engage)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
# Second finger lands on the clockwise pivot button.
|
||
|
|
var pivot_down: InputEventScreenTouch = InputEventScreenTouch.new()
|
||
|
|
pivot_down.index = 1
|
||
|
|
pivot_down.pressed = true
|
||
|
|
pivot_down.position = cw.global_position + cw.size / 2.0
|
||
|
|
controller._handle_touch(pivot_down)
|
||
|
|
|
||
|
|
var both_ok: bool = cw.pressed_down and joy.direction.x > 0.9
|
||
|
|
print("simultaneous: stick=%s pivot_held=%s" % [joy.direction, cw.pressed_down])
|
||
|
|
if not both_ok:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (cannot pivot while steering)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
angle_at_hold_start = _angle()
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 2
|
||
|
|
return false
|
||
|
|
2:
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
# Both fingers still down: the formation must have kept turning
|
||
|
|
# while the stick kept re-issuing move orders every frame.
|
||
|
|
var turned_while_steering: float = absf(angle_difference(angle_at_hold_start, _angle()))
|
||
|
|
print("turned %.3f rad while steering" % turned_while_steering)
|
||
|
|
if turned_while_steering < 0.05:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (no rotation while the stick was held)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
# Steering is re-issued every frame; pivot in the middle of it.
|
||
|
|
fleet.move_in_direction(Vector2.RIGHT)
|
||
|
|
var before_moving_pivot: Vector2 = game_state.formation[1]
|
||
|
|
controller._pivot(PI / 2.0)
|
||
|
|
var after: Vector2 = game_state.formation[1]
|
||
|
|
fleet.move_in_direction(Vector2.RIGHT)
|
||
|
|
|
||
|
|
# The ship's target must reflect the NEW offset even though
|
||
|
|
# steering re-issued move orders straight afterwards.
|
||
|
|
var expected: Vector2 = fleet.fleet_target + after
|
||
|
|
var followed: bool = fleet.ships[1].move_target.distance_to(expected) < 0.01
|
||
|
|
var rotated: bool = after.distance_to(before_moving_pivot.rotated(PI / 2.0)) < 0.01
|
||
|
|
print("moving pivot: %s -> %s rotated=%s ship_target_ok=%s" % [
|
||
|
|
before_moving_pivot, after, rotated, followed
|
||
|
|
])
|
||
|
|
if rotated and followed:
|
||
|
|
print("TEST_PIVOT_CONTROLS: PASS")
|
||
|
|
quit(0)
|
||
|
|
else:
|
||
|
|
print("TEST_PIVOT_CONTROLS: FAIL (pivot lost while moving)")
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
return false
|