Tanca la sèrie d'escalfament de la rama feat/level-manager:
- Levels::LevelManager com a font de veritat de la progressió
(currentPath / advance / setIndex / setStartingPoint).
- 30 sales a data/maps/p<N>/r<M>.yaml + index a data/maps/levels.yaml.
- Avanç automàtic en creuar la porta + bucle al final de la llista.
- Hotkeys debug [ / ] per saltar entre sales.
- Clau opcional starting_level al debug.yaml.
- L'editor salva sobre la sala actualment carregada.
L'esquelet ja és funcional; queda pendent dissenyar les 30 sales,
canviar tilesets/música per piràmide i decidir la pantalla de fi de
joc (ara mateix fa bucle a la primera).
El writer del tilemap deixa de fer servir la constant
Defaults::Editor::SAVE_PATH (que apuntava a data/maps/pyramid.yaml) i
demana al LevelManager el path actual. Resultat: editar i guardar
desa la sala que tens davant — no l'antic mapa monolític — i salts
debug entre sales canviaran també el destí del save.
defaultConfig() llig el path del nivell actual amb
Levels::LevelManager::get().currentPath() en lloc de la constant
Pyramid::MAP_YAML (que desapareix). El ctor sincronitza room_index_
amb LevelManager::currentRoom() per al text d'intro de sala. Encara
no hi ha progressió: cada arrencada continua a la primera sala del
levels.yaml; el comportament visible és idèntic, però la responsabilitat
de 'quin fitxer carreguem' ha passat al LevelManager.
Nou singleton Levels::LevelManager que llig data/maps/levels.yaml a la
primera get() i exposa currentPath/currentPyramid/currentRoom/
currentLabel + advance/restart/setIndex. És pur estat 'on sóc' — no
orquestra cap reload; la GameScene i l'Editor li preguntaran al pas
següent. Afegida constant Defaults::Pyramid::LEVELS_YAML al costat de
la MAP_YAML antiga (la substitució completa va al Step 3).
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.
- 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/`.
Fix: gif.cpp no desfeia l'interlace, trinxava qualsevol GIF entrellaçat
(tota la sortida de Pillow). Afegeix Defaults::UI::Font per centralitzar
noms/rutes i mou data/font → data/fonts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Afegit `Defaults::UI::` amb els 5 índexs (RENDER_INFO_TEXT/SHADOW,
CANVAS_BG, NOTIFICATION_TEXT/BG, EXIT_TEXT/BG) i 2 durades
(NOTIFICATION_TIME_MS, EXIT_TIME_MS). `ui_palette.hpp` i
`notification_presets.hpp` queden com a alias: canviar el tema global
és tocar un sol bloc. Equivalent a tenir un "tema" (vindria d'un YAML el
dia que calga).
Afegits `Defaults::Pyramid::{MAP_YAML, PLAYER_SPRITE, PLAYER_YAML,
MUSIC_TRACK}` a utils/defaults.hpp. `GameScene` exposa `Config` i
`defaultConfig()`; el ctor accepta Config (default = Defaults::Pyramid)
i usa els camps enlloc dels literals. `Player` passa a rebre el anim_key
per constructor en lloc de cablejar "player.yaml". Cache key = basename
(convenció preexistent). Cada piràmide futura serà un Config distint.
La velocitat base del jugador (60 px/s) passa de `constexpr` local a
`player.cpp` a `Defaults::Player::SPEED_PX_PER_S`, seguint el patró usat
a l'audit d'àudio. `player.cpp` manté un alias local per llegibilitat.
Elimina el comentari TODO sobre sprint/slow-zones que PATCH-02 volia fora.
{SDL_AUDIO_S16, 2, 48000} apareixia com a literal a tres llocs de
jail_audio (Sound/Music/audio_spec) i audio.cpp:208 passava SDL_AUDIO_S16LE
i 2 canals per separat — dues convencions de format conviuen i els 2
canals eren número màgic.
Defaults::Audio guanya FORMAT (SDL_AUDIO_S16) i CHANNELS (2). Audio::
initSDLAudio passa ambdós junts a Ja::init. jail_audio obté un fallback
local Ja::DEFAULT_SPEC que consolida les tres instàncies; es queda
independent del projecte (no inclou utils/defaults.hpp) perquè l'engine
és reutilitzable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VOLUME_STEP, FREQUENCY i DEFAULT_CROSSFADE_MS vivien com a membres
constexpr d'Audio. Eren valors de configuració — no invariants de la
classe — i la convenció del projecte és que tots els paràmetres
ajustables visquin a utils/defaults.hpp.
Passen a Defaults::Audio::{VOLUME_STEP, FREQUENCY, CROSSFADE_MS}. L'únic
ús intern (Ja::init al constructor d'Audio) es reapunta al Defaults. Cap
ús extern dels tres símbols.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
render_info.cpp tenia diverses "cifres soltes" amb significat
estructural:
- Índex 0/1/2/3 dins segments_ per a FPS/driver/shader/temps — ara
FPS_SEGMENT, DRIVER_SEGMENT, SHADER_SEGMENT, TIME_SEGMENT (HC-06).
- +1,+1 per a la ombra del text — ara SHADOW_OFFSET_PX (HC-09).
- % 3 al cicle de posicions de global_inputs.cpp — ara
Defaults::RENDER_INFO_POSITION_COUNT (HC-07).
A més, init() assignava mono_digits=true al segment de temps que
refreshSegments() ja marca a cada crida, inclosa la primera d'update()
abans de render. S'elimina el doblegat (PATCH-08); el comentari
explica la garantia.
Build + smoke 5s xvfb OK (exit 124).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>