Vacuum menu on F replaces the pick-up modes (t16)
Pick-up takes everything unless something is marked; F with the Vacuum (when nothing in reach can be used) or the card chips open a menu with pick-up and blow toggles per material. The bag rows lose their buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+50
-51
@@ -21,14 +21,10 @@ with sync_playwright() as p:
|
||||
pg.on('pageerror', lambda e: logs.append(f'PAGEERROR: {e}'))
|
||||
pg.on('console', lambda m: logs.append(f'{m.type}: {m.text}') if m.type == 'error' else None)
|
||||
pg.goto(url); pg.evaluate("SET.fog = false"); pg.wait_for_timeout(300)
|
||||
check('empty marks default to first mode', pg.evaluate("GAME.pickMode === 'first'"))
|
||||
pg.evaluate("toggleCollect(CUORE); setPickMode('all')")
|
||||
pg.evaluate("toggleCollect(CUORE)")
|
||||
pg.reload(); pg.wait_for_timeout(300)
|
||||
check('pick-up marks and a manual mode persist across reload', pg.evaluate("GAME.collect.has(CUORE) && GAME.pickMode === 'all'"))
|
||||
pg.evaluate("localStorage.removeItem('deepgrain.pickMode')")
|
||||
pg.reload(); pg.wait_for_timeout(300)
|
||||
check('old pick-up marks default to marked mode', pg.evaluate("GAME.collect.has(CUORE) && GAME.pickMode === 'marked'"))
|
||||
pg.evaluate("localStorage.removeItem('deepgrain.collect'); localStorage.removeItem('deepgrain.pickMode')")
|
||||
check('pick-up marks persist across reload', pg.evaluate("GAME.collect.has(CUORE)"))
|
||||
pg.evaluate("localStorage.removeItem('deepgrain.collect')")
|
||||
pg.reload(); pg.wait_for_timeout(300)
|
||||
check('storage transactions roll back when a write throws', pg.evaluate("""async () => {
|
||||
try { await DB.op('readwrite', st => { st.put('partial', 'abort-test'); throw new Error('test abort'); }); }
|
||||
@@ -224,7 +220,7 @@ with sync_playwright() as p:
|
||||
pg.screenshot(path=str(out / '01-dug.png'))
|
||||
check('trees live in the background layer', ev('bgm.some(v => v === WOOD)') and ev('(() => { for (let i = 0; i < N; i++) if (mat[i] === LEAVES) return false; return true; })()'))
|
||||
|
||||
# 2. vacuum, left button: only the material you start on
|
||||
# 2. vacuum, left button: everything unless marked
|
||||
pg.keyboard.press('2'); settle(100)
|
||||
tgt = ev(f'''(() => {{ for (let r = 0; r < 30; r++) for (let dy = -r; dy <= r; dy++) for (let dx = -r; dx <= r; dx++) {{
|
||||
const x = {bx + 6} + dx, y = {gy + 3} + dy, m = mat[y*W+x]; if (PH[m] === PH_POWDER && inReach(x, y)) return [x, y, m]; }} return null; }})()''')
|
||||
@@ -236,16 +232,14 @@ with sync_playwright() as p:
|
||||
got = ev(f'bag[{tgt[2]}]')
|
||||
others = ev(f'bagTotal - bag[{tgt[2]}]')
|
||||
check('vacuum (left) picks the clicked material', got > 0, f"{ev(f'MD[{tgt[2]}].name')} x{got}")
|
||||
check('vacuum (left) leaves other materials', others == 0, others)
|
||||
check('vacuum defaults to What I click', ev("GAME.pickMode === 'first'"))
|
||||
check('first mode locks onto dirt and leaves other grains', ev("""(() => {
|
||||
check('unmarked vacuum takes both materials in a mixed pile', ev("""(() => {
|
||||
const x = Math.floor(bot.x) + 6, y = Math.floor(bot.y) - 6, cells = [], dirt = bag[LDIRT], stone = bag[RSTONE];
|
||||
for (let dy = -2; dy <= 2; dy++) for (let dx = -2; dx <= 2; dx++) { const i = (y+dy)*W+x+dx; cells.push([i, mat[i], temp[i], flags[i]]); setCell(i, dx < 0 ? RSTONE : LDIRT, 20, 0); }
|
||||
const aim = GAME.aim; GAME.aim = { x, y }; GAME.lmb = true; primaryPress(false); toolTick();
|
||||
const ok = GAME.vacFilter === LDIRT && bag[LDIRT] > dirt && bag[RSTONE] === stone;
|
||||
const ok = GAME.vacFilter === -1 && bag[LDIRT] > dirt && bag[RSTONE] > stone;
|
||||
primaryRelease(false); GAME.lmb = false; GAME.aim = aim;
|
||||
for (const [i, m, t, f] of cells) setCell(i, m, t, f);
|
||||
bagTake(LDIRT, bag[LDIRT] - dirt); return ok;
|
||||
bagTake(LDIRT, bag[LDIRT] - dirt); bagTake(RSTONE, bag[RSTONE] - stone); return ok;
|
||||
})()"""))
|
||||
check('middle click chooses a loose material even with none in the bag', ev("""(() => {
|
||||
const x = Math.floor(bot.x) + 6, y = Math.floor(bot.y) - 6, i = y*W+x;
|
||||
@@ -262,11 +256,10 @@ with sync_playwright() as p:
|
||||
if tgt2:
|
||||
cx, cy = cell_to_screen(tgt2[0], tgt2[1])
|
||||
pg.mouse.move(cx, cy); settle(80); pg.keyboard.press('r'); settle(80)
|
||||
check('R marks pick-up and switches mode without changing blow', ev(f"GAME.collect.has({tgt2[2]}) && GAME.pickMode === 'marked'") and ev('[...GAME.blow]') == blow_before)
|
||||
check('R marks pick-up without changing blow', ev(f"GAME.collect.has({tgt2[2]})") and ev('[...GAME.blow]') == blow_before)
|
||||
ev('renderBag()'); settle(50)
|
||||
marks = pg.locator('#baglist [data-pick][aria-pressed=true]')
|
||||
if marks.count(): marks.first.click(); settle(80)
|
||||
check('removing the last pick-up mark returns to first', ev(f"!GAME.collect.has({tgt2[2]}) && GAME.pickMode === 'first'"))
|
||||
pg.keyboard.press('r'); settle(80)
|
||||
check('R again unmarks', ev(f"!GAME.collect.has({tgt2[2]})"))
|
||||
pg.screenshot(path=str(out / '02-vacuum.png'))
|
||||
|
||||
# 2b. the vacuum filter is fixed when you click: ore with stone around it, ore marked
|
||||
@@ -290,43 +283,28 @@ with sync_playwright() as p:
|
||||
mixed_pile(px0, py0); settle(400)
|
||||
ore = find_ore()
|
||||
refill('bag.fill(0)')
|
||||
ev('GAME.collect = new Set([CUORE]); setPickMode("marked"); lastBagKey = "";')
|
||||
ev('GAME.collect = new Set([CUORE]); lastBagKey = "";')
|
||||
suck_at(*ore)
|
||||
check('with ore marked, clicking ore never pulls in stone', ev('bag[CUORE]') > 0 and ev('bag[RSTONE]') == 0, f"ore {ev('bag[CUORE]')}, stone {ev('bag[RSTONE]')}")
|
||||
mixed_pile(px0, py0); settle(400); refill('bag.fill(0)')
|
||||
st = ev(f'(() => {{ for (let y = {py0} - 8; y < {gy} + 4; y++) for (let x = {px0} - 6; x < {px0} + 20; x++) if (mat[y*W+x] === RSTONE && mat[(y-1)*W+x] === EMPTY && inReach(x, y)) return [x, y]; return null; }})()')
|
||||
suck_at(*st)
|
||||
check('with ore marked, starting on stone rubble still takes only the ore', ev('bag[CUORE]') > 0 and ev('bag[RSTONE]') == 0, f"ore {ev('bag[CUORE]')}, stone {ev('bag[RSTONE]')}")
|
||||
ev('GAME.collect = new Set(); setPickMode("first"); lastBagKey = "";')
|
||||
ev('GAME.collect = new Set([CUORE]); lastBagKey = "";')
|
||||
mixed_pile(px0, py0); settle(400); refill('bag.fill(0)')
|
||||
ore = find_ore()
|
||||
suck_at(*ore, ms=500, shift=True, fly='d')
|
||||
check('Shift held while flying boosts instead of taking everything', ev('bag[RSTONE]') == 0 or ev('bag[CUORE]') == 0, f"ore {ev('bag[CUORE]')}, stone {ev('bag[RSTONE]')}")
|
||||
check('Shift held while flying boosts instead of taking everything', ev('bag[RSTONE]') == 0 and ev('bag[CUORE]') > 0, f"ore {ev('bag[CUORE]')}, stone {ev('bag[RSTONE]')}")
|
||||
ev(f'bot.x = {bx} - 2; bot.y = {by} - 2; bot.vx = bot.vy = 0;'); settle(300)
|
||||
mixed_pile(px0, py0); settle(400); refill('bag.fill(0)')
|
||||
ore = find_ore()
|
||||
suck_at(*ore, ms=500, shift=True)
|
||||
check('Shift held standing still takes everything', ev('bag[RSTONE]') > 0 and ev('bag[CUORE]') > 0, f"ore {ev('bag[CUORE]')}, stone {ev('bag[RSTONE]')}")
|
||||
pg.keyboard.press('t'); settle(80)
|
||||
check('T cycles first to marked', ev("GAME.pickMode === 'marked'"))
|
||||
before = ev('bagTotal')
|
||||
suck_at(*ore, ms=100)
|
||||
check('empty marked mode picks up nothing and explains why', ev('bagTotal') == before and 'Nothing marked to pick up. R over something, or ⬇ in the bag.' in ev('GAME.status'))
|
||||
pg.keyboard.press('t'); settle(80)
|
||||
check('T cycles marked to all', ev("GAME.pickMode === 'all'"))
|
||||
mixed_pile(px0, py0); settle(400); refill('bag.fill(0)')
|
||||
ore = find_ore(); suck_at(*ore, ms=500)
|
||||
check('Everything mode picks up both materials', ev('bag[RSTONE] > 0 && bag[CUORE] > 0'))
|
||||
pg.keyboard.press('t'); settle(80)
|
||||
check('T cycles all to first', ev("GAME.pickMode === 'first'"))
|
||||
for mode in ['marked', 'all', 'first']:
|
||||
pg.click('#selcard [data-pick-mode]'); settle(80)
|
||||
check(f'card chip cycles to {mode}', ev(f'GAME.pickMode === "{mode}"'))
|
||||
ev("toggleCollect(CUORE); setPickMode('all'); toggleCollect(RSTONE)")
|
||||
check('adding another mark switches a manual mode back to marked', ev("GAME.pickMode === 'marked'"))
|
||||
ev("setPickMode('all'); toggleCollect(RSTONE)")
|
||||
check('removing a mark keeps a manual mode', ev("GAME.pickMode === 'all'"))
|
||||
ev("GAME.collect.clear(); setPickMode('first'); lastBagKey = ''; hudDirty = true")
|
||||
ev("GAME.collect.clear(); lastBagKey = ''; hudDirty = true")
|
||||
ev("GAME.aim = { x: Math.floor(bot.x), y: 1 }")
|
||||
pg.keyboard.press('f'); settle(100)
|
||||
check('F away from usable things opens Vacuum', ev("!$('m-dialog').hidden && $('dlg-title').textContent === 'Vacuum'"))
|
||||
ev("closeModal('m-dialog')")
|
||||
# zoom jumps straight to the bot
|
||||
pg.mouse.move(640, 400); settle(600)
|
||||
pg.mouse.wheel(0, -100); pg.wait_for_timeout(60)
|
||||
@@ -359,32 +337,49 @@ with sync_playwright() as p:
|
||||
pg.locator('#baglist .pick').filter(has_text='Coal').click(); settle(80)
|
||||
check('clicking a bag name chooses one blow material', ev('GAME.blow.size === 1 && GAME.blow.has(COAL)'))
|
||||
pick_before = ev('[...GAME.collect]')
|
||||
pg.click(f'#baglist [data-blow="{ev("CUORE")}"]'); settle(100)
|
||||
ev('GAME.blow = new Set([RSTONE]); hudDirty = true')
|
||||
pg.click('#selcard [data-vac-menu]'); settle(80)
|
||||
pg.click(f'#dlg-body [data-vm-blow="{ev("COAL")}"]'); settle(80)
|
||||
pg.click(f'#dlg-body [data-vm-blow="{ev("RSTONE")}"]'); settle(80)
|
||||
pg.click(f'#dlg-body [data-vm-blow="{ev("CUORE")}"]'); settle(80)
|
||||
pg.get_by_role('button', name='Done', exact=True).click(); settle(100)
|
||||
check('blow marks make a mix without changing pick-up marks', ev('GAME.blow.has(COAL) && GAME.blow.has(CUORE) && GAME.blow.size === 2') and ev('[...GAME.collect]') == pick_before and 'Mix of 2' in pg.inner_text('#selcard'))
|
||||
blow_before = ev('[...GAME.blow]')
|
||||
pg.click(f'#baglist [data-pick="{ev("RSTONE")}"]'); settle(80)
|
||||
pg.click('#selcard [data-vac-menu]'); settle(80)
|
||||
check('card opens vacuum menu', ev("!$('m-dialog').hidden"))
|
||||
pg.click(f'#dlg-body [data-vm-pick="{ev("RSTONE")}"]'); settle(80)
|
||||
check('menu pick button marks material', ev('GAME.collect.has(RSTONE)'))
|
||||
pg.get_by_role('button', name='Pick up everything', exact=True).click(); settle(80)
|
||||
check('Pick up everything clears marks and keeps menu open', ev("!GAME.collect.size && !$('m-dialog').hidden"))
|
||||
pg.get_by_role('button', name='Done', exact=True).click(); settle(80)
|
||||
check('pick-up marks do not change blow marks', ev('[...GAME.blow]') == blow_before)
|
||||
check('vacuum bag rows have both toggles', pg.locator('#baglist [data-pick]').count() == pg.locator('#baglist .bagrow').count() == pg.locator('#baglist [data-blow]').count())
|
||||
check('vacuum bag has no pick or blow buttons', pg.locator('#baglist [data-pick], #baglist [data-blow]').count() == 0)
|
||||
pg.mouse.move(mx_, my_); pg.mouse.down(button='right'); settle(500); pg.mouse.up(button='right'); settle(600)
|
||||
dc, do_ = 60 - ev('bag[COAL]'), 60 - ev('bag[CUORE]')
|
||||
check('right click blows an even marked ore/coal mix', dc > 5 and abs(dc - do_) <= 2, f'coal {dc}, ore {do_}')
|
||||
pg.keyboard.press('e'); settle(80)
|
||||
check('Q/E replaces the mix with one material', ev('GAME.blow.size === 1'))
|
||||
ev("GAME.collect = new Set([COAL, CUORE]); setPickMode('marked'); GAME.blow = new Set([COAL, RSTONE]); GAME.status = ''; lastBagKey = ''; hudDirty = true")
|
||||
ev("GAME.collect = new Set([COAL, CUORE]); GAME.blow = new Set([COAL, RSTONE]); GAME.status = ''; lastBagKey = ''; hudDirty = true")
|
||||
refill('bag[COAL] = 120; bag[CUORE] = 100; bag[RSTONE] = 600')
|
||||
for width, height, suffix in [(1280, 800, ''), (900, 700, '-900')]:
|
||||
pg.set_viewport_size({'width': width, 'height': height}); settle(200)
|
||||
pg.screenshot(path=str(out / f'vacuum-card{suffix}.png'))
|
||||
check(f'vacuum panel fits at {width}', ev("(() => { const c = $('selcard'), b = $('bagpanel'); return [c, b].every(e => { const r = e.getBoundingClientRect(); return r.left >= 0 && r.right <= innerWidth && r.bottom <= innerHeight && e.scrollWidth <= e.clientWidth; }) && [...$('baglist').querySelectorAll('.bagrow')].every(e => e.scrollWidth <= e.clientWidth); })()"))
|
||||
pg.click('#selcard [data-vac-menu]'); settle(80)
|
||||
pg.screenshot(path=str(out / f'vacuum-menu{suffix}.png'))
|
||||
check(f'vacuum menu fits at {width}', ev("(() => { const e = $('dlg-body'), r = e.closest('.plate').getBoundingClientRect(); return e.scrollWidth <= e.clientWidth && document.querySelector('.vpick').scrollWidth <= document.querySelector('.vpick').clientWidth && r.left >= 0 && r.right <= innerWidth && r.bottom <= innerHeight; })()"))
|
||||
pg.get_by_role('button', name='Blow one at a time', exact=True).click(); settle(80)
|
||||
check(f'Blow one at a time keeps menu open at {width}', ev("GAME.blow.size === 1 && !$('m-dialog').hidden"))
|
||||
pg.get_by_role('button', name='Done', exact=True).click(); settle(80)
|
||||
pg.set_viewport_size({'width': 1280, 'height': 800})
|
||||
pg.keyboard.press('3'); settle(80)
|
||||
check('build bag has no vacuum toggles', pg.locator('#baglist .mark').count() == 0)
|
||||
pg.keyboard.press('2'); settle(80)
|
||||
ev("toggleBlow(BARCU)")
|
||||
ev("GAME.blow = new Set([COAL, CUORE]); toggleBlow(BARCU)")
|
||||
check('a bar blow mark replaces the mix', ev('GAME.blow.size === 1 && GAME.blow.has(BARCU)'))
|
||||
ev("toggleBlow(COAL)")
|
||||
check('a grain blow mark replaces a bar', ev('GAME.blow.size === 1 && GAME.blow.has(COAL)'))
|
||||
ev("GAME.collect.clear(); setPickMode('first'); lastBagKey = ''; hudDirty = true")
|
||||
ev("GAME.collect.clear(); lastBagKey = ''; hudDirty = true")
|
||||
refill('bag[COAL] = 120')
|
||||
|
||||
# 4. igniter lights it
|
||||
@@ -510,7 +505,13 @@ with sync_playwright() as p:
|
||||
pg.keyboard.press('2'); settle(60)
|
||||
ev("GAME.status = ''; GAME.statusT = 0"); n_toasts = ev("$('toasts').children.length")
|
||||
pg.mouse.move(*cell_to_screen(ev('molds[0].x') + 3, ev('molds[0].y') + 3)); settle(150); pg.keyboard.press('f'); settle(150)
|
||||
check('F on an ingot mold says nothing', ev('GAME.status') == '' and ev("$('toasts').children.length") == n_toasts, ev('GAME.status'))
|
||||
check('F on an automatic ingot mold opens vacuum menu without a toast', ev("!$('m-dialog').hidden") and ev("$('toasts').children.length") == n_toasts)
|
||||
ev("closeModal('m-dialog')")
|
||||
check('F with vacuum uses what is in reach before opening the menu', ev("""(() => {
|
||||
const old = interactAt; interactAt = () => 'Gate opened.';
|
||||
try { doInteract(); } finally { interactAt = old; }
|
||||
return $('m-dialog').hidden && GAME.status === 'Gate opened.';
|
||||
})()"""))
|
||||
settle(1500)
|
||||
pg.screenshot(path=str(out / '06-bars.png'))
|
||||
pg.keyboard.press('2'); settle(60)
|
||||
@@ -724,10 +725,8 @@ with sync_playwright() as p:
|
||||
check('gamepad RT digs where the right stick aims', ev('stats.dug') > d0, f"{d0}->{ev('stats.dug')}")
|
||||
ev("__pad.buttons[5].pressed = true;"); settle(100); ev("__pad.buttons[5].pressed = false;"); settle(100)
|
||||
check('gamepad RB switches to the vacuum', ev('GAME.tool') == 1)
|
||||
ev("GAME.collect.clear(); setPickMode('first'); GAME.aim = { x: Math.floor(bot.x), y: Math.floor(bot.y) }; setCell(Math.floor(bot.y)*W+Math.floor(bot.x), COAL, 20, 0); __pad.buttons[2].pressed = true; pollPad(); __pad.buttons[2].pressed = false; pollPad()")
|
||||
check('gamepad X keeps R pick-up marking', ev("GAME.collect.has(COAL) && GAME.pickMode === 'marked'"))
|
||||
ev("__pad.buttons[1].pressed = true; pollPad(); __pad.buttons[1].pressed = false; pollPad()")
|
||||
check('gamepad B cycles pick-up mode', ev("GAME.pickMode === 'all'"))
|
||||
ev("GAME.collect.clear(); GAME.aim = { x: Math.floor(bot.x), y: Math.floor(bot.y) }; setCell(Math.floor(bot.y)*W+Math.floor(bot.x), COAL, 20, 0); __pad.buttons[2].pressed = true; pollPad(); __pad.buttons[2].pressed = false; pollPad()")
|
||||
check('gamepad X keeps R pick-up marking', ev("GAME.collect.has(COAL)"))
|
||||
ev("__pad.buttons[15].pressed = true; pollPad(); __pad.buttons[15].pressed = false; pollPad()")
|
||||
check('gamepad d-pad chooses a single blow material', ev('GAME.blow.size === 1'))
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ ticks(run, 30);
|
||||
const got = run('toolVacuum(120, 104, 6, m => m === RSTONE).got');
|
||||
check('vacuum picks only the filtered material', run('bag[RSTONE]') > 0 && run('bag[COAL]') === 0, `${run('bag[RSTONE]')} rubble, ${run('bag[COAL]')} coal`);
|
||||
run('toolVacuum(102, 98, 4, m => true)');
|
||||
check('vacuum everything mode picks coal', run('bag[COAL]') > 0, run('bag[COAL]'));
|
||||
check('unfiltered vacuum picks coal', run('bag[COAL]') > 0, run('bag[COAL]'));
|
||||
// 3. drop: grains fall
|
||||
const c0 = run('bag[COAL]');
|
||||
run('bot.x = 100; bot.y = 60;');
|
||||
|
||||
Reference in New Issue
Block a user