---
id: ground-look-and-levels/002
title: Three levels and ramp hexagrams — the edge rule, the height-field mesh, the mirrors and the editor
epic: ground-look-and-levels
state: done
priority: 1
blocked_by: []
estimate: M
created: 2026-08-18
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/handoff/ground-look-and-levels.md
---

## What to do

Decisions 3–6 of the workshop note (`land-of-lor/docs/design/2026-08-18-ground-look-and-levels-workshop.md`), with its provisional hexagrams (kw 27 high, kw 41
low, kw 42 and 61 ramps) until the meanings pass (005) replaces them.

- Data: `level` (−1/0/+1, default 0) and `ramp: true` on hexagrams in `elements.js`;
  `ground_types.gd` / `groundTypes.ts` read them.
- `grid.gd`: at `apply_ground`/`set_ground` precompute per cell its level and, for ramps, the
  derived axis and direction (recomputed for the neighbours of a changed cell too); expose
  `edge_level(x, y, dir)` and `is_ramp(x, y)`. Hot loops stay one array read.
- `movement.gd`: the edge rule — the step A→B needs B walkable and the two surfaces meeting at
  the shared edge; diagonals need all four cells at one level and none a ramp. Client
  prediction gets it for free (it calls the same function). `flow_field.gd`: the same rule in
  its neighbour loop. New `Result.LEVEL_BLOCKED` with its reason string.
- `ground_mesh.gd`: the height field — `level × BLOCK − sink`, ramps a plane between their two
  end levels — under the "highest touching cell" vertex rule; `standing_height` = the surface at
  the cell centre (a ramp's centre is halfway); mountains and portals sit on their cell's
  surface. `world_units.gd`: `LEVEL_HEIGHT` (a full cell to start; Kris tunes it over time).
- `ground.gdshader`: cliff faces (steep, tall steps) shaded as an earth wall with a lip line
  along the top edge; a bank (a sink) keeps its lighter rim.
- One shared `game/shell/ground_rules.mjs` (ES module, browser and node): `canStep`,
  `reachableFrom` with the corner rule *and* the level rule; `director/src/groundContent.ts`
  and `tools/world-editor/area-editor.js` use it (the editor is already served from `shell/`
  by `server.mjs`). The editor: palette shows level/ramp on a hexagram's chip text, Random mix
  and the director's sprinkle presets never pick a levelled or ramp hexagram, loose ends flag a
  ramp with no level change beside it and a ramp with two candidate axes; the loader refuses
  the same things it always did, now with the level rule in its reachability.
- `_shots.gd`'s built-in showcase gains a plateau with a ramp and a pit with a ramp; take the
  chase-camera shot with a body at the foot of a cliff on the camera side — the occlusion
  check that decides whether `LEVEL_HEIGHT` stays a full cell.
- Tests: `test_runner.gd` (rule cases: same level, ramp ends, ramp flanks, diagonals, dead
  ramp, derived axis; mesh heights on a plateau and a ramp) and `director` tests
  (`groundContent` reachability with a cliff and a ramp).

## Definition of done

All of the above green; the showcase shots in `docs/handoff/ground-look-and-levels/`; a
`docs/handoff/ground-look-and-levels.md` note with what was decided in the build (block height
after the occlusion check, ramp edge cases). No protocol or schema change. Committed and pushed.

## Where the work lands

`land-of-lor/game/world/`, `game/shell/`, `game/director/src/`, `tools/world-editor/`, `docs/handoff/`.

## What was done (2026-08-18, laptop workshop session)

Built and shipped the same day. Kris's assignments (not the provisional four): the Deep Water
family is the deep level (−1, wadeable), Wind over Lake the ramp down; Mountain over Stone the
high level, Wind over Stone the ramp up; the Grass family reserved for variety. One shared
`game/shell/ground_rules.mjs` (levels, derived ramps, the whole step rule) for the director's
loader validation and the area editor; `grid.gd` precomputes levels/ramp directions;
`movement.gd` (`LEVEL_BLOCKED`) and `flow_field.gd` ask it; the mesh is a height field on the
same lattice; the shader keeps a plateau's blend to its level and paints faces; the editor says
every chip's meaning, mixes only variety, flags a level change with no ramp and odd ramps; the
showcase has a plateau + ramp, a pit + ramp, and the occlusion shot (`LEVEL_HEIGHT` stays a
full cell: legs hidden, torso and head visible). test_runner 247/247, director 202/202.
Handoff: `land-of-lor/docs/handoff/ground-look-and-levels.md`.