Summary
Storage supports all 4 planes, but every computed graph layer (blobs, ports, transits, crossings, map_link blob ids, gate links) hardcodes WHERE plane = 0. As a result, anything on plane ≥ 1 is invisible to find_path — Cam Torum, Lassar Undercity, every wizards' tower / castle upper floor, etc.
This was masked until now because the (deleted) compute_walkability.py script wrote 8344 phantom walkable-typed map_links between Voronoi-neighbor cells. Those were the only edges that ever bridged into the upper-plane graph nodes, and they bridged in places where no real connection exists — e.g. routing Civitas → Cam Torum via "walkable" hops between three isolated underground blobs (30709 → 32202 → 16828) that have no walkable tile path between them at all.
Reality check
Confirmed in-game: Cam Torum proper is at (1439, 9599, plane=1). The surface entrance gate is object 51375 at (1435, 3129, plane=0). So even the canonical "walk through the gate" route requires a plane=0 → plane=1 transition, which the current schema can't even express on a map_link.
Inventory
Has plane support (✓):
map_squares table — collision/water/color rasters dumped for all 4 planes
MapSquare API in src/ragger/map.py — plane parameter throughout
object_locations table — plane column populated
Hardcoded to plane=0 (✗):
scripts/pipeline/compute_blobs.py:322,419
scripts/pipeline/compute_ports.py:133
scripts/pipeline/compute_port_crossings.py:79
scripts/pipeline/compute_port_transits.py:141
scripts/pipeline/compute_map_link_blobs.py:67
scripts/pipeline/compute_gate_links.py:62
scripts/pipeline/fetch_ground_items.py:35
Schema gaps (✗):
blobs table: (id, location_id, tile_count) — no plane
ports table: no plane column
map_links table: has src_x/y and dst_x/y but no src_plane / dst_plane. Cross-plane teleports (ladders, stairs, gates that warp you up/down a level) cannot be represented honestly.
find_path PathStep is 2D; A* node keys like ("port", pid) have no plane component.
Spawn counts give a sense of scale
object_locations by plane:
plane 0 → 56708 spawns
plane 1 → 9784
plane 2 → 5050
plane 3 → 3309
So ~25% of interactive objects sit on a plane the graph can't reach.
Suggested ordering
- Schema migration: add
plane to blobs, ports; add src_plane, dst_plane to map_links. Backfill existing rows with 0.
- De-hardcode the six compute_* scripts to iterate planes 0..3 (or all planes present in
map_squares of type collision).
- Update
find_path to key nodes by (kind, id, plane) and propagate plane through PathStep. Heuristic stays admissible since plane shifts are zero-cost.
- Update
MapLink.departing / arriving and other APIs to round-trip plane.
- Backfill plane on existing portal-type
map_links (entrance/exit/teleport/charter/etc.) by reading the cache CS2 destination scripts where available, or by inferring from object spawn planes near the wiki coord.
Out of scope but adjacent
- Cleaning up the 8344 stale Voronoi
walkable map_links from the old compute_walkability.py (separate issue).
- Building the surface↔underground / inter-floor pairing pipeline that uses object_locations + the plane-aware schema to discover entrance / ladder pairs automatically.
Summary
Storage supports all 4 planes, but every computed graph layer (blobs, ports, transits, crossings, map_link blob ids, gate links) hardcodes
WHERE plane = 0. As a result, anything on plane ≥ 1 is invisible tofind_path— Cam Torum, Lassar Undercity, every wizards' tower / castle upper floor, etc.This was masked until now because the (deleted)
compute_walkability.pyscript wrote 8344 phantomwalkable-typedmap_linksbetween Voronoi-neighbor cells. Those were the only edges that ever bridged into the upper-plane graph nodes, and they bridged in places where no real connection exists — e.g. routing Civitas → Cam Torum via "walkable" hops between three isolated underground blobs (30709 → 32202 → 16828) that have no walkable tile path between them at all.Reality check
Confirmed in-game: Cam Torum proper is at
(1439, 9599, plane=1). The surface entrance gate is object 51375 at(1435, 3129, plane=0). So even the canonical "walk through the gate" route requires a plane=0 → plane=1 transition, which the current schema can't even express on amap_link.Inventory
Has plane support (✓):
map_squarestable — collision/water/color rasters dumped for all 4 planesMapSquareAPI insrc/ragger/map.py—planeparameter throughoutobject_locationstable —planecolumn populatedHardcoded to plane=0 (✗):
scripts/pipeline/compute_blobs.py:322,419scripts/pipeline/compute_ports.py:133scripts/pipeline/compute_port_crossings.py:79scripts/pipeline/compute_port_transits.py:141scripts/pipeline/compute_map_link_blobs.py:67scripts/pipeline/compute_gate_links.py:62scripts/pipeline/fetch_ground_items.py:35Schema gaps (✗):
blobstable:(id, location_id, tile_count)— noplaneportstable: noplanecolumnmap_linkstable: hassrc_x/yanddst_x/ybut nosrc_plane/dst_plane. Cross-plane teleports (ladders, stairs, gates that warp you up/down a level) cannot be represented honestly.find_pathPathStepis 2D; A* node keys like("port", pid)have no plane component.Spawn counts give a sense of scale
So ~25% of interactive objects sit on a plane the graph can't reach.
Suggested ordering
planetoblobs,ports; addsrc_plane,dst_planetomap_links. Backfill existing rows with 0.map_squaresof type collision).find_pathto key nodes by(kind, id, plane)and propagate plane throughPathStep. Heuristic stays admissible since plane shifts are zero-cost.MapLink.departing/arrivingand other APIs to round-trip plane.map_links(entrance/exit/teleport/charter/etc.) by reading the cache CS2 destination scripts where available, or by inferring from object spawn planes near the wiki coord.Out of scope but adjacent
walkablemap_links from the oldcompute_walkability.py(separate issue).