Les 10 ocurrències de std::cerr a options.cpp (incloent les de PATCH-04)
passen a std::cout per complir la convenció del projecte ("std::cout
only, no SDL_Log"). Prova manual: amb config.yaml trencat, els missatges
de backup i reescriptura es veuen per stdout.
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.
Afegit bloc `collision:` (size 16, base_size 12) al YAML del player; les
`static constexpr` de `player.hpp` passen a `inline static int` (snake_case)
i es calculen al `Player::loadFrom()`. `sprite_w/sprite_h` es llegeixen
dels mateixos frameWidth/frameHeight que ja hi viuen. GameScene crida el
load abans del spawn (mateix patró que `Map::loadFrom`). Permet ajustar
la caixa del jugador sense recompilar.
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.
Helper `backupFile(path)` que copia el YAML a `<path>.bak` si existeix.
Es crida a `loadFromFile` (versió mismatch i catch) i a
`loadPresetsFromFile<T>` (manca `presets:`, llista buida, catch).
La primera execució no genera .bak. Cada reescriptura forçada registra el
motiu per stderr (INCONS-02 migrarà a std::cout).
Prova manual: config.yaml amb contingut \"===broken===\" genera
config.yaml.bak amb el contingut previ i un nou config.yaml amb defaults.
Afegits `loadPresetsFromFile<T>` i `savePresetsToFile<T>` a l'anonymous
namespace; els quatre entry-points passen a ser one-liners que injecten
parse/write/defaults per tipus. Abans eren dues còpies del mateix codi
diferenciades només pel tipus concret de preset. Els YAML regenerats són
byte-a-byte idèntics als anteriors (el formatter i els comentaris es
preserven).
El fitxer definia presets del Notifier (que ja viu a core/rendering/), però
estava a game/ i era consumit únicament per core/input/global_inputs.cpp
— una dependència invertida core→game. El mogueu al costat del component
que consumeix; el comentari de `notifier.hpp` hi apunta. `game/ui/` es
queda només amb `ui_palette.hpp`.
El bloc `geometry:` de pyramid.yaml porta les 9 constants base (grid, sarc,
pitch, HUD, murs); els derivats (corridor, board, origin, playfield) es
recalculen al `Map::loadFrom()`. `constexpr` passa a `const` on calia i
els membres estàtics adopten snake_case. Desbloqueja la iteració
"pirámide a pirámide" sense recompilar.
Inventari de duplicació, hardcoding, parxes, acoplament, inconsistències,
codi mort i violacions de convencions. Tots els ítems PENDING; els commits
següents en tancaran un a un.