← Ground types · all tasks

The model — a hexagram is a ground type, in every runtime

done priority 1 estimate M

ground-types/001 · created 2026-08-17 · updated 2026-08-18

What to do

Put the hexagram at the centre of the code, the way the spec asks, before any storage or rendering exists. Read land-of-lor/docs/specs/GROUND_TYPES.md first (it is human-owned; read, don’t edit).

Step 0 — fix the world file. game/content/spellgrove.world.json is invalid today: a world-editor session on 2026-08-16 (05fcdd0) shrank beginner-arena to 20×10 and left two portals out of bounds (village-to-arena toCell (0,10), arena-to-badlands fromCell (19,19)). validateWorldContent returns two errors and npm run load-world refuses the file. Everything in this epic rides on the loader. Assumption: keep the 20×10 and move both portals to the bottom edge — (0,9) and (19,9). Say so in the session report.

Then the model, in three runtimes from one table:

  1. game/shell/elements.js — add ground to each of the eight LOR_TRIGRAMS entries ({ name, kind: "flat"|"sunken"|"mountain", passable, colour, pattern }; earth = Grass, wind = Sand, thunder = Snow/Ice, water = Water (sunken), fire = Lava (sunken), mountain = Mountain (impassable), lake = Deep Water (impassable), heaven = Stone/tiles) and groundVariant ({ label, colour, pattern }) to each of the 64 LOR_ELEMENTS (the Earth-upper one is the plain variant; kw 56, Fire over Mountain, is the volcano). Inside the two existing globals, not a third one — card-editor.html regenerates this file from exactly LOR_TRIGRAMS and LOR_ELEMENTS. Placeholder colours are fine; task 006 tunes them.
  2. game/sync-elements.sh also writes game/director/src/data/elements.json; the director’s build script copies src/data into dist/ the way it already copies schema.sql.
  3. New game/world/ground_types.gd at the project root (pure RefCounted, preload-only, loads the same elements.json via load() — no hand-copied tables): kw_of(upper, lower), lower_of(kw), variant_index(kw), is_passable(kw), is_sunken(kw), is_mountain(kw), DEFAULT_KW = 2, plain_kw_for(lower). client/ui/element_db.gd gains ground_type(kw) / ground_variant(kw) and documented stubs for the spec’s other getters (getCreatureModel, getSize, getColour, getBrain, getNature, howManyStackInCell, getSpellKind, getPowerValue, rarity) — the shape is visible, no values are invented.
  4. New game/director/src/groundTypes.ts with the same API, reading data/elements.json.
  5. game/world/grid.gdCELL_TERRAIN_BLOCK = 4; ground: PackedInt32Array filled with 2 in _init; ground_at(), apply_ground(PackedInt32Array) -> bool (length must match; sets CELL_TERRAIN_BLOCK for impassable hexagrams but never overwrites CELL_PORTAL); decode_ground() as the one codec function; from_area_payload reads ground; permits_squeeze() is false for a terrain block; set_portal warns if it lands on one. Retire the “an authored area is open ground (no ground types yet)” test line.
  6. Write the ground content-file format into game/README.md now, so tasks 005–007 can start from it: game/content/areas/<slug>.ground.json = { "$comment", "slug", "width", "height", "layers", "rows": ["02 02 20 02 …", …] }height × layers row strings, two-character zero-padded, space-separated, one row per line.

Definition of done

Where the work lands

land-of-lorgame/shell/, game/director/src/, game/world/, game/README.md.

What was done (2026-08-17, laptop session)

View source in repository · also available as raw markdown.