#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:
Loïc Wisniewski 2026-08-28 14:40:46 +02:00
commit 1e89780bd7
7 changed files with 330 additions and 10 deletions

View file

@ -15,6 +15,11 @@ extends Node2D
# 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
# 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
@ -294,6 +299,11 @@ func _floor_time() -> float:
# 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
# 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:
return beat == Beat.BRIEF or beat == Beat.CLEAR or beat == Beat.AFTERMATH

View file

@ -19,12 +19,21 @@ func _ready() -> void:
open_button.pressed.connect(open_map)
recenter_button.pressed.connect(recenter_view)
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:
# #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.
_apply_intro_lock()
# #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 open_button.visible:
open_button.visible = false