From 9416d0ae080934da7256ef669f7f4283399d53ff Mon Sep 17 00:00:00 2001 From: m3ultra Date: Tue, 14 Jul 2026 18:15:42 +1000 Subject: [PATCH] Lane F R4: wire citizens.setExposure (F1) + permittedMax storeys-checker fix (F2) F1: pass renderer.toneMappingExposure into CitizenSim each street frame so mid-tier impostors track the animated day/night exposure (per LANE_D_NOTES). Verified: boots clean, 0 console errors, exposure animates night 1.18 <-> midday 0.98. F2: scope the consistency storeys check to permittedMax = max>=2 ? min(max+1,3) : max (Lane A's corner-anchor rule, LANE_A_NOTES R3). Consistency now 0 warnings across 6 seeds; a single-storey type at >1 still trips. qa.sh --strict GREEN 4/4. Co-Authored-By: Claude Opus 4.8 --- tools/qa/consistency_check.mjs | 14 ++++++++++---- web/index.html | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/qa/consistency_check.mjs b/tools/qa/consistency_check.mjs index 5ef62a5..3982b03 100644 --- a/tools/qa/consistency_check.mjs +++ b/tools/qa/consistency_check.mjs @@ -83,15 +83,21 @@ for (const seed of seeds) { if (h[0] < 0 || h[0] > 24 || h[1] < 0 || h[1] > 24) fail(`shop ${s.id}: hours ${JSON.stringify(h)} out of range`); if (h[1] >= 22) lateCount++; // Lane A encodes hours as simple 24h ints (open=2 ? min(registryMax+1, 3) : registryMax. Anything outside + // [registryMin, permittedMax] is REAL drift — a single-storey type at >1 still trips. const sr = def.storeys; - if (Array.isArray(sr) && sr.length === 2 && (s.storeys < sr[0] || s.storeys > sr[1])) storeysOff++; + if (Array.isArray(sr) && sr.length === 2) { + const permittedMax = sr[1] >= 2 ? Math.min(sr[1] + 1, 3) : sr[1]; + if (s.storeys < sr[0] || s.storeys > permittedMax) storeysOff++; + } if (/['’]s['’]s\b/.test(s.name || '')) dblApostrophe++; } if (skinOffPool) warn(`${skinOffPool} shop(s) use a facadeSkin outside their registry type pool ` + `(ok if Lane A widened pools intentionally; else a plan↔registry drift)`); - if (storeysOff) warn(`${storeysOff} shop(s) have storeys outside their registry type range ` - + `(ok if these are the CITY_SPEC "occasional 3-storey corner anchor"; else a plan↔registry drift — ask Lane A)`); + if (storeysOff) warn(`${storeysOff} shop(s) have storeys outside [registryMin, permittedMax] ` + + `(corner-anchor 3-storey is allowed for tall types; this is REAL plan↔registry drift — Lane A)`); if (dblApostrophe) warn(`${dblApostrophe} shop name(s) have a doubled possessive ("X's's") — Lane A names.js`); if (lateCount === 0) warn(`no open-late shop (close ≥22:00) — LANE_F §3.5 wants at least one`); else ok(`${lateCount} open-late shop(s) (close ≥22:00) — §3.5 satisfied`); diff --git a/web/index.html b/web/index.html index bcc7c86..29e6761 100644 --- a/web/index.html +++ b/web/index.html @@ -242,6 +242,7 @@ function frame() { lighting.update(dt, player.position); const clk = lighting.getClock(); citizens.setTimeOfDay((clk.seg + clk.frac) / 6); // [Lane F] 0..1 day fraction → density (busy midday, empty at night) + citizens.setExposure(renderer.toneMappingExposure); // [Lane F R4] keep mid impostors matched to the animated day/night exposure (LANE_D_NOTES) citizens.update(dt); // [Lane F] reads camera pos internally for LOD tiers hud.tickToast(dt); hud.update(dt, { clock: clk, chunks: chunks.count }); // reads prev frame's total