diff --git a/docs/ARQUITECTURA.md b/docs/ARQUITECTURA.md new file mode 100644 index 0000000..f55d225 --- /dev/null +++ b/docs/ARQUITECTURA.md @@ -0,0 +1,474 @@ +# 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`), `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` (F1–F10 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 / F1–F12 | `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.*