Skip to content

Authoring levers

A world you can play is three agreements that must not be converted into each other. Salt Road, the felt harbour, is the worked example. Turn one lever, export, and look. Turning two at once is how a person ends up drawn in a room the simulation says they are not in.

Grid Unit Where you edit it Does the simulation hash it?
Occupancy zone id zones, entityPlacements, entry gates Yes. This is who is where, and whether the door opened.
Cartesian gridX / gridY, tile 32, then ×3 at 48 in the sandbox the editor canvas and the Godot .tscn No.
Dimetric 256×128 diamond, span 3 WorldProject.presentation No. The stage draws this and never sends it back.

scaleForSandbox multiplies cartesian geometry and skips presentation by name. A cell in presentation is copied, not computed from gridX / gridY.

You want Lever Heard by
Another room a zone, its neighbours, its prose engine, joined to a scene node by zone id
A door that explains itself entryGate conditions, hard or soft, and reason engine refuses; stage only displays the reason
A person in a room entityPlacements[].zoneId engine. Hashed.
Where that person stands on the diamond presentation.occupancy[].cell and facing stage. Not hashed. Must sit inside that zone’s span box, and the zone must match the placement.
Wet stone, dirt, dry stone presentation.floor plate id (stone_wet, stone_a, dirt_a, dirt_b) stage atlas. Absent floor still wets the long quay.
The anchor of a zone on the map presentation.zoneCells stage.
A thing in someone’s inventory engine itemPlacements of { itemId, entityId } engine. A zone-standing prop is not this. The guild seal on the counting-house floor is a stage marker, not a give-item.
What the player hears cue id on the felt payload, file assets/felt/<cueId>.wav in the stage stage mixer. Missing file is a sine stand-in. The cue id is never hashed.

presentation is not a field on the engine ContentPack. The stage fixture writes it onto pack.json. The sidecar file keeps only the keys engine 3.12.0 loads.

The authored world is dogfood/worlds/salt-road.ts. Export both lanes from one command:

Terminal window
npx tsx dogfood/export-stage-fixture.ts \
--world=salt-road \
--out=<ai-rpg-stage>/fixtures \
--doctor \
--strict \
--engine-out=dogfood/output/salt-road/pack.json

--strict refuses to write when a presentation advisory fires (a cell outside its zone, a person drawn in the wrong room, no player row). The scene it writes is a join graph: zone nodes and gate text, and no play pawn. A full Godot project export still emits CharacterBody2D and world_data; the stage project cannot instance those paths.

Play from the stage checkout with node tools/play.mjs. The sidecar is TCP --listen, with fixtures/salt-road.manifest.json beside the engine pack.

  • Types and the eight presentation advisories: Schema & Types.
  • What the Godot exporter keeps, approximates, and drops: Godot Export.
  • What the engine actually loads: Engine Export.
  • How the client draws a lever you already set: the stage handbook’s authoring levers, and the engine’s Visual Clients.