// Notebook market: prices, orders, claim payments and wallet. function fmtClock(ticks) { const s = Math.max(0, Math.ceil(ticks / 60)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; } function orderName(o) { return `${o.who ? o.who + ': ' : ''}${o.qty.toLocaleString('en-US')} ${MD[o.m].name.toLowerCase()}${PH[o.m] === PH_ITEM && o.qty !== 1 ? 's' : ''}`; } function orderLeft(o) { return Math.max(0, o.due - tick); } const MARKET_GROUPS = [ ['Raw', [COAL, LWOOD, ACORN, SAND, LCLAY, RGRAN, RBAS, CUORE, FEORE, SNORE, ZNORE]], ['Bulk (builders)', [RSTONE, GRAVEL, BARSB, PLANK, GREENB, BARBR]], ['Refined', [CHARCOAL, RBRICK, CULLET, BARGL, SCRCU, SCRFE, SCRST, SCRSN, SCRZN, BARCU, BARSN, BARZN, BARFE, BARST]], ['Alloys', [SCRBZ, SCRBS, BARBZ, BARBS]], ['Precious', [AGORE, AUORE, SCRAG, SCRAU, BARAG, BARAU]], ]; function renderMarketTab(body) { let h = `
The land is yours. Keep building: the ledger has bigger goals.
`; h += `Anything that drops through the trade dock's grate is sold on the spot, once it is cool enough to handle. Junk like dirt, ash and slag goes for nothing. Rock you dig still sells, builders order it by the bagful, and a Grinder finds ore in it. Build walls around the dock to funnel more in.
`; h += `Orders · ${esc(STANDING[standing()])}
`; if (orders.length) { h += 'No open orders. Buyers post new ones every minute or two, for goods you have found.
`; h += `Goods dropped into the dock fill a matching order first. A missed deadline costs nothing: what you delivered is paid at market price. ${ordersDone} filled so far; bigger buyers come with more.
`; h += `Prices
Per grain, or per bar. Selling a lot of one thing lowers its price for a while; it recovers over about half an hour.
`; let rows = '', unknown = 0; for (const [g, list] of MARKET_GROUPS) { const known = list.filter(m => seen[m]); unknown += list.length - known.length; if (!known.length) continue; rows += `| Goods | Base | Now |
|---|
Find something worth selling first.
'; if (unknown) h += `${unknown} more goods to discover.
`; h += `Sales, minute by minute
`; const recent = saleAcc.total > 0 ? [{ total: saleAcc.total, items: saleAcc.items, now: true }].concat(shipLog) : shipLog; h += recent.length ? `Nothing sold yet.
'; body.innerHTML = h; body.querySelectorAll('[data-pay]').forEach(b => b.onclick = () => { const v = b.dataset.pay === 'all' ? money : +b.dataset.pay; const paid = payClaim(v); if (paid > 0) { playSfx('sale'); toast(`Paid ${fmtMoney(paid)} toward the claim.`); } renderNotebook(); hudDirty = true; }); } function fmtPrice(v) { return v >= 10 ? '$' + Math.round(v) : '$' + v.toFixed(2); } function itemsText(items) { const e = Object.entries(items).map(([m, n]) => [+m, n]).sort((a, b) => b[1] * PRICE[b[0]] - a[1] * PRICE[a[0]]); const parts = e.slice(0, 3).map(([m, n]) => PH[m] === PH_ITEM ? `${n} ${MD[m].name.toLowerCase()}${n === 1 ? '' : 's'}` : `${n} ${MD[m].name.toLowerCase()}`); if (e.length > 3) parts.push(`${e.length - 3} more`); return parts.join(', '); } function renderWallet() { $('w-money').textContent = fmtMoney(money); const c = $('w-claim'); c.className = 'line' + (debt <= 0 ? ' paid' : ''); c.innerHTML = debt > 0 ? `Claim owed${fmtMoney(debt)}` : `Claimpaid off ✓`; const h = orders.length ? orders.map(o => { const left = orderLeft(o); return `${o.got}/${esc(orderName(o))}${fmtClock(left)}`; }).join('') : 'No orders yet'; const box = $('w-orders'); if (box.innerHTML !== h) box.innerHTML = h; }