# AGENTS.md — JNR Engine ## Important Files Before starting any task, always read: - **DESIGN.md** — Game design document with vision, mechanics, and level plans - **TODO.md** — Current roadmap and next tasks to implement ## Project Overview 2D side-scrolling platformer (run-and-gun) written in C11 using SDL2, SDL2_image, and SDL2_mixer. Binary: `jnr`. Targets Linux (native), WebAssembly (Emscripten), Windows (MinGW cross-compile). ## Build Commands ```bash make # Release build (Linux) → ./jnr make run # Build + run make debug # Debug build: -g -O0 -DDEBUG make DEBUG=1 # Alternative debug flag make web # WASM build → dist-web/ make web-serve # WASM build + HTTP server on :8080 make windows # Cross-compile → dist-win64/ make clean # Remove all build artifacts ``` Compiler flags: `-Wall -Wextra -std=c11 -I include -I src` There are no test or lint targets. Verify changes by building with `make` and confirming zero warnings. ### Cross-platform prerequisites - **WASM builds** require the Emscripten SDK. The `emsdk/` directory in the project root is gitignored; source the environment before building: ```bash source ~/emsdk/emsdk_env.sh # or wherever emsdk is installed make web ``` - **Windows cross-compilation** requires MinGW (`x86_64-w64-mingw32-gcc`) and vendored SDL2 development libraries in `deps/win64/` (also gitignored). ## Project Structure ``` include/ Global headers (config.h) src/ engine/ Engine subsystems (.c/.h pairs): physics, tilemap, entity, camera, etc. game/ Game logic (.c/.h pairs): player, enemy, level, levelgen, editor, etc. util/ Header-only utilities: vec2.h, darray.h main.c Entry point, game mode switching, level transitions assets/ levels/ .lvl level files (plain text) sounds/ .wav/.ogg audio sprites/ PNG spritesheets tiles/ Tileset PNGs web/ Emscripten HTML shell ``` Engine code lives in `src/engine/`, game code in `src/game/`. Each subsystem is a `.c`/`.h` pair. Header-only utilities use `static inline` functions. ## Code Style ### Formatting - **4 spaces** for indentation (no tabs in source; Makefile uses tabs) - **K&R brace style**: opening brace on same line - Pointer declaration: `Type *name` (space before `*`) - `const` for input-only pointer params: `const Tilemap *map` - No-parameter functions use `void`: `void physics_init(void)` - Unused parameters: `(void)param;` ### Naming Conventions | Kind | Convention | Example | |------|-----------|---------| | Functions | `snake_case`, module-prefixed | `player_update()`, `tilemap_load()` | | Types/Structs | `PascalCase` | `Entity`, `PlayerData`, `Tilemap` | | Enums | `PascalCase` type, `UPPER_SNAKE` values | `EntityType` / `ENT_PLAYER` | | Macros/Constants | `UPPER_SNAKE_CASE` | `MAX_ENTITIES`, `TILE_SIZE` | | Static (file-scope) vars | `s_` prefix | `s_gravity`, `s_renderer` | | Global vars | `g_` prefix | `g_engine`, `g_spritesheet` | | Local vars | Short `snake_case` | `dt`, `pos`, `em`, `tx` | | Function pointer types | `PascalCase` + `Fn` | `EntityUpdateFn` | ### Includes Order within each file: 1. Own module header (`"game/player.h"`) 2. Other project headers (`"engine/physics.h"`, `"game/sprites.h"`) 3. Standard library (``, ``, ``) 4. SDL headers (``) 5. Platform-conditional (`#ifdef __EMSCRIPTEN__`) Paths are forward-slash, relative to `src/` or `include/`: `"engine/core.h"`, `"config.h"`. ### Comments - **Section headers**: `/* ═══...═══ */` box-drawing block - **Subsections**: `/* ── Name ──────── */` light-line style - **Inline/doc comments**: `/* ... */` (C89-style, not `//`) - **Struct field comments**: trailing, aligned with whitespace padding ### Header Guards ```c #ifndef JNR_MODULE_H #define JNR_MODULE_H ... #endif /* JNR_MODULE_H */ ``` ### Types - `float` for positions, velocities, timers, physics (not `double`) - `Vec2` (float x, y) for all 2D quantities - `bool` from `` - `uint16_t` for tile IDs; `uint32_t` for bitfield flags and seeds - `SDL_Color` for colors; `SDL_Rect` for integer rectangles ### Error Handling - Return `bool` for success/failure - Return `NULL` from creation functions on failure - Errors to `stderr` via `fprintf(stderr, "...")` - Info to `stdout` via `printf(...)` - Warnings use `"Warning: ..."` prefix - Early return on failure; no goto-based cleanup ### Memory Management - `calloc(1, sizeof(T))` for entity data (zero-initialized) - `free(ptr); ptr = NULL;` in destroy callbacks - `memset(ptr, 0, sizeof(*ptr))` for struct re-initialization - Fixed-size arrays for most collections (entity pool, tile defs) - Dynamic allocation only for tile layers (`uint16_t *`) - `snprintf` / `strncpy` with explicit size limits for strings - `ASSET_PATH_MAX` (256) for path buffers ## Architecture Patterns ### Entity System - Fixed pool of `MAX_ENTITIES` (512) in `EntityManager` - Dispatch tables: `update_fn[type]`, `render_fn[type]`, `destroy_fn[type]` - `void *data` for type-specific data (cast in callbacks) - Each entity type: `_register()` sets callbacks, `_spawn()` creates instances - Entity registry maps string names to spawn functions for level loading ### Module Pattern - Public API: declared in header, module-prefixed (`camera_init`, `camera_follow`) - Private helpers: `static` in `.c` only - File-scope state: `static` variables with `s_` prefix - Forward declarations to break circular includes ### Level System - `.lvl` files: plain-text directives + tile grid data - `level_load()` for handcrafted levels from files - `level_load_generated()` for procedural levels from `Tilemap` structs - Exit zones trigger transitions; target strings: file path, `"generate"`, `"generate:station"`, or empty (victory) - Procedural generator: segment-based, theme-driven, difficulty-scaled ### Rendering - Sprite batching: submit to queue via `renderer_submit()`, flush layer-by-layer - Draw layers: BG → entities → FG → particles → HUD - Camera transforms world coords to screen coords ## Commit Messages - Imperative mood, concise - No co-authored-by or AI attribution - Example: "Add in-game level editor with auto-discovered tile/entity palettes" ## Key Constants (config.h) | Constant | Value | Notes | |----------|-------|-------| | `SCREEN_WIDTH` | 640 | Logical resolution | | `SCREEN_HEIGHT` | 360 | | | `TILE_SIZE` | 16 | Pixels per tile | | `TICK_RATE` | 60 | Fixed timestep Hz | | `DEFAULT_GRAVITY` | 980.0f | px/s² | | `MAX_ENTITIES` | 512 | Entity pool size | | `MAX_ENTITY_SPAWNS` | 128 | Per-level spawn slots | | `MAX_EXIT_ZONES` | 8 | Per-level exit zones |