# 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

1. **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.
2. ~~**Nueva física del jugador**~~ ✅ **HECHO** — Player reescrito con pipeline de 6 fases, colisiones tile-based, ~20 métodos eliminados.
3. ~~**Motor de colisiones por tiles**~~ ✅ **HECHO** — `TileCollider` con 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).
4. ~~**Transición de pantalla animada**~~ ✅ **HECHO** — Scroll con easing `cubicInOut` (0.5s), ambas rooms visibles, enemigos activos, jugador puede moverse durante la transición.
5. **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 `PaletteManager` y 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:` en `data/locale/*.yaml` y su uso en `source/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.rc` y `jdd.res` (solo bundlean el icono).

## Pendiente

- **PaletteColor enum** en `utils.hpp` sigue con los 16 índices antiguos (ZX Spectrum). Los índices 0-4 coinciden con CPC, pero 5+ mapean a colores diferentes (ej. `PaletteColor::BRIGHT_RED=5` ahora renderiza MAGENTA en CPC). `stringToColor()` y `SPECTRUM_REFERENCE` en `palette_manager.cpp` tampoco 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
```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
./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)
```bash
# Install Xvfb
sudo apt-get install xvfb
```

#### Running the Game in Headless Mode

**Option 1: Using xvfb-run directly (RECOMMENDED)**
```bash
xvfb-run -a ./projecte_2026
```

**Option 2: Custom display configuration**
```bash
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`).

```bash
# 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](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 `unusedStructMember` son 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](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:

```bash
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

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

5. **Room file format — single source of truth:** Cualquier cambio al formato `data/room/*.yaml` (añadir/quitar/renombrar campos) se hace **exclusivamente** en `source/game/gameplay/room_format.cpp`. Esa clase es la única autoridad: combina parser (`loadYAML`), serializer (`saveYAML`, debug-only) y `createDefault` para rooms nuevas. Cuando añadas un campo nuevo:
   - Añade el field a `Room::Data` en `source/game/gameplay/room.hpp`.
   - Actualiza `parseRoomConfig`/`buildContent`/`createDefault` en `room_format.cpp` (los tres en el mismo módulo).
   - **Nunca** escribas yaml a pelo con `std::ofstream` desde otro sitio (especialmente desde `MapEditor::createNewRoom`, que ya delega en `RoomFormat::createDefault` + `RoomFormat::saveYAML`). El editor solo manipula `Room::Data`; el formato yaml es invisible para él. Saltarse esta regla causa bugs como el del crash de `06.yaml` (formato hardcoded en `createNewRoom` que se desincroniza del parser real).

6. **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 en `MapEditor::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 en `MapEditor::handleMouseDown` con el nuevo `EntityType`.
   - **Drag & drop** para mover la entidad por el grid (con autosave al soltar). Extender `moveEntityVisual` y `commitEntityDrag`.
   - **Entry en la statusbar** del editor cuando la entidad está seleccionada (mostrar id, animación, etc). Switch en `updateStatusBarInfo`.
   - **Comandos de consola**: `cmd<Tipo>` para `add/delete/duplicate` y routing en `cmdSet` para `setXxxProperty`. Ver `cmdEnemy`/`cmdItem`/`cmdPlatform`/`cmdKey`/`cmdDoor` en `console_commands.cpp`. Registrar en `data/console/commands.yaml` también.
   - **Helpers genéricos**: `entityCount`, `entityRect`, `entityPosition`, `entityDataCount`, `entityLabel` deben cubrir el nuevo tipo (switch sobre `EntityType`).
   - **Manager debug API**: el manager de la entidad necesita `getCount()` y `getXxx(int)` bajo `#ifdef _DEBUG` para que el editor pueda iterar.
   - **Setter de posición**: la entidad necesita `setPosition(float, float)` bajo `#ifdef _DEBUG` para el drag visual.
   - **`EntityType enum`** en `map_editor.hpp` extendido con el nuevo tipo.
   - **Si la entidad afecta a colisiones** (como `Door` que escribe `WALL` en el `CollisionMap`), el manager debe exponer `move<Tipo>(idx, x, y)` y `remove<Tipo>(idx)` que encapsulen el bookkeeping. **El editor no debe tocar nunca el `CollisionMap` directamente.**

   Cualquier entidad nueva debe nacer con soporte completo, no diferirlo "para después".

---

**Last Updated:** April 2026
**Original Author:** JailDesigner
**Repository:** Gitea (internal)
