@@ -0,0 +1,553 @@
# Arquitectura de **JailDoctor's Dilemma**
> Guía de orientación para un desarrollador nuevo en el proyecto.
>
> Cada afirmación está anclada a código real: se cita el fichero (y, cuando
> ayuda, la función o el número de línea) que la respalda. Donde el código
> contradice a la documentación previa (`CLAUDE.md`), lo señalo: **manda el
> código**.
>
> **JailDoctor's Dilemma** es un *puzzle-platformer* 2D retro en C++20 + SDL3:
> 60+ habitaciones interconectadas, ítems coleccionables, enemigos y logros.
> Resolución de juego **256× 192**. Los comentarios del código están en
> español/valenciano; este documento está en castellano.
---
## Índice
1. [Visión general ](#1-visión-general )
2. [Punto de entrada y bucle principal ](#2-punto-de-entrada-y-bucle-principal )
3. [Escenas y flujo de la aplicación ](#3-escenas-y-flujo-de-la-aplicación )
4. [Renderizado: de la lógica al píxel ](#4-renderizado-de-la-lógica-al-píxel )
5. [Entrada ](#5-entrada )
6. [Lógica del juego: la escena `Game` ](#6-lógica-del-juego-la-escena-game )
7. [Habitaciones y colisión ](#7-habitaciones-y-colisión )
8. [Entidades ](#8-entidades )
9. [Logros, estadísticas y marcador ](#9-logros-estadísticas-y-marcador )
10. [Editor de mapas (Debug) ](#10-editor-de-mapas-debug )
11. [Consola y notificaciones ](#11-consola-y-notificaciones )
12. [Modo demo ](#12-modo-demo )
13. [Recursos ](#13-recursos )
14. [Audio, localización y configuración ](#14-audio-localización-y-configuración )
15. [Convenciones y patrones recurrentes ](#15-convenciones-y-patrones-recurrentes )
16. [Guía de navegación: "si quieres tocar X, mira Y" ](#16-guía-de-navegación-si-quieres-tocar-x-mira-y )
---
## 1. Visión general
El árbol `source/` separa **motor ** y **juego ** :
- **`source/core/` ** — motor genérico: `system` (`director` , `debug` ,
`global_events` ), `rendering` (+ `sprite` , `sdl3gpu` ), `input` , `resources` ,
`audio` , `locale` .
- **`source/game/` ** — el juego concreto: `scenes/` , `gameplay/` , `entities/` ,
`editor/` , `ui/` , `options.*` , `scene_manager.hpp` , `defaults.hpp` .
- **`source/utils/` ** — `delta_timer` , `easing_functions` , `utils` , `defines` .
- **`source/external/` ** — vendorizado: `fkyaml` , `stb_image` , `stb_vorbis` .
Es el proyecto más grande de su familia: **138 ficheros C++, ~54.000 líneas ** .
**Ideas-fuerza que conviene interiorizar: **
1. **Render paletizado por CPU ** : `Surface` de 8 bits indexados + paleta, igual
filosofía que un motor retro clásico; la GPU solo escala y aplica post-FX
(§4).
2. **Flujo por `SceneManager::current` ** (variable global) + un único
`active_scene_` que el `Director` conmuta (§3).
3. **El mundo son habitaciones ** de 256× 128 px en tiles de 8 px, con colisión
por superficies (suelos, paredes, rampas, cintas) y transición entre salas
contiguas (§7).
4. Trae **editor de mapas ** y **consola de comandos ** integrados (solo Debug)
(§10, §11), un **sistema de logros ** persistente (§9), y un **modo demo **
que es un * tour de habitaciones * (§12).
``` mermaid
graph TD
SDL[SDL3 callbacks · main.cpp] --> DIR[Director]
DIR -->|SceneManager::current| SW{switchToActiveScene}
SW --> SCN["BootLoader / Logo / Title / Game / Demo / Ending…"]
SCN --> GAME["Game (Mode GAME/DEMO)"]
GAME --> ROOM[Room + colisión] & RL[room_loader]
GAME --> PLAYER[Player] & EM[enemy_manager] & IM[item_manager]
GAME --> CHV[Cheevos] & STT[Stats] & SCB[Scoreboard]
GAME -->|blit paletizado| SURF["Surface (8-bit indexed)"]
SURF -->|toARGBBuffer / copyToTexture| SCREEN[Screen]
SCREEN --> GPU["ShaderBackend PostFX/CrtPi"] --> WIN[Ventana]
SCREEN -.fallback.-> WIN
RES["Resource::Cache / List"] -.-> GAME & SCN
EDIT["MapEditor (Debug)"] -.-> GAME
CON[Console] -.-> GAME
```
---
## 2. Punto de entrada y bucle principal
### 2.1. SDL conduce el bucle (callbacks)
`source/main.cpp` define `SDL_MAIN_USE_CALLBACKS` : no hay `while` propio.
``` cpp
SDL_AppInit → new Director ( ) ;
SDL_AppIterate → Director : : iterate ( ) ; // un frame
SDL_AppEvent → Director : : handleEvent ( event ) ;
SDL_AppQuit → delete Director ;
```
> ⚠️ **Discrepancia con el `CLAUDE.md`**: este describe un `Director::run()` con
> un bucle `while (SceneManager::current != QUIT)`. **El código actual no es
> así**: usa la API de callbacks de SDL3. El `Director` real
> (`core/system/director.hpp`) expone `iterate()` y `handleEvent()`, no `run()`.
### 2.2. El `Director`
`source/core/system/director.{hpp,cpp}` . Mantiene **un solo
`std::unique_ptr<Scene> active_scene_` ** y un enum `current_scene_` . No guarda un
puntero por escena (a diferencia de los proyectos hermanos): construye la escena
bajo demanda en `switchToActiveScene()` (`director.cpp` ).
El constructor inicializa los subsistemas en orden: `Resource::List` (registro
de assets desde `config/assets.yaml` ), `Options` , `Audio` , `Screen` , `Input` ,
`Resource::Cache` (con `beginLoad()` ), y arranca en la escena `BOOT_LOADER` .
### 2.3. Arranque NO bloqueante
`Resource::Cache` no se carga de golpe. El constructor deja la escena en
`BOOT_LOADER` (una barra de progreso) y cada frame `Director::iterate()` llama a
`Resource::Cache::get()->loadStep(50 /*ms*/)` (`director.cpp` ): carga assets
hasta agotar un presupuesto de 50 ms por frame, manteniendo ventana y eventos
vivos. Cuando termina, `finishBoot()` inicializa lo que depende de los recursos
(`Notifier` , `RenderInfo` , `Console` , `Cheevos` , `Locale` , en Debug `Debug` y
`MapEditor` ) y fija la escena destino (`LOGO` en release; en Debug, la que diga
`debug.yaml` vía `Debug::getInitialScene()` ).
### 2.4. Gestión del tiempo
**Time-based ** : la escena `Game` posee un `DeltaTimer delta_timer_`
(`utils/delta_timer.hpp` ) y toda la física/animación consume `delta_time` en
segundos. Las constantes de tiempo se documentan como "N frames a 66.67 fps →
segundos" (p.ej. `BLACK_SCREEN_DURATION = 0.30F` , `game.hpp:46` ).
---
## 3. Escenas y flujo de la aplicación
### 3.1. La base `Scene` y el `SceneManager`
`source/game/scenes/scene.hpp` es minimalista:
``` cpp
class Scene {
public :
virtual void iterate ( ) = 0 ; // un frame (update + render)
virtual void handleEvent ( const SDL_Event & ) = 0 ; // un evento
} ;
```
`source/game/scene_manager.hpp` define el flujo con **variables globales
`inline` ** en el namespace `SceneManager` :
``` cpp
enum class Scene { BOOT_LOADER , LOGO , LOADING_SCREEN , TITLE , CREDITS ,
GAME , DEMO , GAME_OVER , ENDING , ENDING2 , RESTART_CURRENT , QUIT } ;
inline Scene current = Scene : : BOOT_LOADER ;
inline Options options = Options : : LOGO_TO_LOADING_SCREEN ;
inline Scene scene_before_restart = Scene : : LOGO ;
```
Cualquier escena solicita una transición asignando `SceneManager::current` .
### 3.2. La conmutación
`Director::switchToActiveScene()` (`director.cpp` ):
- `RESTART_CURRENT` es especial: restaura `scene_before_restart` (relanza la
escena que estaba activa).
- `active_scene_.reset()` destruye la anterior (su destructor puede parar la
música, etc.).
- Un `switch` construye la concreta: `BootLoader` , `Logo` , `LoadingScreen` ,
`Title` , `Credits` , `Game(Mode::DEMO)` , `Game(Mode::GAME)` , `GameOver` ,
`Ending` , `Ending2` .
Nótese que **DEMO y GAME son la misma clase `Game` ** , parametrizada por
`Game::Mode` (§12).
``` mermaid
graph LR
BOOT[BOOT_LOADER] --> LOGO --> LOADING[LOADING_SCREEN] --> TITLE
TITLE -->|jugar| GAME --> ENDING --> ENDING2 --> CREDITS
TITLE -->|attract| DEMO --> TITLE
GAME --> GAME_OVER --> TITLE
TITLE --> QUIT
```
---
## 4. Renderizado: de la lógica al píxel
El render es **paletizado por CPU ** : se dibuja sobre superficies de 8 bits
indexados y solo al final se sube a la GPU.
### 4.1. `Surface`: 8 bits indexados + paleta
`source/core/rendering/surface.hpp` . Una `Surface` guarda los píxeles como
**índices `Uint8` ** (`SurfaceData` ) más una * * `Palette` de 256 colores ARGB** y
una * * `SubPalette` ** (remapeo de índices, identidad por defecto vía `std::iota` ).
Operaciones clave:
- `render(...)` / `renderWithColorReplace(src, dst)` — blit con color
transparente y reemplazo de índice (para recolorear sprites/glifos).
- `renderWithVerticalFade(...)` — disolución por hash 2D (cantos).
- `fadePalette()` / `fadeSubPalette()` — fundidos manipulando la paleta.
- `copyToTexture(...)` y * * `toARGBBuffer(buffer)` ** — vuelcan la surface a una
`SDL_Texture` o a un buffer ARGB externo.
Sobre `Surface` se construyen los sprites (`core/rendering/sprite/` ):
`Sprite` → `AnimatedSprite` (frames `.yaml` ) → `MovingSprite` (posición/velocidad)
y `DissolveSprite` (transición). Texto: `text.*` . Efectos: `pixel_reveal.*` .
Paletas: `palette_manager.*` (el juego permite **cambiar de paleta en caliente ** ;
ver §5).
### 4.2. `Screen` y la composición
`source/core/rendering/screen.{hpp,cpp}` . Hay dos superficies/texturas:
- **`game_surface_` / `game_texture_` ** — el canvas de juego 256× 192
(`SDL_TEXTUREACCESS_STREAMING` , ARGB8888; `screen.cpp:125` ).
- **`border_surface_` / `border_texture_` ** — el borde/overscan alrededor del
canvas.
El path de presentación (`Screen::render` , `screen.cpp:197` ):
- **Con backend GPU acelerado**: vuelca las superficies a buffers ARGB
(`toARGBBuffer` ) y los sube al `shader_backend_` (`uploadPixels` ), que renderiza
con el shader activo.
- **Sin backend** (fallback): `copyToTexture` + `SDL_RenderTexture` de
`game_texture_` y `border_texture_` a la ventana.
El backend vive en `core/rendering/sdl3gpu/` (interfaz `shader_backend.hpp` ). Dos
shaders: **PostFX ** y **CrtPi ** (scanlines, curvatura, máscara, etc.), GLSL en
`data/shaders/` compilados a SPIR-V (`spv/*_spv.h` ), o Metal (MSL) en macOS
(`sdl3gpu/msl/` ). En Emscripten (`NO_SHADERS` ) se fuerza la ruta clásica.
``` mermaid
graph TD
OBJ["room, player, enemies, items, HUD…"] -->|blit índices| GS["game_surface_ (256× 192, 8-bit)"]
BORDER["borde / overscan"] --> BS[border_surface_]
GS -->|toARGBBuffer / copyToTexture| SCREEN[Screen]
BS --> SCREEN
SCREEN -->|uploadPixels| SHADER["ShaderBackend (PostFX / CrtPi)"]
SHADER --> WIN[Ventana]
SCREEN -.fallback SDL_Renderer.-> WIN
```
---
## 5. Entrada
### 5.1. `Input`
`source/core/input/input.{hpp,cpp}` + `input_types.*` — abstracción de teclado y
mando bajo un enum `InputAction` . Las vinculaciones se aplican desde `Options`
(`Input::applyKeyboardBindingsFromOptions()` /
`applyGamepadBindingsFromOptions()` , `director.cpp` ). `mouse.*` gestiona el ratón
(usado sobre todo por el editor).
### 5.2. Hotkeys globales
`source/core/input/global_inputs.{hpp,cpp}` traduce eventos a acciones de sistema
(`global_inputs.cpp` ):
- **Ventana/vídeo**: fullscreen, zoom ±, integer scale, vsync, info.
- **Shaders**: toggle; con **Ctrl ** → siguiente shader, con **Shift ** →
siguiente preset.
- **Paletas**: siguiente / anterior (`NEXT_PALETTE` /`PREVIOUS_PALETTE` ), y orden
de paleta — una seña de identidad de este juego (paleta intercambiable).
- **Borde** (overscan) toggle, **consola ** toggle, **EXIT ** .
- En `GAME` , EXIT vuelve a `TITLE` ; el `QUIT` global sale del programa.
Cuando la **consola está activa ** , `EXIT` /`ACCEPT` se redirigen a ella en vez de
a la escena (`global_inputs.cpp:231` ).
---
## 6. Lógica del juego: la escena `Game`
`source/game/scenes/game.{hpp,cpp}` es la escena de gameplay. Hereda de `Scene` y
coordina habitación, jugador, enemigos, ítems, marcador, estadísticas y logros.
### 6.1. FSM de la escena
`Game::State` (`game.hpp:28` ): `PLAYING → BLACK_SCREEN → GAME_OVER →
FADE_TO_ENDING → POST_FADE_ENDING` . Cada estado tiene su `updateX` /`renderX` ;
`transitionToState()` cambia de estado y resetea los timers. El modo
(`Game::Mode::GAME` o `DEMO` ) condiciona el comportamiento.
### 6.2. El frame
`Game::iterate()` calcula el delta con el `DeltaTimer` , llama a `update()`
(input + lógica + colisiones + cambio de sala) y a `render()` . El render del
estado `PLAYING` va directo a las superficies; los fades de fin de juego usan un
`game_backbuffer_surface_` .
### 6.3. Qué gestiona
- **Habitación activa** (`std::shared_ptr<Room> room_` ) y cambio de sala al tocar
un borde (`changeRoom` , `checkPlayerIsOnBorder` ; §7).
- **Jugador** (`Player` ), con muerte (`killPlayer` , `BLACK_SCREEN` ), y la "Jail"
que restaura vidas con el tiempo (`checkRestoringJail` , `JAIL_RESTORE_INTERVAL` ).
- **Colisiones**: jugador↔enemigos (`checkPlayerAndEnemies` ) y jugador↔ítems
(`checkPlayerAndItems` ).
- **Progresión**: `RoomTracker` (salas visitadas), `Stats` , `Scoreboard` , fin de
juego (`checkEndGame` ) y secuencias de **ending ** (fades a `ENDING` /`ENDING2` ).
- **Logros**: `checkSomeCheevos` , `checkEndGameCheevos` (§9).
---
## 7. Habitaciones y colisión
`source/game/gameplay/room.{hpp,cpp}` modela cada sala. Geometría: tiles de
**8 px ** , mapa de **32× 16 ** tiles (256× 128 px). Los tipos de tile
(`Room::Tile` ) son `EMPTY, WALL, PASSABLE, SLOPE_L, SLOPE_R, KILL, ANIMATED`
(`room.hpp:32` ).
### 7.1. Datos de sala
`Room::Data` (`room.hpp:42` ) se carga de YAML (vía `RoomLoader` ,
`Room::loadYAML` ). Contiene número/nombre, colores (fondo, borde, ítems),
**salas contiguas ** (`upper_room` , `lower_room` , `left_room` , `right_room` →
navegación tipo * metroidvania * ), tileset, el `tile_map` embebido, y las listas
de enemigos e ítems.
### 7.2. Colisión por superficies
La colisión no es AABB simple contra tiles, sino consultas de **superficies ** :
`checkRightSurfaces` , `checkLeftSurfaces` , `checkTopSurfaces` ,
`checkBottomSurfaces` , `checkAutoSurfaces` (cintas), más rampas
(`checkLeftSlopes` /`checkRightSlopes` , `getSlopeHeight` , `getSlopeAtPoint` ) y
cintas transportadoras (`checkConveyorBelts` , `conveyor_belt_direction_` ). El
jugador aporta puntos de colisión finos (8 `collider_points_` + `under_left_foot_`
/ `under_right_foot_` ; `player.hpp:147` ). Los tiles `KILL` matan al jugador.
Subobjetos de `Room` : `CollisionMap` (datos de colisión), `TilemapRenderer`
(dibujo del tilemap), `EnemyManager` e `ItemManager` (ciclo de vida de enemigos
e ítems de la sala). `RoomTracker` (`gameplay/room_tracker.*` ) registra las salas
visitadas.
---
## 8. Entidades
`source/game/entities/` :
- **`Player` ** (`player.hpp` ) — física * time-based * con `JUMP_VELOCITY = -80` ,
`GRAVITY_FORCE = 155.6` px/s² (`player.hpp:42` ). FSM de estados (IDLE/WALKING/
JUMPING/…), colisión por 8 puntos + "pies", controladores de sonido de salto y
caída (`JumpSoundController` /`FallSoundController` ), y un `SpawnData` para
reaparecer (también usado al cambiar de sala conservando velocidad).
- **`Enemy` ** (`enemy.hpp` ) — enemigos con datos (`Enemy::Data` ), colisión AABB,
gestionados por `EnemyManager` (`gameplay/enemy_manager.*` ).
- **`Item` ** (`item.hpp` ) — coleccionables (`Item::Data` ), gestionados por
`ItemManager` (`gameplay/item_manager.*` ) y rastreados por `ItemTracker` .
No hay una clase base de entidad común con polimorfismo profundo: cada tipo tiene
su `update` /`render` /colisión y su * manager * dedicado dentro de la `Room` .
---
## 9. Logros, estadísticas y marcador
- **`Cheevos` ** (`source/game/gameplay/cheevos.{hpp,cpp}` ) — singleton del sistema
de logros. `unlock(id)` , `setUnobtainable(id)` , `getTotalUnlockedAchievements()` ;
estado **persistido en `cheevos.bin` ** (`loadFromFile` /`saveToFile` ). La escena
`Game` llama a `checkSomeCheevos` /`checkEndGameCheevos` , y el `Notifier` muestra
el logro en pantalla (§11).
- **`Stats` ** (`gameplay/stats.*` ) — diccionario de estadísticas de partida
(`initStats` ).
- **`Scoreboard` ** (`gameplay/scoreboard.*` ) — datos y dibujo del marcador
(`Scoreboard::Data` se comparte por `shared_ptr` con la sala y el editor).
- **`ItemTracker` ** / * * `RoomTracker` ** — progreso de ítems recogidos y salas
visitadas.
---
## 10. Editor de mapas (Debug)
`source/game/editor/` — **solo se compila en `_DEBUG` ** (todo el header de
`MapEditor` está bajo `#ifdef _DEBUG` , `map_editor.hpp:3` ). Es un editor de
habitaciones * in-game * completo, integrado con la escena `Game` y con la consola.
### 10.1. `MapEditor` (singleton)
`map_editor.hpp` . Se entra con `enter(room, player, room_path, scoreboard_data)`
sobre la sala viva. Funcionalidades:
- **Pintado de tiles** con * brush * (`brush_tile_` , `ERASER_BRUSH` , `painting_` ),
preview bajo el cursor y rejilla opcional (`renderGrid` , `settings_.grid` ).
- **Drag & drop** de jugador, enemigos (posición inicial y * bounds * de patrulla)
e ítems (`DragTarget` , `DragState` , `handleMouseDown/Up` , `updateDrag` ), con
* snap * a rejilla.
- **Edición de propiedades** de enemigos, ítems y de la sala
(`setEnemyProperty` , `setItemProperty` , `setRoomProperty` , colores, color de
fondo…), invocables tanto por teclas como por **comandos de consola ** .
- **Gestión de salas**: crear (`createNewRoom(direction)` ), borrar (`deleteRoom` ),
con conexión a las salas contiguas.
- **Persistencia**: `autosave()` + `room_saver.*` escribe el YAML de la sala;
`revert()` restaura desde el backup del nodo YAML (`yaml_backup_` ).
### 10.2. Subcomponentes del editor
- **`TilePicker` ** (`tile_picker.*` ) — selector visual de tiles del tileset
(`openTilePicker` ).
- **`MiniMap` ** (`mini_map.*` ) — minimapa de salas con conexiones, colores
configurables (`setMiniMapBg` /`setMiniMapConn` ).
- **`EditorStatusBar` ** (`editor_statusbar.*` ) — barra de estado con info de
edición (`updateStatusBarInfo` ).
- **`RoomSaver` ** (`room_saver.*` ) — serialización de la sala a YAML preservando
campos no editados.
El editor guarda/restaura estado del juego al entrar/salir (invencibilidad,
overlay de info) para no contaminar la partida.
---
## 11. Consola y notificaciones
### 11.1. `Console`
`source/game/ui/console.{hpp,cpp}` — consola de comandos * in-game * (singleton),
con estética de terminal verde sobre `Surface` propia. Características
(`console.hpp` ):
- Panel animado (`Status` HIDDEN/RISING/ACTIVE/VANISHING), efecto * typewriter * ,
cursor parpadeante.
- **Historial** navegable (flechas), **autocompletado por TAB ** (`tab_matches_` ),
* word-wrap * por ancho en píxeles.
- **`CommandRegistry` ** (`console_commands.{hpp,cpp}` ): metadatos (desde YAML) +
* handlers * C++. Los comandos cubren depuración del juego y **pilotan el editor
de mapas** (`setEnemyProperty` , `addItem` , `setRoomProperty` , etc.).
- **Scopes** (`setScope` /`getScope` ): filtran qué comandos y autocompletados
están disponibles según el contexto (p.ej. dentro del editor).
- `on_toggle` notifica a la escena cuando se abre/cierra (para pausar input de
juego).
### 11.2. `Notifier`
`source/game/ui/notifier.{hpp,cpp}` — cola de notificaciones en pantalla (logros
desbloqueados, cambios de opción…). Se inicializa en `finishBoot()` y el `Screen`
las pinta como overlay (`renderNotifications` ). El overlay de FPS/driver es
`core/rendering/render_info.*` (toggle por hotkey).
---
## 12. Modo demo
> **El modo demo de este juego NO es reproducción de input grabado.** Es un
> **tour automático de habitaciones** (escaparate de niveles).
La escena `Game` construida con `Game::Mode::DEMO` recorre una **lista curada de
salas** y va cambiando cada `DEMO_ROOM_DURATION = 6.0F` segundos
(`game.hpp:48` ):
``` cpp
// game.cpp — demoInit()
demo_ = DemoData ( 0.0F , 0 , { " 04.yaml " , " 54.yaml " , " 20.yaml " , " 09.yaml " ,
" 05.yaml " , " 11.yaml " , " 31.yaml " , " 44.yaml " } ) ;
// demoCheckRoomChange(): acumula delta_time y, al llegar a 6s,
// avanza demo_.room_index y changeRoom(...). Al agotar la lista, vuelve.
```
No hay ficheros `.bin` ni `DemoKeys` : la demo simplemente pasea por las
habitaciones para la pantalla de atracción. La salida de la demo devuelve a la
escena de título.
---
## 13. Recursos
- **`Resource::List` ** (`core/resources/resource_list.*` ) — registro de rutas de
asset cargado de * * `config/assets.yaml` **, con consulta `get(filename)` O(1).
- **`Resource::Cache` ** (`core/resources/resource_cache.*` ) — caché de surfaces,
música, sonidos y datos de animación (`getSurface` , `getMusic` ,
`getAnimationData` ). Carga **incremental ** vía `beginLoad()` + `loadStep(ms)`
(§2.3), con una FSM interna de etapas (`LoadStage` ).
- **Pack y fallback**: `resource_pack.*` + `resource_loader.*` + `resource_helper.*`
sirven desde * * `resources.pack` ** (release) o el filesystem (desarrollo).
- **Formatos**: GIF (gráficos + paletas, `core/rendering/gif.*` ); `.yaml` para
animaciones y para las salas (`data/room/` ); `.pal` para paletas
(`data/palette/` ); OGG/WAV para audio; GLSL para shaders.
---
## 14. Audio, localización y configuración
- **Audio**: `core/audio/audio.*` (singleton, música y SFX) + `audio_adapter.*`
sobre * * `jail_audio` ** (`jail_audio.hpp` ), wrapper SDL3 * first-party * con
`stb_vorbis` para OGG.
- **Localización**: `core/locale/locale.*` carga las cadenas de `data/locale/` .
En release el locale vive dentro del pack (`Locale::initFromContent` );
`Options::language` selecciona el idioma.
- **Configuración**: `source/game/options.{hpp,cpp}` mantiene las opciones
(ventana, vídeo+shaders, audio, idioma, controles, presets PostFX/CrtPi) y las
persiste; `source/game/defaults.hpp` reúne las constantes de gameplay y layout
(canvas 256× 192, tamaños de tile, colores de paleta). En Debug, `debug.yaml`
(`core/system/debug.*` ) fija la escena inicial.
- **Builds condicionales**: `_DEBUG` (editor, consola, overlay), `RELEASE_BUILD` ,
`__EMSCRIPTEN__` (locale/paths especiales, `NO_SHADERS` ), y la selección de
shaders por plataforma (SPIR-V vs Metal).
---
## 15. Convenciones y patrones recurrentes
- **Singletons con `init()` /`destroy()` /`get()` **: `Screen` , `Input` , `Audio` ,
`Resource::Cache` , `Resource::List` , `Cheevos` , `Console` , `Notifier` ,
`RenderInfo` , `MapEditor` , `Debug` . Se crean/destruyen en orden explícito desde
el `Director` (no por destructores estáticos).
- **Render paletizado por CPU** (`Surface` de 8 bits + `Palette` /`SubPalette` ),
con recoloreado por reemplazo de índice y paletas intercambiables en caliente.
- **Flujo por variable global** (`SceneManager::current` ) + un único
`active_scene_` .
- **Time-based**: todo consume `delta_time` (`DeltaTimer` ); las constantes citan
su equivalencia en frames a 66.67 fps.
- **`#ifdef _DEBUG` ** envuelve editor, consola de propiedades, overlays y atajos
de depuración — ausentes en release.
- **Comentarios** en español/valenciano; muchos `#include` con comentario
"// Para X" (estilo IWYU).
- **El `CLAUDE.md` puede ir por detrás del código** (caso `Director::run()` vs
callbacks). Ante duda, **manda el código ** .
---
## 16. Guía de navegación: "si quieres tocar X, mira Y"
| Quiero… | Empieza por… |
|---|---|
| Entender el arranque | `core/system/director.cpp` (ctor, `iterate` , `finishBoot` ) |
| Cambiar el flujo de pantallas | `game/scene_manager.hpp` + `Director::switchToActiveScene` |
| Añadir/editar una pantalla | `game/scenes/` (hereda de `Scene` ) + un `case` en `switchToActiveScene` |
| La barra de carga / arranque | `Resource::Cache::beginLoad/loadStep` + `scenes/boot_loader.*` |
| Cómo se dibuja todo | `core/rendering/surface.*` + `Screen::render` (`screen.cpp` ) |
| Sprites / animaciones | `core/rendering/sprite/` + `data/**/*.yaml` (animaciones) |
| Paletas / recoloreado | `core/rendering/palette_manager.*` + `Surface` |
| Shaders / CRT | `core/rendering/sdl3gpu/` + `data/shaders/` |
| Controles / hotkeys | `core/input/input.*` + `global_inputs.cpp` |
| **Lógica de partida ** | `game/scenes/game.cpp` (`updatePlaying` , FSM `State` ) |
| Habitaciones / colisión | `game/gameplay/room.*` (`check*Surfaces` , slopes, cintas) |
| Cargar una sala | `game/gameplay/room_loader.*` + `data/room/*.yaml` |
| El jugador (física) | `game/entities/player.*` (`JUMP_VELOCITY` , `GRAVITY_FORCE` , colisión por puntos) |
| Enemigos / ítems | `game/entities/{enemy,item}.*` + `gameplay/{enemy,item}_manager.*` |
| Logros | `game/gameplay/cheevos.*` (+ `cheevos.bin` ) |
| Marcador / estadísticas | `game/gameplay/{scoreboard,stats,item_tracker,room_tracker}.*` |
| **Editor de mapas ** | `game/editor/map_editor.*` (+ `tile_picker` , `mini_map` , `room_saver` ) |
| **Consola / comandos ** | `game/ui/console.*` + `console_commands.*` |
| Notificaciones / FPS | `game/ui/notifier.*` + `core/rendering/render_info.*` |
| Modo demo (tour) | `Game::demoInit/demoCheckRoomChange` (`game.cpp` ) |
| Cargar un recurso | `core/resources/resource_cache.*` + `resource_list.*` + `config/assets.yaml` |
| Audio | `core/audio/audio.*` + `jail_audio.hpp` |
| Idiomas | `core/locale/locale.*` + `data/locale/` |
| Opciones / constantes | `game/options.*` , `game/defaults.hpp` |
| Escena inicial en Debug | `core/system/debug.*` + `debug.yaml` |
---
*Documento generado a partir de la lectura directa del código en el commit
actual de la rama `main` . Si algo aquí no cuadra con el código, el código
manda: actualiza este documento.*