Files
ghostland-game/AGENTS.md

45 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Repository Guidelines
## Project Structure & Module Organization
Core gameplay lives in root-level C++ translation units: `ghostland.cpp` orchestrates the loop, `player.cpp`, `ghost.cpp`, `collisions.cpp`, `shader.cpp`, `text.cpp`, and `config.cpp` own their respective systems. Runtime assets stay alongside code: GLSL shaders (`*shader.glsl`), UI textures (PNG in the repo root), and bitmap fonts under `fonts/`. Level data is serialized in `maze.txt`, typically regenerated via `mazeparser.py` from `maze.png`. Adjustable settings belong in `ghostland.json`; keep defaults mirrored in code so omitted keys never cause null or invalid lookups.
## Build, Test, and Development Commands
- `make` builds all objects with C++17 + O3 and links the `ghostland` binary with GLFW, GLAD, stb_image, and Freetype.
- `./ghostland` runs the game from the repo root; pass config overrides via `ghostland.json`.
- `make clean` removes all `.o` files and the executable; run before release builds to ensure a full relink.
- `python3 mazeparser.py` regenerates `maze.txt` from the source maze image; adjust the image path in the script if you keep art assets elsewhere.
## Coding Style & Naming Conventions
Use 4-space indentation, braces on the same line as control statements, and guard new modules with include guards. Keep standard-library/GLM includes before project headers as in `ghostland.cpp`. Classes use PascalCase (`Player`), local variables use `snake_case`, compile-time constants stay uppercase. Prefer STL containers over manual arrays unless performance-critical, and keep shader uniform names centralized near the top of `ghostland.cpp`.
## Configuration Guidelines
`ghostland.json` is intentionally a flat runtime tuning file. The parser in `config.cpp` is a minimal line-oriented JSON subset reader: it expects one `"key": value` pair per line, ignores braces and comment-like lines, strips one trailing comma, and stores raw strings in a process-wide map. Do not rely on nested objects, arrays, inline comments after values, duplicate-key semantics beyond "last loaded wins", or full JSON validation unless you first replace the parser deliberately.
Load config values once during startup after `Config::loadFromFile("ghostland.json")`, then copy them into typed globals or small config structs. Always use `Config::getFloat`, `Config::getInt`, or `Config::getString` with a meaningful in-code default; missing or malformed values should keep the game playable. Keep booleans as `0` / `1` integer keys for consistency with existing options.
When adding or renaming a setting, update all three surfaces together: the struct or default value in code, the sample value in `ghostland.json`, and the README configuration table/example. Prefer descriptive key names grouped by system prefix (`minimap_*`, `trail_*`, `ghost_*`). If compatibility matters, read the legacy key first and let the new key override it, as with `trail_y_offset` falling back to `trail_y_position`.
Validate numeric config values at the use site when bad ranges can break rendering or gameplay. Clamp or guard zero/negative values where appropriate (`std::max` for lifetimes, spacing, sizes, and shader divisors), and keep color channels documented as 0-1 floats. Avoid adding config knobs for internal implementation details unless they are useful for tuning gameplay, visuals, or reproduction.
## Testing Guidelines
There is no automated framework yet, so rely on focused manual passes. Before opening a PR, build fresh (`make clean && make`), run `./ghostland`, and verify spawn position, collision bounds, ghost ordering, and text rendering. When touching geometry, regenerate `maze.txt` and inspect extremes logged at the bottom of the file. Mention any GPU or driver quirks encountered so others can reproduce.
## Commit & Pull Request Guidelines
Existing history favors short, descriptive subjects ("Player's position is reset when close to ghosts"). Follow that tone, keep subjects under 72 characters, and add body lines for rationale or performance numbers. Each PR should include: summary of gameplay-visible changes, configs or assets touched, reproduction steps or screenshots when visuals change, and confirmation that `make` succeeds on a clean tree. Link related issues and call out any follow-up tasks explicitly.
## Minimap Implementation Plan
- Add minimap config keys in `ghostland.json` (enable flag, width/height, margin, colors). After loading `maze.txt`, cache 2D wall segments plus world bounds for quick reuse.
- Create a lightweight 2D orthographic shader/VAO for overlay drawing; use GL_LINES for walls, triangle quads for the trail stroke, triangle-fan circles for ghosts, and a small triangle for the player arrow. Disable depth test while rendering the overlay near the end of the frame.
- Each frame convert wall endpoints, trail points, and ghost positions relative to the players X/Z so the minimap scrolls with the player centered. Clip or skip segments outside the visible minimap extents.
- Draw order: white background quad, wall lines, trail stroke, ghost dots (border then fill), red arrow rotated by player yaw. Re-enable depth test and continue with HUD text.
## Trail Implementation Overview
The old breadcrumb arrows have been replaced by a continuous time-limited trail rendered as a flat ribbon mesh in world space. `ghostland.cpp` owns the trail data structures: `TrailPoint` stores sampled X/Z player positions, timestamps, and a `starts_segment` flag that prevents respawns from drawing teleport lines. `TrailVertex` stores generated ribbon vertices plus the source timestamp. `update_trail()` prunes samples older than `trail_lifetime_minutes`, adds a new sample only after the player moves at least `trail_sample_spacing`, and removes nearly straight intermediate samples using `trail_simplify_epsilon`.
`rebuild_trail_mesh()` turns adjacent trail samples into quad segments made from two triangles, avoiding driver-dependent OpenGL line width behavior. The VBO is updated only when samples are added, pruned, simplified, or a reset splits the trail. `trailshader.glsl` passes each vertex timestamp to `trailfragshader.glsl`, which blends from `trail_recent_*` color values toward `trail_old_*` based on age. Keep the shader GLSL version aligned with the requested OpenGL 3.3 context.
Trail tuning belongs in `ghostland.json`: `trail_enabled`, `trail_lifetime_minutes`, `trail_sample_spacing`, `trail_width`, `trail_y_position`, `trail_simplify_epsilon`, `trail_reset_marker_size`, and RGB triplets for recent/old colors. The legacy `trail_y_offset` key is still accepted as a fallback. The minimap consumes the same trail point deque and renders a screen-space stroke whose half-width is controlled by `minimap_breadcrumb_radius`, so changes to sampling behavior affect both the world ribbon and overlay path.
## Danger Tint Notes
The red danger tint is driven by the nearest ghost in X/Z space rather than 3D distance, so ghost bobbing does not change the warning. Keep the tint calculation independent of ghost render ordering: the partial sort is only for drawing. The tint also keeps persistent intensity with a 30-unit enter / 35-unit exit hysteresis band and frame-rate-independent smoothing; this prevents threshold jitter from flashing the camera between normal and red.