Bag: rock goes in as rubble, older saves convert, list shows everything held

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 16:53:40 -04:00
co-authored by Claude Opus 5.5
parent 569ec6e5b6
commit a671dcb59a
7 changed files with 34 additions and 3 deletions
+10
View File
@@ -4,6 +4,16 @@ 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 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. open or not verified. Keep each entry short; the git history has the details.
## 2026-10-27: bag counted things its list didn't show (Claude Code)
- Owner's exported save: bag total 1,522 but the list showed nothing for 1,000 basalt (from the
dev console's give), 200 clay (source not traced) and 322 molten slag (vacuumed with the Flask;
not in BAG_ORDER). `bagAdd` now stores solids as their drill yield; `deserializeWorld` converts
solids already in a saved bag; `bagMaterials` appends anything outside BAG_ORDER; dev `give`
reports what it added and refuses gases and solids with no yield.
- Files: `src/sim/items.js`, `src/sim/save.js`, `src/ui/hud.js`, `src/sim/devconsole.js`,
`test/t_basics.js`, `test/t_dev_console.js`. Tests: `python3 test/run_all.py --browser` all passed.
## 2026-10-27: t50 resonance ping (Claude Code) ## 2026-10-27: t50 resonance ping (Claude Code)
- Right click with the drill: `firePing` (`src/ui/actions.js`) calls `resonancePing` (`src/sim/drill.js`), - Right click with the drill: `firePing` (`src/ui/actions.js`) calls `resonancePing` (`src/sim/drill.js`),
+3 -2
View File
@@ -57,9 +57,10 @@ function devCommand(text, cursor) {
if (!args.length || !Number.isInteger(n) || n <= 0) return usage(); if (!args.length || !Number.isInteger(n) || n <= 0) return usage();
const m = devMaterial(args.join(' ')); const m = devMaterial(args.join(' '));
if (typeof m === 'string') return [m]; if (typeof m === 'string') return [m];
if (m === EMPTY) return ['Air cannot go in the bag.']; if (m === EMPTY || PH[m] === PH_GAS || (PH[m] === PH_SOLID && !YLD[m])) return [`${MD[m].name} cannot go in the bag.`];
if (n > 2147483647 - bag[m]) return ['Too many: a material count cannot exceed 2147483647.']; if (n > 2147483647 - bag[m]) return ['Too many: a material count cannot exceed 2147483647.'];
bagAdd(m, n, true); result = `Added ${n} ${MD[m].name}.`; break; const into = PH[m] === PH_SOLID && YLD[m] ? YLD[m] : m;
bagAdd(m, n, true); result = `Added ${n} ${MD[into].name}${into !== m ? ` (as ${MD[m].name.toLowerCase()} breaks up)` : ''}.`; break;
} }
case 'bit': case 'bag': { case 'bit': case 'bag': {
const tiers = cmd === 'bit' ? BIT_TIERS : TANK_TIERS; const tiers = cmd === 'bit' ? BIT_TIERS : TANK_TIERS;
+1
View File
@@ -279,6 +279,7 @@ function entsInCircle(cx, cy, r) {
// ---------- Bag ---------- // ---------- Bag ----------
function bagAdd(m, n, force) { function bagAdd(m, n, force) {
if (PH[m] === PH_SOLID && YLD[m]) m = YLD[m]; // rock and metal go in as what drilling them gives
const room = force ? n : Math.max(0, bagCap() - bagTotal); const room = force ? n : Math.max(0, bagCap() - bagTotal);
const k = Math.min(n, room); const k = Math.min(n, room);
bag[m] += k; bagTotal += k; bag[m] += k; bagTotal += k;
+2
View File
@@ -117,6 +117,8 @@ function deserializeWorld(meta, buffers) {
bot.x = meta.bot.x; bot.y = meta.bot.y; bot.lamp = meta.bot.lamp !== false; bot.vx = bot.vy = 0; bot.x = meta.bot.x; bot.y = meta.bot.y; bot.lamp = meta.bot.lamp !== false; bot.vx = bot.vy = 0;
bag = new Int32Array(NMAT); meta.bag.forEach((v, k) => { if (k < NMAT) bag[k] = v; }); bag = new Int32Array(NMAT); meta.bag.forEach((v, k) => { if (k < NMAT) bag[k] = v; });
for (const m of meta.flyers || []) if (m < NMAT) bag[m]++; // grains that were mid-air when saved for (const m of meta.flyers || []) if (m < NMAT) bag[m]++; // grains that were mid-air when saved
// solids saved in the bag (older saves) come back as what drilling them gives
for (let k = 0; k < NMAT; k++) if (bag[k] && PH[k] === PH_SOLID && YLD[k]) { bag[YLD[k]] += bag[k]; bag[k] = 0; }
bagTotal = 0; for (let k = 0; k < NMAT; k++) bagTotal += bag[k]; bagTotal = 0; for (let k = 0; k < NMAT; k++) bagTotal += bag[k];
Object.assign(player, meta.player); Object.assign(player, meta.player);
seen = new Uint8Array(NMAT); meta.seen.forEach((v, k) => { if (k < NMAT) seen[k] = v; }); seen = new Uint8Array(NMAT); meta.seen.forEach((v, k) => { if (k < NMAT) seen[k] = v; });
+7 -1
View File
@@ -26,7 +26,13 @@ function selectTool(k) {
if (k === GAME.tool) return; if (k === GAME.tool) return;
GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui'); GAME.tool = k; GAME.drag = null; hudDirty = true; renderHotbar(); playSfx('ui');
} }
function bagMaterials() { return BAG_ORDER.filter(m => bag[m] > 0); } // The bag's usual order first, then anything else it holds, so nothing in it is ever hidden.
const IN_BAG_ORDER = new Uint8Array(NMAT); for (const m of BAG_ORDER) IN_BAG_ORDER[m] = 1;
function bagMaterials() {
const l = BAG_ORDER.filter(m => bag[m] > 0);
for (let m = 1; m < NMAT; m++) if (bag[m] > 0 && !IN_BAG_ORDER[m]) l.push(m);
return l;
}
function blowList() { return [...GAME.blow].filter(m => bag[m] > 0); } function blowList() { return [...GAME.blow].filter(m => bag[m] > 0); }
// Shift + right click: an even mix of everything loose in the bag (bars never mix) // 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 blowAllList() { return bagMaterials().filter(m => PH[m] !== PH_ITEM && (PH[m] !== PH_LIQUID || player.flask)); }
+9
View File
@@ -148,4 +148,13 @@ const screwCatch = run(`(() => {
return {airborne, caught, bag: bag[SAND]}; 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)); check('vacuum held under a screw arc catches its launched sand', screwCatch.airborne === 16 && screwCatch.caught === 16 && screwCatch.bag === 16, JSON.stringify(screwCatch));
// Rock in the bag: solids go in as what drilling them gives, and older saves holding solids load
// that way too, so the bag never holds something its list can't show.
flatWorld(run, 120, 80, 60);
check('putting rock in the bag stores its rubble', run('(() => { bag.fill(0); bagTotal = 0; bagAdd(BASALT, 10, true); bagAdd(CLAY, 5, true); return bag[BASALT] === 0 && bag[RBAS] === 10 && bag[CLAY] === 0 && bag[LCLAY] === 5 && bagTotal === 15; })()'));
check('a save holding solid rock loads it as rubble with the same total', run(`(() => {
bag.fill(0); bagTotal = 0; const s = serializeWorld(); s.meta.bag[BASALT] = 1000; s.meta.bag[CLAY] = 200; s.meta.bag[MSLAG] = 7;
deserializeWorld(s.meta, s.buffers);
return bag[BASALT] === 0 && bag[RBAS] === 1000 && bag[CLAY] === 0 && bag[LCLAY] === 200 && bag[MSLAG] === 7 && bagTotal === 1207;
})()`));
done(); done();
+2
View File
@@ -70,4 +70,6 @@ check('dev mark and command log survive save round trip', run('devWorld && devSa
check('load resets speed, free building, pause and pending steps', run('devSpeed === 1 && !devFree && !devPaused && devSteps === 0')); check('load resets speed, free building, pause and pending steps', run('devSpeed === 1 && !devFree && !devPaused && devSteps === 0'));
run('alloc(240,160)'); run('alloc(240,160)');
check('a new world starts unmarked without a dev log', run('!devWorld && devLog.length === 0')); check('a new world starts unmarked without a dev log', run('!devWorld && devLog.length === 0'));
check('give turns rock into its rubble and says so', run(`(() => { enableDevConsole(); const before = bag[RBAS]; const out = devCommand('give basalt 50').join(' '); return bag[RBAS] === before + 50 && bag[BASALT] === 0 && /Basalt rubble/.test(out); })()`));
check('give refuses things that cannot be in a bag', run(`(() => { enableDevConsole(); const out = devCommand('give steam 5').join(' '); return /cannot go in the bag/.test(out) && bag[STEAM] === 0; })()`));
done(); done();