Files
jaildoctors_dilemma/CLAUDE.md

26 KiB
Raw Blame History

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

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

Important: The build directory is /Users/sergio/Gitea/jaildoctors_dilemma/build and the output executable is placed in the project root directory.


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:

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:

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

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

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:

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

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

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():
    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. Register in Director::setFileList():
    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)