Files
jaildoctors-dilemma/docs/ARQUITECTURA.md
T

24 KiB
Raw Blame History

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
  2. Punto de entrada y bucle principal
  3. Escenas y flujo de la aplicación
  4. Renderizado: de la lógica al píxel
  5. Entrada
  6. Lógica del juego: la escena Game
  7. Habitaciones y colisión
  8. Entidades
  9. Logros, estadísticas y marcador
  10. Editor de mapas (Debug)
  11. Consola y notificaciones
  12. Modo demo
  13. Recursos
  14. Audio, localización y configuración
  15. Convenciones y patrones recurrentes
  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).
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.

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:

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:

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).

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/): SpriteAnimatedSprite (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.

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):

// 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.