# PROCITY A standalone, procedurally generated, fully walkable 90s-Australian shopping town — every building enterable (the Vuntra City trick), every interior seeded and themed: record stores, op shops, toy shops, book barns, video rental, pawnbrokers, milk bars, market stalls. Built on the proven house stack (three.js r175 vendored, plain JS, no build step) and the asset DNA of 90sDJsim + thriftgod. v1 is the *system*; the GODVERSE/BaseGod real-item content plugs in later. ## Run ``` cd web && python3 -m http.server 8130 # http://localhost:8130 (game shell — Lane B) # http://localhost:8130/map.html (2D plan debugger — Lane A) ``` ## Read first - **`docs/CITY_SPEC.md`** — the treaty: schema, units, seeds, budgets, file ownership. - **`docs/RESEARCH.md`** — what the parent repos already solved, with exact paths to port from. ## The lanes (parallel Opus 4.8 sessions) Each lane is a self-contained prompt. To run one, start an Opus session in this repo and say: **"Read docs/LANES/LANE_X_….md and execute it."** A–E are parallel-safe (disjoint files, standalone test pages); F integrates after they land. | lane | mission | owns | |---|---|---| | [A — CITYGEN](docs/LANES/LANE_A_CITYGEN.md) | seed → CityPlan data (streets, blocks, lots, shops) + 2D map | `js/citygen`, `js/core/registry.js`, `map.html` | | [B — STREETSCAPE](docs/LANES/LANE_B_STREETSCAPE.md) | chunk-streamed instanced 3D town + game shell | `index.html`, `js/world` | | [C — INTERIORS](docs/LANES/LANE_C_INTERIORS.md) | every door opens: seeded themed shop interiors | `js/interiors`, `interior_test.html` | | [D — CITIZENS](docs/LANES/LANE_D_CITIZENS.md) | rigged-near / impostor-far NPCs + shopkeepers | `js/citizens`, `citizens_test.html`, `models/` | | [E — ASSETS](docs/LANES/LANE_E_ASSETS.md) | audit ultra's library, normalize GLBs, skins, manifest | `pipeline/`, `assets/` | | [F — INTEGRATION](docs/LANES/LANE_F_INTEGRATION.md) | wire A–E into one seed-to-town game + QA gates | (after A–E) | ## Already in the repo - `web/vendor/` — three.js r175 + addons (vendored from 90sDJsim; no CDN at runtime). - `web/assets/gen/` — 69 generated texture skins (25 shopfront facades, 10 skies, 10 grounds, 8 wallpapers, 16 interior surfaces) inherited from thriftgod + 90sDJsim, style-locked. - `web/js/core/` — frozen shared modules: `prng.js` (seeded-everything law), `loaders.js` (fail-soft promise-cached GLB/texture + 3GOD depot), `canvas.js` (text planes). ## House laws (short form — CITY_SPEC has the fine print) Seeded everything, `Math.random` banned in gen code · the game must run with zero assets · instancing from day one · canvas text, no font files · characters = shared base rigs + one clip bank, canonical sources live outside this repo · 🟢 web-ok licensing only. ## v3.1 — THE FLIP: the town shows its best by default The Friday-night layer soaked through alpha, beta, and the v3.0 release — so **v3.1 flips it on by default**, and the mature v2 flags flip with it. Boot the plain URL and each town seeds **2–4 venues** (pub, band room, RSL) with a **full seeded week of gigs**, town-wide **posters**, and a Friday night that reads: a **4-piece band** (trio + a **seated drummer**) with real instruments, a mixed **crowd**, an outdoor **queue**, a per-venue **cover charge**, muffled **gig spill** + **walla and applause**, town-wide **streetlamp pools**, and **identity continuity** — the ped you followed in is in the crowd. Plus **weather**, the **tram**, and the parallax interior-**window shader**, all live by default. **The covenant moved, it didn't disappear.** `?classic=1` boots the **exact v2 town** — byte-identical forever (synthetic golden `0x3fa36874`, zero gig layer, zero fetch delta). That is the new frozen regression target; the default boot is deterministic to the gig golden `0xb1d48ea1`. Asset-free (`?noassets=1`) composes with the default boot and stays silent-and-fine. **Flags** — the four headliners are **ON by default**, each with a `=0` opt-out; `?classic=1` is the master off: | flag | default | what | |---|---|---| | `?classic=1` | off | **the exact v2 boot** — all four below off, byte-identical to the frozen baseline | | `?gigs=0` | **on** | the gig district — venues, week schedule, band/crowd/queue, cover, posters, night pass | | `?weather=0` · `?tram=0` · `?winmap=0` | **on** | seeded weather · the tram loop · the parallax interior-window shader | | `?roster=v1` · `?pop=N` | stream | chunk-streamed citizen density is the baseline; `?roster=v1` restores the fixed roster | | `?dig=0` · `?stock=real` | dig on | crate-riffle on record bins · real GODVERSE sleeves in the stock | | `?noassets=1` · `?plansrc=osm` · `?mute=1` · `?dbg=1` | off | asset-free (zero network) · OSM plan source · silence · the `window.DBG` capture/soak harness | | `?plansrc=osm&town=` | — | boot a **real Australian town**: fixtures `melbourne`/`katoomba`/`silverton`, or a real OSM cache `bendigo_real`/`castlemaine_real`/`fremantle_real`/`katoomba_real`/`newtown_real` (ODbL, `web/assets/towns/SOURCES.md`). **REAL ROADS:** a cache with `roads[]` (schema v2) builds the CityPlan street graph from the town's **actual OSM streets**; without `roads[]` a cache marches onto synthetic avenues. Or just **pick a town from the HUD selector** — no URL needed | ## v4.0 — THE REAL MAP: the towns *are* the product **23 real Australian towns — 1,210 real shops, every state and territory — on their actual streets.** Not "inspired by": Fitzroy's shops sit on Fitzroy's roads, at their real positions, because the pipeline eats OpenStreetMap and marches it through the same CityPlan contract the procedural town uses. **Pick one from the in-HUD selector and walk it.** Everything built across v1–v3 comes along unchanged — the venues and their seeded week of gigs, the 4-piece band with the seated drummer, the crowd that follows you into the pub, the interiors behind every door, weather, the tram — because they all hang off the plan contract and never had to know the geography got real. The towns run from **meccas** (Fitzroy, 160 shops) to the **thin tail** (12), at real retail density: the secondhand heroes keep their footfall while the street around them gets ~4× busier — texture, never a takeover. Venues bias to the retail heart. The tram only runs where it fronts a real high street (elsewhere it was honestly "a highway bus", so it's fenced). Everything stays **deterministic from the seed**, silent under `?noassets=1`, and **`?classic=1` still boots the exact v2 town, byte-identical forever** (`0x3fa36874`). **The tour:** [`docs/shots/release_v4/contact.html`](docs/shots/release_v4/) — the Fremantle hero, the Fitzroy mecca, the thin tail (labelled honestly: the floor is shops-per-street-metre, and Toowoomba is below it), the synthetic control, and the `?classic=1` covenant made visible. Charter: [`docs/V4_REAL_MAP.md`](docs/V4_REAL_MAP.md) · data + licences (ODbL): [`web/assets/towns/SOURCES.md`](web/assets/towns/SOURCES.md). ## v5.0-alpha — THE FIRST REAL CRATE: the shops get real too **v4 made the towns real. v5 makes the shops real** — and the first one is standing. Boot `?plansrc=osm&town=redhill_godverse&stock=real`, walk down **Musgrave Road** into **Monster Robot Party** (147 Musgrave Rd, Red Hill, Brisbane — a real record shop), and riffle its crate. The records in it are **that shop's real stock**: 120 real records off its real point-of-sale, real covers, real titles, real prices. Buy one and the wallet debits what the sticker says. It's gone for the session and back next boot — because until the live tier lands, the crate is a **file**, not a shop's ledger. **Stock is a data tier now**, exactly like assets — each rung fully playable, each degrading softly to the one below, so the game never depends on a server to be a game: | tier | what | deterministic? | |---|---|---| | **0 · parody** | seeded canvas sleeves (v1) — any boot, always works | yes — the QA baseline | | **1 · static real** | per-shop atlases baked from the shop's own POS | yes — atlases are files | | 2 · live GODVERSE | live stock, sold-means-gone *(design ratified, not built)* | no — by design | **The offline law:** the server enriches, never gates. `?noassets=1` still fetches **zero bytes** and plays silent-and-fine; a shop whose atlas is missing falls soft to parody rather than borrowing someone else's crate; and **`?classic=1` still boots the exact v2 town, byte-identical forever** (`0x3fa36874`). The world stays seeded and golden-gated — only the *stock inside it* is data. **Provenance:** item photos are each shop's own product shots, 🟢 in-house — **flagged before any public or commercial release**, and every atlas carries that flag where the QA gate must print it. Real titles and artists are an in-house amendment to the no-real-trademarks law, on the same footing. **Nothing an in-game purchase does touches real inventory** — v5.0 ships sell-sandboxed by ruling; a real reservation tier is chartered for v5.x, not assumed. Charter: [`docs/V5_REAL_SHOP.md`](docs/V5_REAL_SHOP.md) · economy design: [`docs/G3_ECONOMY_DESIGN.md`](docs/G3_ECONOMY_DESIGN.md). | `?seed=N` | `20261990` | town seed | **Release tour:** [`docs/shots/release_v3_1/contact.html`](docs/shots/release_v3_1/) — the money shots on the default boot, plus a `?classic=1` before/after. **QA:** `tools/qa.sh --strict` (classic regression + default-boot gate + smokes + no-giants) `--soak` (the full-week soak); the seated drummer passes the no-giants gate at the seated stature band `[0.9,1.5] m`.