# Project Context

Project knowledge. Agent-facing; read this before any work. Not the doc for humans.

### Overview

A Final Fantasy Tactics-like tactical RPG (grid-based, turn-based combat with height, movement, and class/job systems) built in Godot 4.x with GDScript. **Codename: `operation_tactics`** — the real name is TBD. Nothing about the codename should leak into user-facing strings.

### Goals

- Faithful FFT-style tactical combat: isometric(ish) grid, move/act/end-turn flow, terrain height, facing, and line-of-sight.
- Job/class system with learning abilities from action (JP), not just leveling.
- Branching/unique characters, story-driven campaign.
- One cohesive game, not a tech demo — a single unified codebase.

### Current State

- Engine files exist (project.godot, directory skeleton). Map data core (registry, schema, binary codec), the 3D heightfield renderer (`MapView`/`MapTheme`), the seeded cosmetic layer (`MapDetails`), a demo map asset, and the custom test pipeline are in place. `main.tscn` boots the demo map with an FFT-style isometric camera (WASD pan / QE rotate / wheel zoom) and an **astronomical day-night cycle** (`src/render/day_night.gd`): daylight length computed from latitude + month (Virginia in August ≈ 13.5h), a sun that only lights above the horizon, a phase-driven moon with cloud-dimming, and clear-sky starlight. Tune `latitude_deg`/`month`/`day`/`cloud_cover`/`moon_phase`/`cycle_seconds` on the Sun node. **F2** toggles a free-fly spectator camera (`src/camera/free_cam.gd` — right-mouse look, WASD/QE move, wheel = speed, F3 recenters); it never touches the FFT camera's state, so toggling back is seamless.
- **Rules layer + pathfinding pipeline built** — facts-vs-rules split is done (`TileType` = facts; `MaterialRules`/`MovementRules`/`UnitProfile` = judgments) with a budgeted Dijkstra pathfinder (`Pathfinder`/`PathEvaluator`) providing FFT movement-bubble reachability, lowest-cost paths, hazard reports, and fly/float/swim/status support. See "Rules &amp; Pathfinding pipeline".
- **Combat designed; multiplayer foundation built** — `COMBAT.md` is the authoritative battle spec (deterministic CT turn order, streamlined stats, the eight elements, living-map mutations as commits). Foundation primitives exist: `Elements`, `DeterministicRng`, `RulesetVersion`, `Commit`/`CommitLog` (with the commit vocabulary), and streamlined combat stats + affinities on `UnitProfile`. See "Combat &amp; multiplayer foundation".
- **Procedural arena** — `voxel_arena.vmap` is the seeded showcase/test map: rolling noise terrain + stamped features covering all 10 materials, all 5 shapes, rotations 0-3, hazards, real void, multi-level climbing, and a pathable plaza. Generated by `src/map/arena_generator.gd` (`ArenaGenerator`), written by `tools/build_voxel_arena.tscn`, validated by `tests/map/voxel_arena_test.gd` (determinism + asset-in-sync + feature coverage). Now the default map in `main.gd`. Test suite is 117 green.
- Repository created on Forgejo and pushed. Repo uses the **SHA-256 object format** — never recreate it as sha1, and never rely on push-to-create for this repo (Forgejo's push-to-create ignores object format and would make a sha1 repo).

### Engineering Standards

Applies to every agent and human working here. Goal: a codebase humans can maintain long-term, not one that merely "works."

#### Code quality

- **Human-first.** Idiomatic GDScript, clear names, logical file layout. Readability first, efficiency second — but never accept needless waste (hot paths: avoid per-frame allocations).
- **Concise.** Smallest correct change. No speculative generality, no backward-compat shims without a concrete need.
- **Reuse over copy.** Shared frameworks (tile registry, map codec; later combat math, pathfinding) live once and are used everywhere. Rule of three: third copy → extract.
- **Modular &amp; layered.** Scripts stay focused (one responsibility); layers point one way — data/logic must not import rendering; cross-cutting concerns use signals. No spaghetti, no circular references.
- **Debuggable.** Keep logic in deterministic functions (pure input → output) where practical; avoid hidden global mutation.

#### Comment policy

- Comment the WHY and the intent: design decisions, non-obvious data formats, invariants, cross-module contracts. A reviewer should understand your intent.
- Never narrate WHAT the code does; no AI-style filler (e.g. `# get the player position`). No emoji. Code should look hand-written.

#### Testing (mandatory, Factorio-style)

- Before every commit: `./run_tests.sh` runs the full suite headless and must pass; a pre-commit hook enforces it. Never commit red.
- Suite covers: codec round-trip (byte-exact), registry append-only guard, and every subsystem's core behavior as it lands.
- Benchmark mode (`--bench`, planned) for perf-sensitive paths (combat sim, map load); checked for regressions, not just pass/fail.
- Test layout: `res://tests/**/*_test.gd`, custom runner (see `res://tests/run_tests.gd`). Tests extend the `TestCase` base and use its `eq/is_true/...` helpers.

#### Growing-project guardrails

- **Format/version drift** → schema version in every binary format; loaders reject mismatches loudly, never silently.
- **Duplication creep** → rule of three + review.
- **Scope creep** → anything new must fit the modular layout or the plan changes (and this doc gets updated).
- **Dead code** → no "just in case" branches; keep the tree lean.
- **Conventions drift** → this doc is the arbiter; update it when rules change.
- **Single-file bloat** → split when a script exceeds ~300 lines or has more than one job.

### Map Data Architecture

**`MAP_RULES.md` is the authoritative world-model spec** — read it before touching anything map-related. It defines the voxel model, tile types, shapes, terrain/blocks, movement thresholds, fluids, dynamic edits, generation, validation, and the agent rules. This section is the quick-reference; MAP\_RULES.md wins on any conflict.

**Direction: voxel pivot.** **`VoxelMap`** (`src/map/voxel_map.gd`): a chunked 16³ grid of 1×1×1 voxels (open-world ready; columns are subsumed as vertical runs of solid voxels). Each voxel is `{type, shape, rot}` (2 bytes packed; `TileType.Shape` = FULL/HALF/SLAB/SLOPE/WEDGE…, per-voxel shape + rotation for the editor). Serialized by **`VoxelCodec`** (`voxel_codec.gd`, schema 3, `.vmap` files). **`VoxelMesh`** (`src/render/voxel_mesh.gd`) builds the combined shape mesh with face culling (interior faces shared with a full solid neighbor are dropped); **`VoxelView`** renders it. `main.gd` defaults to the voxel demo (`assets/maps/voxel_demo.vmap`, built by `tools/build_voxel_demo.tscn`) and loads `.vmap` as voxels vs `.otmap` as the legacy column model (still present until the column code is retired). Cosmetic layer, hover/select and picking are column-only for now.

Global design (see `res://src/map/` for implementation). Maps are **2D grids of columns**: each cell owns its entire z column, so interior-under-roof and bridges are expressible as stacked walkable surfaces.

- **`TileType`** (`tile_type.gd`) — one shared entry per tile kind: **material facts only** (`id`, `block_height`, `shape`, `solid`, `fluid`). Behavior (walkability, cost, hazards, flags) lives in the rules layer (`MaterialRules`), never here. **Logic only** — visuals live in a separate theme table (`type_id → scene/texture`).
- **`TileTypeRegistry`** (`tile_type_registry.gd`) — the **global catalog**. **Append-only**: never reorder/delete entries, maps reference types by index, so adding types is always safe. `version` guards against breaking changes; loaders reject `catalog_version > registry.version`.
- **`MapData`** (`map_data.gd`) — size, catalog\_version, spawn points, and a column per cell: `{ elevation, ground (type index), layers: [{z, type, rot}] }`.
- **`MapCodec`** (`map_codec.gd`) — `StreamPeerBuffer` (little-endian) ↔ `PackedByteArray`. Binary layout is the contract (see file header): magic `OTMP` + schema version + catalog version + dims + spawns + per-cell: flags u8, elevation u16, ground u16, optional layer list. Map files use the **`.otmap`** extension.
- **`MapPicker`** (`src/map/map_picker.gd`) — resolves a cursor ray to the **specific block** hit (cell + surface height), not just the column: DDA over the grid + ray-vs-AABB on every block. View-aware (RENDER fills/ thin layers vs RAW data heights). `main.gd` feeds `hover`/`selected` cell+height into the top-grid shader, so a click on a roof highlights the roof, a click on a cliff face highlights that block.
- **`MapTheme`** (`src/render/map_theme.gd`) — visual theme, `type_id → color` (later scene/texture). Kept out of `TileType` so map data stays renderer-agnostic; swapping the theme restyles the whole game.
- **`MapView`** (`src/render/map_view.gd`) — the single data→rendering bridge: one combined `ArrayMesh`. Two view modes (`MapView.ViewMode`, toggled with **F1** in `main.gd`): **RENDER** = the game look (terrain fills to the map base — the **bedrock rule**, no terrain voids; layers drawn thin; cosmetic layer on; no wireframe) and **RAW** = the map data literally (ground blocks at `[elevation, elevation + block_height]` with voids intact, layers at full data height, flat colors + wireframe, no cosmetics) — the geometry rules/pathfinding are written against the RAW view. Renderer direction is **3D heightfield**. The RENDER top grid is a **fragment shader** (`src/render/top_grid.gdshader`) on a duplicate of the terrain mesh: + crosses at lattice corners + dashed edges, uniform and anti-aliased, tinted per `hover_cell`/`selected_cell` uniforms. `main.gd` picks the cell under the cursor (hover on mouse move, blue select on left-click) and feeds the uniforms.
- **`MapDetails`** (`src/render/map_details.gd`) — the **cosmetic layer** (two-layer model): a pure deterministic function of `(map, seed)` producing per-cell tint variance, C0-continuous surface jitter (no seams), and scattered props (rocks/bushes/tufts via `MultiMesh`). Purely visual — never feeds back into map data or rules.
- **Two-layer model + seed contract** — `MapData.seed` is part of the map (codec field, schema 2). Ground truth is the column blocks (drives rules/pathfinding later); the cosmetic layer is derived from `(map, seed)` and regenerated freely — same seed, same result, everywhere. Determinism rules: integer-hash noise keyed by `(seed, x, y, z)`, fixed iteration order, no engine RNG. The seed can later drive other derived content (spawns, variants).
- **`DemoRegistry`** (`src/map/demo_registry.gd`) — canonical demo catalog; stand-in for a future on-disk catalog file (append-only by index). The demo map asset (`res://assets/maps/demo.otmap`, seed 12345) is generated by `tools/build_demo_map.tscn`.
- **`TestRegistry`** (`src/map/test_registry.gd`) + **test map** (`assets/maps/test_map.otmap`, 53×27, seed 20260815) — the scenario museum: every hazard, elevation shape, layered structure, wall/corridor, rotation, and spawn surface the model can express, laid out in 8×6 zones (see `tools/build_test_map.gd` — its coordinates are the contract; `tests/map/test_map_test.gd` spot-checks each zone). View it with `godot --path . -- --map=res://assets/maps/test_map.otmap`.
- **Procedural maps** — a generator is just another producer of `MapData` (same shape as the demo builder); it flows through the same codec and renderer. Generators must produce valid `MapData` (indices within the registry, in-bounds elevations/layers).
- **In-game map editor** (planned) — mutates in-memory `MapData` and re-packs through the same codec; palette is the registry. Editor stays an orthogonal consumer of the schema.

#### Rules &amp; Pathfinding pipeline

- **`MaterialRules`** (`src/rules/material_rules.gd`) — the rules layer: how the world *treats* each material. One `TerrainRule` per type id: `passable`, `move_cost`, `hazard`/`hazard_damage`, behavioral `flags`, `status_on_enter`. `for_registry()` yields the rule set for a catalog (defaults + per-id overrides — the single behavioral source for demo/test ids). All editor-editable as Resources.
- **`MovementRules`** (`src/rules/movement_rules.gd`) — the single tunable module for MAP\_RULES §8.3 thresholds: `free_step` (0.25), `step_ceiling` (1.0), `climb_cost_per_step`, `water_cost_extra`.
- **`UnitProfile`** (`src/rules/unit_profile.gd`) — unit mobility: `move_budget`, traits (FLOAT/FLY/SWIM), status effects (slow/stop/immobilize modify the budget; float/swim statuses grant traits).
- **`PathEvaluator`** (`src/pathfinding/path_evaluator.gd`) — composes map facts (column surfaces from voxel shape geometry) × rules × unit state into per-step verdicts (`step_cost`, `hazard_at`).
- **`Pathfinder`** (`src/pathfinding/pathfinder.gd`) — budgeted 4-directional Dijkstra over the surface graph. `find_path()` → lowest-cost path to a destination; `reachability()` → the FFT movement bubble (cost field + parents). `PathResult` carries the ordered path, total cost, and every hazardous cell crossed (movement/combat applies the rule's damage/status as the unit traverses it).
- Pathfinding operates on `(x, y, surface)` nodes derived from the voxel grid; inter-surface transitions (stairs, ledges) are edge costs per MAP\_RULES §8.3. Surface heights come from shape geometry until the schema-4 `height` byte lands.

#### Combat &amp; multiplayer foundation

**`COMBAT.md` is the authoritative battle spec** — read it before touching combat. It locks the FFT baseline (turn order, turn flow, occupancy, targeting) and defines our divergences: deterministic CT scheduling, the eight-element system with affinities, and the living map (destruction/placement/field events/collapse, all as commits).

- **`Elements`** (`src/rules/elements.gd`) — the eight elements (fire/ice/lightning/water/wind/earth/holy/dark) + affinity helpers (`effective_multiplier`, `standing_multiplier`).
- **`UnitProfile`** (`src/rules/unit_profile.gd`) — now carries the streamlined stats (hp/mp/atk/def/mag/mdf/spd) and per-element `affinities` alongside mobility.
- **`DeterministicRng`** (`src/rules/deterministic_rng.gd`) — SplitMix64-seeded PRNG; the ONLY randomness source allowed in simulation paths (COMBAT.md §7.3, MAP\_RULES §14.8).
- **`RulesetVersion`** (`src/rules/ruleset_version.gd`) — FNV-1a hash of catalog + material rules + movement rules; a match pins it, mismatched clients refuse to join.
- **`Commit`/`CommitLog`** (`src/net/`) — the append-only, sequence-numbered state-change stream (the shared clock of a match). The kind vocabulary (move\_unit, set\_block, remove\_block, apply\_damage, …) is COMBAT.md §10.1, reflected as `Commit.KIND_*` constants.
- Turn controller, damage math, targeting, field effects, collapse, and the snapshot codec: `[planned]` (specified in COMBAT.md).

### Conventions

- Godot 4.x GDScript; follow the `godot4` skill (theme/font override quirks, ConfigFile gotchas, explicit type annotations — type warnings treated as errors). Comments explain intent, not narration (see Engineering Standards).
- snake\_case for scripts/vars, PascalCase for node names.
- Branch `main`; commits concise, imperative mood, lowercase, focused on "why"; push frequently.

### Docs &amp; Wiki

- **Canonical docs live in the repo**: `GAME_BIBLE.md` (the game bible — vision, systems, pipelines, baselines, glossary; read it first), `MAP_RULES.md` (authoritative world-model spec), `COMBAT.md` (authoritative battle spec), and this file.
- **Mirrored to the wiki** (`https://wiki.fifthdread.com/books/operation-tactics`) by `tools/sync_wiki.py` — the wiki is a rendered view, the repo is the source of truth.
- Sync (idempotent; creates missing pages, updates existing): `source ~/opencode/api_secrets.sh && python3 tools/sync_wiki.py`. `--check` previews without writing. Which docs map to which pages is declared in `tools/wiki_manifest.json`.

### Shipping

Shipping = **commit and push** to the project repo.

- Remote: Forgejo — `ssh://git@forgejo.fifthdread.com:223/Fifthdread/operation_tactics.git`. **SHA-256 object format** — the repo must be pre-created on the server as sha256; push-to-create will not work for this repo.
- If the game is ready for distribution on Arch-based Linux: also update the **PKGBUILD** so `paru -S <pkg>` installs it.
- PKGBUILD lives in the `Fifthdread/pkgbuilds` repo at `~/opencode/pkgbuilds/` (one dir per package, `-git` variant recommended for an in-development game).
- Workflow: bump `pkgver=` (`rev-count.commit`), regenerate `makepkg --printsrcinfo > <pkg>/.SRCINFO`, commit `.SRCINFO` alongside, push. Full detail in the pkgbuilds project's CONTEXT.md/AGENTS.md.