# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. --- # JailDoctor's Dilemma - Codebase Architecture Guide ## Overview **JailDoctor's Dilemma** 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 ```bash # From project root mkdir -p build cd build cmake .. cmake --build . ``` ### Rebuild After Changes ```bash # From build directory cmake --build . # Or from project root cmake --build build ``` ### Clean Build ```bash # From build directory cmake --build . --clean-first # Or from project root cmake --build build --clean-first ``` ### Run the Game ```bash # Executable is placed in project root ./jaildoctors_dilemma ``` **Important:** The build directory is `/Users/sergio/Gitea/jaildoctors_dilemma/build` and the output executable is placed in the project root directory. ### Linter (clang-tidy) **IMPORTANT:** Always run the linter on specific files, NOT on the entire project, to avoid long execution times and errors in unrelated files. ```bash # 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 ``` **Note:** Running the linter on the entire project can produce errors in files like `defaults.hpp` that are unrelated to your changes. Always target specific files you're working on. --- ## 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) │ ├── input/ # Input handling │ │ ├── input.hpp/cpp # Input manager (keyboard, gamepad) │ │ ├── global_inputs.hpp # Global input state │ │ └── mouse.hpp # Mouse input │ ├── rendering/ # Graphics rendering │ │ ├── screen.hpp/cpp # Screen/window singleton, SDL renderer │ │ ├── surface.hpp/cpp # 8-bit indexed color surface abstraction │ │ ├── surface_sprite.hpp # Static sprite rendering │ │ ├── surface_animated_sprite.hpp # Animated sprite with frame data │ │ ├── surface_moving_sprite.hpp # Moving sprite with velocity │ │ ├── texture.hpp/cpp # SDL texture wrapper │ │ ├── text.hpp/cpp # Text rendering system │ │ ├── gif.hpp/cpp # GIF image loader │ │ ├── opengl/ # OpenGL shader backend │ │ │ └── opengl_shader.hpp/cpp # CRT shader effects │ │ └── shader_backend.hpp # Abstract shader interface │ ├── resources/ # Asset & Resource management │ │ ├── asset.hpp/cpp # Asset registry (file path mapping) │ │ └── resource.hpp/cpp # Resource singleton (loads/caches assets) │ └── 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, tilemap, collision │ │ ├── room_tracker.hpp/cpp # Tracks visited rooms │ │ ├── scoreboard.hpp/cpp # Score display & data │ │ ├── item_tracker.hpp/cpp # Tracks collected items │ │ ├── stats.hpp/cpp # Game statistics │ │ └── 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 │ ├── options.hpp/cpp # Game configuration/options │ ├── scene_manager.hpp # Scene flow state machine │ ├── defaults.hpp # Game defaults constants │ └── gameplay.hpp # Gameplay constants ├── external/ # Third-party libraries │ ├── jail_audio.hpp/cpp # Custom audio library │ ├── jail_audio.h # C interface for jail_audio │ ├── stb_image.h # Image loading library │ └── stb_vorbis.h # OGG Vorbis audio decoding ├── utils/ # Utility code │ ├── delta_timer.hpp/cpp # Frame-rate independent timing │ ├── defines.hpp # Game constants (resolutions, block sizes) │ └── utils.hpp/cpp # Helper functions (colors, math) └── main.cpp # Application entry point 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: ```cpp 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` - Asset loading, caching, streaming - `Asset` - Asset path registry and verification - `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: ```cpp // namespace SceneManager enum class Scene { LOGO, LOADING_SCREEN, TITLE, CREDITS, GAME, DEMO, GAME_OVER, ENDING, ENDING2, 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 - `SurfaceSprite` - Renders a fixed region of a surface - `SurfaceAnimatedSprite` - Frame-based animation on top of sprite - `SurfaceMovingSprite` - Adds velocity/position to animated sprite - Supports color replacement, palette swapping, and shader effects (CRT) ### 3.5 Tile-Based Collision System Rooms use a sophisticated collision detection system: ```cpp class Room { std::vector bottom_floors_; // Ground surfaces std::vector top_floors_; // Ceiling surfaces std::vector left_walls_; // Left walls std::vector right_walls_; // Right walls std::vector left_slopes_; // Ramps going left std::vector right_slopes_; // Ramps going right std::vector 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: ```cpp struct AnimationData { std::string name; std::vector 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 .ani files (list of animation names) // Rendered with SurfaceAnimatedSprite ``` --- ## 4. System Interactions & Data Flow ### 4.1 Application Lifecycle ``` main() ↓ Director::Director() [Initialization] ├─ Options::init() - Load game configuration ├─ Asset::init() - Register asset paths ├─ Screen::init() - Create window, SDL renderer ├─ Audio::init() - Initialize SDL audio ├─ Input::init() - Bind keyboard/gamepad controls ├─ Resource::init() - Load all game resources └─ 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::destroy() ├─ Audio::destroy() ├─ Input::destroy() ├─ Screen::destroy() └─ Asset::destroy() ``` ### 4.2 Game Scene Flow (Core Gameplay Loop) ```cpp 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() ├─ Asset::add(file, type) - Register all assets └─ Asset::check() - Verify files exist Game Scene initialization └─ Resource::init() - Loads all resources ├─ loadSounds() - WAV files ├─ loadMusics() - OGG files ├─ loadSurfaces() - GIF/PNG images ├─ loadAnimations() - .ani animation definitions ├─ loadTileMaps() - .room tilemap data └─ loadRooms() - Room metadata During gameplay └─ Resource::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: `SurfaceSprite`, `SurfaceAnimatedSprite` **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: ```cpp // 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 - `Surface::setPalette()` changes rendering colors - 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: ```cpp 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 inline Stats stats{}; // Game statistics } Options::loadFromFile(path); // Load from config.txt Options::saveToFile(path); // Save on exit ``` ### 5.6 Achievement System ```cpp 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** | OpenGL | 3.2+ | Shader effects (CRT) | | **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` | Asset caching and loading | Singleton | | `Asset` | Asset path registry | Singleton | | `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, tilemap rendering | | `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 | | `SurfaceSprite` | Renders a sprite region | | `SurfaceAnimatedSprite` | Frame-based animation rendering | | `SurfaceMovingSprite` | Sprite with velocity/position | | `Text` | Text rendering system | | `OpenGLShader` | Shader compilation and effects | ### Utility Classes | Class | Purpose | |-------|---------| | `DeltaTimer` | Frame-rate independent timing | --- ## 8. Common Development Workflows ### Adding a New Input Action 1. Add to `InputAction` enum in `core/input/input.hpp` 2. Bind in `Director::initInput()`: ```cpp Input::get()->bindKey(InputAction::NEW_ACTION, SDL_SCANCODE_X); ``` 3. Check in game code: ```cpp 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. Register in `Director::setFileList()`: ```cpp Asset::get()->add("/data/path/file.ext", AssetType::TYPE); ``` 3. Resource loads automatically during `Resource::init()` 4. Access via: `Resource::get()->getSurface("name")` ### 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 ### Animation Files (.ani) List of animation names, one per line: ``` default jump run fall ``` ### Room Data Files (.room) Key-value pairs defining room properties: ``` number=01 name=Starting Room bg_color=0x000000 border_color=0xFF00FF ... ``` ### 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::setPalete()` - Palette application - `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 - `Asset::add()` - Asset registration - `Resource::load()` - Asset loading - `Director::setFileList()` - Complete asset registry --- ## 13. Future Enhancement Points ### Identified Areas for Expansion 1. **Network Play:** Stats upload to online service 2. **More Scenes:** Additional game modes 3. **Custom Rooms:** Level editor integration 4. **Audio Streaming:** Background music without loading entire file 5. **Particle System:** Visual effects for pickups/collisions 6. **Controller Feedback:** Haptic feedback for game events 7. **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:** November 2022 (per README) **Original Author:** JailDesigner **Repository:** Gitea (internal)