From 54e7756ead73bc161017918b9bd9a1822ad7b833 Mon Sep 17 00:00:00 2001 From: 44r0n7 <44r0n7+gitea@pm.me> Date: Tue, 27 Oct 2026 15:43:20 -0400 Subject: [PATCH] Cooled magma is ordinary rock: visible in the smooth look and drillable Co-Authored-By: Claude Opus 5.5 --- docs/DESIGN.md | 4 ++++ docs/agent-log.md | 13 +++++++++++++ src/render/world.js | 2 +- src/sim/drill.js | 5 +++-- src/sim/heat.js | 5 +++-- test/t_drill.js | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index b8d8bb1..c1615e5 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -517,6 +517,10 @@ above, including: and warned about before it happens (a glow near the limit). A heat-resistant screw comes later as an upgrade. Prompted by a long vertical screw that seemed to leak for no reason while it carried hot rubble. +- **Melts that cool loose are ordinary rock (2026-10-27).** Magma poured from the bag became an + invisible, undrillable basalt the game treated as built. Now any melt except metal and glass + cools into natural rock wherever it came from, and any solid no block, mold or machine owns can + be drilled, so nothing can get permanently stuck in the world. ## Ideas not built yet diff --git a/docs/agent-log.md b/docs/agent-log.md index 002c476..bda25a8 100644 --- a/docs/agent-log.md +++ b/docs/agent-log.md @@ -4,6 +4,19 @@ 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-27: cooled magma visible and drillable (Claude Code) + +- Owner report: bag magma in a brick box left invisible basalt that couldn't be picked up, + drilled or built on; melted screws left similar bits. Cause: melts froze into non-natural + rock unless the liquid was natural, the drill only breaks natural rock, and the smooth look's + terrain field skipped non-natural solids. +- `src/sim/heat.js`: non-metal, non-glass melts freeze into natural rock; bag water into natural + ice. `src/sim/drill.js`: `crumbly` no longer needs F_NAT; `builtCell` protects slope corner cells. + `src/render/world.js`: the terrain field covers any terrain-class solid without a block or + machine. Note: the bot still passes through old stuck basalt until it is drilled (it only + collides with natural solids). +- Tests: 7 checks in `test/t_drill.js`; `python3 test/run_all.py --browser` all passed. + ## 2026-10-27: t67 phase 4, "doesn't connect" marks (Claude Code) - `nearMiss(bx, by, end, planned)` in `src/sim/joins.js`: an end with no join that would join diff --git a/src/render/world.js b/src/render/world.js index 9f1ce26..81ebe9f 100644 --- a/src/render/world.js +++ b/src/render/world.js @@ -291,7 +291,7 @@ function drawWorld() { ((y - ((wb / BW) | 0) * BS + 8) << 16) | ((x - wb % BW * BS + 8) << 8); } top = cl << 24; - fpx[row + bx] = (tb ? 0xff000000 : 0) | (cl === 5 ? 0xff0000 : 0) | (cl === 3 ? 0xff00 : 0) | (cl === 1 && !builtForLight(i, x, y) ? 0xff : 0); + fpx[row + bx] = (tb ? 0xff000000 : 0) | (cl === 5 ? 0xff0000 : 0) | (cl === 3 ? 0xff00 : 0) | (cl === 1 && !blockP[(y >> 2) * BW + (x >> 2)] && !mach[i] ? 0xff : 0); // terrain: any solid nothing owns, natural or not } if (fv < 255) { const k = fv / 255; r *= k; g *= k; b *= k; diff --git a/src/sim/drill.js b/src/sim/drill.js index 974ab49..b521711 100644 --- a/src/sim/drill.js +++ b/src/sim/drill.js @@ -28,7 +28,7 @@ function pulseRay(ox, oy, ang, Wd, f) { } // Is a solid cell something the player built (or a part of a machine or mold)? Pulses leave those alone. function builtCell(i, x, y) { - return PARTMAT[mat[i]] || part[i] || mach[i] || blockP[((y / BS) | 0) * BW + ((x / BS) | 0)] !== 0 || (!(flags[i] & F_NAT) && moldAt(x, y) !== null); + return PARTMAT[mat[i]] || part[i] || mach[i] || blockP[((y / BS) | 0) * BW + ((x / BS) | 0)] !== 0 || slopeSealOwner(i) >= 0 || (!(flags[i] & F_NAT) && moldAt(x, y) !== null); } // One press-and-hold step: fires a pulse when one is due. Returns { fired, status }. function toolPulse(ax, ay, width, tune) { @@ -86,7 +86,8 @@ function crumbleAround(x, y) { for (const j of got) shatterCell(j, j % W, (j / W) | 0, mat[j]); } } -function crumbly(i) { const m = mat[i]; return PH[m] === PH_SOLID && (flags[i] & F_NAT) !== 0 && YLD[m] !== 0 && HARD[m] <= BIT_TIERS[player.bitTier].hard && !builtCell(i, i % W, (i / W) | 0); } +// Any solid nothing owns can be drilled: natural rock, and rock or metal that set loose in the world. +function crumbly(i) { const m = mat[i]; return PH[m] === PH_SOLID && YLD[m] !== 0 && HARD[m] <= BIT_TIERS[player.bitTier].hard && !builtCell(i, i % W, (i / W) | 0); } function shatterCell(i, x, y, m) { const to = YLD[m]; setCell(i, to, temp[i], 0); diff --git a/src/sim/heat.js b/src/sim/heat.js index 144c2ea..491d6cd 100644 --- a/src/sim/heat.js +++ b/src/sim/heat.js @@ -130,7 +130,7 @@ function react(i, x, y, m) { if (rnd() < 0.02 - t * 0.004) { const below = y < H - 1 ? PH[mat[i + W]] : PH_SOLID; const to = below === PH_EMPTY || below === PH_GAS ? SNOW : ICE; - mat[i] = to; life[i] = 0; flags[i] = fl & F_NAT; wakeXY(x, y); discover(to, x, y, WATER, 'freeze', 0); + mat[i] = to; life[i] = 0; flags[i] = to === ICE ? F_NAT : fl & F_NAT; wakeXY(x, y); discover(to, x, y, WATER, 'freeze', 0); } return; } @@ -143,7 +143,8 @@ function react(i, x, y, m) { if (L <= 0) { const to = FRZTO[m]; mat[i] = to; life[i] = 0; temp[i] = fz - 1; - flags[i] = (fl & F_NAT) | (CASTMETAL[m] ? F_CAST : 0); // natural magma freezes into natural rock + // metal and glass set into castings; any other melt cools into ordinary rock you can drill, wherever it came from + flags[i] = CASTMETAL[m] ? (fl & F_NAT) | F_CAST : PH[to] === PH_SOLID ? F_NAT : fl & F_NAT; wakeXY(x, y); discover(to, x, y, m, 'freeze', fz); } else { life[i] = L; temp[i] = fz; } return; diff --git a/test/t_drill.js b/test/t_drill.js index e967d3f..1cba0ec 100644 --- a/test/t_drill.js +++ b/test/t_drill.js @@ -139,4 +139,36 @@ run('for (let y = 80; y < 100; y++) for (let x = 120; x < 122; x++) bgm[y * W + hold(121, 92, 1, 0, 3); check('a tree trunk in the path is cut into wood chunks', count(run, run('LWOOD')) > 0, count(run, run('LWOOD'))); +// Melts that cool loose in the world (magma from the bag, metal spilled outside a mold) are +// ordinary solids the drill can break; built walls, slope corner cells and molds stay protected. +flatWorld(run, 160, 128, 120); +const melt = run(`(() => { + player.bitTier = BIT_TIERS.length - 1; bot.x = 60; bot.y = 60; + for (let y = 80; y < 88; y++) for (let x = 60; x < 72; x++) setCell(y * W + x, MAGMA, 1250, 0); + for (let k = 0; k < 2400; k++) simTick(); + for (let x = 90; x < 96; x++) setCell(110 * W + x, COPPER, 20, F_CAST); // metal that set outside a mold + let bas = 0, nat = 0, cu = 0, drillable = 0, cuDrill = 0; + for (let i = 0; i < N; i++) { + if (mat[i] === BASALT) { bas++; if (flags[i] & F_NAT) nat++; if (crumbly(i)) drillable++; } + if (mat[i] === COPPER) { cu++; if (crumbly(i)) cuDrill++; } + } + return { bas, nat, drillable, cu, cuDrill }; +})()`); +check('magma from the bag cools into natural basalt', melt.bas > 0 && melt.nat === melt.bas, JSON.stringify(melt)); +check('that basalt can be drilled', melt.drillable === melt.bas, JSON.stringify(melt)); +check('copper that set outside a mold can be drilled for scrap', melt.cu > 0 && melt.cuDrill === melt.cu, JSON.stringify(melt)); +check('basalt already stuck without the natural mark can be drilled too', + run('(() => { setCell(20 * W + 20, BASALT, 20, 0); player.bitTier = BIT_TIERS.length - 1; return crumbly(20 * W + 20); })()')); +check('built walls and slope corner cells stay safe from the drill', run(`(() => { + placeBlock(BP_WALL, RSTONE, 5, 5, true); placeBlock(BP_SLOPE_BL, RSTONE, 8, 5, true); + const wallCell = 5 * BS * W + 5 * BS + 1; let seal = -1; + for (let i = 0; i < N; i++) if (slopeSealOwner(i) >= 0 && PH[mat[i]] === PH_SOLID) { seal = i; break; } + return !crumbly(wallCell) && (seal < 0 || !crumbly(seal)); +})()`)); +check('a mold and what sets inside it stay safe from the drill', run(`(() => { + bag[RSTONE] = 400; bagTotal = 400; const err = placeMoldAt('ingot', 30, 10, RSTONE); + const mo = molds[molds.length - 1]; if (err || !mo) return false; + for (const i of moldCellsAt(mo.kind, mo.x, mo.y).walls) if (crumbly(i)) return false; + return true; +})()`)); done();