From 42b11e717d1604dc41a083678675f9b8a73a7ea6 Mon Sep 17 00:00:00 2001 From: m3ultra Date: Thu, 16 Jul 2026 12:16:38 +1000 Subject: [PATCH] Lane C R16 (v3.1): stage.backline[] amp unification, drop primitive ampStacks (ledger #5, C half) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C exposes room.stage.backline[] — 2 seeded up-stage amp slots {x,z,ry,y} (pure data) — and drops its primitive ampStacks from placeStage. Lane D plants E's guitar_amp GLB at backline[0] (D's primitive fallback under ?noassets): one unified backline, GLB when present. - backline[0] = (stage.x + stage.w*0.36, deckY, stage.z - stage.d*0.22) == D's R15-verified amp pose exactly, so plumbing D's plant to backline[0] moves nothing on screen. backline[1] = spare stage-left mirror. ry=pi (audience-facing), y=deckY. - Verified fresh (seed 20261990): slots up-stage, on-deck, clear of all 4 bandPoses (nearest-bass 0.87 pub / 0.70 band_room / 0.92 rsl) and all watchPoints (>=1.95 m); determinism byte-identical; 0 leftover ampStack groups; drawSweep venues 122/120/131 (was 126/124/135, -~4 draws each) <=350. - Determinism/golden-safe: dropping the amp loop only drops draws on the 'stage' sub-stream (nothing downstream reads it); placement snapshot excludes ampStacks; interiors aren't hashed; ?classic has no venues. Disjoint from F's flip. LANE_C_PUB §3 amended (v3.1 marker). C->D handshake: backline shape live. Verify with D once the GLB is wired at backline[0]. Co-Authored-By: Claude Opus 4.8 --- C-progress.md | 30 +++++++++++++++++++++++++++++- docs/LANES/LANE_C_PUB.md | 27 +++++++++++++++++---------- web/js/interiors/interiors.js | 12 +++++++----- web/js/interiors/layout.js | 30 +++++++++++++++++++----------- 4 files changed, 72 insertions(+), 27 deletions(-) diff --git a/C-progress.md b/C-progress.md index 537f409..f7b9ec4 100644 --- a/C-progress.md +++ b/C-progress.md @@ -3,7 +3,35 @@ *Status: **v1 complete & verified**. Standalone interiors library + test page. Every shop door opens into a unique, seeded, themed interior, generated on demand in ~4ms, byte-identical every revisit.* -Last updated: 2026-07-16 (round 15) · owner: PROCITY-C · reviewer: Fable +Last updated: 2026-07-16 (round 16) · owner: PROCITY-C · reviewer: Fable + +--- + +## Update 2026-07-16 (round 16, v3.1 THE FLIP) — the backline handoff (C's half of ledger #5) + +R16 §Lane C (ledger #5, C's half): unify the amp. **`room.stage.backline[]`** now exposes 2 seeded up-stage +amp slots `{x,z,ry,y}` (pure data, no meshes), and C's **primitive ampStacks are dropped** from `placeStage` — +Lane D plants E's `guitar_amp` GLB at `backline[0]` (its own primitive fallback under `?noassets`), one amp +system. Shape published in [LANE_C_PUB.md](docs/LANES/LANE_C_PUB.md) §3 (v3.1 amendment marker; rest of the +frozen doc unchanged). **C→D handshake: backline shape is live.** + +- `backline[0]` = stage-right `(stage.x + stage.w·0.36, deckY, stage.z − stage.d·0.22)` == D's R15-verified + amp pose exactly, so wiring D's plant to it moves **nothing** on screen. `backline[1]` = stage-left spare + (mirror). ry=π, y=deckY. +- **Verified fresh (port-isolated, seed 20261990):** both slots up-stage, on-deck, clear of all 4 bandPoses + (nearest-bass **0.87 m pub / 0.70 m band_room / 0.92 m rsl**) and all watchPoints (≥1.95 m); determinism + byte-identical (placement + stage + watchPoints); 0 leftover ampStack prop groups; drawSweep glb-off venues + **pub 122 · band_room 120 · rsl 131** (was 126/124/135 — the dropped primitives shaved ~4 draws each), worst + 161 (opshop/hall, unchanged). ≤350 ✓. +- **Determinism/golden safety:** removing the ampStack loop only drops draws on the `stage` rng sub-stream, + which nothing downstream reads; the `placement` snapshot excludes ampStacks (never tracked), so it's + byte-identical. Interiors aren't hashed into any golden; the `?classic=1` boot has no venues → untouched. +- Disjoint from F's flip (interiors don't read flags) — landed early so D isn't blocked. **Verify with D + once wired** (ledger #5 D-half): re-render each archetype, confirm the GLB at `backline[0]` doesn't + interpenetrate (pending D's R16 commit). + +**Code:** `layout.js` placeStage (backline[] + drop the amp loop), `interiors.js` API doc. `theme.js` untouched +(recipes carry no ampStack fitting — the primitives were placeStage-planted, not recipe fittings). --- diff --git a/docs/LANES/LANE_C_PUB.md b/docs/LANES/LANE_C_PUB.md index ee08312..7af1596 100644 --- a/docs/LANES/LANE_C_PUB.md +++ b/docs/LANES/LANE_C_PUB.md @@ -2,6 +2,8 @@ > **v3.0-FROZEN** · 2026-07-16 · durable contract for Lane D + Lane F. Describes the *shipped* state; > changes require a version bump (CITY_SPEC amendment law). Verified R13 (build) + R14 (release audit). +> **v3.1 amendment (2026-07-16):** §3 becomes the `stage.backline[]` contract (ledger #5 — the amp +> unification); C dropped its primitive ampStacks. Everything else in this doc is unchanged and stands. *The district ships **three** venue archetypes behind `?gigs=1` — `pub`, `band_room`, `rsl`. Lane A converts a chosen shop to a venue kind in place (`shop.type = kind`); C keys the interior recipe off @@ -97,17 +99,22 @@ No `opts.gig` → a normal interior: `stage` + `watchPoints` are still returned `gigKey`** (room-tone + maybe the seeded radio `musicKey`), and D spawns no crowd. A seeded dark night reads true — the venue is just a quiet pub/club. -## 3. `guitar_amp` placement (v3.0 — RESOLVED R15) +## 3. `stage.backline[]` — the amp slots (v3.1, ledger #5) -**Resolved.** D plants E's `guitar_amp` GLB (`band.js _addBackline`) up-stage-right at **`(stage.x + -stage.w·0.36, deckY, stage.z − stage.d·0.22)`** — C's corrected pose (ledger #4). It's on-deck, up-stage of -the front line, and flanks C's own primitive ampStack (stage-left) as a clean backline. **Verified R15 (fresh -context, seed 20261990):** clearance to the bass rig **0.87 m (pub) / 0.92 m (rsl)** — no interpenetration -(the original R14 pose `stage.z + 0.15` was down-stage, 0.29 m off the bass, and clipped it); amp↔primitive -4.4 m (pub) / 3.5 m (rsl); on-deck both. C's `placeStage` plants **1–2 primitive `ampStack` fittings** at -`(±stage.w·0.28, deckY, stage.z − stage.d·0.18)` as the asset-free backline fallback (no GLB slot, so they -stay primitive). *v3.1:* full unification — C exposes a `stage.backline[]` slot list that D's GLB fills, -replacing the primitive — stays deferred (no new contract fields in v3.0). +`room.stage.backline = [ { x, z, ry, y } ×2 ]` — seeded up-stage amp/spare-cab poses, **pure data** (C plants +no amp meshes as of v3.1). This is the unified backline: **Lane D plants E's `guitar_amp` GLB here** (its own +primitive fallback under `?noassets`) — one amp system, GLB when present. C's old primitive ampStacks are +**dropped** from all three venue recipes. +- **slot[0]** = stage-right (the primary amp) at `(stage.x + stage.w·0.36, deckY, stage.z − stage.d·0.22)` — + identical to D's R15-verified plant, so wiring D's amp to `backline[0]` moves nothing on screen. + **slot[1]** = stage-left spare cab (mirror). `ry = π` faces the audience (+Z, the §0 convention); `y = deckY`. +- **Guarantees** (verified fresh, seed 20261990, pub/band_room/rsl): every slot is **up-stage of the front + line**, flanks the drum riser, **clear of all 4 bandPoses** (nearest-bass clearance **0.87 m pub / 0.70 m + band_room / 0.92 m rsl**) and **all watchPoints** (≥1.95 m), and **on-deck** (within the deck footprint). + Deterministic per `shop.seed`. **Lane D:** verify no interpenetration once wired (C's R15 clearance discipline). +- **Draw impact:** dropping C's primitives shaved ~4 draws off each empty venue shell (drawSweep glb-off: + pub 122 · band_room 120 · rsl 131 ≤350); D's GLB/primitive at `backline[0]` adds it back on the gig night. +- *v3.2 (parked):* the drummer sit clip, instrument LOD, and C's `audioEmitter` idea. ## 4. Budgets (measured R14, seed 20261990, fresh context) - **Empty shells** (`drawSweep` glb-off): band_room **124** · pub **126** · rsl **135** — town-wide worst diff --git a/web/js/interiors/interiors.js b/web/js/interiors/interiors.js index 036a213..d2e41a1 100644 --- a/web/js/interiors/interiors.js +++ b/web/js/interiors/interiors.js @@ -25,10 +25,12 @@ // watchPoints: [ {x,z,ry,dance,slotIndex} ], // VENUE (R12/13, else []) — audience poses facing the stage; // // dance ~1/3 true. Cap by kind: pub/band_room 8, rsl 12. Same // // frame + ry convention as counter.stand (rig-front = local −Z). -// stage: { x,z,w,d,deckY,riserY,frontZ,bandPoses }, // VENUE (R12/13, else null) — the band stage. bandPoses[4] -// // (R13): front-line trio (guitar/vocal/bass) on the deck + a drummer -// // (role 'drums', seated) up-stage on the riser. Each pose carries y -// // (front line = deckY, drummer = riserY). ry=π faces the audience. +// stage: { x,z,w,d,deckY,riserY,frontZ,bandPoses,backline }, // VENUE (else null) — the band stage. bandPoses[4]: +// // front-line trio (guitar/vocal/bass) on the deck + a drummer (role +// // 'drums', seated) up-stage on the riser. Each pose carries y (front +// // line = deckY, drummer = riserY). ry=π faces the audience. backline[] +// // (v3.1) = up-stage amp slots {x,z,ry,y} where Lane D plants the +// // guitar_amp GLB (primitive fallback under ?noassets). // dims: { W, D, H, archetype, type }, // room metrics // placement, // deterministic placement summary (deep-equal per seed) // pathOK, // door→counter connectivity held (always true; carved if needed) @@ -176,7 +178,7 @@ export function buildInterior(shop, THREE, opts) { counter: lay.counter, // { mesh, pose:{x,z,ry}, stand:{x,z,ry} } — Lane D keeper spawn pose browsePoints: lay.browsePoints, // [ {x,z,ry,atKind,slotIndex} ] 0..3 — Lane D browser rig poses (R9) watchPoints: lay.watchPoints || [], // venue: [ {x,z,ry,dance,slotIndex} ] audience poses, cap by kind (pub/band_room 8, rsl 12); else [] - stage: lay.stage || null, // venue: { x,z,w,d,deckY,riserY,frontZ,bandPoses[4] } (4-piece: trio + drummer on the riser); else null + stage: lay.stage || null, // venue: { x,z,w,d,deckY,riserY,frontZ,bandPoses[4],backline[] } (4-piece + up-stage amp slots); else null audio: audioFor(recipe.key, ctx, opts, norm.raw), // { musicKey, toneKey, gigKey? } — Lane E manifest.audio keys (R11/R12) dims: { ...dims, archetype, type: recipe.key }, recipe: { key: recipe.key, label: recipe.label, counterPos: recipe.counterPos, clutter: recipe.clutter }, diff --git a/web/js/interiors/layout.js b/web/js/interiors/layout.js index d438df9..9f12d24 100644 --- a/web/js/interiors/layout.js +++ b/web/js/interiors/layout.js @@ -209,10 +209,12 @@ function placeBrowsePoints(grid, placed, counterStand, spawnCell, r) { return pts; } -// ── VENUE stage (pub, round 12 · behind ?gigs=1) ────────────────────────────────────── -// Places a low stage centred on the back wall, PA cabinets flanking it on the floor, and 1–2 amps on the -// deck. Stage + PA go into `placed` at priority 8 so the door→counter path loop never pulls them. Returns -// the stage frame the crowd faces + 3 seeded band poses on the deck (Lane D drops the band trio in). +// ── VENUE stage (v3, ?gigs) ─────────────────────────────────────────────────────────── +// Places a low stage centred on the back wall + PA cabinets flanking it on the floor (both into `placed` at +// priority 8 so the door→counter path loop never pulls them). Returns the stage frame the crowd faces, the +// 4 band poses on the deck, and `backline[]` — up-stage amp slots (v3.1, ledger #5) where Lane D plants E's +// guitar_amp GLB. C no longer plants its own primitive amps: one unified backline, GLB when present, D's +// primitive fallback under ?noassets. Pure pose data; Lane D drops the rigs + amp in. function placeStage(ctx, grid, W, D, roomGroup, r, spec = {}) { const wf = spec.deckWidthFrac || 0.58, deckH = spec.deckH || 0.32; // per-kind (round 13): band_room const sw = Math.min(W - 1.4, Math.max(3.0, W * wf)), sd = spec.deckDepth || 2.0; // barely a riser, rsl bigger @@ -234,12 +236,7 @@ function placeStage(ctx, grid, W, D, roomGroup, r, spec = {}) { out.push({ fitting: pf, x: px, z: pz, ry: 0, hw: phw, hd: phd, rect: prect, priority: 8, kind: 'prop' }); } - const deckY = f.deckY || deckH; // 1–2 amps on the deck behind the band - for (let i = 0, n = 1 + (r() < 0.5 ? 1 : 0); i < n; i++) { - const af = buildFitting('ampStack', ctx, {}, r); - af.group.position.set(cx + (i === 0 ? -1 : 1) * sw * 0.28, deckY, cz - sd * 0.18); - roomGroup.add(af.group); - } + const deckY = f.deckY || deckH; // deck top surface (band + backline stand height) // 4-PIECE BAND (round 13, debt #3): front-line trio across the deck lip + a drummer up-stage on the // riser. ry=π faces the audience (+Z) under the rig-front=−Z house convention (see LANE_C_PUB.md). Each @@ -251,7 +248,18 @@ function placeStage(ctx, grid, W, D, roomGroup, r, spec = {}) { })); const drums = { x: round(cx), z: round(cz - sd / 4), ry: round(Math.PI), role: 'drums', y: riserY, seated: true }; const bandPoses = [...front, drums]; - return { placed: out, stage: { x: cx, z: cz, w: sw, d: sd, deckY, riserY, frontZ: round(cz + sd / 2), bandPoses } }; + + // BACKLINE (v3.1, ledger #5): up-stage amp/spare-cab slots — the unified backline that replaces C's old + // primitive ampStacks. Pure data (no meshes): Lane D plants E's guitar_amp GLB here (primitive fallback + // under ?noassets). Up-stage of the front line, flanking the drum riser, clear of every bandPose AND all + // watchPoints (which sit downstage of frontZ), on-deck. slot[0] = stage-right (primary), slot[1] = spare + // stage-left. ry=π faces the audience (+Z), same −Z convention as the band; y = deckY. Deterministic. + // slot[0] == D's R15-verified guitar_amp pose (stage.w·0.36, deckY, stage.z − stage.d·0.22, 0.87 m bass + // clearance), so plumbing D's plant to backline[0] moves nothing on screen. + const backline = [sw * 0.36, -sw * 0.36].map(bx => ({ + x: round(cx + bx), z: round(cz - sd * 0.22), ry: round(Math.PI), y: round(deckY), + })); + return { placed: out, stage: { x: cx, z: cz, w: sw, d: sd, deckY, riserY, frontZ: round(cz + sd / 2), bandPoses, backline } }; } // ── WATCH POINTS (venue) — seeded audience floor poses facing the stage ──