Polish dialog and intro transitions

This commit is contained in:
Loïc Wisniewski 2026-09-02 20:18:44 +02:00
commit d413d1da9f
7 changed files with 228 additions and 4 deletions

View file

@ -79,6 +79,11 @@ const BELT_HALF_WIDTH: float = 2600.0
const BELT_DEPTH: float = 1400.0
const BREACH_PULL: float = 620.0
const SHAKE_MAX: float = 34.0
const DEFAULT_SPAWN: Vector2 = Vector2.ZERO
# The cover reaches white after the convoy has visibly collapsed to its
# survivor, then reveals the ordinary game location with time to breathe.
const WHITEOUT_IN_SECONDS: float = 3.2
const WHITEOUT_OUT_SECONDS: float = 2.0
# How far past the horizon the lens reaches. Generous: the bend is what
# sells the hole, and it falls off to nothing on its own well before this.
const BREACH_LENS_SCALE: float = 7.0
@ -100,6 +105,7 @@ const VORTEX_COLORS: Array = [
@onready var narration: Control = get_node("../UI/NarrationBanner")
@onready var asteroid_field: Node2D = get_node("../AsteroidField")
@onready var enemy_spawner: Node2D = get_node("../EnemySpawner")
@onready var fade_rect: ColorRect = get_node_or_null("../FadeOverlay/FadeRect")
var running: bool = false
var beat: int = Beat.BRIEF
@ -119,6 +125,8 @@ var _done: bool = false
# saying cannot surface in the middle of the next one.
var _pending_lines: Array = []
var _pending_at: float = 0.0
var whiteout_teleported: bool = false
var _whiteout_tween: Tween = null
@onready var overlay: ColorRect = get_node_or_null("../UI/BreachOverlay")
@ -209,6 +217,7 @@ func finish() -> void:
GameState.intro_locks_movement = false
GameState.intro_fleet_size = 0
narration.clear()
_reset_whiteout()
camera.offset = Vector2.ZERO
enemy_spawner.set_process(true)
asteroid_field.set_process(true)
@ -354,6 +363,7 @@ func _begin(next: int) -> void:
vortex_time = 0.0
_ensure_survivor_hull()
narration.clear()
_start_whiteout()
Beat.AFTERMATH:
GameState.intro_locks_movement = false
shake = 0.0
@ -434,7 +444,11 @@ func _tick(delta: float) -> void:
vortex_time += delta
shake = maxf(shake - delta * SHAKE_MAX * 0.5, SHAKE_MAX * 0.35)
_drive_breach(1.0)
_whirl(delta)
# Once the opaque frame has moved the survivor to the real spawn,
# nothing from the old side may pull it back into the breach while the
# white cover clears.
if not whiteout_teleported:
_whirl(delta)
queue_redraw()
Beat.AFTERMATH:
shake = maxf(shake - delta * SHAKE_MAX, 0.0)
@ -445,6 +459,50 @@ func _tick(delta: float) -> void:
queue_redraw()
# The existing transition ColorRect already owns the topmost full-screen
# layer. Recolour it for this one transition, and sequence the teleport as a
# callback between fade-in and fade-out so no world-space jump can be seen.
func _start_whiteout() -> void:
whiteout_teleported = false
if fade_rect == null:
# Headless/minimal scenes may omit presentation, but the story still has
# to land at the same gameplay state.
_complete_whiteout()
return
if _whiteout_tween != null and _whiteout_tween.is_valid():
_whiteout_tween.kill()
fade_rect.color = Color(1.0, 1.0, 1.0, 0.0)
_whiteout_tween = create_tween()
_whiteout_tween.tween_property(fade_rect, "color:a", 1.0, WHITEOUT_IN_SECONDS)
_whiteout_tween.tween_callback(_complete_whiteout)
_whiteout_tween.tween_property(fade_rect, "color:a", 0.0, WHITEOUT_OUT_SECONDS)
_whiteout_tween.tween_callback(_finish_whiteout)
func _complete_whiteout() -> void:
if whiteout_teleported or not running:
return
whiteout_teleported = true
fleet.teleport_to(DEFAULT_SPAWN)
camera.global_position = fleet.get_centroid()
camera.offset = Vector2.ZERO
func _finish_whiteout() -> void:
_whiteout_tween = null
if fade_rect != null:
# Restore the shared rect's normal transparent-black resting state so a
# later planet transition remains black rather than reusing intro white.
fade_rect.color = Color(0.0, 0.0, 0.0, 0.0)
func _reset_whiteout() -> void:
if _whiteout_tween != null and _whiteout_tween.is_valid():
_whiteout_tween.kill()
_whiteout_tween = null
_finish_whiteout()
func _apply_shake(delta: float) -> void:
if shake <= 0.01:
if camera.offset != Vector2.ZERO: