Lane B R33: THE WANTLIST in the crate panel · the arrival toast · the build-race fix the shot found
hud.js: THE WANTLIST section in the crate (non-empty only — no header over nothing): hunted
rows, ✕ stops hunting, refresh watches wants length so an open panel follows adds/clears.
THE FIX: R32's game surface BUILT only in street update() frames — a door entered fast after
boot had no crate/bar inside at all, and the interior tickGame() refreshed a UI that never
existed. Every gate idles on the street long enough to build, so green gates missed it; the
R33 money-shot script entered faster and exposed it. tickGame() now builds-or-refreshes in
ANY mode (shots are a detector, not decoration — filed in LANE_F_NOTES §33).
index.html: game.traveled at boot → '🚌 <town> — the ride cost a day. Day N' toast (save.js
already charged the fare and re-keyed the week before the shell read game.day).
This commit is contained in:
parent
37773bc81d
commit
7b26e45fa0
@ -281,6 +281,9 @@ const gigState = (GIGS_ON && plan.gigs && plan.gigs.length) ? createGigState({ p
|
||||
// day-1 stock streams (§30.3). Skipped entirely when either layer is off.
|
||||
const weekNightOf = (day) => (Math.max(1, day) - 1) % 7;
|
||||
if (game && gigState) gigState.setWeekNight(weekNightOf(game.day));
|
||||
// [R33 — TRAVEL COSTS A DAY] this boot adopted a save from another town: the ride ate a day (save.js
|
||||
// adopt() did the increment + pull-pruning; weekNight above already read the incremented day). Tell John.
|
||||
if (game && game.traveled) hud.showToast(`🚌 ${plan.name || 'a new town'} — the ride cost a day. Day ${game.day}`, 4);
|
||||
// [Lane F R9] occupancyOf closure defers to `citizens` (declared just below); only invoked at enter()
|
||||
// time (door click), long after citizens is initialized — so the forward reference is safe.
|
||||
const interiorMode = createInteriorMode({ THREE, renderer, camera, plan, fleet, useGLB: !NOASSETS,
|
||||
|
||||
@ -173,7 +173,7 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
wrap.append(panel, bar); document.body.appendChild(wrap);
|
||||
const elDay = bar.querySelector('#pc-day'), elCash = bar.querySelector('#pc-cash'),
|
||||
btnCrate = bar.querySelector('#pc-crate-btn'), btnSleep = bar.querySelector('#pc-sleep-btn');
|
||||
let shownDay = null, shownCash = null, shownCount = null, open = false;
|
||||
let shownDay = null, shownCash = null, shownCount = null, shownWants = null, open = false;
|
||||
|
||||
function rebuildList() {
|
||||
const col = game.collection || [];
|
||||
@ -184,7 +184,7 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
panel.appendChild(h);
|
||||
if (!col.length) {
|
||||
const d = document.createElement('div'); d.className = 'pc-crate-empty';
|
||||
d.textContent = 'Empty — go dig.'; panel.appendChild(d); return;
|
||||
d.textContent = 'Empty — go dig.'; panel.appendChild(d);
|
||||
}
|
||||
for (let i = col.length - 1; i >= 0; i--) { // newest find first
|
||||
const e = col[i];
|
||||
@ -202,6 +202,29 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
main.append(t, a, meta);
|
||||
row.append(th, main); panel.appendChild(row);
|
||||
}
|
||||
// [R33] THE WANTLIST — what you're hunting (☆ on a pull card adds; ✕ here removes; buying one
|
||||
// clears it automatically). Section exists only when non-empty — no stray header over nothing.
|
||||
const wants = game.wants || [];
|
||||
if (wants.length) {
|
||||
const wh = document.createElement('h3');
|
||||
wh.style.marginTop = '10px';
|
||||
wh.textContent = `THE WANTLIST — ${wants.length} hunted`;
|
||||
panel.appendChild(wh);
|
||||
for (const w of wants) {
|
||||
const row = document.createElement('div'); row.className = 'pc-crate-row';
|
||||
const main = document.createElement('div'); main.className = 'pc-crate-main';
|
||||
const t = document.createElement('div'); t.className = 'pc-crate-title';
|
||||
t.textContent = w.artist || w.title || w.sku;
|
||||
const a = document.createElement('div'); a.className = 'pc-crate-artist';
|
||||
a.textContent = w.artist && w.title ? `“${w.title}”` : '';
|
||||
main.append(t, a);
|
||||
const x = document.createElement('div');
|
||||
x.textContent = '✕'; x.title = 'stop hunting this';
|
||||
x.style.cssText = 'cursor:pointer;opacity:.6;padding:0 4px;flex:none';
|
||||
x.onclick = () => { game.removeWant(w); rebuildList(); };
|
||||
row.append(main, x); panel.appendChild(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
function setOpen(v) {
|
||||
open = v; panel.style.display = v ? 'block' : 'none';
|
||||
@ -222,6 +245,8 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
shownCount = col.length; btnCrate.textContent = `CRATE ${shownCount}`;
|
||||
if (open) rebuildList();
|
||||
}
|
||||
const nw = (game.wants || []).length; // [R33] want add/clear refreshes an open panel too
|
||||
if (nw !== shownWants) { shownWants = nw; if (open) rebuildList(); }
|
||||
// [R32] the surface yields to the map and to the dig/sell cards (they own the screen), and shows
|
||||
// everywhere else — street AND interior. Cheap: a style write only when the answer changes.
|
||||
const P = window.PROCITY, im = P && P.interiorMode;
|
||||
@ -281,14 +306,11 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
frame++;
|
||||
if (frame % 6 === 0) raycastDoor();
|
||||
|
||||
// v7 game surface — lazy construction (the shell builds the game after the HUD; null under
|
||||
// ?classic=1 / ?game=0 ⇒ this never fires), then cheap getter polls offset from the raycast frames.
|
||||
if (!gameUI) {
|
||||
if (frame % 30 === 0) {
|
||||
const g = window.PROCITY && window.PROCITY.game;
|
||||
if (g) gameUI = buildGameUI(g);
|
||||
}
|
||||
} else if (frame % 6 === 3) gameUI.refresh();
|
||||
// v7 game surface — lazy construction + refresh both live in tickGame() (R33 fix: the build used
|
||||
// to happen ONLY here in street frames, so a door entered fast after boot had no crate/bar inside
|
||||
// at all — the interior branch could refresh a surface that never got built). Throttled here; the
|
||||
// interior/map branches call tickGame() per frame (cheap: change-guarded property reads).
|
||||
if (frame % 6 === 3) tickGame();
|
||||
|
||||
// tooltip — shows CLOSED (with opening hour) when the shop is shut at the current hour (§3.5)
|
||||
if (hovered) {
|
||||
@ -333,9 +355,17 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop }
|
||||
root.remove();
|
||||
}
|
||||
|
||||
// [R32] the game surface ticks in EVERY mode (the street path reaches it via update(); interior/map
|
||||
// call this directly) — day/cash/crate stay live while you shop, and the card-yield check runs there.
|
||||
function tickGame() { if (gameUI) gameUI.refresh(); }
|
||||
// [R32/R33] the game surface ticks in EVERY mode (the street path reaches it via update(); interior/
|
||||
// map call this directly) — and BUILDS here too, so the surface exists no matter which mode first
|
||||
// sees a truthy game. day/cash/crate stay live while you shop; the card-yield check runs here.
|
||||
function tickGame() {
|
||||
if (!gameUI) {
|
||||
const g = window.PROCITY && window.PROCITY.game;
|
||||
if (g) gameUI = buildGameUI(g);
|
||||
return;
|
||||
}
|
||||
gameUI.refresh();
|
||||
}
|
||||
|
||||
return { update, tickToast, showToast, setVisible, tickGame, dispose, getHovered: () => hovered,
|
||||
getFps: () => fpsShown };
|
||||
|
||||
Loading…
Reference in New Issue
Block a user