From 73694c97aa848127886add05306fc74f1cac8133 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Thu, 16 Jul 2026 17:38:59 +1000 Subject: [PATCH] Lane B round 19 (v4.0-alpha): in-game town selector (ledger #4, beta pull-forward, non-blocking) Shipped the town picker in hud.js (which already owns its DOM overlay -> NO index.html seam). A compact ` in the HUD overlay (top-left, styled to match, `pointer-events:auto` so it's usable +when the pointer is free — Esc first). **9 options**: `▸ Synthetic — procedural` + the 3 OSM fixtures +(`osmTownKeys()` minus `_real`: Melbourne/Katoomba/Silverton) + the 5 real-road caches (Katoomba, Newtown, +Fremantle, Bendigo, Castlemaine — hardcoded `REAL_TOWNS`, since there's no towns manifest yet; beta derives +it from an index). Selecting navigates: synthetic → clears `plansrc`/`town`; a town → `?plansrc=osm&town=`, +**preserving all other params** (seed, flags). Current town pre-selected; an unknown current town is added +so it still shows. +- **Verified end-to-end**: picked `katoomba_real` through the dropdown → it navigated + booted real Katoomba + (966 edges / 323 intersections / 3 venues), selector then reads "Katoomba · real roads". 0 page errors. +- Lives **inside `#pc-hud`**, so `hud.setVisible(false)` hides it for F's money shots (no extra work). + +**→ Lane F (B→F "selector seam"): there is NO seam** — the selector is self-contained in `hud.js` (already +constructed by `createHUD`). Nothing to wire in `index.html`. If you want it gated (e.g. `?dbg` only) that's +a one-line `setVisible`/guard call, your choice; I left it always-in-HUD as a modest debug-grade feature. + +### Corner-strips spot-check (R18 failure-item #3) — ground fills real intersections OK +Overhead look at a degree-4 real intersection (58 exist on the current graph): the merged road strips +**overlap-fill the junction** (carriageway continuous, lane markings + zebra crossing render, no gross gap). +Acceptable for the alpha; a finer acute-angle / z-fight pass is a beta detail. `ground.js` is road-agnostic +enough for real topology. + +### Budget note → F (measured against A's UNCOMMITTED WIP — caveat, not a final finding) +F's `BIG_CITY` one-liner (`e6fcb7b`) is live and working — real Katoomba now streams big-city (radius 2 + +shadows off), chunks **50 → 26**. **But** against the current working tree the venue block is still **~237k +tris** at night — and that tree carries **A's uncommitted R19 finalization WIP** (`plan_osm.js` modified; +the graph grew 799 → 966 edges / 323 intersections = junction-joining in, but the **far-island CULL not yet +applied**). Fable's ruling calls the cull "half of B's tris problem", so **the ≤200k gate needs F's fix AND +A's committed cull together** — re-measure the busiest real block after A pins (that's the deferred +"tris post-cull" re-measure; it genuinely waits on A's commit, per the R18/C lesson that the dev server +serves other lanes' uncommitted edits). Not filing 237k as a defect — it's mid-finalization. + +### The uncommitted `docs/shots/v4_alpha/katoomba_mainstreet.png` +Present in the tree since before this R19 session (not a deliberate save of mine — my audit screenshots are +browser-tool captures, uncommitted). Likely a prior B-audit capture. **F should reshoot deterministically +via the tour harness** for the official money shot regardless; treat its provenance as unverified. + +--- + ## Round 18 (Fable's ROUND18 → Lane B) — irregular-geometry audit (ledger #4, v4.0-alpha REAL ROADS) **The deliverable is the failure list.** Audited `katoomba_real` on E's real roads (schema v2, **484 OSM diff --git a/web/js/world/hud.js b/web/js/world/hud.js index eb2d819..fc93cb5 100644 --- a/web/js/world/hud.js +++ b/web/js/world/hud.js @@ -4,6 +4,11 @@ // door-click → `procity:enterShop` hand-off. Creates its own DOM overlay so index.html stays thin. import * as THREE from 'three'; +import { osmTownKeys } from '../citygen/index.js'; // R19 town selector: enumerate OSM fixtures + +// R19 (v4.0-alpha, beta pull-forward): the real-roads town caches under web/assets/towns/*.json. Hardcoded +// for the alpha — there is no towns manifest yet; beta derives this from an index file. New towns: add here. +const REAL_TOWNS = ['katoomba_real', 'newtown_real', 'fremantle_real', 'bendigo_real', 'castlemaine_real']; const CSS = ` #pc-hud{position:fixed;inset:0;pointer-events:none;font:13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;color:#f4efe6;z-index:10} @@ -15,6 +20,10 @@ const CSS = ` #pc-clock{font-size:15px;margin-bottom:3px} #pc-toast{position:absolute;left:50%;bottom:64px;transform:translateX(-50%);background:rgba(20,16,10,.9);padding:10px 18px;border-radius:10px;font-size:15px;opacity:0;transition:opacity .25s;text-shadow:0 1px 2px #000} #pc-help{position:absolute;left:12px;bottom:12px;background:rgba(20,16,10,.5);padding:7px 11px;border-radius:8px;opacity:.85} +#pc-townpick{position:absolute;left:10px;top:10px;pointer-events:auto;opacity:.8;transition:opacity .12s} +#pc-townpick:hover{opacity:1} +#pc-townpick select{background:rgba(20,16,10,.7);color:#f4efe6;border:1px solid rgba(255,215,94,.35);border-radius:8px;padding:5px 9px;font:12px/1.3 -apple-system,Segoe UI,Roboto,sans-serif;max-width:230px;cursor:pointer} +#pc-townpick select:focus{outline:1px solid rgba(255,215,94,.6)} `; export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }) { @@ -35,6 +44,7 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
chunks
+
WASD move · shift run · mouse look · click a door · [ ] time · P shot · Esc release
`; document.body.appendChild(root); const $ = (id) => root.querySelector('#' + id); @@ -43,6 +53,26 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop } $('pc-town').textContent = plan.name || '—'; $('pc-seed').textContent = plan.citySeed; + // ── town selector (R19, beta pull-forward) — synthetic + OSM fixtures + real-road caches. Navigates + // to the matching URL params, preserving everything else (seed, flags). Lives in the HUD overlay, so it + // hides with the HUD for money shots; usable when the pointer is free (Esc). No index.html seam. ── + (() => { + const sel = $('pc-town-select'); if (!sel) return; + const params = new URLSearchParams(location.search); + const cur = params.get('plansrc') === 'osm' ? (params.get('town') || '') : ''; + const cap = (k) => k.replace(/_real$/, '').replace(/(^|[\s_])\w/g, (c) => c.toUpperCase()).replace(/_/g, ' '); + const add = (key, label) => { const o = document.createElement('option'); o.value = key; o.textContent = label; if (key === cur) o.selected = true; sel.appendChild(o); }; + add('', '▸ Synthetic — procedural'); + for (const k of osmTownKeys().filter((k) => !k.endsWith('_real'))) add(k, `${cap(k)} · OSM fixture`); + for (const k of REAL_TOWNS) add(k, `${cap(k)} · real roads`); + if (cur && !Array.from(sel.options).some((o) => o.value === cur)) add(cur, `${cap(cur)} · (current)`); // unknown current town → still shown + sel.onchange = () => { + const v = sel.value, p = new URLSearchParams(location.search); + if (!v) { p.delete('plansrc'); p.delete('town'); } else { p.set('plansrc', 'osm'); p.set('town', v); } + location.search = p.toString(); // reload into the chosen town + }; + })(); + const ray = new THREE.Raycaster(); ray.far = 6; const centre = new THREE.Vector2(0, 0);