diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 5e5e543..2c9e8ac 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -158,6 +158,29 @@ emoji in the interface; the words do the work. Phone width first: a sticky bar for the move if the board runs long, lists that shorten, gutters of 16px, no horizontal scroll. +## The voice + +Every game speaks in its own world. Wherever the page tells a player what +happened, it gives an image as well as the fact: not "Corwin takes 5 from +a fireball" but "A fireball bursts over Corwin, for 5". The fact stays +exact and whole (the number, the duration, who is affected, the turn a +thing will happen), and the image carries it. + +- **The chronicle** is where the voice matters most. Each move, blow, + enchantment, arrival and death gets its own picture. Something that + unfolds over turns (an illness, a siege, a fuse) is told a turn at a + time, names the turn it ends on when it begins, and warns on the last + turn anything can be done about it. +- **The rest of the surround** speaks the same way: the lobby, the guide, + the status lines, the verdict. Plain words for controls and buttons; + the images belong to what happens, not to what you press. +- **Our own words.** An older implementation's prose is a guide to the + tone, never a source to copy. +- **Changing text is not changing rules.** Chronicle wording is not part + of the ledger, so it ships without a rules revision; the deploy gate + compares outcomes, not sentences. A test that pins a phrase should pin + the fact inside it. + ## Things learned the hard way - Caddy's `try_files` rewrites `/api` before `reverse_proxy` unless the diff --git a/new-game.sh b/new-game.sh index 4905ce1..fda0887 100755 --- a/new-game.sh +++ b/new-game.sh @@ -37,6 +37,8 @@ Next: and src/lib/components/Board.svelte with its board. Keep the tests green. 2. Fill in the hall's pitch, tag and about panel (src/lib/components/Hall.svelte), the rules page, the guide, src/app.html's description, and the palette in src/app.css. + Write every line a player reads in the game's own voice: an image with each + fact (docs/conventions.md, "The voice"). Add static/og.png (1200 by 630) and static/apple-touch-icon.png (180 by 180): app.html already points at them, and the static fallback serves the hall's HTML for a missing file, so a share card without the image breaks quietly. diff --git a/template/src/lib/components/Board.svelte b/template/src/lib/components/Board.svelte index 5d4e225..a4a6681 100644 --- a/template/src/lib/components/Board.svelte +++ b/template/src/lib/components/Board.svelte @@ -56,12 +56,12 @@ {:else if room.spectating}

You watch from the Peanut Gallery

-

{room.awaitingText ? `${room.awaitingText} ${room.awaiting.length === 1 ? 'is' : 'are'} still choosing.` : 'The round is being written.'}

+

{room.awaitingText ? `${room.awaitingText} ${room.awaiting.length === 1 ? 'turns a card over and over, still choosing.' : 'turn their cards over and over, still choosing.'}` : 'Cards are being chosen, face down.'}

{:else}

Round {g.rounds.length + 1}: name a number

-

The highest number named by exactly one player scores. First to {TARGET}.

+

Every player lays a card face down; the highest card nobody else laid takes the point. First to {TARGET}.

{#each Array.from({ length: HIGHEST }, (_, i) => i + 1) as n (n)} diff --git a/template/src/lib/game/index.ts b/template/src/lib/game/index.ts index 6485635..82b943f 100644 --- a/template/src/lib/game/index.ts +++ b/template/src/lib/game/index.ts @@ -68,7 +68,7 @@ export function resolveRound(previous: State, inputs: Record): St if (scorer) state.players[scorer].points += 1; state.rounds.push({ picks, scorer }); if (scorer && state.players[scorer].points >= TARGET) { - state.over = { winner: scorer, reason: `${state.players[scorer].name} reached ${TARGET} points.` }; + state.over = { winner: scorer, reason: `${state.players[scorer].name} lays down the last card and sweeps the table: ${TARGET} points, and the game.` }; } return state; }