Part II — Systems

The world & how it's stored

What it is. The battlefield is a 3D grid of small cells called voxels. A voxel is either empty or holds one block: a material (grass, lava, water, wall…), a shape (full cube, half block, slab, slope, wedge…), a rotation, and later a fractional height. Terrain is just columns of blocks rising from the ground; a building is blocks stacked on top. There is no separate "terrain" structure — one grid holds everything.

The facts rule. The map stores only facts: what material is where, in what shape and orientation. It never stores judgments like "walkable" or "costs 5." Those live in the rules layer (below). This is the single most important idea in the project: the map is what is; rules are how it behaves.

How it's stored. - The grid is split into 16×16×16 chunks, so a map can be small (a demo) or effectively endless (an open world) with the same code. - Files are saved by a codec that packs the grid into a compact binary format (.vmap), versioned so old files fail loudly rather than load wrong. - Materials are catalogued in a registry (currently built in code, one day an on-disk catalog). Maps reference materials by index, so the catalog is append-only — entries are never reordered or removed.

Where it lives. src/map/ (voxel_map.gd, voxel_codec.gd, tile_type.gd, tile_type_registry.gd, the demo/test registries), assets/maps/ for map files, tools/ for the builders that create them.

Status: [built] — storage, codec, catalog, and demo/hills/test maps. The per-voxel fractional height byte is [planned] (schema 4).

The rules layer

What it is. A separate area of the code that decides how the world treats each material. It turns map facts into gameplay answers: can a unit stand here, what does it cost to move here, does standing here hurt, what statuses does it apply?

How it works. Three rule modules compose together:

  1. Material rules — one entry per material: passable or not, movement cost, hazard (lava, water, poison, fire, spikes) and its damage, status effects applied on contact, and behavioral flags.
  2. Movement rules — the tunable thresholds for climbing: how high a step is free (0.25), the ceiling a normal unit can climb (1.0), how much extra a climb costs, and the surcharge for wading without swim.
  3. Unit mobility — each unit's move budget, plus traits (float, fly, swim) and status effects (slow, stop, immobilize) that change how the rules apply to that unit.

Rules are data (editor-editable Godot resources) — the intent is that they can be tuned later from a debug/config screen without touching code.

Where it lives. src/rules/ (material_rules.gd, terrain_rule.gd, movement_rules.gd, unit_profile.gd).

Status: [built].

The pathfinding pipeline

What it is. The engine that answers "how does this unit get there?" — and, for the player, "where can this unit go?" It is called every time a player (or an NPC) wants to move.

How it works. Four steps:

  1. Read the field. From the map it computes a surface for every column — the exact height a unit would stand on. The map supplies only facts: heights, materials, shapes.
  2. Compose the verdict. For every candidate step, it asks the rules layer: is this step passable, what does it cost, is there a hazard? The rules consider the material, the height difference (a rise costs extra; one above the ceiling is blocked), and the unit's traits/statuses (fly ignores terrain, float ignores height and ground effects, swim skips the wading surcharge).
  3. Search. A budgeted search fans out from the unit's position, tracking how many move points each tile costs. It stops when it has spent the unit's entire move budget. This single pass produces the movement bubble (every reachable tile) — and, given a destination, the lowest-cost path to it.
  4. Report. The result is the ordered list of tiles to walk, the total cost, and every hazardous tile crossed — so the movement system can apply the damage and status effects as the unit actually walks through them.

The same search serves the player (bubble + path) and the AI (which uses the bubble to pick where to move). It is fully deterministic and runs headless — the whole pipeline is unit-tested.

Where it lives. src/pathfinding/ (path_evaluator.gd, pathfinder.gd, path_result.gd).

Status: [built] — flat steps, climbing, walls, water, hazards, flight/float/swim, status effects, movement bubble, hazard report. Jumping is [planned] (the rule supports a ceiling extension).

Multiplayer & determinism

What it is. The goal that shapes the whole architecture: the game is a deterministic simulation — given the same facts and the same rules, every client produces the same outcomes on its own. Multiplayer doesn't require syncing the battlefield every frame; it only requires syncing what changed, and letting every client derive the rest.

Why the architecture is built for it. Because the map stores only facts, the sync surface is tiny. Because the rules derive behavior, every client computes the same move costs, the same movement bubble, the same damage from the same facts — nothing behavioral is transmitted. Because cosmetics are seed-driven, every screen looks the same too.

How a match stays in sync (the determinism contract):

  1. Pinned rules. A match carries a ruleset version — a deterministic hash of the catalog, the material rules, and the movement rules. A client whose ruleset doesn't match refuses to join; otherwise it would derive different outcomes from the same facts.
  2. Ordered commands. Every state change — including every map edit — flows through the commit log: one canonical, append-only, sequence-numbered stream. A host arbitrates order; every client applies commits in the exact same order. A gap or reorder in the sequence is a de-sync by definition, and it is caught the moment it arrives.
  3. Deterministic randomness. All gameplay randomness comes from a single seeded PRNG, driven by the match's simulation seed and the commit sequence. The engine's own random number generator is never used in a simulation path.
  4. Sim-safe math. Gameplay arithmetic stays in integers and exact fractions (move costs, budgets, half-steps), so every platform computes the identical result.
  5. Snapshot & reconnect. A match can be captured as a snapshot (ruleset version, seeds, map, units, RNG position, turn state). A reconnecting client loads the snapshot and rejoins the commit stream — no state divergence.

The commit log is the game's spine: player moves, map edits, and (later) combat all record their decisions there; the simulation is the pure function that replays them.

Where it lives. src/rules/deterministic_rng.gd, src/rules/ruleset_version.gd, src/net/commit.gd, src/net/commit_log.gd.

Status: [foundation built] — deterministic RNG, ruleset versioning, and the commit-log shape exist. The snapshot codec and the host/peer protocol are [planned].

Movement & turn flow (planned)

Once a path is chosen, the movement phase walks the unit along it, applying the reported hazards as it goes, then consumes the turn. The exact order — move → act → end turn — and the rules for facing, disengage, and counterplay are still being designed. The pathfinding pipeline is the substrate this is built on.

Combat

What it is. The battle system: turn order, movement, actions, damage, elements, statuses — and a battlefield that changes as you fight. The authoritative, precise spec is COMBAT.md; this is the short version.

How it works. Turn order runs on a deterministic CT clock: every tick, each unit's Charge Time grows by its Speed, and the unit with the highest CT over the threshold acts (overflow carries). A turn is the FFT shape: pick a destination from the movement bubble → face → choose an action → pick a target → confirm. Actions resolve range, line-of-sight, and height; damage runs the documented formulas through per-unit element affinities; statuses and terrain apply their effects on tick-based durations.

The map is a living battlefield. Blocks can be destroyed (materials carry integrity) or placed, field events are scheduled on the clock (rising lava, spreading fire, freezing water), and unsupported blocks collapse in a deterministic cascade. Every one of those mutations is a commit in the match's commit log — so the battlefield, the turns, and every derived outcome stay in sync across clients (see Multiplayer & determinism).

Where it lives. COMBAT.md (spec); src/rules/elements.gd + src/rules/unit_profile.gd (built); src/combat/ (planned).

Status: [designed] — the spec is documented and the element/stat/commit primitives are built; the turn controller, damage math, targeting, field events, and collapse are [planned].

Units, jobs & abilities (planned)

Units have stats and a move budget. Jobs/classes unlock abilities learned by doing. The mobility traits and statuses already exist in UnitProfile and will be joined by combat stats and an ability catalog.

Items & economy (planned)

Equipment, consumables (including the statuses like float/swim that the rules already understand), shops, and job-point progression between battles.

AI (planned)

Enemy AI will consume the same reachability and cost model the player uses — evaluating its movement bubble, scoring candidate tiles (threat, hazards, height advantage), then pathfinding. Because everything reads the same rules, the AI cannot "cheat" the terrain in ways the player can't.

Rendering & presentation

What it is. Everything visual. It is strictly a consumer: it reads map data and rules and never feeds back into them.

How it works. - The voxel mesh builds each block's shape as triangles and drops interior faces hidden by neighbors (so a solid hill doesn't render its inner walls). - The render view uses the exact same voxel shape geometry as the debug view (each block's true shape, face-culled interior boundaries) — only colors differ: a seeded per-cell tint adds gentle shade variation, and a cosmetic layer scatters rocks and foliage on solid, hazard-free ground. Both are pure functions of the map's seed, so the same map always looks the same. The day/night cycle shades it all (sun, moon, clouds, stars). - The theme maps each material to a color (later, scenes/textures), keeping visuals fully separate from logic. - The top grid is a shader overlay drawn on the terrain: grid lines, hover highlight, and selection — used by the player and by debugging. - Day/night is astronomical: daylight length is computed from the configured latitude and month (Virginia in August ≈ 13.5h of sun, December ≈ 9.5h), the sun only lights the scene above the horizon, a separate moon follows its own phase-driven arc (brightness from the illuminated fraction, dimmed by cloud cover), and clear nights add faint starlight.

Where it lives. src/render/ (voxel_mesh.gd, terrain_mesh.gd, shape_geometry.gd, map_theme.gd, map_details.gd, map_view.gd, day_night.gd, top_grid.gdshader).

Status: [built] — box + smooth views, cosmetic layer, theme, top grid, day/night, F1 toggle. Voxel-side picking/hover is [partial].



Revision #10
Created 2026-08-15 18:17:01 UTC by Fifthdread
Updated 2026-08-15 19:00:00 UTC by Fifthdread