flscreens
Every screen of *First Light: Vagant*, as a widget tree. Each function is a pure builder: it takes a view model and returns a tree. It registers nothing, shows nothing, and writes no game state — `Screens.component` does all of that.
flscreens
Every screen of First Light: Vagant, as a widget tree. Each function is a
pure builder: it takes a view model and returns a tree. It registers nothing,
shows nothing, and writes no game state — Screens.component does all of that.
local S = require("ui.flscreens")
ui.registerScreen("fl-menu", S.title(model), 960)
The builders
| Call | What it draws |
|---|---|
S.title(m) | the title card: the mark, the name, the logline, the menu |
S.howto(m) | the how-to-play broadsheet and its control table |
S.hud(m) | dawn band, clock, pause pip, the seven standards, oil, wig |
S.alert(m) | the hunt vignette and the relight prompt |
S.say(m) | the subtitle line |
S.pause(m) | the pause card, with the night's figures on it |
S.endCard(m) | first light, or the entry in the minute-book |
The view model
{
device = "kbm" | "pad" | "touch", -- which family is holding it
focus = "fl:begin", -- the id carrying the focus ring
items = { { id, label, hint?, danger? }, ... },
backId = "fl:back",
line = { text = "...", speaker = "The Vagant" } | nil,
playerPos = { x, y, z } | nil, -- for the relight prompt
won = boolean, reason = "caught" | "dawn", where = "Middle Ditch",
}
Everything else — the clock, the lamps, the oil, the wig, whether you are being
hunted — is read live from fl.state and fl.lamps.
Why the HUD is three screens
S.hud carries the gauges and is rebuilt about once a second, whenever a
figure on it changes. S.alert and S.say are separate because they are the
parts that carry looping and entrance motion, and re-registering a screen
restarts every animation on it — so they are rebuilt only when being hunted,
standing at a dark lamp, or the spoken line actually changes.
Portrait first
The filmed frame is 3:4 and the portrait arrangement is the default: the dawn
band along the very top, the standards down the left, the oil up the right,
the words low and centred, nothing spanning the width. flui.portrait() picks
the wide variant where a wide screen needs one — a narrower menu column, a
smaller title mark, and less vertical padding, because a wide screen has to
give its vertical room back to the stack.
Scoped to this part · feeds back into the world's score.