staging
The director. It decides when a staged moment plays, drives the camera along it, draws the frame it is read in, holds the skip control, and hands everything back.
game.staging.staging
The director. It decides when a staged moment plays, drives the camera along it, draws the frame it is read in, holds the skip control, and hands everything back.
local Staging = require("game.staging.staging")
Staging.attach({ autoIntro = true }) -- subscribe to the bus
Staging.playIntro() -- the opening
Staging.playLiftBeat(1, 2) -- the lift waking on floor 1
Staging.playWon() -- the loft resolution
Staging.playLost() -- the morning
Staging.skip() -- end the beat on screen early
Staging.stop() -- end it now
Staging.current() -- { beat, playing, t, duration, phase, camera }
Staging.tick(dt) -- called once a frame by Staging.component
Staging.detach() -- release everything
How a beat runs
A beat takes the play-mode camera by standing down the controller.*
component on it and writing that entity's position, rotation and field of view
each frame from @builtin::modules.cutscene.path. Ending the beat re-enables
what it stood down and restores the lens, so the player comes back to their own
rig looking their own way rather than to a second camera.
Two UI screens carry the rest: rdr_staging_frame draws the letterbox bands,
the caption and the skip prompt, and rdr_staging_wash draws the fade over
them. Both lay out from the live ui.screenSize() — bands as a share of the
height, type off the short axis, the caption capped at a readable measure — so
the same beat composes on a phone held upright, on this world's 3:4 frame and
on a wide monitor. The tree is only re-declared when a value in it changed.
Why it does not call cutscene.play
The engine's cutscene stage is a post-process effect, and arming or dropping
one moves the format of the target the UI is drawn into. The engine dies in its
own UI renderer when that happens while a screen is being re-declared per frame
(OrigoZero/zero#12924), which in a game with a live HUD is every frame of play.
A beat here is a camera write, two UI screens and a clock, so it costs the
post-process chain nothing. It still reads its camera paths through the
cutscene system's own path module, which is where shot evaluation and look
rotation live.
State
Everything is in modules.state(), so editing this file mid-run neither
doubles a bus subscription nor strands a camera the player needs back.
Staging.component's onModuleReload re-attaches, which swaps the
subscriptions the old chunk closed over for the new ones.
Scoped to this part · feeds back into the world's score.