Afig una capa d'efectes oberta a la unitat d'àudio. Els sons puntuals
es poden reproduir processats per un eco o un reverb (Freeverb 8C/4AP)
sense afectar la música ni canviar el camí ràpid existent (playSound
sense efecte segueix sent una sola crida sense cost extra).
- data/sounds.yaml: catàleg de presets nominats (echo: cave/short,
reverb: stone_hall/small_room).
- core/audio/audio_effects: processadors purs que prenen un Ja::Sound
(U8/S16, mono o estèreo) i tornen PCM S16 amb la cua de l'efecte
(cap a 800 ms eco, 1500 ms reverb).
- core/audio/sound_effects_config: Meyers singleton que parseja
sounds.yaml a EchoParams/ReverbParams indexats per nom; l'API
pública d'Audio fa el lookup per nom.
- jail_audio::Engine: nou camí playSoundWith{Echo,Reverb} que empenta
el buffer ja processat a un canal lliure marcat com a has_effect.
El cap MAX_EFFECT_CHANNELS limita a 4 sons amb efecte simultanis;
si està al límit, l'engine retorna -1 i Audio cau a la versió seca.
- Audio::playSoundWith{Echo,Reverb}(name, preset, group) com a única
via pública. Si el preset no existeix, fallback dry amb avís.
- director: load("data/sounds.yaml") abans d'Audio::init.
Nova fase 4 a ROOM_OUTRO: black_hold. Va just després del fade_out;
durant aquesta fase la pantalla queda completament en negre opac i
la FSM espera abans de transitar a INTRO. Permet donar respir entre
el final del rètol Fase/Completada i el rellançament del Piramide/Sala
de la sala següent, sense necessitat d'un estat nou.
Implementacio:
- RoomFsm::Config guanya outro_black_hold_ms (default 0 — backward
compatible). YAML key room_outro.black_hold_ms al data/scenes/game.yaml
(default visible 500 ms; lo afines sense recompilar).
- L'array DURS de l'OUTRO passa de 4 a 5 elements; el while-loop
consumeix la fase exactament igual.
- renderRoomFade detecta fase 4 i dibuixa alpha=255 fix; sense aquesta
branca, el fade s'apagaria a final de la fase 3 i el món tornaria
a ser visible durant la pausa.
- renderRoomText no canvia: phases 3 i 4 ja queden fora del rang
0..2 on dibuixa el rètol.
Backward compat: black_hold_ms=0 fa que el while-loop avance
immediatament la fase i recauen tots els tests existents (T7 i
companys) sense modificacions.
Mateixa tècnica que l'INTRO: la línia 1 ("Fase") entra per
l'esquerra, la línia 2 ("Completada") per la dreta, i surten cap
al costat oposat al d'entrada. Centrades verticalment al voltant
del CENTER_Y amb el gap configurable ( a
data/scenes/game.yaml; 0 per defecte, paral·lel a la del room_intro).
Locale: la clau game.room_complete es divideix en
game.room_complete_line1 / room_complete_line2 als ca.yaml i en.yaml
("Fase"/"Completada" i "Stage"/"Complete"). Cada línia es
tradueix per separat — útil si demà voleu paraules de longitud o
ordre distint en altres idiomes.
Hook només Debug que crida EntityManager::forceOpenDoor (nou helper
públic que delega a Door::open). El flux posterior — creuar la porta,
player_exited, level_complete, transició a OUTRO — funciona idèntic
al del joc real, perquè depèn de la Door, no del comptador de
sarcòfags. Útil per saltar la mecànica de pintat/sarcs durant proves
de progressió de sales.
Tecla configurable a data/input/keys.yaml > id: open_door
(per defecte Keypad 1). Mateixos gates que els salts [/]: ignora la
pulsació si la pausa està activa o l'editor obert. En Release la
ENTRADA del YAML segueix carregant-se però no té consumidor.
Dues claus noves a data/ui.yaml > colors:
room_text (relleno, default 232)
room_text_outline (contorn, default 255)
Abans el relleno reciclava notification_text (compartit amb les
notificacions del HUD) i el contorn estava hardcodejat a 255 dins
renderRoomText. Ara són canals separats: tocar el rètol no afecta
les notificacions i tot viu al YAML.
UiConfig guanya roomText() / roomTextOutline(); Defaults::UI hi posa
els fallbacks; renderRoomText els consumeix.
Drive-by: torna a posar la clau room_intro.line_gap_px: 8 al
data/scenes/game.yaml — s'havia perdut en una edició manual del
fitxer per afinar timings.
Nova font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO):
CaesarDressing-Regular, vista com a fonts.room_text al data/ui.yaml i
exposada per UiConfig::roomTextFont(). Director la preloda al costat
de la del HUD i la del placeholder; GameScene construeix
room_text_font_ paral·lel a pause_font_ i renderRoomText l'usa per a
les dues línies de l'INTRO i el text de l'OUTRO. La pausa segueix
amb la font placeholder antiga — no es barregen.
Espai vertical configurable: nova clau room_intro.line_gap_px al
data/scenes/game.yaml (default 8 px). El gap es suma al lineHeight
natural del font i es reparteix simètricament cap amunt i cap avall
del CENTER_Y, deixant el bloc de dues línies sempre centrat. Es
poden afinar des del YAML sense recompilar.
HUD: la GameScene crida hud_->setPyramidRoom amb els valors del
LevelManager al ctor i a cada loadRoom — el marcador deixa de
mostrar 1/1 fix i s'actualitza en avançar de sala, en saltar amb
[/] i en arrencar amb starting_level del debug.yaml.
INTRO a dues línies: substitueix la clau game.room_intro per
game.room_intro_pyramid ("Piramide {p}") i game.room_intro_room
("Sala {r}") als locale ca/en. RoomFsm::formatRoomTitle(int)
desapareix i el reemplacen formatIntroPyramidLine(int) +
formatIntroRoomLine(int). renderRoomText es refactoritza per
dibuixar les dues línies (piràmide damunt, sala davall) amb
writeDX independents — comparteixen animació ara mateix però el
disseny ja les deixa preparades per ser desacoblades en el futur.
Drive-by: elimina el membre room_index_ de GameScene; les tres
ubicacions que el sincronitzaven amb el LevelManager (ctor,
advanceToNextRoom, salt debug) ja no fan falta — renderRoomText
i el HUD pregunten directament al singleton, evitant qualsevol
mirror local desincronitzable.
Test: T9/T10 actualitzats per a les dues noves formatadores.
Tecles [ i ] al keys.yaml (prev_room / next_room) que mouen
LevelManager::setIndex en ±1 i recarreguen la sala via loadRoom +
enterRoomIntro. Ignorades si la pausa està activa o l'editor obert
(qualsevol dels dos invalida l'estat editat).
Refactor de la FSM al mateix pas: l'avanç de sala (LevelManager +
loadRoom) ja no viu dins setState — el dispara updateRoomTransition
quan detecta el final de l'OUTRO via advanceToNextRoom. setState
torna a ser una transició pura, condició necessària perquè el salt
debug puga entrar a INTRO sense disparar un advance addicional.
Nou helper enterRoomIntro() reusable per la sortida de l'OUTRO i pels
salts; idempotent quan ja s'estava a INTRO.
Crea data/maps/levels.yaml com a font de veritat de la progressió (5
piràmides × 6 sales = 30 entrades ordenades) i afegeix els camps
pyramid/room a cada r<M>.yaml per a logs i text d'intro. El
LevelManager (a venir) consumirà aquesta llista; els fitxers de sala
encara són clons de pyramid.yaml — el contingut real el modificaré
després de tenir l'esquelet.
Captura de pantalla amb F9: volca el canvas ARGB a
screenshots/scr_<timestamp>.png via stb_image_write.
Tot el tema d'UI (colors, timings, fonts, flag hud.bg_enabled)
passa a data/ui.yaml via UiConfig; els valors de Defaults::UI
queden com a fallbacks si el fitxer falta o li manca alguna clau.
Retocs estètics: paleta "uzebox" per a la UI, strings del HUD
(P1/P2/V/T/PIRAMIDE/SALA) traduïts a locale, i fons del marcador
opcional amb backdrop continu de franja per evitar el trencament
visual durant el shake o l'animació d'entrada dels panells.
`data` passa a ser una llista de files d'enters (tile index lineal),
sense la indirecció `legend:` + símbols. Desapareix el límit implícit
de ~60 tiles distints i el grid segueix llegint-se com a matriu visual
gràcies al padding de 3 dígits. `-1` marca cel·la buida.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Nou format de pack AE26 v1 (XOR + djb2), eina `pack_resources`,
regeneració automàtica de `build/resources.pack` a cada build.
- `Resource::Helper` amb init/shutdown, fallback a disc només en Debug
(apunta al source tree); Release exigeix pack present.
- Callers de YAML migrats a `loadFile`+deserialize des de buffer.
- Arrel neta: el pack viu a `build/`, eliminat `sync_data`.
- Placeholder font -> `ari-w9500-condensed-display`; esborrades variants
bold/condensed no usades a `data/fonts/`.