Vacuum: deselect what is blown out, Ctrl + wheel scrolls the bag, Ctrl+click fix (t20)

Blowing out the last of a selected material removes it from the selection,
so an emptied selection picks up what you click again. Ctrl + wheel never
zooms the page and scrolls the bag list under the mouse. A bag click while
Ctrl is held adds to the selection even if the click lacks the Ctrl flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 14:16:47 -04:00
co-authored by Claude Opus 5.5
parent cd483dd546
commit 248aba439e
9 changed files with 56 additions and 12 deletions
+2
View File
@@ -222,6 +222,8 @@ function toolTick() {
const list = GAME.blowAll ? blowAllList() : blowList();
if (!GAME.blowAll && !list.length) { setStatus('None of the selected materials are in the bag.'); return; }
const r = GAME.blowAll && !list.length ? { placed: 0, status: 'Nothing loose in the bag to blow.' } : toolBlow(ax, ay, GAME.brush[1], list[0] || 0, list.length > 1 ? list : null);
// blown down to nothing: it leaves the selection, so an empty selection picks up what you click again
for (const m of list) if (bag[m] <= 0 && GAME.blow.delete(m)) { lastBagKey = ''; hudDirty = true; }
if (r.placed) { playSfx('blow'); hudDirty = true; } else if (r.status) setStatus(r.status);
} else if (t === 'build') {
const key = `${(ax / BS) | 0},${(ay / BS) | 0}`;
+9
View File
@@ -68,6 +68,15 @@ canvas.addEventListener('wheel', e => {
if (e.shiftKey || e.ctrlKey) setBrush(d);
else { SET.zoom = Math.max(2, Math.min(12, SET.zoom + d)); saveSettings(); resizeRender(); R.snapNext = true; }
}, { passive: false });
// Ctrl + wheel would zoom the browser page (Ctrl shows the full bag list): scroll the list under the mouse instead
window.addEventListener('wheel', e => {
if (!e.ctrlKey || e.target === canvas) return;
e.preventDefault();
for (let n = e.target; n && n !== document.body; n = n.parentElement) {
const oy = getComputedStyle(n).overflowY;
if ((oy === 'auto' || oy === 'scroll') && n.scrollHeight > n.clientHeight) { n.scrollTop += e.deltaY * (e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? n.clientHeight : 1); break; }
}
}, { passive: false });
window.addEventListener('resize', () => { if (R.canvas) resizeRender(); });
document.addEventListener('visibilitychange', () => {
if (document.hidden && GAME.running) saveWorld('auto', true);
+1 -1
View File
@@ -181,7 +181,7 @@ function controlsHtml() {
];
const tools = [
['1 Resonance drill', 'Hold left click to send pulses from the bot toward the cursor: rock they shake hard enough shatters and falls as rubble, ore, coal or scrap, and trees come apart into wood chunks and acorns. Q and E set the pulse width: narrow bores a tunnel fast, wide clears a big cone slowly, medium is in between. R over a material tunes the drill to it, so only that shatters (great for pulling a vein out of the rock); R again untunes. Built blocks are never harmed. Better bits dig harder rock, faster and further.'],
['2 Vacuum', "Left drag picks up the selection, or what you click if nothing is selected. Right click blows the selection. Click a bag name or Q/E selects one; Ctrl+click mixes. Hold Ctrl to see everything found. Clear removes the selection. Bars blow alone. Selections stay when materials run out. Shift while not flying takes everything; Shift + right click blows everything loose in the bag."],
['2 Vacuum', "Left drag picks up the selection, or what you click if nothing is selected. Right click blows the selection. Click a bag name or Q/E selects one; Ctrl+click mixes. Hold Ctrl to see everything found. Clear removes the selection. Bars blow alone. Blowing out the last of a material deselects it. Shift while not flying takes everything; Shift + right click blows everything loose in the bag."],
['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, tubes, filters, grates, gates, pipes, pumps and outlets, machines (fans, the Refiner) and molds. Drag for a line, hold Shift while dragging for a filled rectangle. Right click takes a piece back. Your bot flies through anything you build.'],
['4 Igniter', 'Heats a small spot to 640°C, and its flame reaches a few grains into a pile. Enough to light coal and wood that touch air.'],
['Copy, cut and paste', 'Hold C (copy) or X (cut) and drag a box over what you built; let go and it is picked up, ready to paste. Cut takes it down and refunds it to your bag. Click to place ghosts: your bot builds them from your bag when it is close. R flips, V pastes again, right click or Esc stops. Ctrl+Z undoes building. The notebook (B) keeps saved blueprints and a few starter designs.'],
+3 -2
View File
@@ -217,10 +217,11 @@ function renderBag() {
const row = el('div', { class: 'bagrow' + (bag[m] > 0 ? '' : ' none') + (sel ? ' on' : '') });
const b = el('button', { class: 'pick', title: 'Select ' + MD[m].name });
b.innerHTML = `<span class="sw" style="background:${rgbOf(m)}"></span><span class="nm">${esc(MD[m].name)}</span><span class="n num">${bag[m] > 0 ? bag[m].toLocaleString('en-US') : '–'}</span>`;
// Ctrl+click adds to or removes from the selection.
// Ctrl+click adds to or removes from the selection. GAME.ctrl too: on some Linux setups the click
// arrives without ctrlKey even though the Ctrl keydown (which opened the full list) was seen.
b.oncontextmenu = e => e.preventDefault();
b.onclick = e => {
if (e.ctrlKey || e.metaKey) { toggleBlow(m); if (t !== 'vacuum') selectTool(1); return; }
if (e.ctrlKey || e.metaKey || GAME.ctrl) { toggleBlow(m); if (t !== 'vacuum') selectTool(1); return; }
if (t === 'build' && piece().filter && filterable(m)) { toggleFilterMat(m); return; }
else if (t === 'build' && WALL_OF[m]) { GAME.wallMat = m; if (!pieceUsesWall(piece())) GAME.pieceIdx = 0; }
else { GAME.blow = GAME.blow.size === 1 && GAME.blow.has(m) ? new Set() : new Set([m]); if (t !== 'vacuum') selectTool(1); }