diff --git a/web/index.html b/web/index.html index 7e2a4b3..1adad8e 100644 --- a/web/index.html +++ b/web/index.html @@ -264,7 +264,9 @@ const game = GAME_ON ? createGame({ onDay: (day) => { if (gigState) gigState.setWeekNight(weekNightOf(day)); // (day โˆ’ 1) % 7 โ€” see the seam note below lighting.setSegment(0); - hud.showToast(`๐ŸŒ… day ${day}`); + // [R34] the morning paper: GODBAY resolved before this hook fired โ€” fold the results in + const news = game ? game.takeAuctionNews() : []; + hud.showToast(`๐ŸŒ… day ${day}` + (news.length ? ' ยท ' + news.join(' ยท ') : ''), news.length ? 6 : 2.6); }, }) : null; const wallet = game ? game.wallet : baseWallet; @@ -283,7 +285,13 @@ 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); +// [R34] the ride also resolved any GODBAY listings the day moved past โ€” the arrival toast carries the news. +if (game) { + const bootNews = game.takeAuctionNews(); + if (game.traveled) hud.showToast(`๐ŸšŒ ${plan.name || 'a new town'} โ€” the ride cost a day. Day ${game.day}` + + (bootNews.length ? ' ยท ' + bootNews.join(' ยท ') : ''), bootNews.length ? 7 : 4); + else if (bootNews.length) hud.showToast(bootNews.join(' ยท '), 6); +} // [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, diff --git a/web/js/world/hud.js b/web/js/world/hud.js index 00de507..9b7879c 100644 --- a/web/js/world/hud.js +++ b/web/js/world/hud.js @@ -200,7 +200,34 @@ export function createHUD({ camera, renderer, plan, getDoorMeshes, onEnterShop } const meta = document.createElement('div'); meta.className = 'pc-crate-meta'; meta.textContent = `paid $${e.pricePaid} ยท ${foundAt(e, game)} ยท day ${e.dayFound}`; main.append(t, a, meta); - row.append(th, main); panel.appendChild(row); + row.append(th, main); + // [R34 GODBAY] โš– LIST โ€” mail-in consignment; the entry leaves the crate, hammers tomorrow. + if (typeof game.listFind === 'function') { + const lb = document.createElement('div'); + lb.textContent = 'โš–'; lb.title = 'LIST at GodBay โ€” the auction resolves tomorrow'; + lb.style.cssText = 'cursor:pointer;opacity:.65;padding:0 4px;flex:none;font-size:15px'; + lb.onclick = () => { if (game.listFind(e)) rebuildList(); }; + row.appendChild(lb); + } + panel.appendChild(row); + } + // [R34] AT AUCTION โ€” consigned finds, hammering overnight. Non-empty only (no header over nothing). + const listings = game.listings || []; + if (listings.length) { + const lh = document.createElement('h3'); + lh.style.marginTop = '10px'; + lh.textContent = `AT AUCTION โ€” ${listings.length} consigned ยท hammers tomorrow`; + panel.appendChild(lh); + for (const l of listings) { + 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 = l.title || l.sku || l.slotId || 'item'; + const meta = document.createElement('div'); meta.className = 'pc-crate-meta'; + meta.textContent = `paid $${l.pricePaid} ยท listed day ${l.listDay}`; + main.append(t, meta); + row.append(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. @@ -245,8 +272,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(); } + const nw = (game.wants || []).length + (game.listings || []).length * 100; // [R33/R34] wants OR + if (nw !== shownWants) { shownWants = nw; if (open) rebuildList(); } // listings change โ†’ rebuild // [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;