The level the whole game started as, and the only one that isn't authored. robotmonster.party/store/ is assembled procedurally from Postgres (the wowplatter virtual_* schema, served by recordgod), and this game's sites are already data — so tools/import_store.py reads the live shop and emits assets/store/shop_floor.json: the actual 6.66 x 9.51 m floor, 35 racks across 21 archetypes, 278 bins carrying their real genre labels (HARD TRANCE, $12-$15 EURO/ITALO HOUSE), 21,159 records of stock. Levels.record_store() loads it; 'store' joins ORDER as the seventh site. The shop's schema IS the cascade. virtual_crate.rack_id already records which rack holds which bin, so bins arrive frozen and supported_by their rack and none of the structure is authored. To carry that, smashables gained two optional generic keys — 'id' and 'supported_by' — so any level can express support as data. Every existing level is unaffected. Two reconciliations, because a storefront is a renderer: - Mesh sizes are a lie: 21 archetypes share a few stand-in GLBs, so each piece carries 'fit' (the archetype's real dims from the shop's table) and _glb_piece scales the visual to it and builds the collider from it. Also fixed racks being flattened to the floor — pos_y is a piece's BOTTOM, which is what sit_on means, so the ceiling beams, aircon and wall shelves now stay up where they belong. - Nothing collides in a renderer: racks that sat inside each other for years get separated (weighted, so racks barely move and aisles keep their shape — 3 m of correction across 35), rack footprints inset 1.5 cm a side for the few genuinely wedged units, and the importer AUDITS ITS OWN OUTPUT with the same test probe_overlap runs. Bins are deliberately not separated from their rack: cargo inside a container is what the shop means, they spawn frozen, and the audit skips frozen bodies because a frozen body cannot be depenetrated across the room. dev/probe_store.gd: 22 racks hold bins, 255 labels survived, and smashing the rack carrying 22 bins releases exactly those 22 while the other 256 stay frozen. All gates green: smoke clean, 7/7 sites 0.00 m/s, overlap CLEAN, LANE9 probes pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
64 lines
3.2 KiB
Markdown
64 lines
3.2 KiB
Markdown
# LANE 10 — SITE 7: MONSTER ROBOT PARTY
|
||
|
||
*The level the whole game started as. Spec'd and executed by Fable, 2026-08-04.*
|
||
|
||
## The idea
|
||
|
||
Every other site is authored. This one is **imported from the real shop**. The live
|
||
storefront at `robotmonster.party/store/` is procedurally assembled from Postgres rows
|
||
(`virtual_rack`, `virtual_crate`, `virtual_rack_type*`, `virtual_crate_type` — the
|
||
wowplatter schema, now served by recordgod/storegod), and the game's sites are already
|
||
data. So Site 7 is not hand-placed: a converter reads the shop and emits a level.
|
||
|
||
`GET https://robotmonster.party/virtual/scene` returns the shop floor:
|
||
|
||
| | |
|
||
|---|---|
|
||
| room | 6.66 m × 9.51 m, 2.89 m ceiling |
|
||
| racks | 35 placed, across 21 archetypes |
|
||
| crates | 302, every one on a rack |
|
||
| labels | real: `HARD TRANCE`, `$12-$15 EURO/ITALO HOUSE`, `SOUND TRACKS, SPOKEN WORD & COMEDY` |
|
||
| stock | **22,049 records** across the bins (`n_items`) |
|
||
|
||
The rack archetype names (`wooden-rack-type-long`, `white-tub-3x3`, `chiu-long`) are the
|
||
same names as the GLBs in `3D-STORE/`, because both were generated from the same table.
|
||
|
||
## Why this is the best-fitting level in the game
|
||
|
||
The shop's schema **is** the cascade. `virtual_crate.rack_id` already says which rack
|
||
holds which bin, so `rack.supports = [its crates]` falls out of the data — knock a rack
|
||
and the real bins that really sit on it really go over. Nothing to author.
|
||
|
||
## Deliverables
|
||
|
||
### 1. Generic data-driven cascade *(`Main.gd`, small)*
|
||
Extend the `smashables` spec entry with two optional keys so a level can express support
|
||
without code: `"id"` (a name other entries can refer to) and `"supported_by"` (the id of
|
||
the piece holding this one up). Main builds in order, keeps `id -> Smashable`, and wires
|
||
`holder.supports.append(piece)`. Every existing level is unaffected.
|
||
|
||
### 2. `tools/import_store.py` — the converter
|
||
Fetches the live scene (`--url`, or `--offline <file>`), filters to the shop-floor space,
|
||
and emits `game/assets/store/shop_floor.json`:
|
||
- world-space rack positions, **re-centred** (the shop's origin is a corner; the game
|
||
centres its bounds on 0)
|
||
- each crate placed by `rack_pos + rotate_y(crate_local, rack.rotation_y)`, carrying its
|
||
real `label` and `n_items`
|
||
- archetype → game GLB + material mapping (tubs are `plastic`, timber racks `wood`,
|
||
columns and aircon `steel`)
|
||
- crates spawn **frozen** and supported by their rack: they don't jitter at spawn (the
|
||
stillness gate stays green) and they go live exactly when the rack does
|
||
- a **measured spawn point** — a grid scan for open floor, because a 6.66 × 9.51 room
|
||
holding 35 racks has very little of it, and spawning inside a rack is not a level
|
||
|
||
Re-runnable: when the real shop is rearranged, re-run it and the level changes.
|
||
|
||
### 3. `Levels.record_store()` — the site
|
||
Loads the JSON and returns a normal Floorplan spec. Timber floor, white walls, the
|
||
brand's pink as accent. Added to `Levels.ORDER` as the seventh site.
|
||
|
||
## Gates
|
||
Headless smoke clean · `probe_levels` **7/7** at 0.00 m/s · `probe_overlap` CLEAN ·
|
||
the LANE9 probes still green. Plus `dev/probe_store.gd`: the site builds, the rack→crate
|
||
cascade is wired, and smashing one rack releases exactly its own bins.
|