Cooled magma is ordinary rock: visible in the smooth look and drillable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 15:43:20 -04:00
co-authored by Claude Opus 5.5
parent ccca66c0c1
commit 54e7756ead
6 changed files with 56 additions and 5 deletions
+4
View File
@@ -517,6 +517,10 @@ above, including:
and warned about before it happens (a glow near the limit). A heat-resistant screw comes later 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 as an upgrade. Prompted by a long vertical screw that seemed to leak for no reason while it
carried hot rubble. 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 ## Ideas not built yet
+13
View File
@@ -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 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: 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) ## 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 - `nearMiss(bx, by, end, planned)` in `src/sim/joins.js`: an end with no join that would join
+1 -1
View File
@@ -291,7 +291,7 @@ function drawWorld() {
((y - ((wb / BW) | 0) * BS + 8) << 16) | ((x - wb % BW * BS + 8) << 8); ((y - ((wb / BW) | 0) * BS + 8) << 16) | ((x - wb % BW * BS + 8) << 8);
} }
top = cl << 24; 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) { if (fv < 255) {
const k = fv / 255; r *= k; g *= k; b *= k; const k = fv / 255; r *= k; g *= k; b *= k;
+3 -2
View File
@@ -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. // 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) { 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 }. // One press-and-hold step: fires a pulse when one is due. Returns { fired, status }.
function toolPulse(ax, ay, width, tune) { 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]); 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) { function shatterCell(i, x, y, m) {
const to = YLD[m]; const to = YLD[m];
setCell(i, to, temp[i], 0); setCell(i, to, temp[i], 0);
+3 -2
View File
@@ -130,7 +130,7 @@ function react(i, x, y, m) {
if (rnd() < 0.02 - t * 0.004) { if (rnd() < 0.02 - t * 0.004) {
const below = y < H - 1 ? PH[mat[i + W]] : PH_SOLID; const below = y < H - 1 ? PH[mat[i + W]] : PH_SOLID;
const to = below === PH_EMPTY || below === PH_GAS ? SNOW : ICE; 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; return;
} }
@@ -143,7 +143,8 @@ function react(i, x, y, m) {
if (L <= 0) { if (L <= 0) {
const to = FRZTO[m]; const to = FRZTO[m];
mat[i] = to; life[i] = 0; temp[i] = fz - 1; 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); wakeXY(x, y); discover(to, x, y, m, 'freeze', fz);
} else { life[i] = L; temp[i] = fz; } } else { life[i] = L; temp[i] = fz; }
return; return;
+32
View File
@@ -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); 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'))); 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(); done();