Files
aee/docs/ARQUITECTURA.md

475 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Arquitectura de **Aventures en Egipte (AEE)**
> 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 no he
> encontrado algo, lo digo explícitamente en lugar de inventarlo.
>
> **Aventures en Egipte** es un juego de aventura/plataformas retro (el
> protagonista "Sam" explora pirámides esquivando momias y recogiendo
> tesoros), escrito en C++ + SDL3. Resolución de juego **320×200**. El rasgo
> que más condiciona el código es que está **en plena migración** desde un
> motor legacy propio ("Jail") hacia C++ moderno (ver
> [§3](#3-el-motor-legacy-jail)). Los comentarios del código están en
> valenciano/español; este documento está en castellano.
>
> Existe además un `docs/scenes-migration-plan.md` (histórico) sobre la
> migración del sistema de escenas; aquí se documenta el **estado actual** del
> código, no el plan.
---
## Índice
1. [Visión general](#1-visión-general)
2. [Punto de entrada y bucle principal](#2-punto-de-entrada-y-bucle-principal)
3. [El motor legacy "Jail"](#3-el-motor-legacy-jail)
4. [Escenas y máquina de estados](#4-escenas-y-máquina-de-estados)
5. [Renderizado: de la lógica al píxel](#5-renderizado-de-la-lógica-al-píxel)
6. [Entrada](#6-entrada)
7. [Gameplay: `ModuleGame` y entidades](#7-gameplay-modulegame-y-entidades)
8. [Sistema de escenas cinemáticas](#8-sistema-de-escenas-cinemáticas)
9. [Recursos](#9-recursos)
10. [Audio y localización](#10-audio-y-localización)
11. [Configuración y persistencia](#11-configuración-y-persistencia)
12. [Modo demo / IA](#12-modo-demo--ia)
13. [Convenciones y patrones recurrentes](#13-convenciones-y-patrones-recurrentes)
14. [Guía de navegación: "si quieres tocar X, mira Y"](#14-guía-de-navegación-si-quieres-tocar-x-mira-y)
---
## 1. Visión general
El árbol `source/` separa **motor** y **juego**, pero con una particularidad:
el motor tiene **dos generaciones** conviviendo (legacy "Jail" + capas nuevas).
- **`source/core/jail/`** — el **motor legacy "Jail"** (APIs C planas): `Jg`
(jgame), `Jd8` (jdraw8), `Ji` (jinput), `Jf` (jfile). En modernización (§3).
- **`source/core/`** (resto) — capas **nuevas**: `rendering` (`Screen`,
`Overlay`, `Text`, `Menu`, `sdl3gpu`), `input`, `system` (`Director`),
`resources`, `audio`, `locale`.
- **`source/game/`** — el juego: gameplay legacy en la raíz (`modulegame`,
`prota`, `momia`, `engendro`, `bola`, `mapa`, `marcador`, `info`, `sprite`) y
el sistema de **escenas** nuevo en `scenes/`.
- **`source/utils/`** — `easing`, `utils`.
- **`source/external/`** — vendorizado (stb, fkyaml).
~117 ficheros C++, ~40.000 líneas.
**El `CLAUDE.md` define una frontera explícita "Original vs New Code"** (§79):
`core/jail/` y `game/*.cpp` son *legacy en modernización* (modificar con
cuidado, preservando comportamiento); `core/rendering`, `core/input`,
`utils/`, `options/defines/defaults` son *código nuevo*. Interiorizar esa
frontera es lo primero para no romper invariantes del juego original.
**Ideas-fuerza:**
1. Un solo hilo, **tick-based**, vía callbacks de SDL3 (§2).
2. El render es **software paletizado 8-bit** (`Jd8`, 320×200) que al final se
convierte a ARGB y pasa por shaders GPU (§5).
3. El flujo de pantallas es una **máquina de estados** apoyada en
`Info::ctx.num_piramide`, con un `SceneRegistry` que va sustituyendo
cinemáticas legacy por escenas nuevas (§4).
```mermaid
graph TD
SDL[SDL3 callbacks · main.cpp] --> DIR[Director]
DIR -->|game_state_ + Info::ctx| DISP{createNextScene}
DISP -->|==0| MG["ModuleGame (gameplay)"]
DISP -->|==1| REG["SceneRegistry::tryCreate(num_piramide)"]
REG --> CINE["intro / banner / slides / mort / secreta / credits…"]
MG --> MAPA[Mapa] & PROTA[Prota] & MOMIA[Momia] & BOLA[Bola] & MARC[Marcador]
DIR --> INP["KeyConfig / GlobalInputs / Gamepad / KeyRemap / Mouse"]
MG -->|dibuja índices| JD8["Jd8 (8-bit 320×200)"]
CINE --> JD8
JD8 -->|JD8_Flip → ARGB| SCREEN[Screen]
OVL[Overlay] --> SCREEN
SCREEN --> GPU["sdl3gpu PostFX / CrtPi / upscale"] --> WIN[Ventana]
RES["Resource::Cache / List"] -.-> MG & CINE
```
---
## 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 (clave
para el port a Emscripten). `SDL_AppInit` monta todo y `SDL_AppIterate` llama a
`Director::iterate()`.
El arranque en `SDL_AppInit` inicializa, en orden: carpeta de config (`Jf`),
sistema de recursos (`resources.pack` + fallback en Debug/WASM), `Options`,
`KeyConfig`, `Locale`, presets de shaders, y luego el **motor**: `Jg::init`,
`Screen::init`, `Jd8::init`, `Audio::init`, `Overlay::init`, `Menu::init`,
`Resource::List`/`Cache` (con `beginLoad()`), y `Director::init` + `setup()`.
### 2.2. El `Director`
`source/core/system/director.{hpp,cpp}`. Es el **orquestador singleton, único
hilo del runtime** (sin fibers, mutex ni condition variables; el comentario de
`director.hpp:11` lo subraya). Posee el estado de escena como miembros:
`current_scene_` (`std::unique_ptr<Scenes::Scene>`), `game_state_`,
`last_tick_ms_`, y dos buffers de frame `[320*200]` (`game_frame_`,
`presentation_buffer_`).
Cada `iterate()` (modelo documentado en `CLAUDE.md:161`):
```
Gamepad/KeyRemap/GlobalInputs/Mouse::update
JA_Update() ← bombeo de audio
if (!paused_) {
if (scene && (scene->done() || JG_Quitting())) game_state_ = scene->nextState(); scene.reset();
if (!scene) scene = createNextScene(); scene->onEnter(); ← ModuleGame o SceneRegistry
JI_Update()
scene->tick(now - last_tick_ms_)
JD8_Flip() ← índices → ARGB
memcpy → game_frame
}
memcpy game_frame → presentation_buffer
Overlay::render(presentation_buffer)
Screen::present(presentation_buffer)
SDL_Delay(frame_target - elapsed) ← cap de FPS
```
### 2.3. Tiempo, pausa y salida
Las escenas reciben `delta_ms` real (time-based). El gameplay (`ModuleGame`)
gatea su `Update()` a 10 ms fijos vía `Jg::shouldUpdate()` (ticker del motor
legacy, ya sin *yield*). La **pausa** (F11) simplemente salta el bloque de
`tick()`: el overlay y el present siguen, re-presentando el último frame
congelado. La **salida** se solicita con `requestQuit()` (doble ESC o
`SDL_QUIT`) y `requestRestart()` hace un reinicio suave (para audio, resetea
`Info::ctx`, vuelve a la intro).
---
## 3. El motor legacy "Jail"
`source/core/jail/` es el sustrato heredado, **APIs C planas con prefijo por
subsistema** (sin clases), que se está convirtiendo progresivamente a C++
idiomático manteniendo los nombres externos estables para no romper los
*call sites* (`CLAUDE.md:92`). Es el rasgo más distintivo del proyecto.
| Subsistema | Fichero | Qué hace |
|---|---|---|
| **`Jg`** (jgame) | `jail/jgame.*` | *Timing*: init/finalize, fixed-timestep (`Jg::shouldUpdate()` a 10 ms), flag de salida (`JG_Quitting`). |
| **`Jd8`** (jdraw8) | `jail/jdraw8.*` | **Renderer software 8-bit paletizado**, buffer de pantalla 320×200 (`Jd8::Surface = Uint8*`), blits con *color key*, fades no bloqueantes (máquina de estados), `flip()` (paleta→ARGB) → `Screen::present`. |
| **`Ji`** (jinput) | `jail/jinput.*` | Sondeo de teclado, *debounce*, códigos *cheat*. Filtra las teclas de GUI del juego y llama a `GlobalInputs`/`Mouse` cada update. |
| **`Jf`** (jfile) | `jail/jfile.*` | I/O de ficheros: carpeta `data/` o pack; carpeta de config en `~/.config/jailgames/aee/`. |
### `Jd8` en detalle (el corazón del render)
`jail/jdraw8.hpp`. API representativa:
- `using Surface = Uint8*` (búfer de índices de paleta), `using Palette = Color*`.
- `loadSurface`/`newSurface`/`freeSurface`, `loadPalette`/`setScreenPalette`.
- Blits: `blit`, `blitCK` (con *color key*/transparencia), `blitCKCut`,
`blitCKScroll`, `blitToSurface`/`blitCKToSurface` (blit entre surfaces).
- `fillRect`/`fillSquare`/`putPixel`/`getPixel`.
- **Fade no bloqueante** (`fadeStart*` + `fadeTickStep` que devuelve `true` al
acabar): sustituye los `JD8_FadeOut` bloqueantes del código original.
- `flip()` convierte el buffer indexado + paleta a ARGB y delega en
`Screen::present`; `getFramebuffer()` devuelve el `Uint32*` resultante.
> Mentalidad clave: **todo el dibujo del juego ocurre sobre índices de 8 bits**
> en un buffer 320×200; el color real y los shaders entran solo al final.
---
## 4. Escenas y máquina de estados
### 4.1. La base `Scene`
`source/game/scenes/scene.hpp` (`namespace Scenes`):
```cpp
class Scene {
public:
virtual void onEnter() {} // una vez, antes del primer tick
virtual void tick(int delta_ms) = 0; // no bloquea, NO llama a Jd8::flip
virtual auto done() const -> bool = 0;
virtual auto nextState() const -> int { return 1; } // 1=siguiente, 0=gameplay, -1=salir
};
```
El `Director` hace avanzar la escena hasta que `done()` es cierto, consulta
`nextState()` y construye la siguiente.
### 4.2. El despachador: `game_state_` + `Info::ctx`
`Director::createNextScene()` decide la siguiente escena:
- `game_state_ == 0``new ModuleGame` (gameplay puro, §7).
- `game_state_ == 1``SceneRegistry::tryCreate(Info::ctx.num_piramide)`
(`game/scenes/scene_registry.hpp`): un mapa `int → factory` que devuelve la
escena registrada para ese estado, o `nullptr` para caer al **path legacy**.
Replica el viejo `ModuleSequence::Go`, incluido el *redirect* heredado
`num_piramide == 6 && diners < 200 → 7` (`CLAUDE.md:104`).
- `game_state_ == -1` → salir.
### 4.3. `Info::ctx`: el estado del juego
`source/game/info.hpp``Info::GameContext ctx` (singleton inline) es la fuente
de verdad del estado:
```cpp
struct GameContext {
int num_piramide, num_habitacio, diners, diamants, vida, momies, engendros;
bool nou_personatge, pepe_activat;
void reset();
};
```
`num_piramide` es la pieza central: hace de **selector tanto de cinemáticas como
de nivel jugable**. El `SceneRegistry` va migrando cada estado de cinemática a
una `Scene` nueva; lo que no esté registrado cae a legacy. Por eso el registro
"crece" a medida que avanza la modernización.
```mermaid
graph LR
SC[Scene activa] -->|done()| NS{nextState}
NS -->|0| MG[ModuleGame]
NS -->|1| REG["SceneRegistry(num_piramide)"]
NS -->|-1| QUIT[salir]
MG -->|muta Info::ctx| REG
REG --> SC
```
---
## 5. Renderizado: de la lógica al píxel
El pipeline tiene dos mitades: **software paletizado** (`Jd8`) y **GPU**
(`Screen` + shaders).
### 5.1. Software: dibujar índices
Escenas y `ModuleGame` dibujan sobre el buffer `screen` de `Jd8` (índices 8-bit,
320×200) con blits y *color key*. `JD8_Flip()` aplica la paleta y produce un
buffer ARGB (formato **ABGR8888**: `0xFF000000 + R + (G<<8) + (B<<16)`;
`CLAUDE.md:220`).
### 5.2. `Overlay`: sobre el buffer ARGB
`source/core/rendering/overlay.*` pinta **directamente sobre el buffer ARGB**
antes de presentar: notificaciones (slide-in), info de render animada (4
segmentos), indicador de **PAUSA** y la lógica de **doble-ESC para salir**.
### 5.3. `Screen`: a la ventana (GPU o fallback)
`source/core/rendering/screen.*` (singleton). Doble camino
(`Screen::present`, `CLAUDE.md:204`):
- **GPU con shaders** (primario, `sdl3gpu/`): sube los píxeles a una textura
de escena 320×200; opcionalmente *upscale*/**supersampling** (3×/6×/9× con
*downscale* Lanczos) y **estiramiento 4:3** fusionado en el *upscale*; luego
el shader **PostFX** o **CRT-Pi** (con presets) a la *swapchain* con
*letterboxing*.
- **GPU sin shaders**: subida limpia a la *swapchain*.
- **Fallback**: `SDL_UpdateTexture` + `SDL_RenderPresent` (`SDL_Renderer`).
`Screen` gestiona ventana, fullscreen, zoom, 4:3, *integer scaling*, VSync, FPS.
Apoyos de UI: `Text` (fuentes bitmap `.fnt`+`.gif` sobre el buffer ARGB, con
*clipping* 2D) y `Menu` (menú flotante de opciones con navegación por páginas,
animaciones y captura de teclas para *remapping*).
---
## 6. Entrada
Toda la entrada de UI/sistema converge en **`KeyConfig`** como fuente única de
verdad de las teclas (`source/core/input/key_config.*`): carga
`data/input/keys.yaml` (F1F10 GlobalInputs + F11 pausa + F12 menú) y aplica
*overrides* del usuario; expone `scancode("id")`, `isGuiKey(sc)` (para que el
`Director` no propague teclas de UI al juego), y persiste solo lo que difiere
del default.
- **`GlobalInputs`** (`global_inputs.*`) — mapea las F-keys a acciones de
presentación (zoom, fullscreen, shaders, 4:3, supersampling, filtro, render
info, pausa, menú). Tabla completa en `CLAUDE.md:139`.
- **`Gamepad`** (`gamepad.*`) — primer mando con *hot-plug* y notificación;
D-pad/stick → flechas virtuales; botones frontales → Enter sintético;
SELECT → menú, START → pausa. Carga `gamecontrollerdb.txt`.
- **`KeyRemap`** (`key_remap.*`) — cada frame copia `Options::keys_game.*` al
estado virtual de scancodes estándar (UP/DOWN/LEFT/RIGHT). Permite remapear el
movimiento **sin tocar el código legacy** de `prota.cpp`/`mapa.cpp`.
- **`Mouse`** (`mouse.*`) — auto-oculta el cursor tras 3 s de inactividad.
> Reparto deliberado: las teclas de **UI/sistema** viven en `KeyConfig`
> (`keys.yaml`); las de **movimiento del jugador** en `Options::keys_game`
> (`config.yaml`, sección `controls:`).
---
## 7. Gameplay: `ModuleGame` y entidades
`source/game/modulegame.{hpp,cpp}` es la **escena de gameplay puro**
(`game_state_ == 0`). Hereda de `Scenes::Scene` y sustituye el viejo `Go()`
bloqueante por un `tick()`. Tres fases internas (`Phase`):
`FADING_IN → PLAYING → FADING_OUT → DONE` (`modulegame.hpp:46`), con un
`PaletteFade` para los fundidos no bloqueantes.
Coordina las entidades del nivel, todas con una `Jd8::Surface gfx_` compartida:
- **`Mapa`** (`game/mapa.*`) — la sala/pirámide: tiles con contenido
(`CONTE_RES`/`CONTE_TRESOR`/`CONTE_FARAO`…), colisión y dibujo del escenario.
- **`Prota`** (`game/prota.*`) — el protagonista "Sam" (hereda de `Sprite`),
movimiento y colisión.
- **`Momia`** (`game/momia.*`) — enemigo que persigue al `Prota` (recibe
`Prota*`); variante `dimoni` (demonio).
- **`Bola`** (`game/bola.*`) — bola/roca que interactúa con el `Prota`.
- **`Engendro`** (`game/engendro.*`) — entidad generada (su `update()` devuelve
`bool`); `Info::ctx.engendros` lleva la cuenta.
- **`Marcador`** (`game/marcador.*`) — el marcador (vidas, dinero, diamantes),
lee del `Prota`/`Info::ctx`.
`ModuleGame::tick()` hace `draw()` (a `screen`, sin `flip` — lo hace el
`Director`) y `update()` (gateado por `Jg::shouldUpdate`). Cuando la partida
acaba (`final_ != 0`: muerte o cambio de sala), `applyFinalTransitions()` muta
`Info::ctx` y `nextState()` devuelve el estado siguiente.
Todas las entidades de gameplay derivan de **`Sprite`** (`game/sprite.*`),
clase base con `draw()`/`update()` sobre `Jd8`.
---
## 8. Sistema de escenas cinemáticas
Las pantallas no jugables (`game_state_ == 1`) son `Scene`s registradas en el
`SceneRegistry`. Las concretas viven en `source/game/scenes/`: `boot_loader`
(carga incremental, §9), `banner`, `intro`, `intro_new_logo`, `intro_sprites`,
`slides`, `menu` (`menu_scene`), `mort` (secuencia de muerte), `secreta`
(pantalla secreta) y `credits`.
Sobre ellas hay un **conjunto de utilidades de animación** reutilizables —el
andamiaje "cinematográfico" del juego—:
- **`Timeline`** (`scenes/timeline.*`) — secuenciación temporal de eventos.
- **`FrameAnimator`** (`scenes/frame_animator.*`) — animación por fotogramas.
- **`SpriteMover`** (`scenes/sprite_mover.*`) — movimiento interpolado de
sprites (con `easing`).
- **`PaletteFade`** (`scenes/palette_fade.*`) — fundidos por paleta no
bloqueantes (también usado por `ModuleGame`).
- **`SurfaceHandle`** (`scenes/surface_handle.*`) — propiedad RAII de una
`Jd8::Surface` (las escenas poseen sus assets y los liberan en el destructor).
- **`scene_utils`** — helpers compartidos.
---
## 9. Recursos
- **`Resource::List`** (`core/resources/resource_list.*`) — registro de rutas de
asset desde **`data/config/assets.yaml`**, con consulta O(1).
- **`Resource::Cache`** (`core/resources/resource_cache.*`) — caché de assets con
**carga incremental**: `beginLoad()` (en `SDL_AppInit`) + la escena
`BootLoaderScene` que el `Director` arranca automáticamente mientras
`Resource::Cache::isLoadDone()` sea falso (una barra de progreso con la
ventana viva).
- **Pack y fallback**: `resource_pack.*` + `resource_helper.*` sirven desde
**`resources.pack`** (formato propio "AEE1", `CLAUDE.md:237`); en release
nativo es estricto (solo pack), en Debug/WASM hay *fallback* a `data/`.
- **Formatos**: GIF/paletas para gráficos (vía `Jd8`), fuentes `.fnt`+`.gif`,
OGG/WAV para audio, GLSL para shaders.
---
## 10. Audio y localización
- **Audio**: `core/audio/audio.*` (singleton, aplica `Options::audio`) sobre
**`jail_audio`** (`Ja`, `jail/jail_audio.hpp` / `core/audio/jail_audio.hpp`),
mezcla propia con streams SDL3 (OGG vía `stb_vorbis`, WAV). `JA_Update()` se
bombea cada frame desde el `Director`; pausa/resume con F11.
- **Localización**: `core/locale/locale.*` — mapa plano clave→cadena cargado de
YAML (`data/locale/ca.yaml`, valenciano por defecto). Claves con notación de
puntos (`menu.items.zoom`); si falta una clave, devuelve la propia clave
(fallback visible para depurar).
---
## 11. Configuración y persistencia
- **`Options`** (`source/game/options.*`) — namespace con globals `inline` y
carga/guardado YAML. Structs: `KeysGame` (movimiento), `Video`, `RenderInfo`,
`Audio`, `Window`, `Game`, `PostFXPreset`, `CrtPiPreset`.
- **`defines.hpp`** — constantes del juego: `Texts::WINDOW_TITLE`/`VERSION`,
`GameScreen::WIDTH=320`/`HEIGHT=200`/`BUFFER_SIZE=64000`.
- **`defaults.hpp`** — valores por defecto (`Defaults::KeysGame`, `Video`,
`Audio`, `Window`, `Game`).
- **`KeyConfig`** — teclas de UI (§6).
Ficheros persistentes en `~/.config/jailgames/aee/` (`CLAUDE.md:224`):
`config.yaml` (vídeo/audio/ventana/render_info/controles), `keys.yaml`
(overrides de UI), `postfx.yaml` (6 presets), `crtpi.yaml` (4 presets), y en
Debug `debug.yaml` (estado inicial de gameplay para pruebas).
**Builds condicionales**: `NDEBUG` (release: pack estricto, sin `debug.yaml`),
`__EMSCRIPTEN__` (WASM: fuerza ventana/zoom/4:3, mantiene fallback de assets,
sin persistencia MEMFS).
---
## 12. Modo demo / IA
**No he encontrado un modo demo, *attract mode* ni IA de demostración en este
proyecto.** Lo he buscado explícitamente: la única aparición de "replay" es un
comentario en `jail_audio.hpp` sobre re-reproducción de pistas de audio, sin
relación con un modo demo.
A diferencia de los proyectos hermanos `coffee_crisis` (playback de input
grabado) o `jaildoctors_dilemma` (tour de habitaciones), aquí el flujo de
atracción —si lo hubiera— se construiría sobre el `SceneRegistry` y las escenas
cinemáticas (§8), pero **hoy no existe** tal cosa implementada.
---
## 13. Convenciones y patrones recurrentes
- **Frontera legacy/nuevo** (`CLAUDE.md:79`): lo más importante. `core/jail/` y
`game/*.cpp` son legacy en modernización (preservar comportamiento); el resto
es código nuevo. Los assets de `data/gfx`/`data/music` son **intocables**.
- **Namespaces legacy abreviados** `Jg`/`Jd8`/`Ji`/`Jf`/`Ja` (APIs C planas),
con nombres externos estables durante la transición.
- **Single-thread, tick-based**: sin fibers/mutex; las escenas no bloquean ni
llaman a `Jd8::flip` (lo hace el `Director`).
- **Render paletizado 8-bit** sobre buffer 320×200; color y shaders solo al
final (`JD8_Flip``Screen`).
- **Singletons con `init()`/`destroy()`/`get()`** (`Screen`, `Audio`,
`Director`, `Menu`, `Overlay`, `Resource::*`, `Jd8`/`Jg` como módulos),
creados/destruidos en orden explícito en `SDL_AppInit`/`SDL_AppQuit`.
- **Máquina de estados por `Info::ctx.num_piramide`** + `SceneRegistry` que
migra cinemáticas legacy a `Scene`s nuevas de forma incremental.
- **Fades y timing no bloqueantes**: lo que antes eran bucles bloqueantes
(`JD8_FadeOut`) ahora son máquinas de estados que avanzan un paso por tick.
- **Comentarios** en valenciano/español; muchos `#include` con comentario de
justificación (estilo IWYU).
---
## 14. Guía de navegación: "si quieres tocar X, mira Y"
| Quiero… | Empieza por… |
|---|---|
| Entender el arranque | `source/main.cpp` (`SDL_AppInit`) + `core/system/director.cpp` |
| El bucle / orden del frame | `Director::iterate()` (`director.cpp`) |
| Tocar el motor de dibujo | `core/jail/jdraw8.*` (`Jd8`) |
| Timing / fixed-timestep | `core/jail/jgame.*` (`Jg::shouldUpdate`) |
| Añadir/editar una pantalla | `game/scenes/` (hereda de `Scenes::Scene`) + `SceneRegistry::registerScene` |
| Cómo se decide la siguiente pantalla | `Director::createNextScene` + `Info::ctx.num_piramide` + `scene_registry.*` |
| El estado del juego | `game/info.hpp` (`Info::ctx`) |
| **Gameplay** | `game/modulegame.*` (fases) + entidades `prota/momia/bola/engendro/mapa/marcador` |
| Animaciones de cinemáticas | `game/scenes/{timeline,frame_animator,sprite_mover,palette_fade,surface_handle}.*` |
| Composición final / shaders | `core/rendering/screen.*` + `sdl3gpu/` + presets `postfx.yaml`/`crtpi.yaml` |
| Overlays (notificaciones/PAUSA/info) | `core/rendering/overlay.*` |
| Texto / menú | `core/rendering/text.*`, `core/rendering/menu.*` |
| Teclas de UI / F1F12 | `core/input/key_config.*` + `global_inputs.*` + `data/input/keys.yaml` |
| Remapear movimiento | `core/input/key_remap.*` + `Options::keys_game` |
| Mando | `core/input/gamepad.*` |
| Cargar recursos / barra de carga | `core/resources/resource_cache.*` + `resource_list.*` + `scenes/boot_loader_scene.*` |
| Audio | `core/audio/audio.*` + `jail_audio.hpp` (`Ja`) |
| Idiomas | `core/locale/locale.*` + `data/locale/` |
| Opciones / constantes | `game/options.*`, `game/defines.hpp`, `game/defaults.hpp` |
| Carpetas y pack | `core/jail/jfile.*` (`Jf`) + `core/resources/resource_pack.*` |
---
*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.*