Files
aee/CLAUDE.md
Sergio Valor a4ee304a79 - unitat mouse per amagar el punter
- overlay captura el esc i confirma la eixida (falla en game)
2026-04-04 22:32:53 +02:00

8.7 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Aventures En Egipte (AEE) — a retro-style 2D game written in C++ using SDL3. The game uses a software-rendered 8-bit paletted graphics engine (320x200, 256 colors), custom audio (JailAudio), and GIF-based assets. The codebase and commit messages are in Valencian/Catalan.

Build

# Linux
cmake -B build
cmake --build build

# Windows (MinGW)
cmake -B build -G "MinGW Makefiles"
cmake --build build

Dependencies: SDL3. Uses CMake (minimum 3.10) with C++20. SPIR-V shaders compiled automatically if glslc is available; precompiled headers used as fallback.

The executable is output to the project root. The data/ folder must be in the working directory at runtime.

Architecture

Golden Rule: Do Not Touch Gameplay

The original game logic (gameplay, entities, map, scoring, collisions, animations) must remain untouched. All modernization work targets the presentation layer and infrastructure only. Any new feature must be implemented as an overlay on top of the existing game, never by modifying original gameplay code.

Boundary: Original vs New Code

Path Owner Rule
source/core/jail/ Original engine Do not modify gameplay behavior
source/game/*.cpp/hpp (except options/defines/defaults) Original game Do not modify
source/core/rendering/ New presentation layer Free to modify
source/core/input/ New input layer Free to modify
source/utils/ New utilities Free to modify
source/game/options,defines,defaults New config system Free to modify
data/*.gif, *.ogg Original assets Do not modify
data/fonts/, data/ui/, data/shaders/ New assets Free to modify

Original "Jail" Engine (source/core/jail/)

Flat C-style APIs (no classes), prefixed by subsystem. Do not touch gameplay logic.

  • JG (jgame) — Game loop timing: init/finalize, fixed-timestep update via JG_ShouldUpdate()
  • JD8 (jdraw8) — 8-bit paletted software renderer. 320x200 screen buffer (JD8_Surface = Uint8*), palette-indexed blitting with color-key transparency, fade effects. JD8_Flip() converts palette→ARGB and delegates to Screen::present()
  • JA (jail_audio) — Custom audio mixing using SDL3 audio streams (OGG via stb_vorbis, WAV)
  • JI (jinput) — Input: keyboard state polling, key debouncing, cheat code detection. Filters GUI keys from game, calls GlobalInputs::handle() and Mouse::updateCursorVisibility() each update
  • JF (jfile) — File I/O: filesystem folder mode (data/) or packed resource file (.jrf). Config folder at ~/.config/jailgames/aee/

Presentation Layer (source/core/rendering/)

  • Screen (screen.hpp/cpp) — Singleton. Manages SDL_Window, SDL_Renderer, SDL_Texture. Dual rendering path: SDL3GPU with shaders (primary) or SDL_Renderer fallback. Handles fullscreen, zoom, aspect ratio 4:3, integer scaling, VSync. Counts FPS and updates render info text
  • Overlay (overlay.hpp/cpp) — Paints directly on the ARGB pixel buffer before presentation. Handles notifications (slide-in animation), render info display (top/bottom/off, configurable colors), and double-ESC-to-quit logic
  • Text (text.hpp/cpp) — Bitmap font renderer. Loads .fnt + .gif pairs, renders UTF-8 glyphs directly on Uint32* ARGB buffer. No dependency on SDL_Texture or palettes
  • SDL3GPUShader (sdl3gpu/) — GPU shader backend (Vulkan/Metal). PostFX and CRT-Pi shaders with presets, supersampling (3×/6×/9×), Lanczos downscaling. Supports 4:3 aspect ratio stretch fused into the upscale pass to avoid artifacts

Input Layer (source/core/input/)

  • GlobalInputs (global_inputs.hpp/cpp) — Maps configurable function keys to presentation actions. Uses debounce. Returns whether a key was consumed (to suppress from game layer)
  • Mouse (mouse.hpp/cpp) — Auto-hides cursor after 3 seconds of inactivity

Configuration System (source/game/)

Follows the pattern from jaildoctors_dilemma, persists to YAML:

  • defines.hpp — Game constants: Texts::WINDOW_TITLE, Texts::VERSION, GameScreen::WIDTH/HEIGHT
  • defaults.hpp — Default values: Defaults::KeysGUI, Defaults::KeysGame, Defaults::Video, Defaults::Audio, Defaults::Window, Defaults::Game
  • options.hpp/cppOptions namespace with inline globals and YAML load/save. Structs: KeysGUI, KeysGame, Video, RenderInfo, Audio, Window, Game, PostFXPreset, CrtPiPreset

Utilities (source/utils/)

  • utils.hpp/cpptoLower() and other helpers

Function Key Map

Key Action
F1 Decrease window zoom
F2 Increase window zoom
F3 Toggle fullscreen
F4 Toggle shaders on/off
F5 Toggle aspect ratio (square pixels ↔ 4:3 CRT)
F6 Toggle supersampling
F7 Cycle shader type (PostFX ↔ CRT-Pi)
F8 Cycle shader presets
F9 Toggle stretch filter (nearest ↔ linear)
F10 Cycle render info (off → top → bottom → off)
ESC Double-press to quit (with overlay notification)

All key bindings are configurable via Options::keys_gui and stored in config.yaml.

Rendering Pipeline

JD8_Flip():
  1. palette→ARGB in pixel_data[320×200]        (original engine)
  2. Screen::present(pixel_data):
     a. FPS count + render info text update
     b. Overlay::render(pixel_data)              (notifications, render info, directly on ARGB)
     c. IF GPU + shaders enabled:
        - uploadPixels → scene_texture (320×200)
        - [IF 4:3] stretch pass fused with upscale: scene → scaled_texture (W×factor, H×factor×1.2)
        - [IF SS] upscale pass: scene → scaled_texture (W×factor, H×factor)
        - PostFX or CRT-Pi shader → swapchain (with viewport letterboxing)
     d. ELSE IF GPU without shaders:
        - uploadPixels → clean render → swapchain
     e. ELSE (fallback):
        - SDL_UpdateTexture → SDL_RenderPresent

Pixel Format

JD8_Flip produces ABGR byte order: 0xFF000000 + R + (G<<8) + (B<<16). SDL texture uses SDL_PIXELFORMAT_ABGR8888. GPU textures use SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM (same byte layout on little-endian). Overlay colors are ABGR format.

Persistence Files

File Content
~/.config/jailgames/aee/config.yaml Main config (video, audio, window, render_info, game, shader selection)
~/.config/jailgames/aee/postfx.yaml PostFX shader presets (6 defaults: CRT, NTSC, CURVED, SCANLINES, SUBTLE, CRT LIVE)
~/.config/jailgames/aee/crtpi.yaml CRT-Pi shader presets (4 defaults: DEFAULT, CURVED, SHARP, MINIMAL)

External Libraries (source/external/)

  • gif.h — Header-only GIF decoder. Cannot be included from more than one .cpp (no include guards on functions). Other files use extern declarations for LoadGif()
  • stb_vorbis.h — stb single-header OGG decoder
  • fkyaml_node.hpp — Header-only YAML parser (fkYAML v0.4.2)

Data Assets (data/)

  • *.gif, *.ogg — Original game assets (do not modify)
  • fonts/8bithud.fnt + .gif — Bitmap font for overlay (8×8, 124 glyphs, UTF-8 with accents)
  • shaders/ — GLSL sources: postfx.vert, postfx.frag, upscale.frag, downscale.frag, crtpi_frag.glsl
  • ui/ — Reserved for future UI graphics

Known Issues & Technical Debt

  1. ESC double-press does not work in ModuleGame: modulegame.cpp:136 polls JI_KeyPressed(ESCAPE) each frame and calls JG_QuitSignal() immediately. The overlay intercepts the KEY_UP event and blocks polling via esc_blocked_, but there is a race condition — the game's polling can fire before the block takes effect. Needs deeper integration (possibly intercepting at JI_KeyPressed level with state tracking across frames, or modifying the game module to use the overlay's quit flow)

  2. Overlay freezes during intro sequences: ModuleSequence does blocking loops with delays that don't call JI_Update(), so Overlay::render() doesn't execute and notifications appear frozen. Would require refactoring the original modules to use non-blocking animation — conflicts with golden rule

  3. gif.h cannot be included twice: Functions are not static or inline, causing multiple definition errors. Text class uses extern forward declarations as workaround

Main Loop (source/main.cpp)

Init order: file_setconfigfolderOptions::loadOptions::loadPostFX/CrtPiJG_InitScreen::initJD8_InitJA_InitOverlay::init. Shutdown reverse. A state machine alternates between ModuleSequence (state 1) and ModuleGame (state 0). Each module's Go() returns the next state (-1 to quit).