Lane F R15 (v3.0): retire the R12 single-venue alias (ledger #2)
The compat alias delegating to the primary venue is gone — every read is now keyed by venueShopId. B swept the cross-lane readers in R14; this migrates F's own tools + the spec, one atomic commit. - gig_state.js: delete the alpha-shape alias block + the now-unused primaryId/P; update() just ticks all latches - flags_check.py + gig_shot.py: .venueShopId->venueShopIds[0], .state->stateOf(id), .cover->coverOf(id), .bandName->bandNameOf(id) - CITY_SPEC \S v3: delete the two alias sentences, add the omitted nightOf(id) to the accessor list selfcheck 14264/14264 green (base 0x3fa36874, gig 0x4f4a549d); flags_off + smoke_gigs green, 0 warn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2446ae3a5d
commit
146b5f3d2d
@ -272,9 +272,8 @@ gigs: [ { gigId, venueShopId, bandName, genreKey, night, startSeg, endSeg, cover
|
||||
(startSeg−1 = DUSK) → on → done`. (Day-segment→hour map is `lighting.js`: `0 DAWN … 5 NIGHT`.)
|
||||
**Runtime surface** F publishes on `window.PROCITY.gigs` (B's `venue.js`/`audio.js` and D's `setGig` read
|
||||
it): `.byVenue → { [venueShopId]: 'quiet'|'doors'|'on'|'done' }` plus per-venue getters
|
||||
`stateOf/onOf/openOf/gigOf/coverOf/bandNameOf/paidOf(id)` + `markPaidOf(id)` and `.venueShopIds`. The R12
|
||||
single-venue alias (`.state/.on/.open/.venueShopId/.cover/.paid/…` → the **primary** venue = `gigs[0]`'s)
|
||||
is kept for **one round** so mid-round single-venue callers don't break; it retires next round.
|
||||
`stateOf/onOf/openOf/gigOf/coverOf/bandNameOf/nightOf/paidOf(id)` + `markPaidOf(id)` and `.venueShopIds`.
|
||||
Every read is keyed by `venueShopId` — there is no scalar alias (the R12 single-venue alias retired in R15).
|
||||
- `cover` — integer in `{0} ∪ [2,10]`, **~half free town-wide, skewed by kind**: pub 50% free / `$2–10`,
|
||||
RSL 45% free / `$2–5` (cheap — the bistro pays the bills), band_room 70% free / `$2–6` (a hat on a
|
||||
milk crate). F debits the wallet **per venue** at each door; a night at two venues is two covers.
|
||||
|
||||
@ -630,9 +630,10 @@ def smoke_gigs(p):
|
||||
pg.keyboard.press('Space') # gesture → AudioContext unlock (headless-safe, no pointer-lock)
|
||||
night = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG, THREE = P.THREE, v = new THREE.Vector3();
|
||||
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
|
||||
D.setSegment(5); // NIGHT — the gig is on
|
||||
const state = P.gigs.state;
|
||||
D.enterShop(P.gigs.venueShopId);
|
||||
const state = P.gigs.stateOf(id);
|
||||
D.enterShop(id);
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
const room = P.interiorMode.current;
|
||||
if (!room) return { entered: false, state };
|
||||
@ -777,7 +778,7 @@ def smoke_gigs(p):
|
||||
q = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG;
|
||||
if (typeof P.queueCountOf !== 'function') return { wired: false };
|
||||
const id = P.gigs.venueShopId;
|
||||
const id = P.gigs.venueShopIds[0];
|
||||
D.setSegment(4); await new Promise(r => setTimeout(r, 700)); // DUSK — doors open, the line forms
|
||||
const atDoors = P.queueCountOf(id) | 0;
|
||||
D.setSegment(0); await new Promise(r => setTimeout(r, 700)); // DAWN — closing time ('done'), line gone
|
||||
@ -806,7 +807,7 @@ def smoke_gigs(p):
|
||||
st = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG;
|
||||
D.setSegment(5); // NIGHT — frontage lit, posters up
|
||||
const id = P.gigs.venueShopId, shop = (P.plan.shops || []).find(s => s.id === id);
|
||||
const id = P.gigs.venueShopIds[0], shop = (P.plan.shops || []).find(s => s.id === id);
|
||||
const lot = shop && (P.plan.lots || []).find(l => l.id === shop.lot);
|
||||
if (lot) {
|
||||
const fx = Math.sin(lot.ry || 0), fz = Math.cos(lot.ry || 0);
|
||||
@ -834,15 +835,16 @@ def smoke_gigs(p):
|
||||
boot(pg, 'gigs=1')
|
||||
paid = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG;
|
||||
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
|
||||
D.setSegment(5);
|
||||
const cover = P.gigs.cover;
|
||||
const cover = P.gigs.coverOf(id);
|
||||
if (!cover) return { skip: true, cover };
|
||||
const before = P.wallet.cash();
|
||||
D.enterShop(P.gigs.venueShopId); await new Promise(r => setTimeout(r, 350));
|
||||
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
|
||||
const inRoom1 = !!P.interiorMode.current, afterPay = P.wallet.cash();
|
||||
D.exitShop(); await new Promise(r => setTimeout(r, 250));
|
||||
const mid = P.wallet.cash();
|
||||
D.enterShop(P.gigs.venueShopId); await new Promise(r => setTimeout(r, 350));
|
||||
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
|
||||
const inRoom2 = !!P.interiorMode.current, afterReentry = P.wallet.cash();
|
||||
D.exitShop(); await new Promise(r => setTimeout(r, 250));
|
||||
// now go broke and try again on the SAME night (stamp already paid → still free), then roll the
|
||||
@ -850,7 +852,7 @@ def smoke_gigs(p):
|
||||
P.wallet.buy({ title: 'qa: drain', price: P.wallet.cash() });
|
||||
D.setSegment(1); D.setSegment(5); // NIGHT → MORNING → NIGHT = a new night, cover due
|
||||
const brokeCash = P.wallet.cash();
|
||||
D.enterShop(P.gigs.venueShopId); await new Promise(r => setTimeout(r, 350));
|
||||
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
|
||||
const inRoomBroke = !!P.interiorMode.current;
|
||||
return { cover, before, afterPay, inRoom1, mid, afterReentry, inRoom2, brokeCash, inRoomBroke,
|
||||
inv: P.wallet.inventory().map(i => i.title) };
|
||||
@ -882,10 +884,11 @@ def smoke_gigs(p):
|
||||
pg.evaluate("() => { const o=document.getElementById('pc-start'); if(o) o.style.display='none'; }")
|
||||
free = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG;
|
||||
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
|
||||
D.setSegment(5);
|
||||
const cover = P.gigs.cover, before = P.wallet.cash();
|
||||
D.enterShop(P.gigs.venueShopId); await new Promise(r => setTimeout(r, 350));
|
||||
return { cover, before, after: P.wallet.cash(), inRoom: !!P.interiorMode.current, band: P.gigs.bandName };
|
||||
const cover = P.gigs.coverOf(id), before = P.wallet.cash();
|
||||
D.enterShop(id); await new Promise(r => setTimeout(r, 350));
|
||||
return { cover, before, after: P.wallet.cash(), inRoom: !!P.interiorMode.current, band: P.gigs.bandNameOf(id) };
|
||||
}""")
|
||||
if free['cover'] == 0 and free['inRoom'] and free['after'] == free['before']:
|
||||
OK(f"cover: free night (seed {FREE_GIG_SEED}, {free['band']}) → walked straight in, wallet untouched (${free['after']})")
|
||||
@ -906,12 +909,13 @@ def smoke_gigs(p):
|
||||
pg.keyboard.press('Space')
|
||||
na = pg.evaluate("""async () => {
|
||||
const P = window.PROCITY, D = window.DBG;
|
||||
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — the primary venue by id
|
||||
D.setSegment(5);
|
||||
D.enterShop(P.gigs.venueShopId); await new Promise(r => setTimeout(r, 450));
|
||||
D.enterShop(id); await new Promise(r => setTimeout(r, 450));
|
||||
const info = P.interiorMode.crewInfo;
|
||||
return { state: P.gigs.state, inRoom: !!P.interiorMode.current, band: info && info.band,
|
||||
return { state: P.gigs.stateOf(id), inRoom: !!P.interiorMode.current, band: info && info.band,
|
||||
crowd: info && info.crowd, gigs: (P.plan.gigs || []).length,
|
||||
bandName: P.gigs.bandName, audio: P.audio && P.audio.state.manifest };
|
||||
bandName: P.gigs.bandNameOf(id), audio: P.audio && P.audio.state.manifest };
|
||||
}""")
|
||||
if na['inRoom'] and na['band'] == 4 and na['crowd'] > 0:
|
||||
OK(f"?noassets=1&gigs=1: the gig still happens (4-piece band, crowd {na['crowd']}, placeholders)")
|
||||
|
||||
@ -46,11 +46,12 @@ def ensure_server():
|
||||
INTERIOR_JS = r"""
|
||||
async () => {
|
||||
const P = window.PROCITY, D = window.DBG, THREE = P.THREE;
|
||||
const id = P.gigs.venueShopIds[0]; // R15: no scalar alias — read the primary venue by id
|
||||
D.setSegment(5); // NIGHT — the gig is on
|
||||
D.enterShop(P.gigs.venueShopId);
|
||||
D.enterShop(id);
|
||||
await new Promise(r => setTimeout(r, 900)); // room build + crew spawn + rig clips settle
|
||||
const room = P.interiorMode.current;
|
||||
if (!room) return { ok: false, why: 'not in the venue', state: P.gigs.state };
|
||||
if (!room) return { ok: false, why: 'not in the venue', state: P.gigs.stateOf(id) };
|
||||
const st = room.stage;
|
||||
P.camera.position.set(st.x + 0.2, 1.6, room.dims.D / 2 - 1.2);
|
||||
P.camera.lookAt(new THREE.Vector3(st.x, 1.35, st.z));
|
||||
@ -58,8 +59,8 @@ async () => {
|
||||
P.renderer.render(P.interiorMode.scene, P.camera);
|
||||
const crew = P.interiorMode.crew, mem = crew ? crew.members : [];
|
||||
const band = mem.filter(m => m.part === 'band');
|
||||
return { ok: true, venue: (P.plan.shops.find(s => s.id === P.gigs.venueShopId) || {}).name,
|
||||
band: P.gigs.bandName, state: P.gigs.state, cover: P.gigs.cover, crew: P.interiorMode.crewInfo,
|
||||
return { ok: true, venue: (P.plan.shops.find(s => s.id === id) || {}).name,
|
||||
band: P.gigs.bandNameOf(id), state: P.gigs.stateOf(id), cover: P.gigs.coverOf(id), crew: P.interiorMode.crewInfo,
|
||||
roles: band.map(m => m.role),
|
||||
dancers: mem.filter(m => m.part === 'crowd' && m.dance).length,
|
||||
draws: P.renderer.info.render.calls, tris: P.renderer.info.render.triangles };
|
||||
@ -76,9 +77,9 @@ async () => {
|
||||
if (P.mode === 'interior') { D.exitShop(); await new Promise(r => setTimeout(r, 350)); }
|
||||
D.shot('venue_night'); // B's bookmark: seg 5 + pose at the pub frontage
|
||||
await new Promise(r => setTimeout(r, 1600)); // frontage ramp + queue spawn + chunk/poster stream settle
|
||||
const id = P.gigs.venueShopId;
|
||||
const id = P.gigs.venueShopIds[0];
|
||||
return { ok: P.mode === 'street', venue: (P.plan.shops.find(s => s.id === id) || {}).name,
|
||||
state: P.gigs.stateOf ? P.gigs.stateOf(id) : P.gigs.state,
|
||||
state: P.gigs.stateOf(id),
|
||||
queue: P.queueCountOf ? P.queueCountOf(id) : null,
|
||||
posters: (P.plan.posters || []).length, venues: (P.gigs.venueShopIds || []).length,
|
||||
draws: P.renderer.info.render.calls, tris: P.renderer.info.render.triangles };
|
||||
|
||||
@ -97,10 +97,9 @@ function createVenueLatch(gig, lighting) {
|
||||
|
||||
export function createGigState({ plan, lighting }) {
|
||||
const gigs = (plan && plan.gigs) || [];
|
||||
// The district's venues, in first-seen (gigId) order. Primary = gigs[0].venueShopId — the alpha's
|
||||
// "tonight", kept as the compat alias so B's mid-round single-venue callers still light the right pub.
|
||||
// The district's venues, in first-seen (gigId) order. Every read is keyed by venueShopId — the R12
|
||||
// single-venue alias retired in R15 (B swept its cross-lane readers in R14; F migrated its own tools).
|
||||
const venueIds = [...new Set(gigs.map((g) => g.venueShopId))];
|
||||
const primaryId = gigs.length ? gigs[0].venueShopId : null;
|
||||
|
||||
// one latch per venue, keyed by its night-0 gig (null → dark tonight → 'quiet')
|
||||
const latch = new Map();
|
||||
@ -117,7 +116,6 @@ export function createGigState({ plan, lighting }) {
|
||||
tickAll(); // settle every latch on the boot segment
|
||||
|
||||
const L = (id) => latch.get(id) || null;
|
||||
const P = () => L(primaryId);
|
||||
|
||||
return {
|
||||
// ── per-venue API (R13 district) ──────────────────────────────────────────────────────────────
|
||||
@ -134,21 +132,8 @@ export function createGigState({ plan, lighting }) {
|
||||
// the map B's venue.update() and audio.js consume: a fresh { [venueShopId]: 'quiet'|'doors'|'on'|'done' }.
|
||||
// Called per street frame over a handful of venues — cheap.
|
||||
get byVenue() { const o = {}; for (const [id, l] of latch) o[id] = l.tick(); return o; },
|
||||
// shell calls this once per street frame — ticks every latch (so B/D read fresh state) and returns the
|
||||
// primary state for the alpha-compat caller.
|
||||
update() { tickAll(); return P() ? P().state : 'quiet'; },
|
||||
|
||||
// ── alpha-shape alias → the PRIMARY venue (kept for one round; B reads it mid-round) ─────────────
|
||||
get state() { const p = P(); return p ? p.state : 'quiet'; },
|
||||
get on() { const p = P(); return p ? p.on : false; },
|
||||
get open() { const p = P(); return p ? p.open : false; },
|
||||
get gig() { const p = P(); return p ? p.gig : null; },
|
||||
get venueShopId() { return primaryId; },
|
||||
get bandName() { const p = P(); return p ? p.bandName : null; },
|
||||
get cover() { const p = P(); return p ? p.cover : 0; },
|
||||
get night() { const p = P(); return p ? p.night : 0; },
|
||||
get paid() { const p = P(); return p ? p.paid : false; },
|
||||
markPaid() { const p = P(); if (p) p.markPaid(); },
|
||||
// shell calls this once per street frame — ticks every latch so B/D read fresh per-venue state.
|
||||
update() { tickAll(); },
|
||||
|
||||
dispose() { if (typeof window !== 'undefined') window.removeEventListener('procity:segment', onSegment); },
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user