aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
blob: 5e54edde94cd2125fc580dce72805526c0293bee (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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.