Files
projecte_2026/CLAUDE.md

45 KiB
Raw Blame History

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

  1. 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.
  2. Nueva física del jugador HECHO — Player reescrito con pipeline de 6 fases, colisiones tile-based, ~20 métodos eliminados.
  3. Motor de colisiones por tiles HECHOTileCollider con 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).
  4. Transición de pantalla animada HECHO — Scroll con easing cubicInOut (0.5s), ambas rooms visibles, enemigos activos, jugador puede moverse durante la transición.
  5. 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 PaletteManager y a todos los assets de color.

Estado del renombrado

Se han renombrado las referencias de JailDoctor's DilemmaProjecte 2026 y jaildoctors_dilemmaprojecte_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: en data/locale/*.yaml y su uso en source/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.rc y jdd.res (solo bundlean el icono).

Log de cambios realizados (sesiones de trabajo)

Eliminaciones

  • Clase Stats (persistencia CSV de muertes/visitas) eliminada por completo: archivos source/game/gameplay/stats.hpp/cpp, entrada en CMakeLists, referencias en Game scene (stats_, initStats(), addVisit/addDeath), ficheros stats.csv/stats_buffer.csv en config/assets.yaml, patrón *stats.txt en .gitignore, campo worst_nightmare en Options::Stats + Defaults::Stats::WORST_NIGHTMARE, display de "worst nightmare" en game_over.cpp/.hpp, traducciones en locales, menciones en docs.
  • Mantenido: Options::stats.items y Options::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.ogg y 574071_EA_DTV.ogg (7 MB total).
  • config/assets.yaml reducido 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.pal de ../pollo/data/palettes/ (JASC-PAL, 28 entradas: CLEAR+27 colores CPC).
  • config/assets.yaml: solo cpc.pal.
  • Defaults::Video::PALETTE_NAME = "cpc".
  • Traída la clase Color de pollo a source/utils/color.hpp + color.cpp (enum Color::Cpc con 28 valores 0-27 + Color::fromString()).
  • Struct RGB Color en utils.hpp renombrada a Rgb para evitar conflicto con la nueva clase. Propagado a utils.cpp (colorAreEqual), screen.hpp/.cpp (clearRenderer).
  • Añadido source/utils/color.cpp a CMakeLists.txt.
  • Pendiente de revisar: enum PaletteColor en utils.hpp sigue con los 16 índices antiguos (ZX Spectrum). Los índices 0-4 coinciden con CPC, pero 5+ mapean a colores diferentes (ej. PaletteColor::BRIGHT_RED=5 ahora renderiza MAGENTA en CPC). stringToColor() también sin actualizar. SPECTRUM_REFERENCE en palette_manager.cpp sigue 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.ini y Thumbs.db al .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 cubicInOut durante 0.5s.
    • Render offset global añadido a Screen (setRenderOffset) aplicado en los 6 métodos de Surface::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.

Impulso de salto

  • JUMP_VELOCITY incrementado 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) y collision: (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 del collision_tile_map (antes deducía el tipo por rangos de índice del tileset de dibujo). Constructor ya no necesita tile_set_width.
  • Editor de colisiones: Tecla 7 o EDIT DRAW/EDIT COLLISION alterna entre editar el tilemap de dibujo y el de colisiones. En modo collision se superpone el collision.gif (7 tiles) sobre el mapa de dibujo. Right-click abre el tile picker del tileset correspondiente. RoomSaver guarda ambos sub-mapas.
  • collision.gif registrado en assets.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 via Room::getTileCollider().
  • Player reescrito con pipeline de 6 fases claras:
    1. handleInput() — leer input
    2. updateVelocity() + applyGravity() — calcular velocidades
    3. handleJumpAndDrop() — salto + drop-through (plataformas y slopes con DOWN)
    4. moveHorizontal() + moveVertical() — movimiento con colisión tile-based
    5. checkFalling() — detectar caída
    6. syncSpriteAndCollider() + 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 en foot_y y foot_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_ += 1 y ON_AIR. No hay flags dropping_through_.
    • checkFloor para PASSABLE: solo aterriza si foot_y_current <= tile_top (pies estaban por encima).
    • checkFloor para slopes: solo aterriza si foot_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.
  • Pendiente: tiles 5 (kill) y 6 (conveyor) no soportados aún en el nuevo motor.

Otros

  • Añadidos desktop.ini y Thumbs.db al .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 improvements
  • performance-*: Performance optimizations
  • bugprone-*: Potential bug detection

Known False Positives:

  • defaults.hpp: May report errors in constant definitions
  • readability-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 methods
  • passedByValue: Acceptable for small types like SDL_FRect in game code
  • constParameter: 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

  1. Run linters after compilation succeeds - Fix compile errors first
  2. Analyze specific files - Faster feedback, avoids unrelated errors
  3. Review before applying --fix - Understand changes before accepting
  4. Context matters - Game code may legitimately have "magic numbers" for gameplay constants
  5. Use both tools - They catch different issues and complement each other
  6. Check suppressions file - tools/linter/cppcheck_suppressions excludes external/ and system headers

Integration with Development Workflow

When refactoring code (especially with /refactor-class command):

  1. Make structural changes
  2. Compile to verify syntax
  3. Run clang-tidy (without --fix) to identify issues
  4. Run cppcheck -w to catch bugs
  5. Review and fix legitimate issues
  6. Apply automatic fixes if appropriate
  7. 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 effects
  • Input - Keyboard & gamepad input binding and checking
  • Audio - Music and sound effect playback
  • Resource::Cache - Asset loading, caching, streaming (with error handling)
  • Resource::List - Asset path registry from assets.yaml (O(1) lookups)
  • Director - Main application controller
  • Cheevos - Achievement state management
  • Debug - 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
  • Director switches between scenes based on SceneManager::current
  • Scenes can request transitions via SceneManager::current assignment

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 support
  • Sprite - Renders a fixed region of a surface
  • AnimatedSprite - Frame-based animation on top of sprite
  • MovingSprite - Adds velocity/position to animated sprite
  • DissolveSprite - 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:

  • PascalCase for classes: Player, Room, Screen, Director
  • Suffix Sprite for 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 updates
  • render() for drawing

Variables:

  • snake_case for member variables with _ suffix: x_, y_, sprite_
  • UPPER_CASE for constants: BLOCK, MAX_VY, WIDTH
  • Private members: private_member_

Structs for Data:

  • XxxData suffix: PlayerData, RoomData, AnimationData
  • Used as initialization structures passed to constructors

5.2 Memory Management

  • Smart Pointers: Uses std::shared_ptr for shared ownership (Surfaces, Sprites, Rooms)
  • Unique Pointers: Uses std::unique_ptr for 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 inline for 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

  1. Add to InputAction enum in core/input/input.hpp
  2. Bind in Director::initInput():
    Input::get()->bindKey(InputAction::NEW_ACTION, SDL_SCANCODE_X);
    
  3. Check in game code:
    if (Input::get()->checkInput(InputAction::NEW_ACTION)) {
        // Handle action
    }
    

Adding a New Scene

  1. Create class inheriting appropriate base (usually standalone run() method)
  2. Add to SceneManager::Scene enum
  3. Implement in Director::run() switch statement
  4. Set SceneManager::current to transition

Adding Game Assets

  1. Place file in data/ directory
  2. Add entry to config/assets.yaml under the appropriate category:
    assets:
      category_name:  # e.g., player, enemies, music, etc.
        - type: BITMAP
          path: ${PREFIX}/data/path/file.ext
    
    Available types: DATA, BITMAP, ANIMATION, MUSIC, SOUND, FONT, ROOM, PALETTE
  3. Optional flags can be added:
    - type: DATA
      path: ${SYSTEM_FOLDER}/file.txt
      required: false  # Don't fail if missing
      absolute: true   # Path is absolute
    
  4. Resource loads automatically during Resource::init()
  5. Access via: Resource::Cache::get()->getSurface("name")

Note: No recompilation needed when adding/removing/modifying assets in config/assets.yaml

Modifying Collision Detection

  1. Update Room::setBottomSurfaces(), setLeftSlopes(), etc.
  2. Modify Player collision point generation in Player::updateColliderPoints()
  3. Adjust tile classification in TileType enum
  4. 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

  1. Rendering: Uses indexed color (8-bit) to reduce memory bandwidth
  2. Surfaces: Shared smart pointers reduce copying
  3. Collision: Pre-computed tile surface lists avoid per-frame searches
  4. Animation: Frame-based animation reduces computation vs. bone systems
  5. Audio: Cached music and sound effects, no runtime decoding
  6. 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 files
  • ROOM - Room data files (.yaml)
  • PALETTE - Color palette files (.pal)

Variables:

  • ${PREFIX} - Replaced with /../Resources on 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 method
  • Screen::setPaletteByName() - Palette switching
  • PaletteManager - Palette loading and sorting
  • Surface class - Pixel buffer operations

For Input Issues

  • Input::checkInput() - Input state checking
  • Director::initInput() - Binding configuration

For Audio Issues

  • Audio::playMusic() - Music playback
  • Audio::playSound() - SFX playback
  • jail_audio library - Low-level audio operations

For Game Logic

  • Game::run() - Main game loop
  • Room class - Collision and level logic
  • Player::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 file
  • Resource::List::get() - Retrieves asset path (O(1) lookup with unordered_map)
  • Resource::Cache - Asset loading and caching
  • Director::setFileList() - Calls Resource::List::loadFromFile() with PREFIX and system_folder

13. Future Enhancement Points

Identified Areas for Expansion

  1. More Scenes: Additional game modes
  2. Custom Rooms: Level editor integration
  3. Audio Streaming: Background music without loading entire file
  4. Particle System: Visual effects for pickups/collisions
  5. Controller Feedback: Haptic feedback for game events
  6. 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/class forward declaration inconsistency warnings that previously appeared

Important Code Consistency Rules

  1. Forward Declarations: Always use class for forward declarations of classes defined with class (not struct). The Surface class is defined as class Surface in surface.hpp:57, so all forward declarations must use class Surface;

  2. Singleton Pattern: When creating new singletons, follow the existing pattern:

    • Private static instance pointer
    • Private constructor/destructor
    • Public init(), destroy(), and get() methods
  3. Delta Time: Always use DeltaTimer for frame-rate independent physics calculations

  4. Memory Management: Prefer std::shared_ptr for shared resources (Surfaces, Sprites) and std::unique_ptr for sole ownership


Last Updated: April 2026 Original Author: JailDesigner Repository: Gitea (internal)