Vacuum: holding Shift shows the blow-everything mix on the card and in the bag (t19)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 11:59:05 -04:00
co-authored by Claude Opus 5.5
parent 52a273b656
commit a95f664f7d
7 changed files with 58 additions and 8 deletions
+2 -2
View File
@@ -395,9 +395,9 @@ The notebook (N): milestones, market, wallet, blueprints, controls.
- functions: openNotebook, renderNotebook, fmtClock, orderName, orderLeft, renderMarketTab, fmtPrice, itemsText, renderWallet, renderBlueprintTab, copyCode, controlsHtml
- variables: nbTab, MARKET_GROUPS
### src/ui/hud.js (39 lines)
### src/ui/hud.js (40 lines)
The tool hotbar and what is selected.
- functions: renderHotbar, toolId, steering, modShift, grainAt, selectTool, bagMaterials, blowList, blowAllList, ensureBlow, piece
- functions: renderHotbar, toolId, steering, modShift, grainAt, selectTool, bagMaterials, blowList, blowAllList, blowingAll, ensureBlow, piece
### src/ui/build-menu.js (102 lines)
The build menu along the bottom: categories, piece tiles and their icons.
+12
View File
@@ -4,6 +4,18 @@ What agents have done, newest first. Add an entry for every piece of work: the d
note or request it was for, what changed, the files touched, the tests run, and anything left
open or not verified. Keep each entry short; the git history has the details.
## 2026-10-24: t19 Vacuum blow all visual (Claude Code, Codex)
- The owner's description was lost in kandown; chosen in chat: bag rows light up and the card
shows it while Shift is held. t18 was deleted at the owner's request (to decide later: what
blow does when the chosen material runs out).
- `hud.js` `blowingAll()` (Shift held and not flying, or blowing everything); `selection.js` card
("Everything · mix of N", no swatches so it stays one line) and bag `.on` rows; `previews.js`
cursor swatches.
- `test/play.py`: Shift preview on and off, flying doesn't preview (the bot is put back after,
or a later gamepad dig check fails). Tests: `run_all.py --browser` all pass (220 `play.py`
checks).
## 2026-10-24: t16 follow-up: Shift blows all, Ctrl+click in the bag, compact menu (Claude Code)
- The owner's subtask "shift to blow all" and two chat requests. Codex was out of credits, so
+1
View File
@@ -31,6 +31,7 @@ function bagMaterials() { return BAG_ORDER.filter(m => bag[m] > 0); }
function blowList() { return [...GAME.blow]; }
// 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()); }
// Empty materials stay chosen until the player changes the marks.
function ensureBlow() {
if (GAME.blow.size) return;
+1 -1
View File
@@ -153,7 +153,7 @@ function drawPreviews() {
if (t === 'vacuum') {
// what the right button would blow, next to the cursor; what the left is sucking, while it sucks
const sz = Math.max(6, zp * 1.6), qx = p.x + (r + 1.2) * zp, qy = p.y - (r + 1.2) * zp;
const swatches = GAME.lmb ? GAME.vacFilter >= 0 && GAME.collect.size ? [...GAME.collect] : [] : GAME.rmb && GAME.blowAll ? blowAllList() : blowList();
const swatches = GAME.lmb ? GAME.vacFilter >= 0 && GAME.collect.size ? [...GAME.collect] : [] : blowingAll() ? blowAllList() : blowList();
swatches.slice(0, 4).forEach((m, k) => { ctx.fillStyle = rgbOf(m); ctx.fillRect(qx + k * (sz + 2), qy, sz, sz); });
if (blowing || GAME.lmb) {
// faint line from the nozzle: the path the grains take
+5 -5
View File
@@ -229,11 +229,11 @@ function renderSelCard() {
+ '<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 list = blowList(), marks = [...GAME.collect];
const all = blowingAll(), list = all ? blowAllList() : blowList(), marks = [...GAME.collect];
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>${sws(list)}${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="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>`;
} 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>`;
@@ -248,14 +248,14 @@ function renderBag() {
$('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 t = toolId();
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;
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;
if (key === lastBagKey) return;
lastBagKey = key;
const box = $('baglist'); box.innerHTML = '';
if (!l.length) { box.appendChild(el('div', { class: 'empty' }, 'Empty. Break rock with the Drill, then use the Vacuum.')); return; }
for (const m of l) {
const sel = (t === 'vacuum' && GAME.blow.has(m)) || (t === 'build' && m === GAME.wallMat && pieceUsesWall(piece())) || (t === 'build' && piece().filter && GAME.filterCfg.m.includes(m));
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}` });
+25
View File
@@ -0,0 +1,25 @@
---
id: t19
status: Review
order: 4
priority: P3
created: 2026-10-24
updated: 2026-10-24T15:59:05Z
title: vacuum blow all visual
report: |
(agent) With the Vacuum, holding Shift (while not flying) now shows what Shift + right click
will blow before you click: the card's Blow out line reads "Everything · mix of N", every bag
row that will be blown lights up, and the swatches by the cursor show the whole mix. Let go of
Shift and it goes back to your chosen material. It also shows while you're blowing everything.
To check: with a few things in the bag and one chosen to blow, hold Shift and watch the card and
bag; then hold Shift with WASD and see it doesn't change (Shift is boost then).
---
## Context
- (owner's answers, 2026-10-24) The description typed in kandown was lost. Chosen: "Bag rows light up" (while Shift is held, or while blowing everything, every row that will be blown lights up in the bag) and "Card shows it while Shift is held" (the Blow out line reads "Everything · mix of N" and the swatches by the cursor show the whole mix, before you click).
## Subtasks
- [x] Card shows "Everything · mix of N" while Shift is held
- [x] Bag rows that will be blown light up
+12
View File
@@ -330,6 +330,18 @@ with sync_playwright() as p:
# Shift + right click blows an even mix of everything loose in the bag, without changing the choice
refill('bag[COAL] = 60; bag[CUORE] = 60')
ev('GAME.collect = new Set([COAL, CUORE]); GAME.blow = new Set([COAL]); lastBagKey = ""; hudDirty = true;')
pg.keyboard.down('Shift'); settle(150)
check('Shift previews everything in the blow card', 'Everything · mix of' in pg.inner_text('#selcard'))
check('Shift lights every loose bag row', ev("blowAllList().every(m => [...$('baglist').querySelectorAll('.bagrow.on .nm')].some(e => e.textContent === MD[m].name))"))
pg.screenshot(path=str(out / '03b-blow-all-preview.png'))
pg.keyboard.up('Shift'); settle(150)
check('releasing Shift restores the chosen blow material', ev("$('selcard').querySelector('.vacchoice').textContent.includes(MD[COAL].name) && !$('selcard').querySelector('.vacchoice').textContent.includes('Everything')"))
check('releasing Shift lights only coal', ev("(() => { const rows = [...$('baglist').querySelectorAll('.bagrow.on')]; return rows.length === 1 && rows[0].querySelector('.nm').textContent === MD[COAL].name; })()"))
bot_at = ev('[bot.x, bot.y]')
pg.keyboard.down('d'); pg.keyboard.down('Shift'); settle(150)
check('Shift while flying does not preview everything', 'Everything' not in pg.locator('#selcard .vacchoice').inner_text())
pg.keyboard.up('Shift'); pg.keyboard.up('d')
ev(f'bot.x = {bot_at[0]}; bot.y = {bot_at[1]}; bot.vx = bot.vy = 0;'); settle(150)
mx_, my_ = cell_to_screen(bx - 26, gy - 6)
pg.mouse.move(mx_, my_); pg.keyboard.down('Shift'); pg.mouse.down(button='right'); settle(400); pg.mouse.up(button='right'); pg.keyboard.up('Shift'); settle(600)
check('Shift + right click blows everything loose in the bag', ev('bag[CUORE]') < 60 and ev('bag[COAL]') < 60 and ev('GAME.blow.size === 1 && GAME.blow.has(COAL)'), ev('[bag[COAL], bag[CUORE]]'))