// The tool hotbar and what is selected. // ---------- HUD ---------- function renderHotbar() { const hb = $('hotbar'); hb.innerHTML = ''; TOOLS.forEach((t, k) => { const b = el('button', { class: 'slot' + (k === GAME.tool ? ' on' : ''), title: `${t.name} (${t.key})`, 'aria-label': `${t.name} tool, key ${t.key}`, 'aria-pressed': k === GAME.tool }); b.innerHTML = `${t.key}${ICONS[t.id]}`; b.onclick = () => { selectTool(k); }; hb.appendChild(b); }); } function toolId() { return TOOLS[GAME.tool].id; } // Shift does two jobs. While you are steering the bot it boosts; while you are not, // it is the tool modifier (vacuum everything, fill a rectangle). function steering() { return ['w', 'a', 's', 'd', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].some(k => keys.has(k)) || GAME.padRun > 0; } function modShift() { return keys.has('Shift') && !steering(); } // The loose material exactly under the cursor, or the nearest one touching it. function grainAt(ax, ay) { if (ax < 0 || ay < 0 || ax >= W || ay >= H) return 0; const i = ay * W + ax, m = mat[i]; if (occ[i]) { const e = ents.find(e => ax >= e.ix && ax < e.ix + BAR_W && ay >= e.iy && ay < e.iy + BAR_H); if (e) return e.m; } if (PH[m] === PH_POWDER || (PH[m] === PH_LIQUID && player.flask)) return m; return vacuumTargetAt(ax, ay, 1); } function selectTool(k) { if (k === GAME.tool) return; GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui'); } function bagMaterials() { return BAG_ORDER.filter(m => bag[m] > 0); } function blowList() { return [...GAME.blow].filter(m => bag[m] > 0); } // Shift + right click: an even mix of everything loose in the bag (bars never mix) function blowAllList() { return bagMaterials().filter(m => PH[m] !== PH_ITEM && (PH[m] !== PH_LIQUID || player.flask)); } function blowingAll() { return toolId() === 'vacuum' && (GAME.rmb ? !GAME.rmbConsumed && GAME.blowAll : modShift()); } function piece() { return PIECES[GAME.pieceIdx]; }