feat: Finishing polish + Audio gestion

This commit is contained in:
gauvainboiche
2026-10-25 22:12:27 +01:00
parent c55e5a2484
commit 7a525d084a
6 changed files with 189 additions and 8 deletions
+9 -3
View File
@@ -15,7 +15,7 @@ The project folder is empty, so everything below is new code.
| Backend | **Go, standard library only** (`net/http` + `embed`). It serves static files and has a `/healthz` endpoint. No game logic runs on the server. |
| Frontend | **Vanilla JS ES modules + CSS, no bundler and no npm.** The only runtime dependency is zero: no framework, no chart library (charts are hand-drawn SVG). |
| Fonts | Special Elite (typewriter text, **Apache-2.0**, not OFL), IM Fell English (headings, OFL) and Courier Prime (figures, OFL), stored with their licence files in `web/fonts/` so the game works offline. |
| Time | **1 day = 10 s at 1× speed** (a full run is about 5 h). Speed controls: pause, 1×, 2×, 4×, plus a "slow" setting (60 s/day) in Settings. **The game pauses automatically** when the weekly card arrives, at the quarterly budget review, and on crisis telegrams. |
| Time | **1 day = 10 s at 1× speed** (a full run is about 5 h). Speed controls: pause, 1×, 2×, 4×, plus a "slow" setting (60 s/day) in Settings. Since M8b the weekly proposals pile up in a tray and the game only pauses when 10 are waiting, at the quarterly budget review (and the emergency session), and on danger telegrams. The game keeps running in a background tab. |
| Saves | Auto-save to `localStorage` every in-game day. 3 manual save slots. Export/import as a **plain base64 string** (and as a downloadable `.sav` file). |
| Language | **English and French.** Every text field is `{ "en": "...", "fr": "..." }`, and there's a language switch in the menu. |
| Randomness | A seeded PRNG (mulberry32) whose state is stored in the save, so reloading a save doesn't re-roll the cards. |
@@ -140,7 +140,7 @@ A weighted score over the indicators averaged across the whole term (not just fi
## Time and the quarterly cycle
- The main loop uses `requestAnimationFrame` with a time accumulator. A new day starts when the accumulator reaches `msPerDay / speed`.
- **Every Monday** a card is drawn and the game pauses. You can't resume until you answer it (stamp APPROVED or REJECTED).
- **Every Monday** a proposal is drawn and put in the in-tray; the game does not stop. You answer the one on top (stamp APPROVED or REJECTED). The game pauses when 10 proposals are waiting (not 10 weeks), until one is answered.
- **Every 13 weeks** the budget ledger opens and the game pauses. You can change the sector allocations and the three tax levers, with a live projection of the deficit and of debt over the next 4 quarters. The rest of the time the ledger is read-only.
- Crisis events can arrive in the middle of a week as telegrams. They either need an answer or are information only.
@@ -207,7 +207,7 @@ A weighted score over the indicators averaged across the whole term (not just fi
- [x] **M6 Saves and menu**: auto-save, slots, export/import string and file, main menu, settings, language switch. Sound is not in the settings yet because the game has no sounds (M8, optional).
- [x] **M7 Content production**: grow the card pool to at least 800 cards in batches by category, plus 50 crisis cards and the follow-up chains. Done: 864 cards, 117 of them follow-up-only, about 100 chains. Tools: `tools/add-cards.mjs`, `tools/card-stats.mjs`, `tools/cardfmt.mjs`, guide in `docs/CARD-WRITING.md`.
- [x] **M8 Balance**: the difficulty is now measured and gated. Passive play is risky, a balanced plan wins, bold play pays up to a point and extremes end in a fall. Tools `tools/balance-check.mjs` (with `--check`), `balance-search.mjs`, `bal-edit.mjs`; guide in `docs/BALANCE.md`.
- [ ] **M8b Polish (optional)**: animations, sounds (typewriter, stamp, telegraph; off by default), a second scenario, a repo README.
- [x] **M8b Polish**: animations, synthesised sounds, a dial clock that turns once per day, background play, a tray of up to 10 waiting proposals, a one-column phone layout with a shelf, the rules in EN and FR (also shown in the game), a README and a scenario authoring guide. A second scenario itself is not written: `docs/SCENARIO-WRITING.md` is the brief for agents to create them.
## Verification
- `docker compose up --build` → open http://localhost:8080. Check that the menu loads, a new game starts, days advance at 10 s, and 4× speed works.
@@ -254,3 +254,9 @@ A weighted score over the indicators averaged across the whole term (not just fi
- 2026-10-25 (M8): Cards react to the player's choices. New condition keys `ratio:<sector>` and `tax:<id>` (engine, Go validator, guide). 50 new crisis cards: 34 backlash (`cri_bk_*`: police state, army excess or neglect, high and low taxes, debt and rating, starved services, waste) and 16 windfall (`cri_wf_*`: sustained high sector quality). The pool is 914 cards. Windfalls fire about 1.1 times per run for the balanced plan and 2.6 for the 1.5x plan; backlash fires 2 to 3 times per run for police state, tax hike and spendthrift and almost never for the milder versions.
- 2026-10-25 (M8): Result, 24 seeds each, human-like card answers: random 79 % survive (grade D); planner 100 % and score 61 (A in 17 % of runs); the same plan at 1.5x boldness 100 % with A in 29 %; at 2x 63 % survive; at 3x default every time; ruin, starve, deep cuts, tyrant 0 %; spendthrift 33 %; mild versions of each idea 75 to 100 %. The best budget the search found scores about 63 to 65. `balance-check --check` encodes these bands. Soft spots: S is rare, unrest runs slightly positive in the card pool, blanket austerity is fatal.
- 2026-10-25 (M8): Newspaper headlines for a global slump or boom. The ledger hint says needs grow yearly. Dev parameter `?untilAlert` stops a fast-forward at the first danger warning. Engine checks cover the world cycle, growing needs, the new condition keys, coup from neglect, the emergency session with its cooldown and the version 1 migration.
- 2026-10-25 (M8b): The in-tray. `state.pendingCard` became `state.inbox` (oldest first, entries `{id, day, category}`) and `pending.inboxFull` was added (state version 3, migration from version 2 turns a pending card into a one-card tray). `advanceDay` still raises `pending.weeklyCard` on a Monday, but it is momentary: the caller answers it at once with `ensureCard()`, which queues the proposal and sets `inboxFull` when the tray holds `cards.inboxMax` (10). `answerCard(state, bal, cards, choice, id?)` answers the top one or a chosen one. A card in the tray is not drawn twice and counts for the "same category recently" penalty. The balance harness is unchanged: its bots still answer at once.
- 2026-10-25 (M8b): Time and background play. The dial on the calendar and the small one on the phone shelf turn once per in-game day; the hand reads the clock accumulator, so it follows the speed. The accumulator is kept across pauses (it used to be reset). The loop is `step(now)`, called by animation frames while visible and by a worker heartbeat (`js/timer-worker.js`, every 100 ms) while hidden, so the game runs in a background tab; a long gap (a frozen tab waking up) is caught up within `time.catchUpMs` (60 s). A hidden tab skips the drawing, still autosaves, and shows "● (n) The Desk" in the title when it is waiting for the player. A new danger alert, review, full tray or new proposal plays a sound.
- 2026-10-25 (M8b): Sounds are synthesised with the Web Audio API (no files): stamp, paper, page, telegram, alert, chime, tick, click, fall, fanfare. Settings gained `sound` (default on) and `volume` (default 0.6). Motion lives in `css/anim.css` and is switched off by `prefers-reduced-motion`.
- 2026-10-25 (M8b): Phone layout (`css/mobile.css`, up to 900 px wide): one paper at a time, picked with a shelf along the top (date and dial, a speed button that cycles pause, 1x, 2x, 4x, then Tray with a count badge, Briefing, News, Calendar, Menu, and a Ledger tab while a review is open). The ledger takes the screen when a review opens and returns to the tray after signing. A full tray switches to the tray view.
- 2026-10-25 (M8b): Rules. `web/data/rules/rules.en.md` and `rules.fr.md` are plain Markdown, shown in the game (Menu, Rules) by a small renderer, `js/ui/markdown.js`, which escapes everything and supports headings, paragraphs, lists, bold, italic, code, tables, quotes and rules. `go test` checks that both files use only that subset and have the same structure. Scenario-only cards: optional card field `scenarios` (a whitelist of scenario ids, checked by the Go tests, honoured by `drawCard`). New tools: `scenario-check.mjs` and `balance-check.mjs --scenario <id>` with an optional `<id>.targets.json`.
- 2026-10-25 (M8b): Browser checks (headless Edge, desktop and a 390x844 phone): the tray fills to 10 on day 63 and stops, answering one resumes the clock; with animation frames disabled and the page marked hidden, the worker alone advanced the game to day 63 and stopped at the full tray; the phone shows exactly one paper per tab with no horizontal overflow; the ledger, menu, rules and telegram fit; all 13 sounds play without errors.
+3 -3
View File
@@ -145,8 +145,8 @@ Most of the pool is shared. A scenario can add cards visible only in that scenar
|---|---|
| `go test ./server` | scenario index (id equals the file name, localized name, listed and present), card schema including `scenarios`, i18n. **The economy, sectors, indicators and taxes of a scenario other than `default` are not covered by Go tests**: use the tools below. |
| `node tools/engine-check.mjs` | engine rules (uses `default.json`) |
| `node tools/scenario-check.mjs <id>` (new) | prints the start fiscal picture (revenue, sector spending, interest, deficit, debt) and a one-year hands-off drift; warns when the start is far from balanced. The draft warns for a deficit above 2, debt above 110, sector ratios outside 0.5 to 1.5, indicators outside 5 to 95, and exits with 1 on any warning. |
| `node tools/balance-check.mjs 24 --check --scenario <id>` (new) | plays the scenario with the standard player types and compares them to the bands in `balance-check.mjs` (about 30 s) |
| `node tools/scenario-check.mjs <id>` | prints the start fiscal picture (revenue, sector spending, interest, deficit, debt) and a one-year hands-off drift; warns when the start is far from balanced. The draft warns for a deficit above 2, debt above 110, sector ratios outside 0.5 to 1.5, indicators outside 5 to 95, and exits with 1 on any warning. |
| `node tools/balance-check.mjs 24 --check --scenario <id>` | plays the scenario with the standard player types and compares them to the bands in `balance-check.mjs` (about 30 s) |
Compare the hands-off drift with the default (about -5 on opinion, stability and economy, +8 unrest in a year). A scenario that
collapses or ignores a year of inaction was built on the wrong numbers.
@@ -190,7 +190,7 @@ near +0.5); run `scenario-check` on the real values.
## Assumptions
- The `scenarios` card field, `scenario-check.mjs` and `--scenario` behave as specified; the draft code in `tools/` was read for thresholds and may change.
- The `scenarios` card field, `scenario-check.mjs` and `--scenario` exist and were exercised on the default scenario only; a second scenario has not been played yet.
- The targets file is read as `{ "notes": "...", "targets": { player: {...} } }` (what `balance-check.mjs` reads today), and a player entry replaces the default entry whole (shallow merge), so restate every band you keep.
- Go tests currently load only `default.json` for economy, sector and card-condition checks.
- Shared card text names Valdoria; the guide treats this as a known limit, not something a scenario can fix.