# 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, verificat în browser)* Cauză reală: orchestratorul crea `beep._ctx` lazy la primul `parent.beep()` din iframe; gestul din iframe NU deblochează AudioContext-ul părintelui → ctx `suspended` → tăcere. (Ipoteza HANDOFF „beep nedefinit" era greșită; `beep` e la `escape-builder.html:1725`.) Fix: deblocare ctx în handler-ul `btn-start` (gest direct pe părinte), `escape-builder.html:1928`. Verificat: `scratch/verify-audio-s1.mjs` → ctx `running` după start (era `NO_CTX`). Smoke 21/21. TODO la S4: portează asertarea `beep._ctx.state==='running'` în `tests/smoke.mjs`. - [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).