Verified fresh Chromium across 9 town configs (synthetic, 3 fixtures, 5 real). TRAM RULING (ledger #3): spinePolyline now walks from each main-subgraph dead-end, greedily taking the unused main edge fronting the most shops (lot.frontEdge), keeping the best-scoring path (ties -> longer). SYNTHETIC + FIXTURES BYTE-IDENTICAL by construction AND measurement (a synthetic main node has <=2 mains so there's never a choice; its two dead-ends tie exactly and strict > keeps the pre-R20 start). Old vs new logic on the same plan = identical polylines (Boolarra: 7 pts, 6 edges, 810m, 172 shops, 2 stops). The alpha tram fronted 0 shops on 4 of 5 real towns (it was a highway bus). New: katoomba 0->3, newtown 0->8, fremantle 0->2, bendigo 0->1, castlemaine 1->6. Where the best main chain still fronts <5 shops the tram is FENCED per-town (Fable's fallback). RUNS: synthetic, all fixtures, newtown (8 shops/1924m/16 stops), castlemaine (6/2184m/10 stops). FENCED: katoomba (3), fremantle (2), bendigo (1) -- highway, not high street. The fence keys off the REAL-ROADS signal (edges>200), NEVER shop count alone: the marched fixtures front 0 shops on their mains (bare spine, avenues carry shops) so a naive fence would have killed their v2 tram. routeInfo{fenced,reason,shopsFronted,routeMetres} exposed for F's smoke. BUG FOUND+FIXED: the stop projection accepted every shelter in the town with no distance gate -- harmless on synthetic (2 shelters on the spine), nonsense on real graphs: Newtown made 149 phantom stops (~9 min of dwell at points the tram never passes). Added a 30m gate (a shelter sits halfRoad+1.7 <=~16m off its centreline, so genuine stops survive): newtown 149->16, castlemaine 37->10; synthetic 2 / fixtures 3 unchanged. GROUND POLISH (ledger #4): measured, NO fix needed. Real Katoomba has 325 deg-3+ junctions, 28 under 30 deg, worst 4.3 deg (near-parallel). At grazing eye level the worst reads clean -- no z-fight, no gaps. Layers are already y-separated (road 0.0/foot 0.02/kerb 0.06) and road-on-road overlap is invisible by design (OVERLAP=1.5 overruns nodes on purpose; one skin, one y, one merged draw on uniform asphalt). Cosmetic only: footpath/kerb double up on the sliver between <10deg near-parallel roads -- v4.0 candidate, not a defect. Selector polish (ledger #5): BLOCKED -- E's towns/index.json not landed (build_towns.py WIP); REAL_TOWNS hardcode stands, one-line swap when E lands. -> A (finding, measured): only a slice of shops front a MAIN edge on real towns -- katoomba 7/69, fremantle 4/79, bendigo 7/35, newtown 32/64, castlemaine 11/22. The real high street classifies as 'side', which is WHY three towns need the fence. If the main/side classifier promoted shop-dense ways to main, the fence would lift. Note onMain also gates gigs.js pickVenues pub placement. -> F: tram verdict above; selector gating (HUD vs ?dbg) is your call, one line either way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| pipeline | ||
| priorwork | ||
| tools | ||
| web | ||
| .gitignore | ||
| B-progress.md | ||
| C-progress.md | ||
| D-progress.md | ||
| E-progress.md | ||
| F-progress.md | ||
| README.md | ||
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 | seed → CityPlan data (streets, blocks, lots, shops) + 2D map | js/citygen, js/core/registry.js, map.html |
| B — STREETSCAPE | chunk-streamed instanced 3D town + game shell | index.html, js/world |
| C — INTERIORS | every door opens: seeded themed shop interiors | js/interiors, interior_test.html |
| D — CITIZENS | rigged-near / impostor-far NPCs + shopkeepers | js/citizens, citizens_test.html, models/ |
| E — ASSETS | audit ultra's library, normalize GLBs, skins, manifest | pipeline/, assets/ |
| F — INTEGRATION | 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=<key> |
— | 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). v4.0-alpha REAL ROADS: a cache with roads[] (schema v2) builds the CityPlan street graph from the town's actual OSM streets — walk Katoomba's real main street; without roads[] a cache marches onto synthetic avenues |
?seed=N |
20261990 |
town seed |
Release tour: docs/shots/release_v3_1/contact.html — 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.