Files
major_tom/AGENTS.md
2026-03-01 09:51:17 +00:00

6.6 KiB

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

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:
    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 (<stdlib.h>, <string.h>, <math.h>)
  4. SDL headers (<SDL2/SDL.h>)
  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

#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 <stdbool.h>
  • 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