screens
Every screen in IRONSPINE, and the routing between them.
ui/screens.module — the authored UI
Every screen in IRONSPINE, and the routing between them.
| Phase | Screen |
|---|---|
boot / title | ironspine_title (+ ironspine_howto when opened) |
intro | ironspine_intro — letterbox, place card, SKIP |
climb / fall | ironspine_hud — built by ui/hud.module |
won | ironspine_won |
lost | ironspine_lost |
| any, over a climb | ironspine_pause |
How it is put together
- Type.
Antonfor display (the title, the end-card verdicts),BerkeleyMonofor every readout and label,Arimoat a single size for prose. - Colour. Every value comes from
Game.PALETTEthroughassets/ui/art.module, which translates it into CSS strings once. - Art.
assets/ui/art.modulealso draws the Ironspine silhouette, the reticle, the hold ring, the piton and crown glyphs, the mouse diagram and the gauge arcs, as inline SVG. - Motion. Every panel has an entrance with a staggered delay; live things (a gust, a slipping hand, the beacon) have small infinite loops.
Talking to the rest of the game
It reads Game.state and reports the player's choices with
Game.emit("uiAction", { action = … }) — start, retry, resume, quit,
plus pause when the menu opens over a live climb. It calls
require("game.staging").skip() when the player cuts the opening short.
If no slice has claimed an action after a moment, SCREENS applies the smallest phase move that keeps the menus reachable, so a menu is never a dead end. That fallback never runs when another slice answers the event.
Keyboard
Escape is carried by a real button widget's shortcut on the HUD (MENU · ESC),
on the pause card and on the how-to card, so the engine's own shortcut plumbing
fires it.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
conforms to
zero/source-extract/v2ui/screens.module — SLICE: SCREENS. Owner: the screens agent. Every screen the player ever sees, and the routing between them: title the storefront frame — the Ironspine's silhouette, the type, the menu howto the how-to-climb card, over the title intro a skip prompt while STAGING's opening plays hud ui/hud.module — the suit instrumentation (see that file) pause frosted, over a live climb won the beacon caught lost the sea took you It reads Game.state and reports the player's choices back with Game.emit("uiAction", { action = ... }). It does not drive the simulation. UNCLAIMED ACTIONS. A menu choice is published on the bus first. If no slice has taken it a moment later — because that slice is still being written — SCREENS applies the smallest phase move that keeps the menus reachable, so no screen is ever a dead end. The moment another slice answers the event, the fallback never fires.
scrim(vertical: string, horizontal: string?) → void
═══════════════════════════════════════════════════════════════════════ shared pieces ═══════════════════════════════════════════════════════════════════════
| arg | type | description |
|---|---|---|
| vertical | string | |
| horizontal | string? |
menuItem(id: string, label: string, note: string, tone: string, delay: number) → void
The menu row. A styled box with onClick — the whole plate is the target.
| arg | type | description |
|---|---|---|
| id | string | |
| label | string | |
| note | string | |
| tone | string | |
| delay | number |
statCell(label: string, value: string, tone: string?) → void
| arg | type | description |
|---|---|---|
| label | string | |
| value | string | |
| tone | string? |
eyebrowRow(text: string, tone: string) → void
| arg | type | description |
|---|---|---|
| text | string | |
| tone | string |
timeText(t: number) → string
| arg | type | description |
|---|---|---|
| t | number |
buildTitle( ) → void
═══════════════════════════════════════════════════════════════════════ TITLE ═══════════════════════════════════════════════════════════════════════
bracket(l: ?, t: ?, bt: ?, bl: ?) → void
corner brackets
| arg | type | description |
|---|---|---|
| l | ? | |
| t | ? | |
| bt | ? | |
| bl | ? |
controlRow(art: ?, title: string, body: string, delay: number) → void
═══════════════════════════════════════════════════════════════════════ HOW TO CLIMB ═══════════════════════════════════════════════════════════════════════
| arg | type | description |
|---|---|---|
| art | ? | |
| title | string | |
| body | string | |
| delay | number |
cap(g: string, wide: boolean?) → void
| arg | type | description |
|---|---|---|
| g | string | |
| wide | boolean? |
buildHowto( ) → void
buildIntro( ) → void
═══════════════════════════════════════════════════════════════════════ INTRO — skip prompt only; the shot belongs to STAGING ═══════════════════════════════════════════════════════════════════════
buildPause( ) → void
═══════════════════════════════════════════════════════════════════════ PAUSE ═══════════════════════════════════════════════════════════════════════
gripLine(side: string, h: ?) → void
| arg | type | description |
|---|---|---|
| side | string | |
| h | ? |
buildEnd(won: boolean) → void
═══════════════════════════════════════════════════════════════════════ END CARDS ═══════════════════════════════════════════════════════════════════════
| arg | type | description |
|---|---|---|
| won | boolean |
readPos(e: ?) → void
The climber's body is the origin the reach test is measured from. The proxy is re-found on any read failure and refreshed on a slow timer, because a respawn hands out a new one.
| arg | type | description |
|---|---|---|
| e | ? |
bodyPos(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
toCanvas(px: number, py: number) → void
| arg | type | description |
|---|---|---|
| px | number | |
| py | number |
handView(side: string) → void
| arg | type | description |
|---|---|---|
| side | string |
hudView(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
show(name: string, on: boolean) → void
═══════════════════════════════════════════════════════════════════════ registration + routing ═══════════════════════════════════════════════════════════════════════ Asserted every frame rather than diffed: a screen shown from anywhere else (a console call, a tool) is reconciled on the next tick instead of sticking.
| arg | type | description |
|---|---|---|
| name | string | |
| on | boolean |
refresh(name: string, tree: ?) → void
| arg | type | description |
|---|---|---|
| name | string | |
| tree | ? |
stagingSkip( ) → void
act(action: string) → void
| arg | type | description |
|---|---|---|
| action | string |
onCallback(id: string, _data: any) → void
| arg | type | description |
|---|---|---|
| id | string | |
| _data | any |
settlePending(dt: number) → void
If nothing claimed the action, keep the menus reachable. This is the smallest move that leaves no screen a dead end; it never runs when another slice answers the event.
| arg | type | description |
|---|---|---|
| dt | number |
build(_ctx: any) → void
| arg | type | description |
|---|---|---|
| _ctx | any |
onCallback(cbId: ?, data: ?) → void
| arg | type | description |
|---|---|---|
| cbId | ? | |
| data | ? |
update(dt: number) → void
| arg | type | description |
|---|---|---|
| dt | number |
enter( ) → void
exit( ) → void
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
agent_score is exposed.+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
Scoped to this part · feeds back into the world's score.