138 lines
5 KiB
GDScript3
138 lines
5 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless test: run with
|
||
|
|
# godot --headless -s res://tests/test_input_after_dialog.gd
|
||
|
|
# Forgejo #153: "after a dialog popup the godot input system is lost on
|
||
|
|
# keypress. If I had a key held it keeps moving in the key direction."
|
||
|
|
#
|
||
|
|
# Nothing was lost. A dialog stops the tree (#128) and a paused node receives
|
||
|
|
# no input, so a key RELEASED while the line was up never reached the
|
||
|
|
# controller - keys_down kept it, and the fleet flew on in that direction
|
||
|
|
# once the game restarted. The keyboard is the authority on what is held;
|
||
|
|
# keys_down is only a record of what we were told, and it went stale.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. holding a key steers, as it always did
|
||
|
|
# B. a release that arrives normally is honoured
|
||
|
|
# C. the bug: a key held when a dialog opens, released while it is up -
|
||
|
|
# the release never arrives, and the fleet must still stop
|
||
|
|
# D. and it is the pause LIFTING that does it, not the dismissal itself,
|
||
|
|
# so anything else that stops the tree is covered too
|
||
|
|
#
|
||
|
|
# Forgejo #154 rides along here, being the same scene: a dialog has to be
|
||
|
|
# drawn over every panel, and it was drawn under the skill tree.
|
||
|
|
|
||
|
|
const KEY_UP: Key = KEY_W
|
||
|
|
|
||
|
|
var game_state: Node
|
||
|
|
var scene: Node
|
||
|
|
var controller: Node
|
||
|
|
var narration: Control
|
||
|
|
var stage: int = 0
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
# GameState is an autoload: a bare GameState.<instance member> does not
|
||
|
|
# compile in a SceneTree script, so the node is fetched by name instead.
|
||
|
|
game_state = root.get_node("GameState")
|
||
|
|
game_state.reset_new_game()
|
||
|
|
game_state.intro_active = false
|
||
|
|
game_state.reset_quests()
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> bool:
|
||
|
|
print("TEST_INPUT_AFTER_DIALOG: FAIL (%s)" % message)
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
|
||
|
|
func _key(keycode: Key, pressed: bool) -> void:
|
||
|
|
var event: InputEventKey = InputEventKey.new()
|
||
|
|
event.keycode = keycode
|
||
|
|
event.pressed = pressed
|
||
|
|
controller._handle_key(event)
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
match stage:
|
||
|
|
0:
|
||
|
|
scene = load("res://world/Main.tscn").instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
current_scene = scene
|
||
|
|
controller = scene
|
||
|
|
narration = scene.get_node("UI/NarrationBanner")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 1
|
||
|
|
return false
|
||
|
|
1:
|
||
|
|
if elapsed < 0.3:
|
||
|
|
return false
|
||
|
|
|
||
|
|
# --- A. holding a key steers ----------------------------
|
||
|
|
_key(KEY_UP, true)
|
||
|
|
if controller.keyboard_direction() == Vector2.ZERO:
|
||
|
|
return _fail("holding a key steers nowhere")
|
||
|
|
print("held key steers %s" % controller.keyboard_direction())
|
||
|
|
|
||
|
|
# --- B. and a release that arrives is honoured ----------
|
||
|
|
_key(KEY_UP, false)
|
||
|
|
if controller.keyboard_direction() != Vector2.ZERO:
|
||
|
|
return _fail("a delivered release did not stop the steering")
|
||
|
|
print("a delivered release stops it")
|
||
|
|
|
||
|
|
# --- C. the bug ------------------------------------------
|
||
|
|
# Held, then a dialog opens. The player lets go while reading;
|
||
|
|
# that release is never delivered, because this node is paused.
|
||
|
|
_key(KEY_UP, true)
|
||
|
|
narration.say("Let go while you read this.", "TEST")
|
||
|
|
if not paused:
|
||
|
|
return _fail("the dialog did not stop the game, so this proves nothing")
|
||
|
|
if controller.keyboard_direction() == Vector2.ZERO:
|
||
|
|
return _fail("the key was already forgotten before the dialog closed")
|
||
|
|
# ...the release happens here, and reaches nobody.
|
||
|
|
narration.finish_typing()
|
||
|
|
if not narration.dismiss():
|
||
|
|
return _fail("the dialog would not close")
|
||
|
|
if paused:
|
||
|
|
return _fail("closing the dialog left the game stopped")
|
||
|
|
# NOTIFICATION_UNPAUSED is delivered as part of lifting the
|
||
|
|
# pause, so by now the stale key must be gone.
|
||
|
|
if controller.keyboard_direction() != Vector2.ZERO:
|
||
|
|
return _fail("the fleet is still steering %s after a release that was never delivered" \
|
||
|
|
% controller.keyboard_direction())
|
||
|
|
if controller.keys_down.has(KEY_UP):
|
||
|
|
return _fail("the key is still recorded as held")
|
||
|
|
print("a release swallowed by the pause no longer strands the fleet")
|
||
|
|
|
||
|
|
# --- D. by the pause lifting, not by the dismissal -------
|
||
|
|
_key(KEY_UP, true)
|
||
|
|
paused = true
|
||
|
|
paused = false
|
||
|
|
if controller.keyboard_direction() != Vector2.ZERO:
|
||
|
|
return _fail("stopping and restarting the tree left a stale key held")
|
||
|
|
print("any pause, not just a dialog, reconciles what is held")
|
||
|
|
|
||
|
|
# --- #154. a dialog draws over every panel ---------------
|
||
|
|
var ui: Node = scene.get_node("UI")
|
||
|
|
var banner_at: int = narration.get_index()
|
||
|
|
var panels: Array = []
|
||
|
|
for menu in get_nodes_in_group("modal_menus"):
|
||
|
|
if ui.is_ancestor_of(menu):
|
||
|
|
panels.append(menu)
|
||
|
|
if panels.is_empty():
|
||
|
|
return _fail("no modal panels found to be drawn over")
|
||
|
|
for menu in panels:
|
||
|
|
if menu.get_index() > banner_at:
|
||
|
|
return _fail("%s is a later child than the dialog, so it draws on top of it" \
|
||
|
|
% menu.name)
|
||
|
|
if banner_at != ui.get_child_count() - 1:
|
||
|
|
return _fail("the dialog is child %d of %d - something still draws over it" \
|
||
|
|
% [banner_at, ui.get_child_count()])
|
||
|
|
print("the dialog is the last child of the UI layer, over all %d panels" % panels.size())
|
||
|
|
|
||
|
|
print("TEST_INPUT_AFTER_DIALOG: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
return false
|