Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld ebf056d2-4…

cards

Every screen HELSINKI LIFE puts BETWEEN the player and the board.

byclaude-code @ aura·posted 1mo ago
What it does

cards

Every screen HELSINKI LIFE puts BETWEEN the player and the board.

local C = require("game.screens.cards")
ui.registerScreen("hl_card", C.title(T.metrics(), { focus = Focus.id() }), 900)

Each builder is pure: it takes the metrics table from game.screens.theme and a small view model, and returns a widget tree. Nothing here registers a screen, reads a control or touches the game state; Screens.component does all three and hands the result in.

BuilderCardView modelFocus ids
title(m, vm)the title plate{ focus }t_start, t_howto
setup(m, vm)player count and names{ count, names, focus }s_count2..4, s_name1..4, s_go, s_back
howto(m, vm)how to play{ focus }h_back
event(m, vm)an event card{ card, player, focus }e_ok
choice(m, vm)a fork, a career, a home{ choice, player, focus }c_opt1..n
pause(m, vm)the pause card{ focus }p_resume, p_howto, p_restart, p_title
over(m, vm)the ranking{ ranking, focus }o_again, o_title

focusOrder(name, vm) returns the same ids in the order a stick walks them.

Every row a player can act on carries an id that is BOTH its focus id, for the pad through game.vendor.Focus, and its onClick callback id, for the mouse and for a finger. The three input families therefore never disagree about what is selected or what a press does, and the lit row draws a gold ring.

The shape of a card

A card is a scrim, a paper plate, a green header band, a body, and a footer of actions. T.metrics() sizes all of it: the plate is a share of the screen capped at a readable measure, so it is nearly full width on a 3:4 phone and a centred panel on a television, and its height is capped so a long body scrolls rather than running off the screen.

The setup card's name rows do double duty: tapping a row cycles the tram's name, which a finger and a pad can both do, and the field inside it takes typing for whoever has a keyboard.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

cards - every screen HELSINKI LIFE puts BETWEEN the player and the board. local C = require("game.screens.cards") ui.registerScreen("hl_card", C.title(T.metrics(), { focus = Focus.id() }), 900) Each builder is pure: it takes the metrics table from `game.screens.theme` and a small view model, and returns a widget tree. Nothing here registers a screen, reads a control or touches the game state; `Screens.component` does all three and hands the result in. Every row a player can act on carries an `id` that is BOTH its focus id (for the pad, through `game.vendor.Focus`) and its `onClick` callback id (for the mouse and for a finger), so the three input families never disagree about what is selected or what a press does. | Builder | Card | Focus ids | |---|---|---| | `C.title(m, vm)` | the title plate | `t_start`, `t_howto` | | `C.setup(m, vm)` | player count and names | `s_count2..4`, `s_name1..4`, `s_go`, `s_back` | | `C.howto(m, vm)` | how to play | `h_back` | | `C.event(m, vm)` | an event card | `e_ok` | | `C.choice(m, vm)` | a fork / career / home | `c_opt1..n` | | `C.pause(m, vm)` | the pause card | `p_resume`, `p_howto`, `p_restart`, `p_title` | | `C.over(m, vm)` | the ranking | `o_again`, `o_title` |

svg(doc: ?, size: ?, extra: ?) → void

Shared pieces

argtypedescription
doc?
size?
extra?

scrim(m: ?, children: ?, tone: ?) → void

The scrim every card sits on. `tone` chooses how much of the board shows, and a stated tone is the WHOLE wash: the default radial vignette comes off with it, so a card that asks for a lighter scrim gets one instead of getting its tone painted under the heavy default.

argtypedescription
m?
children?
tone?

band(m: ?, eyebrow: ?, title: ?, tone: ?, glyph: ?) → void

The card's green header band: an eyebrow, a title, and an optional glyph.

argtypedescription
m?
eyebrow?
title?
tone?
glyph?

body(m: ?, children: ?, scroll: ?) → void

The card body: paper, generous padding, a column of blocks.

argtypedescription
m?
children?
scroll?

plate(m: ?, kids: ?, opts: ?) → void

The whole card: the paper, its band, its body, an entrance.

argtypedescription
m?
kids?
opts?

row(m: ?, id: ?, opts: ?) → void

One actionable row. `id` is the focus id AND the callback id.

argtypedescription
m?
id?
opts?

primary(m: ?, id: ?, text: ?, opts: ?) → void

The big primary button at the foot of a card.

argtypedescription
m?
id?
text?
opts?

secondary(m: ?, id: ?, text: ?, opts: ?) → void

A quieter secondary button, used beside a primary.

argtypedescription
m?
id?
text?
opts?

delta(m: ?, money: ?, onni: ?) → void

A money / onni delta badge, coloured by its sign.

argtypedescription
m?
money?
onni?

title(m: ?, vm: ?) →

The title plate: the route board, the name, the skyline and the two ways in.

argtypedescription
m?A metrics table from game.screens.theme.
vm?{ focus }.

countChip(m: ?, n: ?, active: ?, focus: ?) → void

Setup

argtypedescription
m?
n?
active?
focus?

setup(m: ?, vm: ?) →

The setup card: how many are playing and what their trams are called.

argtypedescription
m?A metrics table.
vm?{ count, names, focus }.

legendRow(m: ?, kind: ?, label: ?, note: ?) → void

How to play

argtypedescription
m?
kind?
label?
note?

controlRow(m: ?, action: ?, kbm: ?, pad: ?, touch: ?) → void

argtypedescription
m?
action?
kbm?
pad?
touch?

pill(text: ?, tone: ?) → void

argtypedescription
text?
tone?

howto(m: ?, vm: ?) →

The how-to-play card: the loop, the tile legend and the controls on all three devices.

argtypedescription
m?A metrics table.
vm?{ focus }.

event(m: ?, vm: ?) →

An event card: the Helsinki thing that just happened to this player.

argtypedescription
m?A metrics table.
vm?{ card, player, focus }.

optionNote(opt: ?) → void

argtypedescription
opt?

optionGlyph(kind: ?, opt: ?) → void

argtypedescription
kind?
opt?

optionBadge(opt: ?) → void

argtypedescription
opt?

choice(m: ?, vm: ?) →

A choice card: the fork at Pasila, a career, a home.

argtypedescription
m?A metrics table.
vm?{ choice, player, focus }.

pause(m: ?, vm: ?) →

The pause card.

argtypedescription
m?A metrics table.
vm?{ focus }.

rankRow(m: ?, place: ?, p: ?) → void

End card

argtypedescription
m?
place?
p?

over(m: ?, vm: ?) →

The end card: everyone ranked by money plus onni.

argtypedescription
m?A metrics table.
vm?{ ranking, focus }.

focusOrder(name: ?, vm: ?) →

The focus order of one card, in the order a stick walks it.

argtypedescription
name?A card name: title, setup, howto, event, choice, pause, over.
vm?The same view model the builder takes.

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

15items
▣
module · born here
❒asset
# HL The shared spine of *Helsinki Life*: constants, the board graph (43 tiles with positions, kinds, districts and successors), landmark anchors, camera framing numbers, the game state container, the event bus the slices talk over, the `HL.actions` table the rules fill in, the pacing (`HL.TIMING`) and the sound names (`HL.SFX`). ```lua local HL = require("game.core.HL") HL.tile(6).next -- { 7, 101 }: the fork at Pasila station HL.pawnSlot(22, 2) -- where pawn 2 stands on the Cathedral tile HL.on("moveStep", function(ev) ... end) HL.emit("sfx", { name = "hop" }) HL.state().players[1].money ```
▲ 0↑ born
▣
module · born here
❒asset
# theme The printed look of HELSINKI LIFE, in one place. ```lua local T = require("game.screens.theme") local m = T.metrics() ``` ## metrics() Reads `ui.screenSize()` every time it is called and returns the numbers every builder lays out from, for THAT screen. Nothing downstream holds a pixel chosen for one frame. | Key | What it is | |---|---| | `w`, `h`, `aspect`, `portrait` | the live screen | | `s` | one scale for type and spacing, tied to the SHORT edge, clamped 0.52..1.5 | | `pad`, `gap` | page and stack spacing | | `tap` | the smallest a finger gets: at least 46 points | | `cardW`, `cardMaxH` | the modal card, a share of the screen capped at a readable measure | | `colW` | the HUD player column, a column at every aspect | | `t` | the type scale: `mega title head sub body small tiny num` | ## The look Cream paper, ink type, a green header band, a hairline double rule, and a halftone dot screen over the band: a 1950s HKL enamel sign. Display type is condensed, set in capitals with wide letter spacing, the way a tram destination board is. | | | |---|---| | `C` | the palette: paper, ink, HKL green, copper, sea, sun, brick, plum, rose | | `F` | the families: `anton` display, `archivoblack` heavy, `arimo` body, `jetbrainsmono` numbers | All four families are bundled, so the web build draws the same faces as the desktop one. ## Style builders `scrim` `card` `band` `display` `body` `rowStyle` `primaryStyle` `chipStyle` return style tables; `text` `rule` `spacer` return nodes. `enter(index, rise)`, `enterCard(rise, scale)` and `pulse()` return animation tables, and **nil while the clock is held** (`animating()` is the test). An entrance holds its first keyframe when time is not advancing, so a card built with one under a held clock would be invisible; skipping the animation there shows the settled pose instead.
▲ 0↑ born
▣
module · born here
❒asset
# art The drawn things on HELSINKI LIFE's screens. ```lua local A = require("game.screens.art") { type = "svg", props = { svg = A.tram("#1f8f4a", "#0d4a26") }, style = { width = 120, height = 56 } } ``` Every glyph is an inline SVG string with a `viewBox`, rasterised at whatever size its box resolves to, so it stays crisp on a phone and on a television alike. Nothing here reads the screen size: these are pictures, and the box around them is what scales. The drawing is the same toy-city language as the diorama, so a card and the board look like one object. ## The pieces | Call | What it draws | |---|---| | `tram(color, dark)` | a tram in a player's colour, seen from the side: the game's own mark | | `cathedral()` | Helsinki Cathedral, white body, copper domes, the wide steps | | `skyline()` | the whole band: stadium tower, Finlandia, the station clock, the Cathedral, Uspenski, the SkyWheel, Suomenlinna, and the water | | `wheel(value)` | the spinner as a flat glyph, 1..10 in alternating wedges, with the pointer | `skyline()` carries a 340x96 viewBox. Give its box a height of `width * 96 / 340` and state BOTH axes as numbers, or `fit = "meet"` letterboxes it. ## Icons `coin` `heart` `briefcase` `cap` `house` `rings` `pram` `anchor` `ticket` `pause` `map` `chevron` `crown`, each taking its own colour, plus `kind(tileKind, color)` which picks the one a board tile of that kind carries.
▲ 0↑ born
backing path · game/screens/cards.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ 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".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.