staging
Saltcrown has one camera and the player never touches it, so this slice is the whole of how the game is seen. It is the only code that moves `GameCamera`, and it owns everything under `StagingRoot`.
game.staging — the camera and the moments
Saltcrown has one camera and the player never touches it, so this slice is
the whole of how the game is seen. It is the only code that moves
GameCamera, and it owns everything under StagingRoot.
local Core = require("game.gamecore") -- everything this slice reads
The four things it does
The climb. The everyday shot. It frames the live tower — from the waterline up to the top of what stands — and re-frames continuously as the stack grows and the water rises, so the two things the player is choosing between are always both on screen. A slow parallax swing keeps the frame from going dead.
The opening. Nineteen seconds on the empty pillar and the empty sea,
before the first course is laid. Fires on phaseChanged → "play", holds the
season clock still (Core.state.paused) while it runs, and hands over on
exactly the pose the scene authored, so the cut to gameplay is a
continuation rather than a jump. Authored in art.staging.scOpening.
The beat. A floor going under is the only irreversible thing in
Saltcrown, so it is the one thing that takes the camera: the frame drops to
the surface, pushes in on the water closing over the course, holds, and
lets go. A ruled plate names what was lost. Fires on floorDrowned and
floorSacrificed; only one beat runs at a time, so a season that takes two
floors still gets one clean moment.
The ending. On runEnded, a sixteen-second crane off the waterline up
the whole tower to the crown — which then holds. The last frame of a run
is the thing the run built, with the end card over it.
Things worth knowing before changing it
- The ledger owns the bottom of the screen. On a 1080×1440 portrait
viewport the clear window the 3D plays in is only the top of the frame.
BAND_CENTREandBAND_HEIGHTare where that is compensated for, and both were set by capturing the running game. Retune them if the ledger's panel changes height. The beat's look points sit below the waterline for the same reason. - The opening does not take the camera. It borrows the cutscene
system's letterbox, fades, subtitle band and skip key, but
takeCamera = false; the shot list is evaluated withcutscene.evaluateand written ontoGameCamera. Socapture { source = "main" }always shows what a player sees, and no second camera ever enters the scene. - The overlay and the camera are two clocks. Ending the camera move does not end the letterbox; anything that takes the frame away from the opening has to stop the overlay too.
- Entity refs are re-resolved by name every frame. A scene reload destroys and rebuilds everything, and this slice writes the camera on every frame, so a proxy held across a reload is a warning per frame until a reboot.
Asking for a shot
Other slices do not move the camera; they raise an event and this slice
answers it. The bus events it listens on are phaseChanged,
floorDrowned, floorSacrificed and runEnded. For anything an
execute wants to drive directly:
local Staging = require("game.staging")
Staging.playOpening()
Staging.beat(Core.state.floors[4])
Staging.playEnding("crown")
What it spawns
art.staging.scSkiff — the settlers' boat, under StagingRoot. It floats,
so it rides the tide up the tower for the whole run.
Scoped to this part · feeds back into the world's score.