11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Projecte 2026 - Nuevo juego (derivado de JailDoctor's Dilemma)
IMPORTANTE: Este repositorio parte del código de JailDoctor's Dilemma como base, pero es un juego nuevo. projecte_2026 es un nombre placeholder hasta tener el diseño final. Gran parte de la arquitectura documentada más abajo describe el código heredado; varios subsistemas se van a reescribir.
Cambios de diseño previstos respecto al juego original
- Habitaciones más grandes — aprovechar el espacio del marcador (scoreboard) para ampliar el área jugable. Esto implica revisar la resolución de canvas, el layout de la HUD y el tamaño de los tilemaps.
Nueva física del jugador✅ HECHO — Player reescrito con pipeline de 6 fases, colisiones tile-based, ~20 métodos eliminados.Motor de colisiones por tiles✅ HECHO —TileCollidercon queries directas al grid. Collision tilemap editado desde el editor (tecla 8). Sistema antiguo de superficies preservado pero no usado. Pendiente: tiles kill (5) y conveyor (6).Transición de pantalla animada✅ HECHO — Scroll con easingcubicInOut(0.5s), ambas rooms visibles, enemigos activos, jugador puede moverse durante la transición.- Paleta Amstrad CPC — subir de la paleta actual (8-bit indexada limitada) a la paleta del Amstrad CPC (27 colores / más colores que la actual). Afecta a
PaletteManagery a todos los assets de color.
Estado del renombrado
Se han renombrado las referencias de JailDoctor's Dilemma → Projecte 2026 y jaildoctors_dilemma → projecte_2026 en código, configs, Makefile, CMake, LICENSE, Info.plist, scripts, locales, etc. Copyright actualizado a 2026. Se han mantenido como placeholders:
- La clave de locale
jaildoctor:endata/locale/*.yamly su uso ensource/game/scenes/ending2.cpp(es contenido de juego, pendiente de rediseño). - El publisher
jailgames(carpeta de config de sistema:~/.config/jailgames/projecte_2026). - Los archivos
release/windows/jdd.rcyjdd.res(solo bundlean el icono).
Pendiente
- PaletteColor enum en
utils.hppsigue con los 16 índices antiguos (ZX Spectrum). Los índices 0-4 coinciden con CPC, pero 5+ mapean a colores diferentes (ej.PaletteColor::BRIGHT_RED=5ahora renderiza MAGENTA en CPC).stringToColor()ySPECTRUM_REFERENCEenpalette_manager.cpptampoco están actualizados. Falta decidir cómo migrar cuando se vea el resultado visual. - Tile 6 (conveyor) no soportado aún en el nuevo motor de Player.
next()/previous()en PaletteManager desactivados de facto (solo 1 paleta). Posible futura reutilización para ciclar modos de ordenación.
Overview
Projecte 2026 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
./projecte_2026
Important: The build directory is build/ relative to the project root 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 xvfb-run directly (RECOMMENDED)
xvfb-run -a ./projecte_2026
Option 2: Custom display configuration
xvfb-run -a -s "-screen 0 1280x720x24" ./projecte_2026
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 & Formatting (CMake targets)
Los linters y el formateador están integrados como targets de CMake. Todos excluyen source/external/ y los headers SPIR-V generados (*_spv.h).
# Desde el directorio build/
cmake --build . --target tidy # clang-tidy sobre todo el código (sin fixes)
cmake --build . --target tidy-fix # clang-tidy aplicando fixes automáticos
cmake --build . --target cppcheck # cppcheck (warning/style/performance/portability, --std=c++20)
cmake --build . --target format # clang-format -i sobre todo el código
cmake --build . --target format-check # clang-format en modo dry-run (CI)
Los targets se definen en CMakeLists.txt (sección STATIC ANALYSIS TARGETS) y se generan únicamente si las herramientas correspondientes (clang-tidy, clang-format, cppcheck) están instaladas en el sistema.
Falsos positivos conocidos de clang-tidy:
defaults.hpp: puede reportar errores en definiciones de constantes.readability-magic-numbers/cppcoreguidelines-avoid-magic-numbers: aceptables para constantes de juego (tamaños de bloque, velocidades, etc).
Notas sobre cppcheck:
- Avisos
unusedStructMemberson habituales en estructuras de datos accedidas vía serialización/yaml o por código#ifdef _DEBUG; revisar antes de borrar nada. - El target usa la lista explícita de fuentes (excluyendo
source/external/y*_spv.h), no--project=compile_commands.json. Si añades flags nuevos, actualiza el target en CMakeLists.txt. - Por defecto cppcheck solo evalúa 12 combinaciones de
#ifdef. Para analizar todas, añadir--force(mucho más lento).
Para análisis puntual de un solo fichero, llamar la herramienta directamente:
clang-tidy -p build source/game/entities/player.cpp
cppcheck --enable=warning,style --std=c++20 -I source source/game/entities/player.cpp
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 -
Room file format — single source of truth: Cualquier cambio al formato
data/room/*.yaml(añadir/quitar/renombrar campos) se hace exclusivamente ensource/game/gameplay/room_format.cpp. Esa clase es la única autoridad: combina parser (loadYAML), serializer (saveYAML, debug-only) ycreateDefaultpara rooms nuevas. Cuando añadas un campo nuevo:- Añade el field a
Room::Dataensource/game/gameplay/room.hpp. - Actualiza
parseRoomConfig/buildContent/createDefaultenroom_format.cpp(los tres en el mismo módulo). - Nunca escribas yaml a pelo con
std::ofstreamdesde otro sitio (especialmente desdeMapEditor::createNewRoom, que ya delega enRoomFormat::createDefault+RoomFormat::saveYAML). El editor solo manipulaRoom::Data; el formato yaml es invisible para él. Saltarse esta regla causa bugs como el del crash de06.yaml(formato hardcoded encreateNewRoomque se desincroniza del parser real).
- Añade el field a
-
Nuevas entidades de room — soporte completo en editor y consola: Cuando añadas un tipo nuevo de entidad a las rooms (al estilo de
enemies,items,platforms,keys,doors), no basta con el parser/serializer y el manager runtime. Debes implementar también su contrapartida en el editor y en la consola, en paridad con los tipos existentes:- Renderizado dentro del editor: añadir la llamada al
room_->renderXxx()correspondiente enMapEditor::render(). Sin esto la entidad no se ve cuando el editor está activo (aunque sí se vea en juego), porque el editor renderiza entidades por separado del flujo normal del juego. - Selección visual en
MapEditor(click sobre la entidad →selection_con su tipo y índice). Extender el loop de hit test enMapEditor::handleMouseDowncon el nuevoEntityType. - Drag & drop para mover la entidad por el grid (con autosave al soltar). Extender
moveEntityVisualycommitEntityDrag. - Entry en la statusbar del editor cuando la entidad está seleccionada (mostrar id, animación, etc). Switch en
updateStatusBarInfo. - Comandos de consola:
cmd<Tipo>paraadd/delete/duplicatey routing encmdSetparasetXxxProperty. VercmdEnemy/cmdItem/cmdPlatform/cmdKey/cmdDoorenconsole_commands.cpp. Registrar endata/console/commands.yamltambién. - Helpers genéricos:
entityCount,entityRect,entityPosition,entityDataCount,entityLabeldeben cubrir el nuevo tipo (switch sobreEntityType). - Manager debug API: el manager de la entidad necesita
getCount()ygetXxx(int)bajo#ifdef _DEBUGpara que el editor pueda iterar. - Setter de posición: la entidad necesita
setPosition(float, float)bajo#ifdef _DEBUGpara el drag visual. EntityType enumenmap_editor.hppextendido con el nuevo tipo.- Si la entidad afecta a colisiones (como
Doorque escribeWALLen elCollisionMap), el manager debe exponermove<Tipo>(idx, x, y)yremove<Tipo>(idx)que encapsulen el bookkeeping. El editor no debe tocar nunca elCollisionMapdirectamente.
Cualquier entidad nueva debe nacer con soporte completo, no diferirlo "para después".
- Renderizado dentro del editor: añadir la llamada al
Last Updated: April 2026 Original Author: JailDesigner Repository: Gitea (internal)