diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5e54edd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,186 @@ +# Forts Clone — developer guide + +An open-source, from-scratch clone of **Forts** (2D physics-based artillery RTS): +build a fort out of struts, place weapons, and blast the enemy reactor to bits. +C++20, custom engine, data-driven (Lua), no game engine. + +This file is the orientation for anyone (human or agent) picking up the project. +Read `research/` and `requirements/` for the deep dives; this is the map. + +--- + +## 1. Build & run + +**Everything builds inside a Podman container** (Fedora 41 with all deps pre-built +in `/usr/local`, defined by `Containerfile`). The **binary runs on the host** +(needs the host's SDL3 + a display). + +```bash +# one-time (and after editing Containerfile): build the dev image +podman build -t forts-clone-dev . + +# configure + compile (from repo root) +podman run --rm -v "$PWD":/src:Z --userns=keep-id localhost/forts-clone-dev:latest \ + bash -c "cd /src && meson setup builddir && meson compile -C builddir" + +# run ON THE HOST, from the repo root (data/ is resolved relative to CWD) +./builddir/forts-clone +``` + +- `scripts/build.sh` wraps the container build/compile. +- Lua is **statically linked** (Containerfile builds `liblua.a`) so the + container-built binary runs on any host regardless of its lua soname. +- If you add a dependency: add it to `Containerfile` **at the end** (so cached + layers don't rebuild) unless it must come earlier. + +**Verify without a display:** you can't run the GUI in the container, so validate +by (a) compiling clean, and (b) running Lua data files through a standalone `lua` +to check they parse. Do both after changes. + +--- + +## 2. Architecture + +Two layers under `src/`: + +- **`src/engine/`** — reusable: windowing/input (SDL3), 2D renderer (SDL_Renderer + + textures + ImGui), camera, Lua host (sol2), Box2D wrapper (idle, kept for + future projectiles). +- **`src/game/`** — the game: the building graph + solver, and the data loaders. + +### Key files +| File | Role | +|---|---| +| `game/build_graph.{hpp,cpp}` | **The heart.** Node/strut graph + stiff mass-spring solver, stress/fire/breaking, splash & beam damage, materials table. | +| `game/data.{hpp,cpp}` | Lua loaders: materials, weapons, devices, prebuilt structures, map. Layered mod loading. | +| `engine/script.{hpp,cpp}` | sol2 wrapper (`ScriptEngine`); runs Lua files with errors caught. | +| `engine/renderer.{hpp,cpp}` | Draw boxes/sprites/arcs/HUD, texture cache (+ `forts:` BYO paths), ImGui lifecycle. | +| `engine/app.cpp` | The whole game loop: input, build/fire/place, fixed-step sim, rendering, dev UI. Big; most gameplay glue lives here. | +| `engine/physics.*` | Box2D wrapper — currently **idle**, reserved for M4+ projectiles/terrain. | + +> `src/game/build_system.hpp` and `src/engine/verlet.*` are legacy/removed — the +> solver is now the mass-spring in `build_graph`. If you see references, they're stale. + +--- + +## 3. The physics model (this is what makes it Forts) + +Canon Forts is a **stiff damped mass-spring system**, and so is ours (see +`research/forts-data-reference.org` for the extracted numbers). + +- **Node** = point mass with velocity; foundation nodes are pinned to the ground. +- **Strut** = a damped Hookean spring (`F = k·Δx + c·v`), integrated semi-implicit + with `OVERSAMPLES` (14) substeps for stiff-spring stability, inside a fixed + 60 Hz timestep. +- **Rigidity is geometric** — from triangulation, not a per-strut property. A lone + strut is a free-hinging pin joint. You can only *start* a build from an existing + node or the ground (no floating structure). +- **Breaking:** (a) axial deformation past per-material `max_compression` / + `max_expansion` (wood ±10%), and (b) the **30° angle-stress** rule on + un-triangulated struts. Both from the shipped game data. +- **Build grace:** a fresh strut is held rigid briefly (`SETTLE_TIME`, Forts' + "TempBracing") so you can triangulate it before it hinges. +- **Fire:** per-strut HP-over-time that **spreads along the graph** to flammable + neighbours; bg-brace burns ~3× faster (the counter to the "hide supports as + bg-brace" trick). +- **Ground destroys** any debris that falls below the surface (our choice; Forts + bounces it). Collapse is emergent: cut supports → chunk falls → ground kills it. +- **Damage model:** splash = pure radius falloff (ignores structure, like Forts + AoE); cannon shells **pass through bg-brace/rope** and detonate on wood; the + laser beam passes through bg-brace/rope, damages+ignites everything it crosses, + and **stops at wood**. + +**Tunables** live at the top of `build_graph.hpp` (`OVERSAMPLES`, `SETTLE_TIME`, +`gravity`, `air_drag`, strengths) and in the Lua data. Most are exposed **live in +the ImGui Dev panel** — use it; the constants were tuned by eye, not proven. + +### Gotchas +- Removing a node/edge **shifts indices**. Never cache node/edge indices across a + frame where destruction can happen — re-query (see the `hover_node` + re-validation in `app.cpp`). Use `break_edge`/`break_node`/`apply_splash` which + keep everything consistent. +- **Determinism:** the solver is order-deterministic but uses `sin/cos/sqrt`, so + it is **not** cross-platform deterministic. Revisit before any lockstep netcode. +- The **data path is relative to CWD** — run from the repo root. + +--- + +## 4. Data-driven + modding (Lua / sol2) + +Game content is **Lua data**, loaded at startup. This is the seam mods hook into. + +Base files in `data/`: `materials.lua`, `weapons.lua`, `devices.lua`, +`structures.lua`, `map.lua`. Each defines a global table (`Materials`, `Weapons`, +`DeviceDefs`, `Structures`, `Deposits`/`MapDevices`) plus `Find*`/`IndexOf*` helpers. + +**Layered loader** (`data.cpp::run_layered`): base file runs first, then every +`data/mods/<name>/<file>.lua` runs on top in **(priority, name)** order, mutating +or extending the global tables — exactly like Forts. A mod's `mod.lua` sets +`Priority`. Example: `data/mods/enemy-fort/` adds the target fort + enemy reactor. + +To add content, prefer **editing Lua data** over hard-coding C++. When you must +add a field, add it to the `*Def` struct, read it in `data.cpp` (`get_or` with a +default), and document it in the Lua file. + +--- + +## 5. Assets policy — IMPORTANT + +**Never commit Forts' proprietary art/audio/Lua to this repo.** It's EarthWork +Games' copyrighted work. + +- The repo ships **CC0 placeholder art** only (Kenney — see + `data/textures/CREDITS.md`) plus public-domain code (`third_party/stb`, + MIT `third_party/imgui`). +- To debug with the *real* game art, we use **bring-your-own game files** (like a + Doom source port + WAD): a texture path `forts:REL` resolves at runtime to + `$FORTS_DATA/REL` (auto-detected Steam path if unset). Missing game → graceful + fallback to CC0 / colored boxes. Nothing copyrighted ever enters the repo. +- Keep personal/BYO override mods under `data/mods/local-*/` (git-ignored). + +--- + +## 6. Status (milestones) + +See `requirements/milestones.org` for the live checklist. Roughly: + +- **M0–M3 done:** scaffolding, rendering/physics, building system, structural + destruction (stress/collapse/fire). +- **M4 in progress:** cannon (ballistic) + laser (beam/fire) with the Forts + select-a-weapon-and-aim-in-its-arc UX. Missing: mortar, flak/point-defence, + machinegun/sniper (hitscan), guided missiles, EMP, per-shot recoil. +- **M5 in progress:** reactors + win/loss + restart (R), real resource economy + (mines on deposits, turbines by height, storage caps, firing costs energy), + device framework + placement. +- **Not started / later:** repair & recycle (M7), armour/door/shield materials, + tech tree + upgrades, commanders, real map/terrain, AI, netcode. + +`research/gameplay-gaps.org` is the **sourced** list of what's missing and why — +consult it before deciding what to build; don't guess from vibes. + +--- + +## 7. Working conventions + +- **Branch for work.** `main` is the baseline. Do features and fixes on their own + branches: `feat/<thing>`, `fix/<thing>` (e.g. `feat/mortar`, `fix/beam-passthru`). + Don't commit feature work straight to `main`; open a branch, then merge. +- **Commit/push only when asked.** End commit messages with the + `Co-Authored-By: Claude ...` trailer. +- **Build before you claim done** (compile in the container) and validate any Lua + you touched with a standalone `lua` run. Be honest about what you couldn't + verify (the GUI can't run headless here). +- **Don't commit game assets** (§5), `builddir/`, or `imgui.ini`. +- Match surrounding code style; keep the engine/game split; prefer data (Lua) + over hard-coded constants. + +--- + +## 8. Reference docs +- `requirements/v0.1-core.org` — the v0.1 design spec. +- `requirements/milestones.org` — milestone checklist + architecture notes. +- `research/forts-gameplay.org` — gameplay research. +- `research/forts-data-reference.org` — **exact numbers** extracted from the game + data (physics constants, material/weapon/fire values, modding API). +- `research/gameplay-gaps.org` — sourced list of missing systems, prioritized. +- `research/tech-stack.org` — locked tech choices and rationale. |
