Files
escape-builder/TODOS.md
Claude Agent 651025bd28 S1 fix real: deblocare audio pe primul gest (acopera resume), nu doar btn-start
Fix-ul initial deblocà AudioContext-ul doar in handlerul btn-start. Lacuna:
calea de resume (reload mid-campanie) intra direct pe harta fara btn-start ->
ctx nedeblocat -> camere mute. Plus resume() singur nu ajunge pe iOS Safari.

- unlockAudio() + listener global one-time (pointerdown+keydown capture):
  acopera fresh SI resume; buffer silentios iOS-safe.
- beep() se auto-vindeca daca ctx redevine suspended.
- Test smoke #9 rescris: headless creeaza ctx direct 'running' (ignora autoplay)
  -> vechiul "ctx running" trecea trivial. Acum: gest tastatura fara btn-start
  -> running (cale resume) + beep self-heal din ctx suspendat.
- Demo campanie regenerat. Suita 24/24.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 11:29:21 +00:00

129 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TODOS — Escape Room Builder
Backlog post-PR1 și note tehnice pentru iterațiile viitoare.
Referință plan complet: `~/.gstack/projects/romfast-escape-builder/ceo-plans/2026-06-12-campania-multi-stil.md`
> **Acest fișier e BOARD-UL DE PROGRES (sursa durabilă).** Task list-ul din harness se
> resetează între sesiuni → se uită. Aici nu. La fiecare sesiune: citește board-ul activ
> de mai jos ÎNTÂI, mută `[ ]→[~]→[x]` pe măsură ce avansezi, commit-uiește schimbarea.
> Convenție: `[ ]` neînceput · `[~]` în lucru · `[x]` gata+verificat · `[!]` blocat.
---
## ▶ BOARD ACTIV — Iterația 2 (Adventure Mode / restyle)
Direcția cerută de user (decizii confirmate, vezi `HANDOFF.md`). Model hibrid ca la PR1:
părțile grele se prototipează în PARALEL în `scratch/`, verificate jucabile, apoi integrator le
portează în `escape-builder.html` (un singur fișier, integrare secvențială).
- [x] **S1 — fix sunet campanie** *(GATA — REVENIT: fix-ul inițial era incomplet, user raporta tăcere)*
Cauză reală: gestul din iframe NU deblochează AudioContext-ul părintelui → ctx `suspended` → tăcere.
Fix v1 (incomplet): deblocare DOAR în handler-ul `btn-start`. Lacună: calea de **resume**
(reload mid-campanie, `escape-builder.html:2199`) intră direct pe hartă FĂRĂ btn-start → ctx
nedeblocat → camere mute. Plus `resume()` singur nu ajunge pe iOS Safari.
Fix v2 (real): `unlockAudio()` + listener GLOBAL one-time pe primul gest (`pointerdown`+`keydown`,
capture) — acoperă fresh ȘI resume (mers pe hartă = keydown pe părinte); buffer silențios
iOS-safe; `beep()` se auto-vindecă dacă ctx redevine `suspended`. `escape-builder.html:1893`.
**Lecție testare:** headless Chromium creează ctx direct `running` (ignoră autoplay policy) →
vechiul test „ctx running" trecea trivial, NU putea prinde tăcerea. Test nou (smoke #9):
gest tastatură FĂRĂ btn-start → running (cale resume) + beep self-heal din ctx suspendat.
Verificat: smoke 24/24 + live MCP (ArrowDown singur deblochează). Demo-uri regenerate.
- [x] **S2a — prototip Bomberman complet**`scratch/bomberman-proto.html` (GATA, 8/8 verificat de mine)
Grid 15×13, bombe timer 2.4s + explozii lanț, cutii distructibile, AI dușmani BFS urmărire,
3 vieți + respawn cu progres puzzle PĂSTRAT (stare separată), PRNG seedat (`window.__seed`),
uși roșii `openPuzzle(id,cb)` + cufăr = scăpare. Hooks `window.__game`.
Test: `scratch/verify-bomberman.mjs` (+ `pw-bomberman.config.mjs`). Am corectat testul AI:
dușmanii merg DOAR pe podea → cei închiși în cutii nu se mișcă (corect); testul curăță cutiile.
Note S3: dușmanii confinați de cutii e intenționat — la integrare asigură căi sau acceptă.
- [x] **S2b — prototip hartă overworld**`scratch/overworld-proto.html` (GATA, 7/7 verificat de mine)
Hartă tile 20×18, player top-down (săgeți/WASD/dpad), 4 uși + exit deblocat după toate.
Orchestrator identic cu `gameCampaign`: `mountRoom`/`roomReady`/`nextRoom`/`roomError`/timeout 4s,
resume localStorage, idempotență. Hooks test `window.__map`. Test: `scratch/test-overworld.mjs`.
Note S3: stub `makeSrcdoc``TPL[style].replace('__CFG__', fn)`; DOOR_TILES paralel cu puzzles;
backslash dublu la portare. Ordine rezolvare LIBERĂ.
- [x] **S2c — `STYLES.md`** — direcție restyle pentru cele 5 stiluri (GATA, 775 linii).
Top 3 impact/efort: terminal `.line.dim` fix WCAG (3.1:1→6.1:1); classic card glow +
progres bar; chat header `backdrop-filter` + bulă NPC distinctă. Consumat de S3.
- [x] **S3 — integrare în `escape-builder.html`** *(GATA — toate 3 pas-urile; smoke 21/21)*
Portează prototipurile (template literals → DUBLEAZĂ backslash-urile) + regenerează demo-urile.
Pas 1: Bomberman → `gameArcade`. Pas 2: Overworld → `gameCampaign`. Pas 3: restyle 5 stiluri.
- [x] Pas 1 — Bomberman în `gameArcade` (GATA). Păstrează `openPuzzle`/`onDoorSolved`/`showFinal`/
`modalOpen()`/`roomReady`; uși=N puzzle-uri, cufăr=scăpare. Demo regenerat. Smoke 21/21 +
verificare gameplay 6/6 (`scratch/verify-arcade-integrated.mjs`) + captură.
- [x] Pas 2 — Overworld în `gameCampaign` (GATA). Hartă top-down `#overworld` înlocuiește
coridorul; intro→`showOverworld(0)`, nextRoom/skip/resume→`showOverworld`. Contractul
(mountRoom/nextRoom/roomReady/roomError/timeout/finale) NESCHIMBAT. Cod coridor șters.
Cele 8 teste campanie rescrise (`enterRoom`/`waitOverworld`/`__ow`). Smoke 21/21 + captură.
- [x] Pas 3 — restyle 5 stiluri din `STYLES.md` (GATA, toate 5). Classic spotlight+card glow+
tile 44px; Terminal fix WCAG `.dim` #2ecc71 + bordură CRT + flicker; Arcade canvas neon +
dpad fizic; Chat header frosted + bule distincte + tile reward; Point fundal distinct +
fix contrast `.note` + ușă glow. `prefers-reduced-motion` peste tot. Toate 5 demo-uri
regenerate. Smoke 21/21 + capturi pe fiecare stil.
- [x] **S4 — extinde `tests/smoke.mjs`** *(GATA — 24/24)* — 3 teste noi: audio S1 (ctx running),
navigare overworld (mers tastatură + ieșire blocată), bomberman gameplay (bombă/AI/respawn).
Arbore AGENTS.md actualizat 21→24.
**Stare la 2026-06-13:** PR1 livrat (`a42c960`). **Iterația 2 COMPLETĂ** — S1+S2+S3+S4 livrate
și verificate. Suita 24/24. Comituri: S1 `52f97af`, S2c `a9f3065`, S3 `4454df9` (+pas1/pas2).
---
## Post-PR1 (după ship-ul campaniei)
### Muzică accelerată la timer (PR2 / T10)
- Audio ambient în campanie: track calm → accelerare progresivă sub 1 minut.
- Ownership: părintele deține AudioContext; camerele nu știu de muzică.
- Fallback: zero pedeapsă dacă AudioContext lipsă (webview restricitve).
- Edge: muzica se oprește la `speechSynthesis.cancel()` dacă vocea e activă simultan.
- Legat de: T10 (PR2), timer countdown în bara chrome (§Design pct. 10).
### Edge case-uri voce (SpeechSynthesis) — PR2
- `speechSynthesis.getVoices()` poate fi gol sincron → ascultă `voiceschanged`.
- Fără voce `ro-*` → fallback la vocea default (nu crash, nu tăcere).
- Voce activă mid-cameră → `speechSynthesis.cancel()` la demontare cameră (pater deține).
- `parent.voiceSay(text)` = no-op în jocurile simple (funcția nu există) → guard `typeof parent.voiceSay === 'function'`.
- Referință: D10 din plan; E2 Etapa 2 pct. 3.
### Unificarea `finale()` din terminal pe `SNIP.finalJs` (PR2 primul pas)
- Astăzi terminalul are propria funcție `finale()` (escape-builder.html:863) care NU folosește `SNIP.finalJs`.
- Migrarea pe SNIP.finalJs deblochează ramura `_campaign` uniformă pentru toate cele 5 motoare.
- Prim pas al Etapei 2 (D7): migrarea `gameClassic` pe `libJS+SNIP` → regresie manuală pe classic.
- Referință: planul §Etapa 2 pct. 1; D7.
### Audit a11y motoare existente (post-PR1, sub harness Playwright)
- **Ținte tap ≥ 44px**: dpad arcade, butoane tf/choice, butonul "Trimite" din chat.
- **Contrast ≥ 4.5:1**: text terminal dim (`#1f9c4a` pe `#04130a` — verifică), hint text clasic.
- **`@media (prefers-reduced-motion: reduce)`**: dezactivează `pop`, `flip`, `flipin`, `shake`, `confetti`, `bin` — stările finale apar direct.
- **Focus & Enter**: "Deschide ușa" (campanie) focusabil + Enter; dpad arcade accesibil cu keyboard.
- **`aria-label` pe progres**: bara chrome din campanie (`aria-label="Camera X din Y"`).
- Referință: §Design pct. 13 (TD5, PR2); D19 din plan.
---
## Iterația 2 — Adventure Mode v0
*(decizie office-hours: fundația contractului de azi e infrastructura directă)*
- Contract de montare (`nextRoom`, `roomReady`, `roomError`) se refolosesc as-is.
- Motoarele noi (orice stil) implementează aceleași 3 puncte + `parent.beep`.
- `gameCampaign` se extinde cu ramificare: `if (answer === 'left') nextRoom({dir: 'left'})`.
- Builder UI: adaugă câmpul "ramificare" per puzzle; drag & drop între camere.
- Referință: design doc §NOT in scope "Adventure Mode v0".
## Iterația 3 — Joc-în-URL + QR
*(depinde de măsurarea dimensiunii JSON comprimate)*
- `gameHTML(cfg)` → URL data: sau LZW/gzip → QR code printabil.
- Open Question 2 din design doc: câte puzzle-uri încap în 2KB (URL QR L)?
- Alternative: GitHub Pages export automat; sau link scurt cu backend minimal.
- Referință: design doc §NOT in scope "Joc-în-URL + QR".
---
## Known improvements (oricând)
- **`updateHud` duplicat**: arcade linia 1003 și point linia 1283 au funcții identice → consolidat în `SNIP` (T8 din plan, igienă PR1).
- **`persist()` fără try/catch**: builder-ul poate crăpa pe storage plin → guard (D12, T8).
- **`esc(L)` la inserția innerHTML din point** (:1274): un `<` în câmpul `letter` strică scena SVG (D13, T8).
- **Validare 0 puzzle-uri**: export și preview blocate cu mesaj ghidant (T5).
- **Stil invalid la import JSON**: avertisment în builder + rotație automată (T5, D8).