#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
|
|
@ -13,8 +13,19 @@ extends Control
|
|||
#
|
||||
# The beats that are about the guide talking hold while this is speaking
|
||||
# (see IntroDirector._waits_for_narration), so dismissing a line is what
|
||||
# advances the prologue - bounded by each beat's own deadline, so a player
|
||||
# who never clicks is never stuck.
|
||||
# advances the prologue.
|
||||
#
|
||||
# #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
|
||||
|
||||
|
|
@ -42,12 +53,53 @@ var queued: Array = []
|
|||
var current: String = ""
|
||||
var speaker: String = ""
|
||||
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:
|
||||
# #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()
|
||||
|
||||
|
||||
# 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,
|
||||
# which readies afterwards - so the first line can arrive before _ready().
|
||||
# Building on demand is simpler than making the director wait a frame, and
|
||||
|
|
@ -55,6 +107,7 @@ func _ready() -> void:
|
|||
func _ensure_label() -> void:
|
||||
if label != null:
|
||||
return
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
visible = false
|
||||
label = Label.new()
|
||||
|
|
@ -118,6 +171,7 @@ func clear() -> void:
|
|||
speaker = ""
|
||||
shown_for = 0.0
|
||||
visible = false
|
||||
_sync_pause()
|
||||
|
||||
|
||||
func is_speaking() -> bool:
|
||||
|
|
@ -141,6 +195,7 @@ func _advance() -> void:
|
|||
current = ""
|
||||
speaker = ""
|
||||
visible = false
|
||||
_sync_pause()
|
||||
return
|
||||
var next: Dictionary = queued.pop_front()
|
||||
current = String(next.line)
|
||||
|
|
@ -150,6 +205,7 @@ func _advance() -> void:
|
|||
visible = true
|
||||
_layout()
|
||||
queue_redraw()
|
||||
_sync_pause()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
|
|
|
|||
Loading…
Reference in a new issue