- Link every modal's enabled controls into a wrapping UiFocus ring and give each screen a default focus target. - Add gamepad A/B bindings for ui_accept/ui_cancel; include Start menu footer actions and Continue in its focus ring. - Skip hidden/disabled controls, close focus traps via per-modal cancel behavior, and return review to the menu on cancel. - Record the focus audit and add regression coverage.
56 lines
2.3 KiB
GDScript
56 lines
2.3 KiB
GDScript
extends RefCounted
|
|
## Headless tests for movement constants, surface lookup, gamepad bindings, and
|
|
## the settings that drive the camera feel.
|
|
|
|
const GuardController := preload("res://src/game/guard_controller.gd")
|
|
|
|
func test_surface_lookup_covers_the_block() -> bool:
|
|
var guard := GuardController.new()
|
|
var a := TestUtils.check_eq(guard._surface_at(Vector3(0, 0.2, 2.0)), "stone",
|
|
"the entrance steps are stone")
|
|
var b := TestUtils.check_eq(guard._surface_at(Vector3(3, 0.2, 8.0)), "concrete",
|
|
"the sidewalk is concrete")
|
|
var c := TestUtils.check_eq(guard._surface_at(Vector3(2, 0.1, 18.0)), "asphalt",
|
|
"the roadway is asphalt")
|
|
guard.free()
|
|
return a and b and c
|
|
|
|
func test_step_and_stride_stay_modest() -> bool:
|
|
var a := TestUtils.check(GuardController.STEP_HEIGHT <= 0.35,
|
|
"step-up stays at kerb height")
|
|
var b := TestUtils.check(GuardController.STRIDE_LENGTH >= 0.5
|
|
and GuardController.STRIDE_LENGTH <= 1.2, "stride stays human-sized")
|
|
var c := TestUtils.check(GuardController.SLOW_WALK_FACTOR < 1.0
|
|
and GuardController.SLOW_WALK_FACTOR > 0.3, "precision walk is slower but usable")
|
|
return a and b and c
|
|
|
|
func test_gamepad_bindings_are_added_once() -> bool:
|
|
GuardController._ensure_gamepad_bindings()
|
|
GuardController._ensure_gamepad_bindings()
|
|
var expected := ["look_left", "look_right", "look_up", "look_down",
|
|
"move_forward", "move_back", "move_left", "move_right"]
|
|
var problems: Array[String] = []
|
|
for action in expected:
|
|
if not InputMap.has_action(action):
|
|
problems.append("%s missing" % action)
|
|
continue
|
|
var motions := 0
|
|
for event in InputMap.action_get_events(action):
|
|
if event is InputEventJoypadMotion:
|
|
motions += 1
|
|
if motions == 0:
|
|
problems.append("%s has no joypad axis" % action)
|
|
if InputMap.action_get_deadzone(action) > 0.25:
|
|
problems.append("%s deadzone too high" % action)
|
|
var buttons := {"action_menu": JOY_BUTTON_A, "cancel": JOY_BUTTON_B,
|
|
"ui_accept": JOY_BUTTON_A, "ui_cancel": JOY_BUTTON_B,
|
|
"inspect": JOY_BUTTON_X, "walk_slow": JOY_BUTTON_LEFT_SHOULDER}
|
|
for action in buttons.keys():
|
|
var found := false
|
|
for event in InputMap.action_get_events(action):
|
|
if event is InputEventJoypadButton and event.button_index == buttons[action]:
|
|
found = true
|
|
if not found:
|
|
problems.append("%s has no gamepad button" % action)
|
|
return TestUtils.check(problems.is_empty(), "gamepad actions bound (%s)" % str(problems))
|