#128: a dialog stops the game until it is acknowledged
While a line is on the banner the whole tree is paused, which freezes the fleet, the enemies and GameState's own production and wave clocks, and stops input reaching any node but the banner. So a press while a dialog is up acknowledges it and does nothing else - it cannot also steer the fleet or open a menu on the way through. The pause is owned by NarrationBanner rather than by its callers: the thing that must not get out of step is "a line is showing" and "the game is stopped", and the banner is the only code that knows the first. It releases on the last line, on clear(), and on leaving the tree - a scene swapped out mid-line must not leave the game stopped with nothing left to start it. Two consequences worth naming. A beat's deadline in the prologue is now game time, so it can no longer run out from under an unread line; it remains the way past a beat the player will not finish. And "the prologue leaves you movement and nothing else" was applied by _process on the first frame, which no longer comes - the prologue opens ON a dialog - so BuildController and WorldMap apply it at _ready too.
This commit is contained in:
parent
902d6922e7
commit
1e89780bd7
7 changed files with 330 additions and 10 deletions
|
|
@ -325,6 +325,13 @@ func _ready() -> void:
|
||||||
transfer_button.visible = false
|
transfer_button.visible = false
|
||||||
hangar_button.visible = false
|
hangar_button.visible = false
|
||||||
hangar_menu.visible = false
|
hangar_menu.visible = false
|
||||||
|
# #128: the prologue's "movement and nothing else" used to be applied by
|
||||||
|
# _process on the first frame. A dialog now stops the tree, and the
|
||||||
|
# prologue opens ON one - so that first frame never came and every action
|
||||||
|
# button was left on screen behind the opening line. Applied here as well,
|
||||||
|
# where it does not depend on the game running.
|
||||||
|
if GameState.intro_active:
|
||||||
|
_hide_all_actions()
|
||||||
_layout_context_buttons()
|
_layout_context_buttons()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
208
tests/test_dialog_pause.gd
Normal file
208
tests/test_dialog_pause.gd
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
# Headless test: run with
|
||||||
|
# godot --headless -s res://tests/test_dialog_pause.gd
|
||||||
|
# Forgejo #128: "When a dialog appears, no command should work and the game
|
||||||
|
# physics should be paused as long as the message is not acknowledged."
|
||||||
|
#
|
||||||
|
# Two claims, and they are checked separately because they can fail
|
||||||
|
# separately: the world must STOP, and the controls must not ANSWER. Presses
|
||||||
|
# are pushed through the viewport rather than handed to a handler directly -
|
||||||
|
# calling a handler by hand would prove nothing about whether the engine
|
||||||
|
# would have delivered the event at all, which is the whole question here.
|
||||||
|
#
|
||||||
|
# What a press does while a dialog is up is ACKNOWLEDGE it (#127) and
|
||||||
|
# nothing else. So "no command works" is not "the press vanishes": it is
|
||||||
|
# "the press reached the dialog and reached nothing else", and that is what
|
||||||
|
# is asserted below.
|
||||||
|
#
|
||||||
|
# Covers:
|
||||||
|
# A. a line goes up and the tree is paused
|
||||||
|
# B. the world is frozen: the fleet holds still under a movement order
|
||||||
|
# C. GameState's own clock is frozen too - the wave clock does not run
|
||||||
|
# D. no command works: a press on a HUD button, delivered the way a finger
|
||||||
|
# delivers one, acknowledges the dialog and does not fire the button
|
||||||
|
# E. queued lines hold the pause until the LAST one is acknowledged
|
||||||
|
# F. after the last one, the world runs and the same press does fire
|
||||||
|
# G. leaving the tree with a line up does not leave the game stopped with
|
||||||
|
# nothing left that could start it again
|
||||||
|
|
||||||
|
var game_state: Node
|
||||||
|
var scene: Node
|
||||||
|
var fleet: Node2D
|
||||||
|
var narration: Control
|
||||||
|
var button: Control
|
||||||
|
var build_menu: Control
|
||||||
|
var presses: int = 0
|
||||||
|
var stage: int = 0
|
||||||
|
var elapsed: float = 0.0
|
||||||
|
var frozen_at: Vector2 = Vector2.ZERO
|
||||||
|
var frozen_wave_timer: 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()
|
||||||
|
# Not the prologue. The prologue has its own dialog coverage
|
||||||
|
# (test_intro_scene); this is about dialogs in the ordinary game, where
|
||||||
|
# there are commands to block in the first place.
|
||||||
|
game_state.intro_active = false
|
||||||
|
# And no quest chain. Quest briefings are dialogs too (#132), and this
|
||||||
|
# test is about what a dialog DOES - it needs to be the one deciding
|
||||||
|
# when one appears.
|
||||||
|
game_state.reset_quests()
|
||||||
|
game_state.rocks = 500
|
||||||
|
|
||||||
|
|
||||||
|
func _fail(message: String) -> bool:
|
||||||
|
print("TEST_DIALOG_PAUSE: FAIL (%s)" % message)
|
||||||
|
quit(1)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _on_pressed() -> void:
|
||||||
|
presses += 1
|
||||||
|
|
||||||
|
|
||||||
|
# A press delivered the way the player delivers one: through the viewport, at
|
||||||
|
# the button's own screen rect, so the engine's routing decides whether it
|
||||||
|
# arrives rather than the test deciding for it.
|
||||||
|
func _press_button() -> void:
|
||||||
|
var at: Vector2 = button.get_global_rect().get_center()
|
||||||
|
for is_down in [true, false]:
|
||||||
|
var event: InputEventMouseButton = InputEventMouseButton.new()
|
||||||
|
event.button_index = MOUSE_BUTTON_LEFT
|
||||||
|
event.pressed = is_down
|
||||||
|
event.position = at
|
||||||
|
event.global_position = at
|
||||||
|
# in_local_coords, deliberately. The other form runs the position
|
||||||
|
# through the window's screen transform, and headless has no window -
|
||||||
|
# the event then lands nowhere and the button looks broken.
|
||||||
|
root.push_input(event, true)
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> bool:
|
||||||
|
elapsed += delta
|
||||||
|
match stage:
|
||||||
|
0:
|
||||||
|
scene = load("res://world/Main.tscn").instantiate()
|
||||||
|
root.add_child(scene)
|
||||||
|
current_scene = scene
|
||||||
|
fleet = scene.get_node("Fleet")
|
||||||
|
narration = scene.get_node("UI/NarrationBanner")
|
||||||
|
button = scene.get_node("UI/BuildButton")
|
||||||
|
build_menu = scene.get_node("UI/BuildMenu")
|
||||||
|
button.pressed.connect(_on_pressed)
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 1
|
||||||
|
return false
|
||||||
|
1:
|
||||||
|
if elapsed < 0.4:
|
||||||
|
return false
|
||||||
|
if paused:
|
||||||
|
return _fail("the game was already stopped before any dialog")
|
||||||
|
# The button answers when nothing is in the way. Without this
|
||||||
|
# baseline, D below would prove only that the button was
|
||||||
|
# unreachable all along.
|
||||||
|
presses = 0
|
||||||
|
_press_button()
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 2
|
||||||
|
return false
|
||||||
|
2:
|
||||||
|
if elapsed < 0.2:
|
||||||
|
return false
|
||||||
|
if presses == 0:
|
||||||
|
return _fail("the build button does not answer a press even with no dialog up")
|
||||||
|
print("baseline: a press on the build button fires it")
|
||||||
|
# It opened the build menu, which is modal and would block every
|
||||||
|
# later press for a reason that has nothing to do with #128.
|
||||||
|
build_menu.visible = false
|
||||||
|
|
||||||
|
# --- A. a line stops the game ----------------------------
|
||||||
|
narration.say("Hold position.", "TEST")
|
||||||
|
narration.say("And stand by.", "TEST")
|
||||||
|
if not paused:
|
||||||
|
return _fail("a dialog went up and the game kept running")
|
||||||
|
print("dialog up: the tree is paused")
|
||||||
|
|
||||||
|
frozen_at = fleet.get_centroid()
|
||||||
|
frozen_wave_timer = game_state.wave_timer
|
||||||
|
# Ordered somewhere it is nowhere near, so even a little drift
|
||||||
|
# would show up. steer_direction persists, so this order stands
|
||||||
|
# for the whole frozen second below.
|
||||||
|
fleet.move_in_direction(Vector2.RIGHT)
|
||||||
|
presses = 0
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 3
|
||||||
|
return false
|
||||||
|
3:
|
||||||
|
# --- B and C. a whole second of frozen world -------------
|
||||||
|
if elapsed < 1.0:
|
||||||
|
return false
|
||||||
|
var moved: float = fleet.get_centroid().distance_to(frozen_at)
|
||||||
|
if moved > 0.5:
|
||||||
|
return _fail("the fleet drifted %.1fpx while a dialog was up" % moved)
|
||||||
|
print("fleet held still for 1s under a movement order (%.2fpx)" % moved)
|
||||||
|
var ran: float = absf(game_state.wave_timer - frozen_wave_timer)
|
||||||
|
if ran > 0.001:
|
||||||
|
return _fail("the wave clock ran on %.2fs while a dialog was up" % ran)
|
||||||
|
print("the wave clock did not advance")
|
||||||
|
|
||||||
|
# --- D. the press acknowledges, and does nothing else ----
|
||||||
|
_press_button()
|
||||||
|
if presses != 0:
|
||||||
|
return _fail("the build button fired while a dialog was up")
|
||||||
|
if build_menu.visible:
|
||||||
|
return _fail("the build menu opened from a press meant for the dialog")
|
||||||
|
print("a press while the dialog was up did not reach the button")
|
||||||
|
|
||||||
|
# --- E. the queued line keeps the game stopped -----------
|
||||||
|
if not narration.is_speaking():
|
||||||
|
return _fail("one press acknowledged both queued lines")
|
||||||
|
if not paused:
|
||||||
|
return _fail("the game restarted with a line still on screen")
|
||||||
|
print("a second queued line keeps the game stopped")
|
||||||
|
|
||||||
|
_press_button()
|
||||||
|
if presses != 0:
|
||||||
|
return _fail("the build button fired on the acknowledging press")
|
||||||
|
if narration.is_speaking():
|
||||||
|
return _fail("the banner is still speaking after the last line")
|
||||||
|
if paused:
|
||||||
|
return _fail("the last line was acknowledged and the game is still stopped")
|
||||||
|
print("last line acknowledged: the game is running again")
|
||||||
|
|
||||||
|
frozen_at = fleet.get_centroid()
|
||||||
|
presses = 0
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 4
|
||||||
|
return false
|
||||||
|
4:
|
||||||
|
# --- F. and the world answers again ----------------------
|
||||||
|
if elapsed < 0.4:
|
||||||
|
return false
|
||||||
|
if fleet.get_centroid().distance_to(frozen_at) < 1.0:
|
||||||
|
return _fail("the fleet is still frozen after the dialog closed")
|
||||||
|
_press_button()
|
||||||
|
if presses == 0:
|
||||||
|
return _fail("the build button stayed dead after the dialog closed")
|
||||||
|
build_menu.visible = false
|
||||||
|
print("the world moves again: the fleet flies and the button fires")
|
||||||
|
|
||||||
|
# --- G. torn down mid-line ------------------------------
|
||||||
|
narration.say("Interrupted.", "TEST")
|
||||||
|
if not paused:
|
||||||
|
return _fail("the dialog did not stop the game the second time")
|
||||||
|
scene.free()
|
||||||
|
scene = null
|
||||||
|
if paused:
|
||||||
|
return _fail("the scene was torn down mid-dialog and left the game stopped")
|
||||||
|
print("a scene torn down mid-dialog leaves the game running")
|
||||||
|
|
||||||
|
print("TEST_DIALOG_PAUSE: PASS")
|
||||||
|
quit(0)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
1
tests/test_dialog_pause.gd.uid
Normal file
1
tests/test_dialog_pause.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://clgtbnc7pfxbd
|
||||||
|
|
@ -29,6 +29,9 @@ extends SceneTree
|
||||||
# N. #127 - a dialog stays up until it is dismissed: no timer closes it,
|
# N. #127 - a dialog stays up until it is dismissed: no timer closes it,
|
||||||
# a click does, and the click is eaten rather than also steering
|
# a click does, and the click is eaten rather than also steering
|
||||||
# O. #127 - every dialog names its speaker
|
# O. #127 - every dialog names its speaker
|
||||||
|
# P. #128 - the prologue opens STOPPED behind its first line, and
|
||||||
|
# acknowledging it is what starts the world
|
||||||
|
# Q. #133 - the handover runs straight into the first quest
|
||||||
|
|
||||||
var game_state: Node
|
var game_state: Node
|
||||||
var scene: Node
|
var scene: Node
|
||||||
|
|
@ -105,6 +108,14 @@ func _process(delta: float) -> bool:
|
||||||
return _fail("the line changed on its own after 10s - #127 asks for a click")
|
return _fail("the line changed on its own after 10s - #127 asks for a click")
|
||||||
if not narration.visible:
|
if not narration.visible:
|
||||||
return _fail("the banner closed itself")
|
return _fail("the banner closed itself")
|
||||||
|
# --- P. and the game is STOPPED behind it (#128) ---------
|
||||||
|
if not paused:
|
||||||
|
return _fail("the prologue opened on a line with the game still running")
|
||||||
|
if director.beat_time > 0.01:
|
||||||
|
return _fail("the director's clock ran on %.2fs behind an unread line" \
|
||||||
|
% director.beat_time)
|
||||||
|
print("the prologue opens stopped: beat clock at %.2fs" % director.beat_time)
|
||||||
|
|
||||||
var click: InputEventMouseButton = InputEventMouseButton.new()
|
var click: InputEventMouseButton = InputEventMouseButton.new()
|
||||||
click.button_index = MOUSE_BUTTON_LEFT
|
click.button_index = MOUSE_BUTTON_LEFT
|
||||||
click.pressed = true
|
click.pressed = true
|
||||||
|
|
@ -136,10 +147,13 @@ func _process(delta: float) -> bool:
|
||||||
return _fail("the opening beat was over before the first line finished")
|
return _fail("the opening beat was over before the first line finished")
|
||||||
if not narration.is_speaking():
|
if not narration.is_speaking():
|
||||||
return _fail("the opening beat is silent, so waiting for it proves nothing")
|
return _fail("the opening beat is silent, so waiting for it proves nothing")
|
||||||
# A player who never clicks must not be stuck: the beat's own
|
# Every beat has room to run past its floor before its deadline,
|
||||||
# deadline is the way out.
|
# so a player who will not finish a beat's task is carried past
|
||||||
|
# it. Since #128 that deadline is game time and a dialog stops
|
||||||
|
# the clock, so it is a way past an unfinished BEAT, never past
|
||||||
|
# an unacknowledged line.
|
||||||
if director._deadline() <= director._floor_time():
|
if director._deadline() <= director._floor_time():
|
||||||
return _fail("the opening beat has no deadline past its floor - a silent player would stall")
|
return _fail("the opening beat has no deadline past its floor - a stalled player would be stuck")
|
||||||
print("opening beat still running at %.1fs, guide still talking" % director.beat_time)
|
print("opening beat still running at %.1fs, guide still talking" % director.beat_time)
|
||||||
|
|
||||||
# --- C. the convoy --------------------------------------
|
# --- C. the convoy --------------------------------------
|
||||||
|
|
@ -220,6 +234,13 @@ func _process(delta: float) -> bool:
|
||||||
return _fail("the ambush counts as cleared while its raiders are alive")
|
return _fail("the ambush counts as cleared while its raiders are alive")
|
||||||
for enemy in director.ambush:
|
for enemy in director.ambush:
|
||||||
enemy.queue_free()
|
enemy.queue_free()
|
||||||
|
# #128: the beat announced itself, and an unacknowledged line
|
||||||
|
# stops the tree - including the director, whose advance is what
|
||||||
|
# stage 2 is waiting for. Acknowledged here, the way a player
|
||||||
|
# would, so the clock below is running.
|
||||||
|
narration.clear()
|
||||||
|
if paused:
|
||||||
|
return _fail("clearing the last line left the game stopped")
|
||||||
elapsed = 0.0
|
elapsed = 0.0
|
||||||
stage = 2
|
stage = 2
|
||||||
return false
|
return false
|
||||||
|
|
@ -335,8 +356,16 @@ func _process(delta: float) -> bool:
|
||||||
scene.get_node("BuildController")._process(0.1)
|
scene.get_node("BuildController")._process(0.1)
|
||||||
if _hidden("UI/BuildButton"):
|
if _hidden("UI/BuildButton"):
|
||||||
return _fail("the build button never came back")
|
return _fail("the build button never came back")
|
||||||
if narration.is_speaking():
|
# #133: the guide has nothing more to say - but the quest chain
|
||||||
return _fail("the guide is still talking after the handover")
|
# starts exactly here, so what is on the banner now is the first
|
||||||
|
# objective rather than any leftover of the prologue.
|
||||||
|
var quest: Dictionary = game_state.active_quest()
|
||||||
|
if quest.is_empty():
|
||||||
|
return _fail("the handover left no first quest")
|
||||||
|
if narration.is_speaking() and narration.current != String(quest.brief):
|
||||||
|
return _fail("the banner is still on \"%s\" after the handover" \
|
||||||
|
% narration.current)
|
||||||
|
print("the prologue hands over to the first quest: \"%s\"" % quest.title)
|
||||||
if scene.get_node("UI/BreachOverlay").visible:
|
if scene.get_node("UI/BreachOverlay").visible:
|
||||||
return _fail("the breach lens is still over the screen after the handover")
|
return _fail("the breach lens is still over the screen after the handover")
|
||||||
if is_instance_valid(director) and director.breach != null:
|
if is_instance_valid(director) and director.breach != null:
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,19 @@ extends Control
|
||||||
#
|
#
|
||||||
# The beats that are about the guide talking hold while this is speaking
|
# The beats that are about the guide talking hold while this is speaking
|
||||||
# (see IntroDirector._waits_for_narration), so dismissing a line is what
|
# (see IntroDirector._waits_for_narration), so dismissing a line is what
|
||||||
# advances the prologue - bounded by each beat's own deadline, so a player
|
# advances the prologue.
|
||||||
# who never clicks is never stuck.
|
#
|
||||||
|
# #128 makes that unconditional: while a line is up the WHOLE GAME is
|
||||||
|
# stopped - get_tree().paused, which freezes the fleet, the enemies, the
|
||||||
|
# production in GameState, and the director's own beat clock, and which
|
||||||
|
# stops input reaching any node that is not this one. So there is nothing
|
||||||
|
# to miss while reading and nothing to fumble a command into, and a beat's
|
||||||
|
# deadline can no longer run out from under an unacknowledged line: the
|
||||||
|
# player's click is the only thing that starts the world again.
|
||||||
|
#
|
||||||
|
# The pause is owned HERE rather than by each caller, because the thing that
|
||||||
|
# must not get out of step is "a line is showing" and "the game is stopped",
|
||||||
|
# and the only code that knows the first is this file.
|
||||||
|
|
||||||
signal dismissed
|
signal dismissed
|
||||||
|
|
||||||
|
|
@ -42,12 +53,53 @@ var queued: Array = []
|
||||||
var current: String = ""
|
var current: String = ""
|
||||||
var speaker: String = ""
|
var speaker: String = ""
|
||||||
var shown_for: float = 0.0
|
var shown_for: float = 0.0
|
||||||
|
# Whether the pause currently in force is OURS. Checked before lifting it, so
|
||||||
|
# a pause somebody else put on (a menu, a future one) is never lifted by a
|
||||||
|
# dialog closing.
|
||||||
|
var _holds_pause: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
# #128: the one node that keeps running while the game is stopped. Set
|
||||||
|
# here as well as in _ensure_label() because a banner that pauses the
|
||||||
|
# tree and then cannot process its own dismissal would wedge the game.
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
_ensure_label()
|
_ensure_label()
|
||||||
|
|
||||||
|
|
||||||
|
# The pause goes on when a line goes up and comes off when the last one is
|
||||||
|
# gone. Both are idempotent, so it does not matter how many times the state
|
||||||
|
# is re-derived - only that it is derived from one place (_sync_pause).
|
||||||
|
func _sync_pause() -> void:
|
||||||
|
var wanted: bool = current != ""
|
||||||
|
if wanted == _holds_pause:
|
||||||
|
return
|
||||||
|
var tree: SceneTree = get_tree()
|
||||||
|
if tree == null:
|
||||||
|
# Not in the tree: there is nothing to pause, and nothing we could
|
||||||
|
# have paused earlier is still ours to lift.
|
||||||
|
_holds_pause = false
|
||||||
|
return
|
||||||
|
if wanted:
|
||||||
|
tree.paused = true
|
||||||
|
_holds_pause = true
|
||||||
|
return
|
||||||
|
tree.paused = false
|
||||||
|
_holds_pause = false
|
||||||
|
|
||||||
|
|
||||||
|
# Leaving the tree with a line up - the scene swapping to a planet surface,
|
||||||
|
# a test dropping the scene - must not leave the game stopped with nothing
|
||||||
|
# left that could start it again.
|
||||||
|
func _exit_tree() -> void:
|
||||||
|
if not _holds_pause:
|
||||||
|
return
|
||||||
|
var tree: SceneTree = get_tree()
|
||||||
|
if tree != null:
|
||||||
|
tree.paused = false
|
||||||
|
_holds_pause = false
|
||||||
|
|
||||||
|
|
||||||
# The director is a world node and this banner lives under the UI layer,
|
# The director is a world node and this banner lives under the UI layer,
|
||||||
# which readies afterwards - so the first line can arrive before _ready().
|
# which readies afterwards - so the first line can arrive before _ready().
|
||||||
# Building on demand is simpler than making the director wait a frame, and
|
# Building on demand is simpler than making the director wait a frame, and
|
||||||
|
|
@ -55,6 +107,7 @@ func _ready() -> void:
|
||||||
func _ensure_label() -> void:
|
func _ensure_label() -> void:
|
||||||
if label != null:
|
if label != null:
|
||||||
return
|
return
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
visible = false
|
visible = false
|
||||||
label = Label.new()
|
label = Label.new()
|
||||||
|
|
@ -118,6 +171,7 @@ func clear() -> void:
|
||||||
speaker = ""
|
speaker = ""
|
||||||
shown_for = 0.0
|
shown_for = 0.0
|
||||||
visible = false
|
visible = false
|
||||||
|
_sync_pause()
|
||||||
|
|
||||||
|
|
||||||
func is_speaking() -> bool:
|
func is_speaking() -> bool:
|
||||||
|
|
@ -141,6 +195,7 @@ func _advance() -> void:
|
||||||
current = ""
|
current = ""
|
||||||
speaker = ""
|
speaker = ""
|
||||||
visible = false
|
visible = false
|
||||||
|
_sync_pause()
|
||||||
return
|
return
|
||||||
var next: Dictionary = queued.pop_front()
|
var next: Dictionary = queued.pop_front()
|
||||||
current = String(next.line)
|
current = String(next.line)
|
||||||
|
|
@ -150,6 +205,7 @@ func _advance() -> void:
|
||||||
visible = true
|
visible = true
|
||||||
_layout()
|
_layout()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
_sync_pause()
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ extends Node2D
|
||||||
# and the next one starts. skip() ends the whole thing at any point and
|
# and the next one starts. skip() ends the whole thing at any point and
|
||||||
# lands in the same state as playing it through, so there is one way for the
|
# lands in the same state as playing it through, so there is one way for the
|
||||||
# game to start rather than two that have to be kept in step.
|
# game to start rather than two that have to be kept in step.
|
||||||
|
#
|
||||||
|
# #128: those deadlines are wall clock only while the game is RUNNING. A
|
||||||
|
# dialog stops the tree, which stops this node too, so a beat's clock never
|
||||||
|
# ticks past an unread line - the deadline is the way out of a beat the
|
||||||
|
# player will not finish, not a way past a line they have not acknowledged.
|
||||||
|
|
||||||
signal finished
|
signal finished
|
||||||
|
|
||||||
|
|
@ -294,6 +299,11 @@ func _floor_time() -> float:
|
||||||
# Beats where the guide is the point, and cutting the line off would be
|
# Beats where the guide is the point, and cutting the line off would be
|
||||||
# cutting the beat off. The action beats are deliberately not here: a wave
|
# cutting the beat off. The action beats are deliberately not here: a wave
|
||||||
# arriving must not wait for a sentence to finish.
|
# arriving must not wait for a sentence to finish.
|
||||||
|
#
|
||||||
|
# Since #128 stops the whole tree while a line is up, this node is not
|
||||||
|
# running to ask the question in the first place - so this now says the same
|
||||||
|
# thing twice. Kept because it is the DIRECTOR's own reason for waiting, and
|
||||||
|
# it should not quietly depend on how dialogs happen to be implemented.
|
||||||
func _waits_for_narration() -> bool:
|
func _waits_for_narration() -> bool:
|
||||||
return beat == Beat.BRIEF or beat == Beat.CLEAR or beat == Beat.AFTERMATH
|
return beat == Beat.BRIEF or beat == Beat.CLEAR or beat == Beat.AFTERMATH
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,21 @@ func _ready() -> void:
|
||||||
open_button.pressed.connect(open_map)
|
open_button.pressed.connect(open_map)
|
||||||
recenter_button.pressed.connect(recenter_view)
|
recenter_button.pressed.connect(recenter_view)
|
||||||
close_button.pressed.connect(close_map)
|
close_button.pressed.connect(close_map)
|
||||||
|
# #128: a dialog stops the tree, and the prologue opens on one - so
|
||||||
|
# _process's first frame never comes and the button below would sit on
|
||||||
|
# screen behind the opening line. Applied once here too, where it does
|
||||||
|
# not depend on the game running.
|
||||||
|
_apply_intro_lock()
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
# #122: the prologue leaves movement and nothing else, and the map is a
|
_apply_intro_lock()
|
||||||
# button like any other. Owned here rather than by BuildController, which
|
|
||||||
# hides the rest - this button belongs to the map.
|
|
||||||
|
# #122: the prologue leaves movement and nothing else, and the map is a
|
||||||
|
# button like any other. Owned here rather than by BuildController, which
|
||||||
|
# hides the rest - this button belongs to the map.
|
||||||
|
func _apply_intro_lock() -> void:
|
||||||
if GameState.intro_active:
|
if GameState.intro_active:
|
||||||
if open_button.visible:
|
if open_button.visible:
|
||||||
open_button.visible = false
|
open_button.visible = false
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue