Vacuum: pick up what you click, blow out what's in hand (t20)
A drag takes only the material it starts on (Shift: everything). The blow selection is the hand: click or Q/E for one material, Ctrl+click for a mix. Empty materials leave the hand, an empty hand blows nothing, and a pickup fills an empty hand. Removes the pick-up marks and the F vacuum menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+11
-57
@@ -23,7 +23,7 @@ function cycleItem(d) {
|
||||
if (t === 'vacuum') {
|
||||
const l = bagMaterials();
|
||||
if (!l.length) return;
|
||||
const k = l.indexOf(blowList()[0]);
|
||||
const k = GAME.blow.size ? l.indexOf(blowList()[0]) : d > 0 ? -1 : 0;
|
||||
GAME.blow = new Set([l[(k + d + l.length) % l.length]]);
|
||||
} else if (t === 'build') {
|
||||
let k = BUILD_ORDER.indexOf(piece().id); k = (k + d + BUILD_ORDER.length) % BUILD_ORDER.length;
|
||||
@@ -48,18 +48,10 @@ function rotate() {
|
||||
if (pieceUsesWall(P)) cycleWall(1);
|
||||
else if (P.dirs) GAME.pieceDir[P.id] = ((GAME.pieceDir[P.id] || 0) + 1) % P.dirs.length;
|
||||
else return;
|
||||
} else if (t === 'vacuum') { toggleCollectUnderCursor(); return; }
|
||||
else if (t === 'drill') { tuneUnderCursor(); return; }
|
||||
} else if (t === 'drill') { tuneUnderCursor(); return; }
|
||||
else return;
|
||||
playSfx('ui'); hudDirty = true;
|
||||
}
|
||||
function toggleCollect(m) {
|
||||
if (!m) return;
|
||||
const add = !GAME.collect.has(m);
|
||||
if (add) GAME.collect.add(m); else GAME.collect.delete(m);
|
||||
try { localStorage.setItem('deepgrain.collect', JSON.stringify([...GAME.collect])); } catch (e) { /* storage unavailable */ }
|
||||
lastBagKey = ''; hudDirty = true; playSfx('ui');
|
||||
}
|
||||
function toggleBlow(m) {
|
||||
if (GAME.blow.has(m)) GAME.blow.delete(m);
|
||||
else if (PH[m] === PH_ITEM) GAME.blow = new Set([m]);
|
||||
@@ -80,11 +72,6 @@ function toggleFilterMat(m) {
|
||||
hudDirty = true; lastBagKey = ''; playSfx('ui');
|
||||
setStatus(`New filter selection updated.`, 'info');
|
||||
}
|
||||
function toggleCollectUnderCursor() {
|
||||
const m = grainAt(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y));
|
||||
if (!m) { setStatus('Aim at something loose to mark it.', 'info'); return; }
|
||||
toggleCollect(m);
|
||||
}
|
||||
function setBrush(d) {
|
||||
const t = toolId();
|
||||
if (t === 'drill') { setDrillWidth(GAME.drillW + d); return; }
|
||||
@@ -141,37 +128,6 @@ function openFilterPicker(cur, onDone, title) {
|
||||
box.querySelectorAll('[data-m]').forEach(b => b.onclick = () => { const m = +b.dataset.m; if (sel.has(m)) sel.delete(m); else sel.add(m); playSfx('ui'); sync(); });
|
||||
sync();
|
||||
}
|
||||
function openVacuumMenu() {
|
||||
ensureBlow();
|
||||
dialog('Vacuum', '', [
|
||||
{ label: 'Pick up everything', cls: 'ghost', fn: () => {
|
||||
GAME.collect.clear();
|
||||
try { localStorage.setItem('deepgrain.collect', '[]'); } catch (e) { /* storage unavailable */ }
|
||||
lastBagKey = ''; hudDirty = true; playSfx('ui'); sync(); return true;
|
||||
} },
|
||||
{ label: 'Blow one at a time', cls: 'ghost', fn: () => {
|
||||
GAME.blow = new Set(blowList().slice(0, 1));
|
||||
lastBagKey = ''; hudDirty = true; playSfx('ui'); sync(); return true;
|
||||
} },
|
||||
{ label: 'Done', cls: 'primary' },
|
||||
]);
|
||||
const box = $('dlg-body');
|
||||
function sync() {
|
||||
const list = BAG_ORDER.filter(m => bag[m] > 0)
|
||||
.concat(BAG_ORDER.filter(m => !bag[m] && (GAME.collect.has(m) || GAME.blow.has(m))))
|
||||
.concat(BAG_ORDER.filter(m => !bag[m] && !GAME.collect.has(m) && !GAME.blow.has(m) && seen[m] && filterable(m)));
|
||||
const blow = blowList();
|
||||
box.innerHTML = `<p class="note" id="vm-sum">${esc(GAME.collect.size ? 'Picking up only ' + matList([...GAME.collect]) + '.' : 'Picking up everything.')} Blowing out ${blow.length > 1 ? `an even mix of ${blow.length}` : blow.length ? esc(MD[blow[0]].name.toLowerCase()) : 'nothing'}.</p>`
|
||||
+ (!GAME.collect.size ? '<p class="note">Mark materials to pick up only those.</p>' : '')
|
||||
+ (list.length ? `<div class="vpick"><div class="vrow vhead"><span></span><span></span><span></span><span>Pick up</span><span>Blow</span></div>${list.map(m => {
|
||||
const n = esc(MD[m].name);
|
||||
return `<div class="vrow"><span class="sw" style="background:${rgbOf(m)}"></span><span class="nm">${n}</span><span class="num">${bag[m] ? bag[m].toLocaleString('en-US') : 'none'}</span><button data-vm-pick="${m}" aria-pressed="${GAME.collect.has(m)}" aria-label="Pick up ${n}" title="Pick up ${n}" class="btn${GAME.collect.has(m) ? ' on' : ''}">⬇</button><button data-vm-blow="${m}" aria-pressed="${GAME.blow.has(m)}" aria-label="Blow out ${n}" title="Blow out ${n}" class="btn${GAME.blow.has(m) ? ' on' : ''}"${!bag[m] && !GAME.blow.has(m) ? ' disabled' : ''}>⬆</button></div>`;
|
||||
}).join('')}</div>` : '<p class="note">Find something loose first.</p>');
|
||||
box.querySelectorAll('[data-vm-pick]').forEach(b => b.onclick = () => { toggleCollect(+b.dataset.vmPick); sync(); });
|
||||
box.querySelectorAll('[data-vm-blow]').forEach(b => b.onclick = () => { toggleBlow(+b.dataset.vmBlow); sync(); });
|
||||
}
|
||||
sync();
|
||||
}
|
||||
function openWallPicker() {
|
||||
const P = piece(), title = P.kind === 'mold' ? `${P.name} material` : 'Wall material';
|
||||
const list = WALL_LIST.filter(m => bag[m] > 0).concat(WALL_LIST.filter(m => !bag[m]));
|
||||
@@ -228,13 +184,12 @@ function renderSelCard() {
|
||||
+ '<div class="sub">Place pieces from the menu below. Drag for a line, <kbd>Shift</kbd>-drag for a box; out of reach becomes ghosts.</div>'
|
||||
+ '<div class="sub"><kbd>Q</kbd>/<kbd>E</kbd> piece · <kbd>Shift</kbd>+<kbd>Q</kbd>/<kbd>E</kbd> tab · <kbd>R</kbd> turn or material · <kbd>F</kbd> material menu · hold <kbd>G</kbd> replace · right click take back · hold <kbd>C</kbd>/<kbd>X</kbd> copy/cut · <kbd>V</kbd> paste · <kbd>Ctrl</kbd>+<kbd>Z</kbd> undo</div>';
|
||||
} else if (t === 'vacuum') {
|
||||
ensureBlow();
|
||||
const all = blowingAll(), list = all ? blowAllList() : blowList(), marks = [...GAME.collect];
|
||||
const all = blowingAll(), list = all ? blowAllList() : blowList();
|
||||
const sws = ms => ms.slice(0, 4).map(m => `<span class="sw" title="${esc(MD[m].name)}" style="background:${rgbOf(m)}"></span>`).join('') + (ms.length > 4 ? `+${ms.length - 4}` : '');
|
||||
const count = list.reduce((n, m) => n + bag[m], 0);
|
||||
h = `<div class="row vacrow"><b>Pick up</b><button class="chip hot" data-vac-menu>${marks.length ? 'Only marked' : 'Everything'} · F</button>${sws(marks)}</div>
|
||||
<div class="row vacrow"><b>Blow out</b><kbd>Q</kbd><button class="name grow vacchoice" data-vac-menu>${all ? '' : sws(list)}${all ? list.length ? `Everything · mix of ${list.length}` : 'Everything · nothing loose' : list.length > 1 ? `Mix of ${list.length}` : list.length ? esc(MD[list[0]].name) : 'bag empty'}</button><span class="num chip${count ? '' : ' bad'}">${count ? count.toLocaleString('en-US') : 'none left'}</span><kbd>E</kbd></div>
|
||||
<div class="sub">Left picks up, right blows out. F chooses what. R marks what is under the cursor. Shift takes or blows everything. <span class="chip">nozzle ${GAME.brush[1]}</span></div>`;
|
||||
h = `<div class="row vacrow"><b>Pick up</b><span>What you click · Shift: everything</span>${sws(GAME.lmb && GAME.vacFilter > 0 ? [GAME.vacFilter] : [])}</div>
|
||||
<div class="row vacrow"><b>In hand</b><kbd>Q</kbd><span class="name grow vacchoice">${all ? '' : sws(list)}${all ? list.length ? `Everything · mix of ${list.length}` : 'Everything · nothing loose' : list.length > 1 ? `Mix of ${list.length}` : list.length ? esc(MD[list[0]].name) : 'empty'}</span><span class="num chip${count ? '' : ' bad'}">${count ? count.toLocaleString('en-US') : 'none'}</span><kbd>E</kbd></div>
|
||||
<div class="sub">Left picks up what you click, right blows out what's in hand. Shift: everything. Click or Q/E in the bag to change the hand. <span class="chip">nozzle ${GAME.brush[1]}</span></div>`;
|
||||
} else if (t === 'ignite') {
|
||||
h = `<div class="row"><span class="name">Igniter</span><span class="chip hot">up to 640°C</span></div><div class="sub">Hold on fuel to light it. Coal catches around 420°C, wood around 300°C. Fuel needs open air next to it.</div>`;
|
||||
}
|
||||
@@ -247,9 +202,9 @@ function renderBag() {
|
||||
$('bagcap').textContent = `${bagTotal.toLocaleString('en-US')} / ${cap.toLocaleString('en-US')}`;
|
||||
$('bagbar').firstChild.style.width = pct + '%';
|
||||
$('bagbar').classList.toggle('full', bagTotal >= cap);
|
||||
const l = BAG_ORDER.filter(m => bag[m] > 0 || GAME.collect.has(m) || GAME.blow.has(m));
|
||||
const l = BAG_ORDER.filter(m => bag[m] > 0 || GAME.blow.has(m));
|
||||
const t = toolId(), all = blowingAll(), blowing = all ? blowAllList() : blowList();
|
||||
const key = l.map(m => m + ':' + bag[m] + (GAME.collect.has(m) ? 'c' : '') + (GAME.blow.has(m) ? 'b' : '')).join(',') + '|' + GAME.wallMat + '|' + GAME.filterCfg.m.join('.') + '|' + t + '|' + GAME.pieceIdx + '|' + all;
|
||||
const key = l.map(m => m + ':' + bag[m] + (GAME.blow.has(m) ? 'b' : '')).join(',') + '|' + GAME.wallMat + '|' + GAME.filterCfg.m.join('.') + '|' + t + '|' + GAME.pieceIdx + '|' + all;
|
||||
if (key === lastBagKey) return;
|
||||
lastBagKey = key;
|
||||
const box = $('baglist'); box.innerHTML = '';
|
||||
@@ -257,11 +212,10 @@ function renderBag() {
|
||||
for (const m of l) {
|
||||
const sel = (t === 'vacuum' && blowing.includes(m)) || (t === 'build' && m === GAME.wallMat && pieceUsesWall(piece())) || (t === 'build' && piece().filter && GAME.filterCfg.m.includes(m));
|
||||
const row = el('div', { class: 'bagrow' + (sel ? ' on' : '') });
|
||||
if (t === 'vacuum' && GAME.collect.has(m)) row.appendChild(el('span', { class: 'tag', title: 'Marked to pick up', 'aria-label': 'Marked to pick up' }, '⬇'));
|
||||
const b = el('button', { class: 'pick', title: WALL_OF[m] && t === 'build' ? `Build with ${MD[m].name}` : `Blow out ${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].toLocaleString('en-US')}</span>`;
|
||||
// Ctrl+click adds to (or removes from) what you blow; Ctrl+right-click, what you pick up
|
||||
b.oncontextmenu = e => { e.preventDefault(); if (!(e.ctrlKey || e.metaKey)) return; toggleCollect(m); if (t !== 'vacuum') selectTool(1); };
|
||||
// Ctrl+click adds to or removes from the hand.
|
||||
b.oncontextmenu = e => e.preventDefault();
|
||||
b.onclick = e => {
|
||||
if (e.ctrlKey || e.metaKey) { toggleBlow(m); if (t !== 'vacuum') selectTool(1); return; }
|
||||
if (t === 'build' && piece().filter && filterable(m)) { toggleFilterMat(m); return; }
|
||||
@@ -272,5 +226,5 @@ function renderBag() {
|
||||
row.appendChild(b);
|
||||
box.appendChild(row);
|
||||
}
|
||||
if (t === 'vacuum') box.appendChild(el('div', { class: 'baghint' }, 'Click: blow only this · Ctrl+click: add to blow · Ctrl+right-click: add to pick up · F: vacuum menu'));
|
||||
if (t === 'vacuum') box.appendChild(el('div', { class: 'baghint' }, 'Click: put in hand · Ctrl+click: add to the mix · Q/E: next'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user