34 KiB
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.
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 the wrapper script (RECOMMENDED)
./run_headless.sh
Option 2: Using xvfb-run directly
xvfb-run -a ./jaildoctors_dilemma
Option 3: Custom display configuration
xvfb-run -a -s "-screen 0 1280x720x24" ./jaildoctors_dilemma
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)
│ ├── 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
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, 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 supportSurfaceSprite- Renders a fixed region of a surfaceSurfaceAnimatedSprite- Frame-based animation on top of spriteSurfaceMovingSprite- 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]
├─ Resource::List::init() - Initialize asset registry singleton
├─ Director::setFileList() - Load assets.yaml configuration (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()
└─ Asset::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:SurfaceSprite,SurfaceAnimatedSprite
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
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.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 | 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
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 |
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
- 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:
# JailDoctor's Dilemma - Asset Configuration
# Variables: ${PREFIX}, ${SYSTEM_FOLDER}
assets:
# FONTS
fonts:
- type: BITMAP
path: ${PREFIX}/data/font/smb2.gif
- type: FONT
path: ${PREFIX}/data/font/smb2.txt
# 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 (.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 methodScreen::setPalete()- Palette applicationSurfaceclass - 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)Asset::loadFromFile()- Loads assets from config fileResource::List::get()- Retrieves asset path (O(1) lookup with unordered_map)Resource::load()- Asset loadingDirector::setFileList()- CallsAsset::loadFromFile()with PREFIX and system_folder
13. Future Enhancement Points
Identified Areas for Expansion
- Network Play: Stats upload to online service
- 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: November 2022 (per README) Original Author: JailDesigner Repository: Gitea (internal)