45 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Projecte 2026 - Nuevo juego (derivado de JailDoctor's Dilemma)
IMPORTANTE: Este repositorio parte del código de JailDoctor's Dilemma como base, pero es un juego nuevo. projecte_2026 es un nombre placeholder hasta tener el diseño final. Gran parte de la arquitectura documentada más abajo describe el código heredado; varios subsistemas se van a reescribir.
Cambios de diseño previstos respecto al juego original
- Habitaciones más grandes — aprovechar el espacio del marcador (scoreboard) para ampliar el área jugable. Esto implica revisar la resolución de canvas, el layout de la HUD y el tamaño de los tilemaps.
Nueva física del jugador✅ HECHO — Player reescrito con pipeline de 6 fases, colisiones tile-based, ~20 métodos eliminados.Motor de colisiones por tiles✅ HECHO —TileCollidercon queries directas al grid. Collision tilemap editado desde el editor (tecla 7). Sistema antiguo de superficies preservado pero no usado. Pendiente: tiles kill (5) y conveyor (6).Transición de pantalla animada✅ HECHO — Scroll con easingcubicInOut(0.5s), ambas rooms visibles, enemigos activos, jugador puede moverse durante la transición.- Paleta Amstrad CPC — subir de la paleta actual (8-bit indexada limitada) a la paleta del Amstrad CPC (27 colores / más colores que la actual). Afecta a
PaletteManagery a todos los assets de color.
Estado del renombrado
Se han renombrado las referencias de JailDoctor's Dilemma → Projecte 2026 y jaildoctors_dilemma → projecte_2026 en código, configs, Makefile, CMake, LICENSE, Info.plist, scripts, locales, etc. Copyright actualizado a 2026. Se han mantenido como placeholders:
- La clave de locale
jaildoctor:endata/locale/*.yamly su uso ensource/game/scenes/ending2.cpp(es contenido de juego, pendiente de rediseño). - El publisher
jailgames(carpeta de config de sistema:~/.config/jailgames/projecte_2026). - Los archivos
release/windows/jdd.rcyjdd.res(solo bundlean el icono).
Log de cambios realizados (sesiones de trabajo)
Eliminaciones
- Clase
Stats(persistencia CSV de muertes/visitas) eliminada por completo: archivossource/game/gameplay/stats.hpp/cpp, entrada en CMakeLists, referencias enGamescene (stats_,initStats(),addVisit/addDeath), ficherosstats.csv/stats_buffer.csvenconfig/assets.yaml, patrón*stats.txten.gitignore, campoworst_nightmareenOptions::Stats+Defaults::Stats::WORST_NIGHTMARE, display de "worst nightmare" engame_over.cpp/.hpp, traducciones en locales, menciones en docs. - Mantenido:
Options::stats.itemsyOptions::stats.rooms— son contadores de runtime del marcador/game_over, no tienen persistencia CSV. Coincidencia de nombre con la clase Stats eliminada.
Música
- Eliminadas las 10 pistas originales (14 MB) de
data/music/. - Copiadas 2 pistas de
../pollo/data/music/:574070_KUVO_Farewell_to_school.oggy574071_EA_DTV.ogg(7 MB total). config/assets.yamlreducido a esas 2 entradas.- Las 10 llamadas
playMusic()existentes (title/game/loading_/ending/game_over) remapeadas alternadamente a una de las 2 pistas. Mapeo concreto en esta misma documentación si hace falta consultarlo (ver git log del cambio).
Paleta
- Eliminadas las 23 paletas ZX Spectrum/otras de
data/palette/. - Traída
cpc.palde../pollo/data/palettes/(JASC-PAL, 28 entradas: CLEAR+27 colores CPC). config/assets.yaml: solocpc.pal.Defaults::Video::PALETTE_NAME = "cpc".- Traída la clase
Colorde pollo asource/utils/color.hpp+color.cpp(enumColor::Cpccon 28 valores 0-27 +Color::fromString()). - Struct RGB
Colorenutils.hpprenombrada aRgbpara evitar conflicto con la nueva clase. Propagado autils.cpp(colorAreEqual),screen.hpp/.cpp(clearRenderer). - Añadido
source/utils/color.cppaCMakeLists.txt. - Pendiente de revisar: enum
PaletteColorenutils.hppsigue con los 16 índices antiguos (ZX Spectrum). Los índices 0-4 coinciden con CPC, pero 5+ mapean a colores diferentes (ej.PaletteColor::BRIGHT_RED=5ahora renderiza MAGENTA en CPC).stringToColor()también sin actualizar.SPECTRUM_REFERENCEenpalette_manager.cppsigue siendo la referencia ZX Spectrum de 16 colores. Falta decidir cómo migrar estos tres puntos cuando se vea el resultado visual. next()/previous()en PaletteManager: con una sola paleta hacen wrap al mismo índice (no crashean). El concepto de ciclar paletas queda desactivado de facto; posible futura reutilización para ciclar modos de ordenación.
Otros
- Añadidos
desktop.iniyThumbs.dbal.gitignore.
Transiciones de pantalla (sesión abril 2026)
- Cambio de pantalla por punto central:
Player::handleBorders()usa el centro del rectángulo del jugador para detectar cambio de pantalla (antes usaba bordes). - Conservación de momento:
Player::switchBorders()conserva velocidad y estado al cambiar de pantalla (antes forzaba ON_GROUND y reseteaba vy_). - Transición animada con scroll: Al cambiar de habitación, ambas rooms se desplazan con easing
cubicInOutdurante 0.5s.- Render offset global añadido a
Screen(setRenderOffset) aplicado en los 6 métodos deSurface::render(). - Enemigos de ambas habitaciones se actualizan durante la transición.
- El jugador puede moverse durante la transición.
- Estado en
Game:transitioning_,transition_timer_,transition_old_room_,transition_direction_. - Ficheros:
screen.hpp/cpp,surface.cpp,game.hpp/cpp.
- Render offset global añadido a
Impulso de salto
JUMP_VELOCITYincrementado 5%: de -170.0 a -178.5.
Collision tilemap (sesión abril 2026)
- Formato YAML nuevo:
tilemap:tiene dos sub-mapas:draw:(tilemap de dibujo, el original) ycollision:(mapa de colisiones).RoomLoader::parseTilemap()lee ambos con fallback al formato antiguo. Room::Data::collision_tile_map— vector con tipos: 0=vacío, 1=muro, 2=plataforma, 3=slope_l, 4=slope_r, 5=kill, 6=conveyor.- CollisionMap migrado:
getTile()lee directamente delcollision_tile_map(antes deducía el tipo por rangos de índice del tileset de dibujo). Constructor ya no necesitatile_set_width. - Editor de colisiones: Tecla 7 o
EDIT DRAW/EDIT COLLISIONalterna entre editar el tilemap de dibujo y el de colisiones. En modo collision se superpone elcollision.gif(7 tiles) sobre el mapa de dibujo. Right-click abre el tile picker del tileset correspondiente.RoomSaverguarda ambos sub-mapas. collision.gifregistrado enassets.yaml(7 tiles: vacío, muro, plataforma, slope_l, slope_r, kill, conveyor).
Nuevo motor de colisiones tile-based (sesión abril 2026)
- Clase
TileCollider(source/game/gameplay/tile_collider.hpp/cpp): queries directas contra el grid de tiles sin listas de superficies intermedias. API:checkWallLeft/Right,checkCeiling,checkFloor(con FloorHit struct),hasGroundBelow,checkSlopeBelow(con SlopeInfo struct),getSlopeY. - Integrado en
CollisionMap(miembro + getter) y expuesto viaRoom::getTileCollider(). - Player reescrito con pipeline de 6 fases claras:
handleInput()— leer inputupdateVelocity()+applyGravity()— calcular velocidadeshandleJumpAndDrop()— salto + drop-through (plataformas y slopes con DOWN)moveHorizontal()+moveVertical()— movimiento con colisión tile-basedcheckFalling()— detectar caídasyncSpriteAndCollider()+animate()+handleBorders()
- ~20 métodos eliminados del Player antiguo. Slopes gestionadas por tile (slope_tile_x/y/type) en vez de puntero a LineDiagonal.
- Sistema antiguo de superficies preservado en CollisionMap (no eliminado), simplemente ya no usado por Player.
- Slopes — escalera diagonal de tiles:
- Las slopes de 45° se pintan como escalera en el collision tilemap: cada tile una fila arriba/abajo y una columna al lado.
checkSlopeBelow: escanea la fila de los pies Y la de arriba (la slope entry siempre está una fila arriba del suelo).followSlope: busca el siguiente tile de slope en la fila actual Y la de abajo (para descenso en escalera).exitSlope: comprueba suelo enfoot_yyfoot_y+1(boundary de fila al salir por abajo) y snapea al borde del tile.
- Drop-through — sin flags, puramente posicional:
- Al pulsar DOWN sobre slope/plataforma:
y_ += 1y ON_AIR. No hay flagsdropping_through_. checkFloorpara PASSABLE: solo aterriza sifoot_y_current <= tile_top(pies estaban por encima).checkFloorpara slopes: solo aterriza sifoot_y_current <= slope_y(pies por encima de la superficie).isInsideAnySlope(): si algún pie está por debajo de la superficie de cualquier slope que solape, bloquea TODOS los aterrizajes en slopes. Esto asegura que al hacer drop o saltar desde abajo, el jugador atraviesa toda la escalera de slopes sin quedarse pegado.
- Al pulsar DOWN sobre slope/plataforma:
- Pendiente: tiles 5 (kill) y 6 (conveyor) no soportados aún en el nuevo motor.
Otros
- Añadidos
desktop.iniyThumbs.dbal.gitignore.
Overview (legacy)
Projecte 2026 is a retro-style 2D puzzle platformer game built in C++20 using SDL3 for graphics and audio. The game features 60+ rooms, collectible items, enemies, and an achievement system. It targets multiple platforms (Windows, macOS, Linux) with a focus on retro aesthetics and precise collision detection.
Language: C++20 Graphics: SDL3, OpenGL Audio: SDL3 + custom jail_audio library Build: CMake 3.10+ Game Canvas: 256x192 pixels (retro resolution)
Build Commands
Initial Setup & Build
# From project root
mkdir -p build
cd build
cmake ..
cmake --build .
Rebuild After Changes
# From build directory
cmake --build .
# Or from project root
cmake --build build
Clean Build
# From build directory
cmake --build . --clean-first
# Or from project root
cmake --build build --clean-first
Run the Game
# Executable is placed in project root
./projecte_2026
Important: The build directory is build/ relative to the project root and the output executable is placed in the project root directory.
Testing in Headless Environment (SSH/Remote Server)
IMPORTANT: When working on a remote server via SSH without a physical display, the game MUST be run using Xvfb (X Virtual Framebuffer) to avoid SDL3 display initialization errors.
Required Setup (One-time)
# Install Xvfb
sudo apt-get install xvfb
Running the Game in Headless Mode
Option 1: Using xvfb-run directly (RECOMMENDED)
xvfb-run -a ./projecte_2026
Option 2: Custom display configuration
xvfb-run -a -s "-screen 0 1280x720x24" ./projecte_2026
Why This Is Critical
- SDL3 requires a display: The game uses SDL3 which requires X11/Wayland display
- No code changes needed: Xvfb simulates a virtual display without modifying the codebase
- Full logging: Console output and logs work normally, essential for debugging resource loading
- Testing resource loading: When modifying asset loading code, running with xvfb-run allows seeing all initialization logs
ALWAYS use xvfb-run when testing on the remote server, especially when:
- Modifying resource loading code
- Testing asset initialization
- Debugging startup issues
- Verifying configuration changes
Static Analysis Tools (Linters)
This project uses two complementary static analysis tools for code quality:
clang-tidy (Modernization & Best Practices)
Purpose: Detects style issues, modernization opportunities, and potential bugs. Best for refactoring and applying C++20 best practices.
IMPORTANT: Always run on specific files, NOT on the entire project, to avoid long execution times and errors in unrelated files.
# Analyze a specific file (RECOMMENDED)
tools/linter/run_clang-tidy.sh source/game/entities/player.cpp
# Analyze multiple specific files
tools/linter/run_clang-tidy.sh source/game/entities/player.cpp source/game/entities/enemy.cpp
# Apply fixes automatically to a specific file
tools/linter/run_clang-tidy.sh --fix source/game/entities/player.cpp
# Analyze entire project (SLOW, may have errors in defaults.hpp)
tools/linter/run_clang-tidy.sh
Common clang-tidy Checks:
modernize-*: C++ modernization (use auto, range-for, etc.)readability-*: Code readability improvementsperformance-*: Performance optimizationsbugprone-*: Potential bug detection
Known False Positives:
defaults.hpp: May report errors in constant definitionsreadability-magic-numbers: Acceptable for game constants (block sizes, speeds, etc.)cppcoreguidelines-avoid-magic-numbers: Same as above, acceptable in game code
cppcheck (Bug Detection & Memory Safety)
Purpose: Detects bugs, memory leaks, undefined behavior, and style issues. Complementary to clang-tidy.
# Warning, style, and performance analysis (RECOMMENDED for daily use)
tools/linter/run_cppcheck.sh -w --path source/game/entities/player.cpp
# Exhaustive analysis (slower, more thorough)
tools/linter/run_cppcheck.sh -a --path source/game/entities/
# Detect unused functions (whole project analysis)
tools/linter/run_cppcheck.sh -u
# Analyze entire project with warning level
tools/linter/run_cppcheck.sh -w
Output: Results are saved in tools/linter/cppcheck-result-*.txt
Common cppcheck Checks:
- Memory leaks and resource management
- Null pointer dereferences
- Buffer overflows and array bounds
- Uninitialized variables
- Dead code and unused functions
Known False Positives:
unusedFunction: May report false positives for callback functions or public API methodspassedByValue: Acceptable for small types like SDL_FRect in game codeconstParameter: Not always applicable for API consistency
When to Use Each Tool
| Scenario | Tool | Reason |
|---|---|---|
| Daily refactoring | clang-tidy | Fast, auto-fixable issues |
| Before committing | Both | Comprehensive quality check |
| After major changes | cppcheck -a | Deep bug analysis |
| Hunting memory leaks | cppcheck | Specialized detection |
| Code modernization | clang-tidy --fix | Automatic C++20 upgrades |
Best Practices
- Run linters after compilation succeeds - Fix compile errors first
- Analyze specific files - Faster feedback, avoids unrelated errors
- Review before applying --fix - Understand changes before accepting
- Context matters - Game code may legitimately have "magic numbers" for gameplay constants
- Use both tools - They catch different issues and complement each other
- Check suppressions file -
tools/linter/cppcheck_suppressionsexcludes external/ and system headers
Integration with Development Workflow
When refactoring code (especially with /refactor-class command):
- Make structural changes
- Compile to verify syntax
- Run clang-tidy (without --fix) to identify issues
- Run cppcheck -w to catch bugs
- Review and fix legitimate issues
- Apply automatic fixes if appropriate
- Recompile and test
Note: The /refactor-class command automatically runs both linters after compilation and provides intelligent analysis of results.
1. High-Level Architecture Overview
The architecture follows a layered, modular design with clear separation of concerns:
┌─────────────────────────────────────┐
│ Application Layer │
│ (Director, Scene Manager) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Game Logic Layer │
│ (Game Scene, Entities, Gameplay) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Core Systems Layer │
│ (Rendering, Audio, Input, Resources)│
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ SDL3 & External Libraries │
│ (jail_audio, stb_image, stb_vorbis) │
└─────────────────────────────────────┘
Key Design Principles
- Singleton Pattern: Core systems (Director, Screen, Audio, Input, Resource, Asset) use singleton pattern for global access
- Scene-Based Architecture: Game flow managed through discrete scenes (Logo, Title, Loading, Game, Ending, etc.)
- Component-Based Entities: Player, enemies, and items are discrete entities with render/update patterns
- Resource Management: All assets (textures, sounds, animations, tilemaps) cached through Resource singleton
- Delta Time: Frame-rate independent physics using DeltaTimer class
2. Directory Structure
source/
├── core/ # Core engine systems
│ ├── audio/ # Audio management
│ │ ├── audio.hpp/cpp # Audio singleton (music, sounds, volumes)
│ │ └── jail_audio.hpp # Custom jail_audio library wrapper
│ ├── input/ # Input handling
│ │ ├── input.hpp/cpp # Input manager (keyboard, gamepad)
│ │ ├── input_types.hpp # Input type definitions
│ │ ├── global_inputs.hpp # Global input state
│ │ └── mouse.hpp # Mouse input
│ ├── locale/ # Localization
│ │ └── locale.hpp # Locale/language support
│ ├── rendering/ # Graphics rendering
│ │ ├── screen.hpp/cpp # Screen/window singleton, SDL renderer
│ │ ├── surface.hpp/cpp # 8-bit indexed color surface abstraction
│ │ ├── sprite/ # Sprite rendering classes
│ │ │ ├── sprite.hpp # Static sprite rendering
│ │ │ ├── animated_sprite.hpp # Animated sprite with frame data
│ │ │ ├── moving_sprite.hpp # Moving sprite with velocity
│ │ │ └── dissolve_sprite.hpp # Dissolve transition sprite
│ │ ├── text.hpp/cpp # Text rendering system
│ │ ├── gif.hpp/cpp # GIF image loader
│ │ ├── pixel_reveal.hpp # Pixel reveal effect
│ │ ├── render_info.hpp # Render information data
│ │ ├── palette_manager.hpp # Palette management
│ │ ├── sdl3gpu/ # SDL3 GPU shader backend
│ │ │ ├── sdl3gpu_shader.hpp/cpp # CRT/post-processing shader effects
│ │ │ └── *_spv.h # Pre-compiled SPIR-V shader headers
│ │ └── shader_backend.hpp # Abstract shader interface
│ ├── resources/ # Asset & Resource management
│ │ ├── resource_list.hpp # Asset path registry (O(1) lookups)
│ │ ├── resource_cache.hpp # Resource caching singleton
│ │ ├── resource_loader.hpp # Asset loading logic
│ │ ├── resource_pack.hpp # Resource pack handling
│ │ ├── resource_helper.hpp # Resource utility functions
│ │ └── resource_types.hpp # Resource type definitions
│ └── system/ # System management
│ ├── director.hpp/cpp # Main application controller
│ ├── debug.hpp/cpp # Debug info overlay
│ └── global_events.hpp/cpp # Global event broadcasting
├── game/ # Game-specific logic
│ ├── entities/ # Game objects
│ │ ├── player.hpp/cpp # Player entity with physics
│ │ ├── enemy.hpp/cpp # Enemy entities
│ │ └── item.hpp/cpp # Collectible items
│ ├── gameplay/ # Core gameplay systems
│ │ ├── room.hpp/cpp # Room/level logic, collision
│ │ ├── room_loader.hpp/cpp # Room loading from YAML files
│ │ ├── room_tracker.hpp/cpp # Tracks visited rooms
│ │ ├── collision_map.hpp/cpp # Collision map data
│ │ ├── tilemap_renderer.hpp/cpp # Tilemap rendering
│ │ ├── enemy_manager.hpp/cpp # Enemy lifecycle management
│ │ ├── item_manager.hpp/cpp # Item lifecycle management
│ │ ├── scoreboard.hpp/cpp # Score display & data
│ │ ├── item_tracker.hpp/cpp # Tracks collected items
│ │ └── cheevos.hpp/cpp # Achievement system
│ ├── scenes/ # Game scenes (flow states)
│ │ ├── logo.hpp/cpp # JailGames logo screen
│ │ ├── loading_screen.hpp/cpp # Resource loading progress
│ │ ├── title.hpp/cpp # Title screen & menus
│ │ ├── game.hpp/cpp # Main gameplay loop
│ │ ├── game_over.hpp/cpp # Game over screen
│ │ ├── ending.hpp/cpp # Ending sequence 1
│ │ ├── ending2.hpp/cpp # Ending sequence 2
│ │ └── credits.hpp/cpp # Credits screen
│ ├── ui/ # User interface
│ │ ├── notifier.hpp/cpp # Achievement/notification display
│ │ ├── console.hpp/cpp # In-game debug console
│ │ └── console_commands.hpp/cpp # Console command definitions
│ ├── options.hpp/cpp # Game configuration/options
│ ├── game_control.hpp # Game control logic
│ ├── scene_manager.hpp # Scene flow state machine
│ └── defaults.hpp # Game defaults constants
├── external/ # Third-party libraries
│ ├── fkyaml_node.hpp # YAML parsing library
│ ├── stb_image.h # Image loading library
│ └── stb_vorbis.h # OGG Vorbis audio decoding
├── utils/ # Utility code
│ ├── delta_timer.hpp/cpp # Frame-rate independent timing
│ ├── easing_functions.hpp # Easing/interpolation functions
│ ├── defines.hpp # Game constants (resolutions, block sizes)
│ └── utils.hpp/cpp # Helper functions (colors, math)
└── main.cpp # Application entry point
config/ # Configuration files
└── assets.yaml # Asset registry (text-based configuration)
data/ # Game assets
├── font/ # Bitmap fonts + descriptors
├── palette/ # Color palettes (.pal files)
├── shaders/ # GLSL vertex/fragment shaders
├── room/ # Tilemaps & room definitions (01.tmx-60.tmx)
├── tilesets/ # Tileset graphics
├── enemies/ # Enemy sprites & animations
├── player/ # Player sprites & animations
├── items/ # Item sprite sheets
├── music/ # Background music (OGG)
├── sound/ # Sound effects (WAV)
├── logo/ # Logo images
├── loading/ # Loading screen graphics
├── title/ # Title screen graphics
├── ending/ # Ending sequence images
└── credits/ # Credits screen assets
3. Key Architectural Patterns
3.1 Singleton Pattern (Core Systems)
Most core systems use thread-safe singleton pattern:
class Screen {
private:
static Screen* screen_; // Singleton instance
Screen(); // Private constructor
~Screen();
public:
static void init(); // Creates singleton
static void destroy(); // Destroys singleton
static Screen* get(); // Accesses singleton
};
Singleton Systems:
Screen- Rendering, window management, palette/shader effectsInput- Keyboard & gamepad input binding and checkingAudio- Music and sound effect playbackResource::Cache- Asset loading, caching, streaming (with error handling)Resource::List- Asset path registry from assets.yaml (O(1) lookups)Director- Main application controllerCheevos- Achievement state managementDebug- Debug information overlay
3.2 Scene-Based State Machine
The game uses a scene manager to control application flow:
// namespace SceneManager
enum class Scene {
LOGO, LOADING_SCREEN, TITLE, CREDITS, GAME, DEMO,
GAME_OVER, ENDING, ENDING2, RESTART_CURRENT, QUIT
};
inline Scene current = Scene::LOGO; // Global scene state
inline Options options = ...; // Transition options
Scene Hierarchy:
- Each scene runs its own update/render loop
Directorswitches between scenes based onSceneManager::current- Scenes can request transitions via
SceneManager::currentassignment
3.3 Entity-Component Pattern (Simplified)
Entities (Player, Enemies, Items) have:
- Update - Logic, physics, animation
- Render - Draw to screen surface
- Collision - Hit detection (player collides with room, enemies, items)
3.4 Surface-Based Rendering Pipeline
The rendering system uses a 8-bit indexed color pipeline:
SurfaceData (pixel buffer)
↓
Surface (palette + rendering operations)
↓
SDL_Texture (GPU texture)
↓
SDL_Renderer (to SDL_Window)
↓
Display
Key Components:
Surface- 8-bit indexed pixel buffer with palette supportSprite- Renders a fixed region of a surfaceAnimatedSprite- Frame-based animation on top of spriteMovingSprite- Adds velocity/position to animated spriteDissolveSprite- Dissolve transition effect sprite- Supports color replacement, palette swapping, and shader effects (CRT)
3.5 Tile-Based Collision System
Rooms use a sophisticated collision detection system:
class Room {
std::vector<LineHorizontal> bottom_floors_; // Ground surfaces
std::vector<LineHorizontal> top_floors_; // Ceiling surfaces
std::vector<LineVertical> left_walls_; // Left walls
std::vector<LineVertical> right_walls_; // Right walls
std::vector<LineDiagonal> left_slopes_; // Ramps going left
std::vector<LineDiagonal> right_slopes_; // Ramps going right
std::vector<LineHorizontal> conveyor_belt_floors_; // Moving platforms
};
Collision Detection:
- Player has collision points and "feet" points for fine-grained detection
- Room provides
check*Surfaces()methods for collision resolution - Supports ramps, slopes, conveyor belts, and auto-surfaces
- Enemies have axis-aligned bounding boxes
3.6 Sprite Animation System
Animation is data-driven:
struct AnimationData {
std::string name;
std::vector<SDL_FRect> frames; // Frame rectangles
int speed; // Milliseconds per frame
int loop; // Frame to return to (-1 = no loop)
bool completed;
int current_frame;
int counter;
};
// Loaded from .yaml animation definition files
// Rendered with AnimatedSprite
4. System Interactions & Data Flow
4.1 Application Lifecycle
main()
↓
Director::Director() [Initialization]
├─ Resource::List::init() - Initialize asset registry singleton
├─ Director::setFileList() - Load assets.yaml via Resource::List (no verification)
├─ Options::loadFromFile() - Load game configuration
├─ Audio::init() - Initialize SDL audio
├─ Screen::init() - Create window, SDL renderer
├─ Input::init() - Bind keyboard/gamepad controls
├─ Resource::Cache::init() - Load ALL game resources (with verification)
│ └─ Throws exception if any required asset is missing
└─ Cheevos::init() - Load achievement state
Director::run() [Main loop]
├─ while (SceneManager::current != QUIT)
│ ├─ Logo::run()
│ ├─ LoadingScreen::run()
│ ├─ Title::run()
│ ├─ Game::run()
│ ├─ Ending::run()
│ └─ ...
└─ return 0
Director::~Director() [Cleanup]
├─ Options::saveToFile() - Save game settings
├─ Resource::Cache::destroy()
├─ Audio::destroy()
├─ Input::destroy()
├─ Screen::destroy()
└─ Resource::List::destroy()
4.2 Game Scene Flow (Core Gameplay Loop)
Game::run() {
while (game is running) {
// Update
Input::checkInput() - Get player commands
Player::update() - Physics, animation, collision
Room::update() - Enemy AI, animated tiles
checkCollisions() - Player vs enemies, items, room
checkGameOver() - Win/lose conditions
// Render
Screen::start() - Prepare for drawing
Room::renderMap() - Draw tilemap
Room::renderEnemies() - Draw enemy sprites
Room::renderItems() - Draw item sprites
Player::render() - Draw player sprite
renderScoreboard() - Draw HUD
Screen::render() - Flush to GPU
}
}
4.3 Resource Loading & Caching
Director::setFileList()
└─ Resource::List::loadFromFile(config_path, PREFIX, system_folder_)
├─ Read config/assets.yaml - Parse text configuration file
├─ Parse YAML structure: assets grouped by category
├─ Replace variables (${PREFIX}, ${SYSTEM_FOLDER})
└─ Store in unordered_map (O(1) lookup) - Fast asset path retrieval
Game Scene initialization
└─ Resource::Cache::init() - Loads all resources
├─ loadSounds() - WAV files (with error handling)
├─ loadMusics() - OGG files (with error handling)
├─ loadSurfaces() - GIF/PNG images (with error handling)
├─ loadPalettes() - PAL palette files (with error handling)
├─ loadTextFiles() - Font definition files (with error handling)
├─ loadAnimations() - YAML animation definitions (with error handling)
└─ loadRooms() - Room YAML files (with error handling)
Note: Asset verification happens during actual loading.
If a required file is missing, Cache::load() throws detailed exception.
During gameplay
└─ Resource::Cache::get*(name) - Return cached resource
4.4 Input Flow
SDL_Event (from OS)
↓
Input::checkInput(action)
├─ Check keyboard bindings - SDL_Scancode → InputAction
├─ Check gamepad bindings - SDL_GamepadButton → InputAction
└─ Return true if action active
Game logic uses checked inputs
└─ Player responds to actions
4.5 Audio Control Flow
Game code
├─ Audio::playMusic(name, loop)
├─ Audio::playSound(name, group)
└─ Audio::stopMusic()
↓
Audio [wrapper/manager]
├─ Resource::getMusic(name)
├─ Resource::getSound(name)
└─ jail_audio C library
└─ SDL3 Audio device
5. Important Design Patterns & Conventions
5.1 Naming Conventions
Classes:
PascalCasefor classes:Player,Room,Screen,Director- Suffix
Spritefor sprite classes:Sprite,AnimatedSprite,MovingSprite
Methods:
get*()for getters:getWidth(),getRect()set*()for setters:setColor(),setPos()check*()for boolean checks:checkInput(),checkCollision()update()for game logic updatesrender()for drawing
Variables:
snake_casefor member variables with_suffix:x_,y_,sprite_UPPER_CASEfor constants:BLOCK,MAX_VY,WIDTH- Private members:
private_member_
Structs for Data:
XxxDatasuffix:PlayerData,RoomData,AnimationData- Used as initialization structures passed to constructors
5.2 Memory Management
- Smart Pointers: Uses
std::shared_ptrfor shared ownership (Surfaces, Sprites, Rooms) - Unique Pointers: Uses
std::unique_ptrfor sole ownership (Director, local objects) - Raw Pointers: Minimal use, mainly for singleton access or SDL objects
- Static Allocation: Singletons use static pointer pattern for RAII
5.3 Frame-Independent Physics
Uses DeltaTimer for delta time calculation:
// In game loop
float delta = deltaTimer.tick(); // Get seconds since last frame
// Apply to physics
player.vy_ += gravity * delta;
player.y_ += player.vy_ * delta;
Provides time scaling for slow-motion effects.
5.4 Palette System
- 8-bit indexed color (256 colors per palette)
- Multiple palettes can be loaded and swapped via
PaletteManager Screen::setPaletteByName()changes the active palette- Supports palette sort modes (by luminosity, similarity to Spectrum palette, etc.)
- Supports color replacement per-render:
renderWithColorReplace() - CRT shader effects can modify colors in real-time
5.5 Configuration System
Game settings stored in configuration file:
namespace Options {
inline Video video{}; // Screen resolution, fullscreen, etc.
inline Audio audio{}; // Music/sound volumes
inline Notification notifications{};
inline Cheat cheats{}; // Cheat codes
inline ControlScheme keys{}; // Control mapping
}
Options::loadFromFile(path); // Load from config.yaml
Options::saveToFile(path); // Save on exit
5.6 Achievement System
struct Achievement {
int id;
std::string caption;
std::string description;
int icon;
bool completed;
bool obtainable;
};
Cheevos::unlock(id); // Unlock achievement
Cheevos::setUnobtainable(id); // Lock achievement
Cheevos::saveToFile(); // Persist state
Achievements trigger notifications on unlock.
6. Technology Stack
Core Technologies
| Component | Technology | Version | Role |
|---|---|---|---|
| Graphics | SDL3 | Latest | Window, rendering, input |
| GPU Rendering | SDL3 GPU | Latest | Shader effects (CRT, post-processing via SPIR-V) |
| Audio | SDL3 Audio | Latest | Audio device, mixing |
| Audio Decoding | jail_audio (custom) | 1.x | OGG/WAV playback |
| Image Loading | stb_image | v2.x | PNG/GIF image loading |
| Audio Decoding | stb_vorbis | v1.x | OGG Vorbis support |
| Language | C++ | C++20 | Standard library features |
| Build System | CMake | 3.10+ | Cross-platform building |
C++ Features Used
- Smart Pointers:
std::shared_ptr,std::unique_ptr - Standard Containers:
std::vector,std::array - Modern Features:
std::move, lambda functions, constexpr - Namespaces: Extensive use for organization
- Inline Variables: C++17
inlinefor global game state
Platform Support
- Windows: MinGW/MSVC with SDL3
- macOS: Apple Clang, arm64 architecture, OpenGL
- Linux: GCC with SDL3 and OpenGL
7. Key Classes & Their Responsibilities
Core System Classes
| Class | Purpose | Pattern |
|---|---|---|
Director |
Main application controller, scene manager | Singleton |
Screen |
Rendering, window, palette management | Singleton |
Input |
Keyboard & gamepad input | Singleton |
Audio |
Music and SFX playback | Singleton |
Resource::Cache |
Asset caching and loading (with detailed error messages) | Singleton |
Resource::List |
Asset path registry from config/assets.yaml, O(1) lookups, variable substitution | Singleton |
PaletteManager |
Palette loading, switching, and sorting | Managed by Screen |
Debug |
Debug overlay information | Singleton |
globalEvents |
Global SDL event handling (quit, device reset, mouse) | Namespace |
Game Logic Classes
| Class | Purpose |
|---|---|
Game |
Main gameplay scene, orchestrates update/render |
Player |
Player entity with physics and animation |
Room |
Level data, collision detection |
RoomLoader |
Room loading from YAML files |
TilemapRenderer |
Tilemap rendering |
CollisionMap |
Collision map data |
EnemyManager |
Enemy lifecycle management |
ItemManager |
Item lifecycle management |
Enemy |
Enemy entity behavior and rendering |
Item |
Collectible items |
Scoreboard |
HUD display data |
Cheevos |
Achievement unlock and state |
ItemTracker |
Tracks collected items |
RoomTracker |
Tracks visited rooms |
Rendering Classes
| Class | Purpose |
|---|---|
Surface |
8-bit indexed color pixel buffer with palette |
Sprite |
Renders a sprite region |
AnimatedSprite |
Frame-based animation rendering |
MovingSprite |
Sprite with velocity/position |
DissolveSprite |
Dissolve transition sprite |
Text |
Text rendering system |
Sdl3gpuShader |
SDL3 GPU shader compilation and effects |
PaletteManager |
Palette loading and management |
Utility Classes
| Class/Header | Purpose |
|---|---|
DeltaTimer |
Frame-rate independent timing |
easing_functions.hpp |
Easing/interpolation functions for animations |
8. Common Development Workflows
Adding a New Input Action
- Add to
InputActionenum incore/input/input.hpp - Bind in
Director::initInput():Input::get()->bindKey(InputAction::NEW_ACTION, SDL_SCANCODE_X); - Check in game code:
if (Input::get()->checkInput(InputAction::NEW_ACTION)) { // Handle action }
Adding a New Scene
- Create class inheriting appropriate base (usually standalone
run()method) - Add to
SceneManager::Sceneenum - Implement in
Director::run()switch statement - Set
SceneManager::currentto transition
Adding Game Assets
- Place file in
data/directory - Add entry to
config/assets.yamlunder the appropriate category:Available types:assets: category_name: # e.g., player, enemies, music, etc. - type: BITMAP path: ${PREFIX}/data/path/file.extDATA,BITMAP,ANIMATION,MUSIC,SOUND,FONT,ROOM,PALETTE - Optional flags can be added:
- type: DATA path: ${SYSTEM_FOLDER}/file.txt required: false # Don't fail if missing absolute: true # Path is absolute - Resource loads automatically during
Resource::init() - Access via:
Resource::Cache::get()->getSurface("name")
Note: No recompilation needed when adding/removing/modifying assets in config/assets.yaml
Modifying Collision Detection
- Update
Room::setBottomSurfaces(),setLeftSlopes(), etc. - Modify Player collision point generation in
Player::updateColliderPoints() - Adjust tile classification in
TileTypeenum - Test with debug visualization (F12 key)
9. Debug Features
Available at Runtime
- F12 - Toggle debug info overlay (FPS, player position, collision points)
- F1/F2 - Decrease/increase window zoom
- F3 - Toggle fullscreen mode
- F4 - Toggle shader effects
- F5/F6 - Next/previous palette
- F7 - Toggle integer scaling
- M - Toggle music
- B - Toggle border display
- P - Pause game
Debug Mode Compilation
In debug builds (#ifdef DEBUG), renders:
- Player collision boxes
- Collision point visualization
- Debug information overlay
- Special debug event handling
10. Performance Considerations
Optimization Points
- Rendering: Uses indexed color (8-bit) to reduce memory bandwidth
- Surfaces: Shared smart pointers reduce copying
- Collision: Pre-computed tile surface lists avoid per-frame searches
- Animation: Frame-based animation reduces computation vs. bone systems
- Audio: Cached music and sound effects, no runtime decoding
- Delta Time: Frame-rate independent logic for smooth gameplay
Known Limitations
- Single-threaded architecture (SDL3 requires single-thread rendering)
- Surfaces stored entirely in CPU memory (not GPU-side textures)
- Palette system requires full surface redraw when changing colors
11. File Format Reference
Asset Configuration File (config/assets.yaml)
YAML-based asset registry with grouped structure:
# Projecte 2026 - Asset Configuration
# Variables: ${PREFIX}, ${SYSTEM_FOLDER}
assets:
# FONTS
fonts:
- type: BITMAP
path: ${PREFIX}/data/font/smb2.gif
- type: FONT
path: ${PREFIX}/data/font/smb2.fnt
# PLAYER
player:
- type: BITMAP
path: ${PREFIX}/data/player/player.gif
- type: ANIMATION
path: ${PREFIX}/data/player/player.yaml
# MUSIC
music:
- type: MUSIC
path: ${PREFIX}/data/music/title.ogg
# SYSTEM FILES (optional, absolute paths)
system:
- type: DATA
path: ${SYSTEM_FOLDER}/config.yaml
required: false
absolute: true
Asset Structure:
- Assets are organized into categories (fonts, palettes, shaders, player, enemies, etc.)
- Each asset entry contains:
type- Asset type (required)path- File path with variable substitution (required)required- Whether file must exist (optional, default:true)absolute- Whether path is absolute (optional, default:false)
Available Asset Types:
DATA- General data files (text, JSON, etc.)BITMAP- Images (GIF, PNG)ANIMATION- Animation definition files (.yaml)MUSIC- Music files (OGG)SOUND- Sound effects (WAV)FONT- Font definition filesROOM- Room data files (.yaml)PALETTE- Color palette files (.pal)
Variables:
${PREFIX}- Replaced with/../Resourceson macOS bundles, empty otherwise${SYSTEM_FOLDER}- Replaced with user's system config folder
Animation Files (.yaml)
YAML-based animation definitions with frame data:
animations:
- name: default
frameWidth: 16
frameHeight: 16
speed: 100
loop: 0
frames: [...]
Room Data Files (.yaml)
YAML-based room definitions:
room:
name_en: Starting Room
bgColor: 0x000000
connections: [...]
tilemap: [...]
Tilemap Files (.tmx)
Tiled map format with tileset and collision data.
Palette Files (.pal)
Binary 256-color palette format (256 × 4 bytes RGBA).
12. Quick Reference: Main Entry Points
For Graphics Issues
Screen::render()- Main rendering methodScreen::setPaletteByName()- Palette switchingPaletteManager- Palette loading and sortingSurfaceclass - Pixel buffer operations
For Input Issues
Input::checkInput()- Input state checkingDirector::initInput()- Binding configuration
For Audio Issues
Audio::playMusic()- Music playbackAudio::playSound()- SFX playbackjail_audiolibrary - Low-level audio operations
For Game Logic
Game::run()- Main game loopRoomclass - Collision and level logicPlayer::update()- Physics and movement
For Asset Management
config/assets.yaml- Asset configuration file (text-based, no recompilation needed)Resource::List::loadFromFile()- Loads asset registry from config fileResource::List::get()- Retrieves asset path (O(1) lookup with unordered_map)Resource::Cache- Asset loading and cachingDirector::setFileList()- CallsResource::List::loadFromFile()with PREFIX and system_folder
13. Future Enhancement Points
Identified Areas for Expansion
- More Scenes: Additional game modes
- Custom Rooms: Level editor integration
- Audio Streaming: Background music without loading entire file
- Particle System: Visual effects for pickups/collisions
- Controller Feedback: Haptic feedback for game events
- Accessibility: Font scaling, color-blind modes, key remapping UI
Development Notes
- Language: All code comments and some variable names are in Spanish (maintaining original author style)
- Compilation: Use CMake - automatically handles platform differences
- Performance Profiling: Use debug overlay (F12) for basic metrics; consider external profilers for deeper analysis
- Common Warnings: The codebase has been cleaned of the
struct/classforward declaration inconsistency warnings that previously appeared
Important Code Consistency Rules
-
Forward Declarations: Always use
classfor forward declarations of classes defined withclass(notstruct). TheSurfaceclass is defined asclass Surfaceinsurface.hpp:57, so all forward declarations must useclass Surface; -
Singleton Pattern: When creating new singletons, follow the existing pattern:
- Private static instance pointer
- Private constructor/destructor
- Public
init(),destroy(), andget()methods
-
Delta Time: Always use
DeltaTimerfor frame-rate independent physics calculations -
Memory Management: Prefer
std::shared_ptrfor shared resources (Surfaces, Sprites) andstd::unique_ptrfor sole ownership
Last Updated: April 2026 Original Author: JailDesigner Repository: Gitea (internal)