Files
jaildoctors_dilemma/CLAUDE.md

991 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
### 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)
```bash
# Install Xvfb
sudo apt-get install xvfb
```
#### Running the Game in Headless Mode
**Option 1: Using the wrapper script (RECOMMENDED)**
```bash
./run_headless.sh
```
**Option 2: Using xvfb-run directly**
```bash
xvfb-run -a ./jaildoctors_dilemma
```
**Option 3: Custom display configuration**
```bash
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.
```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
```
**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.
```bash
# 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)
│ ├── 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:
```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::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:
```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<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:
```cpp
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)
```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::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: `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.yaml
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::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
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. Add entry to `config/assets.yaml` under the appropriate category:
```yaml
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:
```yaml
- 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:
```yaml
# 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 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 (.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
- `config/assets.yaml` - Asset configuration file (text-based, no recompilation needed)
- `Asset::loadFromFile()` - Loads assets from config file
- `Resource::List::get()` - Retrieves asset path (O(1) lookup with unordered_map)
- `Resource::load()` - Asset loading
- `Director::setFileList()` - Calls `Asset::loadFromFile()` with PREFIX and system_folder
---
## 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)