fase 1: jail i game a c++ idiomàtic (raii, info::ctx, cheats arreglats)
This commit is contained in:
55
CLAUDE.md
55
CLAUDE.md
@@ -24,26 +24,56 @@ The executable is output to the project root. The `data/` folder must be in the
|
||||
|
||||
## Architecture
|
||||
|
||||
### Golden Rule: Do Not Touch Gameplay
|
||||
### New Rules (Modernization Phase)
|
||||
|
||||
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.
|
||||
The old "Golden Rule: Do Not Touch Gameplay" has been **revoked**. The original C-style code (jail engine + gameplay modules) is now a **modernization target**, not a sacred zone. The parallel-overlay approach has reached its ceiling: fades and cinematics are still blocking loops, audio relies on an async `SDL_AddTimer`, and the emulator-style game thread blocking at `publishFrame` is incompatible with an emscripten port.
|
||||
|
||||
The five current objectives are:
|
||||
|
||||
1. **Idiomatic C++**: RAII, `std::vector`/`std::string`/`std::optional`, classes with real constructors/destructors. No more raw `malloc/free` in structs.
|
||||
2. **Zero blocking events**: no `while (...) { poll; }`, no `SDL_Delay` inside gameplay, no `cv.wait()` in `publishFrame`. Every subsystem must be able to advance in a single tick call.
|
||||
3. **Time-based**: animations, cinematics and fades measured in milliseconds, not frames. `JG_ShouldUpdate()` as gameplay gate is on its way out.
|
||||
4. **Overlay integrated**: overlay stops being a post-game layer painted by Director — it becomes part of the same render pass the game tick produces.
|
||||
5. **SDL3 callbacks**: main loop handed over to `SDL_AppInit` / `SDL_AppIterate` / `SDL_AppEvent` / `SDL_AppQuit`, single-threaded, compatible with emscripten.
|
||||
|
||||
**Iron rule: zero gameplay regressions.** Each phase of the modernization must leave the game playing identically — same feel, same timings, same collisions, same scoring. See [glittery-sprouting-pumpkin.md](../../.claude/plans/glittery-sprouting-pumpkin.md) for the phased plan.
|
||||
|
||||
The current emulator-thread architecture (Director + game thread + `publishFrame` mutex/cv) is **transitional**. It will be dismantled in Phase 5 and replaced by a single-threaded `SDL_AppIterate` loop in Phase 7.
|
||||
|
||||
### Modernization Targets
|
||||
|
||||
**Invariants to preserve** (touch these and you broke the game):
|
||||
- Gameplay feel, movement speed, enemy AI behavior
|
||||
- Collision detection, scoring, lives, level progression
|
||||
- Visible animation cadence (once translated to ms, must look identical)
|
||||
- Difficulty curves and cinematic timings
|
||||
- Cheat codes (`reviu`, `alone`, `obert`) — currently broken, should be restored
|
||||
- Original palettes, fades, music cues
|
||||
|
||||
**Free to change** (internal representation):
|
||||
- Data structures (structs → classes with RAII)
|
||||
- Ownership (raw pointers → `std::unique_ptr`/`std::vector`/`std::string`)
|
||||
- Timing representation (frame counters → ms accumulators)
|
||||
- Threading model (game thread → single-threaded state machine)
|
||||
- Global state (the old `info::` namespace is now an `inline` singleton `info::ctx` of type `GameContext`; access is `info::ctx.X` instead of `info::X`. Can be reset with `info::ctx.reset()`)
|
||||
- API shapes of jail subsystems (as long as callers are updated consistently)
|
||||
|
||||
### 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/jail/` | Legacy engine, modernization target | Free to modify with care — preserve external behavior |
|
||||
| `source/game/*.cpp/hpp` | Legacy gameplay, modernization target | Free to modify with care — preserve gameplay invariants |
|
||||
| `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/*.gif, *.ogg` | Original assets | **Do not modify** — assets remain untouchable |
|
||||
| `data/fonts/, data/ui/, data/shaders/` | New assets | Free to modify |
|
||||
|
||||
### Original "Jail" Engine (`source/core/jail/`)
|
||||
### Legacy "Jail" Engine (`source/core/jail/`) — modernization target
|
||||
|
||||
Flat C-style APIs (no classes), prefixed by subsystem. **Do not touch gameplay logic.**
|
||||
Flat C-style APIs (no classes), prefixed by subsystem. Being progressively converted to idiomatic C++ (see Phase 1 of the plan). External API names are kept stable during the transition to avoid churning call sites.
|
||||
|
||||
- **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()`
|
||||
@@ -109,7 +139,10 @@ Follows the pattern from `jaildoctors_dilemma`, persists to YAML:
|
||||
|
||||
All key bindings are configurable via `Options::keys_gui` and stored in `config.yaml` (section `controls:` with SDL scancode names). Game movement keys (`Options::keys_game.up/down/left/right`) can be remapped via the CONTROLS submenu — the `KeyRemap` module mirrors custom physical keys to virtual standard scancodes so hardcoded game code keeps working.
|
||||
|
||||
### Threading Model (Emulator Architecture)
|
||||
### Threading Model (Emulator Architecture — transitional)
|
||||
|
||||
> ⚠️ This architecture is **transitional**. It will be dismantled in Phase 5 of the modernization plan (the game thread + `publishFrame` mutex/cv disappear) and replaced by a single-threaded `SDL_AppIterate` loop in Phase 7. Document changes here when that happens.
|
||||
|
||||
|
||||
```
|
||||
Main thread (Director) Game thread (ModuleGame/Sequence::Go())
|
||||
@@ -179,8 +212,12 @@ JD8_Flip produces ABGR byte order: `0xFF000000 + R + (G<<8) + (B<<16)`. SDL text
|
||||
### Known Issues & Technical Debt
|
||||
|
||||
1. **gif.h cannot be included twice**: Functions are not `static` or `inline`, causing multiple definition errors. Text class uses `extern` forward declarations as workaround
|
||||
2. **Cheats are broken (`reviu`, `alone`, `obert`)**: `JI_CheatActivated` in [jinput.cpp:46](source/core/jail/jinput.cpp#L46) compares `SDL_Scancode` values (e.g. `SDL_SCANCODE_R`=21) against ASCII chars (`'r'`=114). They never match. Regression from SDL3 migration. Fix requires either scancode→char conversion in `JI_moveCheats` or storing chars directly.
|
||||
2. **Cheats are broken (`reviu`, `alone`, `obert`)**: `JI_CheatActivated` in [jinput.cpp:46](source/core/jail/jinput.cpp#L46) compares `SDL_Scancode` values (e.g. `SDL_SCANCODE_R`=21) against ASCII chars (`'r'`=114). They never match. Regression from SDL3 migration. **Now fixable** — scheduled for Phase 1 of modernization since jail is no longer off-limits.
|
||||
3. **No sound effects in game**: Game code never calls `JA_PlaySound*`/`JA_LoadSound` — only music via `JA_PlayMusic`/`JA_FadeOutMusic`. The SONS and VOL SONS menu items control volume of an empty channel pool. Infrastructure ready for future SFX.
|
||||
4. **Raw `malloc`/`free` in gameplay structs**: `Sprite`/`Entitat` use `malloc` for `Frame[]` and `Animacio[]`; `jfile.cpp` uses a global `scratch[255]` buffer (UB under concurrent calls); `jail_audio.cpp` mixes `new`/`malloc`/`SDL_malloc`. Scheduled for Phase 1 (RAII pass).
|
||||
5. **Blocking loops in cinematics and fades**: `ModuleSequence::doIntro()` has 15+ `while(!JG_ShouldUpdate())` spin-waits; `JD8_FadeOut`/`JD8_FadeToPal` run 32 internal iterations calling `JD8_Flip`. Incompatible with `SDL_AppIterate`. Scheduled for Phase 2 (state-machine refactor).
|
||||
6. **`SDL_AddTimer` in audio**: `JA_Init` registers a 30ms timer callback for mixing/fade update. Incompatible with emscripten single-threaded model. Scheduled for Phase 3 (manual `JA_Update(delta_ms)` driven from Director).
|
||||
7. **Game thread + `publishFrame` mutex/cv**: the emulator-style architecture is only tenable while native. Incompatible with `SDL_AppIterate`. Scheduled for Phase 5 (single-threaded state machine).
|
||||
|
||||
### Pending / Ideas for Later
|
||||
|
||||
|
||||
Reference in New Issue
Block a user