Vacuum picks up grains in mid-air (t63)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 13:00:52 -04:00
co-authored by Claude Opus 5.5
parent 65338b2b27
commit 30c811ea80
2 changed files with 73 additions and 1 deletions
+52
View File
@@ -96,4 +96,56 @@ const before = run('JSON.stringify({bag: bagTotal, ents: ents.length, molds: mol
run('globalThis.__sv = serializeWorld(); deserializeWorld(__sv.meta, __sv.buffers);');
const after = run('JSON.stringify({bag: bagTotal, ents: ents.length, molds: molds.length, mach: machines.filter(m => m && m.type !== "dock").length, blocks: blockP.reduce((a,b)=>a+(b>0),0)})');
check('save/load keeps bag, bars, molds, machines and blocks', before === after, before + ' vs ' + after);
// 12. vacuum catches grains in flight, before loose cells, with the same limits.
flatWorld(run, 240, 160, 140);
run('bot.x = 98; bot.y = 58; player.flask = false; EV.length = 0;');
run('flyers.push({x: 102.9, y: 62.9, m: SAND});');
const airborne = run('toolVacuum(100, 60, 3, () => true)');
check('vacuum takes airborne sand using its current cell', airborne.got === 1 && airborne.m === run('SAND') && run('bag[SAND] === 1 && flyers.length === 0'));
check('airborne pickup emits a suck at the grain cell', run('EV.some(e => e.t === "suck" && e.x === 102 && e.y === 62 && e.m === SAND)'));
run('flyers.push({x: 103, y: 63, m: SAND}, {x: 180, y: 60, m: SAND});');
check('vacuum leaves flyers outside the circular brush', run('toolVacuum(100, 60, 3, () => true).got === 0 && flyers.length === 2'));
check('vacuum leaves flyers out of reach', run('toolVacuum(180, 60, 3, () => true).got === 0 && flyers.length === 2 && vacuumTargetAt(180, 60, 3) === 0'));
run('flyers = [{x: 100, y: 60, m: WATER}];');
check('liquid flyers need the flask for pickup and targeting', run('toolVacuum(100, 60, 3, () => true).got === 0 && vacuumTargetAt(100, 60, 3) === 0 && flyers.length === 1'));
run('player.flask = true;');
check('flask allows liquid flyer targeting', run('vacuumTargetAt(100, 60, 3) === WATER'));
check('flask allows liquid flyer pickup', run('toolVacuum(100, 60, 3, () => true).got === 1 && bag[WATER] === 1 && flyers.length === 0'));
run('flyers = [{x: 100, y: 60, m: WATER, rain: true}];');
const moisture = run('weather.moist.reduce((a, b) => a + b, 0)');
check('rain is neither targeted nor collected', run('vacuumTargetAt(100, 60, 3) === 0 && toolVacuum(100, 60, 3, () => true).got === 0 && flyers.length === 1 && bag[WATER] === 1') && moisture === run('weather.moist.reduce((a, b) => a + b, 0)'));
run('flyers = [{x: 100, y: 60, m: COAL}, {x: 101, y: 60, m: SAND, bag: true}];');
check('pickup filter also applies to bag-blown flyers', run('toolVacuum(100, 60, 3, m => m === SAND).got === 1 && flyers.length === 1 && flyers[0].m === COAL && bag[COAL] === 0'));
check('targeting finds a material only in flight', run('vacuumTargetAt(100, 60, 3) === COAL'));
run('setCell(60*W+102, SAND, 20, 0);');
check('nearer flyer wins over a loose cell', run('vacuumTargetAt(100, 60, 3) === COAL'));
run('flyers[0].x = 103;');
check('nearer loose cell wins over a flyer', run('vacuumTargetAt(100, 60, 3) === SAND'));
run('spawnBar(BARCU, 100, 60, 0, 0);');
check('bars still take targeting priority', run('vacuumTargetAt(100, 60, 3) === BARCU'));
run('ents = []; bag.fill(0); bag[SAND] = bagTotal = bagCap();');
const fullPickup = run('toolVacuum(100, 60, 3, () => true)');
check('full bag leaves both flyers and cells untouched', fullPickup.full && fullPickup.got === 0 && run('flyers.length === 1 && mat[60*W+102] === SAND && bagTotal === bagCap()'));
run('bagTake(SAND, 1);');
const lastRoom = run('toolVacuum(100, 60, 3, () => true)');
check('last bag space goes to the flyer before the cell', lastRoom.full && lastRoom.got === 1 && lastRoom.m === run('COAL') && run('flyers.length === 0 && mat[60*W+102] === SAND && bagTotal === bagCap()'));
run('bag.fill(0); bagTotal = 0; EV.length = 0; for (let k = 0; k < 50; k++) flyers.push({x: 100, y: 60, m: COAL});');
check('flyers share the 48-grain budget and leave cells for later', run('toolVacuum(100, 60, 3, () => true).got === 48 && flyers.length === 2 && bag[COAL] === 48 && mat[60*W+102] === SAND'));
check('flyers emit suck on every other collected grain', run('EV.filter(e => e.t === "suck").length === 24'));
check('remaining budget collects both flyers and cells', run('toolVacuum(100, 60, 3, () => true).got === 3 && flyers.length === 0 && bag[COAL] === 50 && bag[SAND] === 1'));
// A real screw launches sand; hold the brush below its airborne arc, away from its mouth.
flatWorld(run, 240, 160, 140);
run('RS = 7; nextPowerBuild = 0; bot.x = 106; bot.y = 70; placeBlock(BP_SCREW, 1, 24, 15, true);');
rect(run, 96, 60, 99, 63, run('SAND'));
const screwCatch = run(`(() => {
let airborne = 0, caught = 0;
for (let k = 0; k < 120; k++) {
simTick();
const n = flyers.length;
const result = toolVacuum(106, 70, 3, m => m === SAND);
airborne += n - flyers.length; caught += result.got;
}
return {airborne, caught, bag: bag[SAND]};
})()`);
check('vacuum held under a screw arc catches its launched sand', screwCatch.airborne === 16 && screwCatch.caught === 16 && screwCatch.bag === 16, JSON.stringify(screwCatch));
done();