Grains riding a belt or filter cast no shadow (t37)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 20:04:44 -04:00
co-authored by Claude Opus 5.5
parent 8087c28cb5
commit f744d786f1
4 changed files with 64 additions and 4 deletions
+2 -2
View File
@@ -352,9 +352,9 @@ Drawing (src/render/): browser only. This file: the renderer state R, colour tab
- functions: glowIndex, thermIdx, pipeCellColor, pipeBodyCell, thinPipeColor, beltLike, buildSky, initRender, resizeRender, viewCells, updateCamera, snapCamera, screenToWorld, worldToScreenPx, renderMonoFont - functions: glowIndex, thermIdx, pipeCellColor, pipeBodyCell, thinPipeColor, beltLike, buildSky, initRender, resizeRender, viewCells, updateCamera, snapCamera, screenToWorld, worldToScreenPx, renderMonoFont
- variables: R, GLOW_W, GLOW_R, GLOW_G, GLOW_B, THERM, PC, TREE_R, TREE_G, TREE_B, BG, SKY_R, SKY_G, SKY_B - variables: R, GLOW_W, GLOW_R, GLOW_G, GLOW_B, THERM, PC, TREE_R, TREE_G, TREE_B, BG, SKY_R, SKY_G, SKY_B
### src/render/light.js (95 lines) ### src/render/light.js (103 lines)
Propagated coloured light from glowing and burning cells. Propagated coloured light from glowing and burning cells.
- functions: builtForLight, computeLight - functions: builtForLight, onBeltForLight, computeLight
### src/render/world.js (332 lines) ### src/render/world.js (332 lines)
Scale colour without changing the smooth-look class. Scale colour without changing the smooth-look class.
+8
View File
@@ -4,6 +4,14 @@ 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-25: t37 follow-up: belt loads cast no shadows (Claude Code, Codex)
- The owner saw grains riding a belt overhead still shading the ground. `onBeltForLight(i)` in
`src/render/light.js` walks down through up to 12 loose cells to a belt or filter; such cells
are skipped by the light roof and pass light like air. `builtForLight` is unchanged, so the t33
loose-grain shading still applies to belt loads. Heaps on rock, silos and wall floors still shade.
- `test/t_light.js` (56 checks); `run_all.py --browser` ALL PASSED.
## 2026-10-25: t35: diagonal screw tubes (Claude Code, Codex) ## 2026-10-25: t35: diagonal screw tubes (Claude Code, Codex)
- Parts `P_TUR`..`P_TUL` (11-14), `TUBE_PARTS`, and an `IS_TUBE` table replacing every - Parts `P_TUR`..`P_TUL` (11-14), `TUBE_PARTS`, and an `IS_TUBE` table replacing every
+10 -2
View File
@@ -5,6 +5,14 @@
function builtForLight(i, x, y) { function builtForLight(i, x, y) {
return (PH[mat[i]] === PH_SOLID && !(flags[i] & F_NAT)) || blockP[(y >> 2) * BW + (x >> 2)] || mach[i] || part[i]; return (PH[mat[i]] === PH_SOLID && !(flags[i] & F_NAT)) || blockP[(y >> 2) * BW + (x >> 2)] || mach[i] || part[i];
} }
function onBeltForLight(i) {
for (let depth = 0; depth <= 12 && i < N; depth++, i += W) {
const m = mat[i], ph = PH[m];
if (m === BELTL || m === BELTR || part[i] === P_FILTER) return true;
if (ph !== PH_POWDER && ph !== PH_LIQUID) return false;
}
return false;
}
function computeLight() { function computeLight() {
const res = SET.lighting === 'fast' ? 4 : 2; const res = SET.lighting === 'fast' ? 4 : 2;
const M = Math.ceil(28 / res); const M = Math.ceil(28 / res);
@@ -32,7 +40,7 @@ function computeLight() {
let y = roofY[x]; let y = roofY[x];
for (; y < roofEnd; y++) { for (; y < roofEnd; y++) {
const i = y * W + x, ph = PH[mat[i]]; const i = y * W + x, ph = PH[mat[i]];
if ((ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) && !builtForLight(i, x, y)) break; if ((ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) && !builtForLight(i, x, y) && !((ph === PH_POWDER || ph === PH_LIQUID) && onBeltForLight(i))) break;
} }
lightRoof[lx] = y; lightRoof[lx] = y;
} }
@@ -42,7 +50,7 @@ function computeLight() {
const wx = (lx0 + lx) * res, k = ly * lw + lx; const wx = (lx0 + lx) * res, k = ly * lw + lx;
if (wx < 0 || wy < 0 || wx >= W || wy >= H) { la[k] = 0; lr[k] = lg[k] = lb[k] = 0; continue; } if (wx < 0 || wy < 0 || wx >= W || wy >= H) { la[k] = 0; lr[k] = lg[k] = lb[k] = 0; continue; }
const i = wy * W + wx, m = mat[i], ph = PH[m]; const i = wy * W + wx, m = mat[i], ph = PH[m];
la[k] = builtForLight(i, wx, wy) ? aAir : ph === PH_EMPTY ? (bgm[i] ? aGas : aAir) : ph === PH_GAS ? aGas : ph === PH_LIQUID ? aLiq : m === GLASS || m === ICE ? aGas : aSol; la[k] = builtForLight(i, wx, wy) || ((ph === PH_POWDER || ph === PH_LIQUID) && onBeltForLight(i)) ? aAir : ph === PH_EMPTY ? (bgm[i] ? aGas : aAir) : ph === PH_GAS ? aGas : ph === PH_LIQUID ? aLiq : m === GLASS || m === ICE ? aGas : aSol;
let r = 0, g = 0, b = 0; let r = 0, g = 0, b = 0;
if (wy < lightRoof[lx]) { r = skyK; g = skyK * 0.98; b = skyK * 0.94; } if (wy < lightRoof[lx]) { r = skyK; g = skyK * 0.98; b = skyK * 0.94; }
else if (wy < surfY[wx] && ph !== PH_SOLID) { const v = skyK * 0.9; r = v; g = v * 0.98; b = v * 0.95; } else if (wy < surfY[wx] && ph !== PH_SOLID) { const v = skyK * 0.9; r = v; g = v * 0.98; b = v * 0.95; }
+44
View File
@@ -60,4 +60,48 @@ run(`SET.daylight = 0; temp[140 * W + 80] = 1500; computeLight();`);
check('hot material inside the base lights its surroundings', run('lightAt(R.lr, 88, 140) > 0.2')); check('hot material inside the base lights its surroundings', run('lightAt(R.lr, 88, 140) > 0.2'));
run(`temp[140 * W + 80] = AMB[140]; bot.x = 72; bot.y = 130; bot.lamp = true; computeLight();`); run(`temp[140 * W + 80] = AMB[140]; bot.x = 72; bot.y = 130; bot.lamp = true; computeLight();`);
check('the bot lamp still lights the base', run('lightAt(R.lr, 80, 140) > 0.2')); check('the bot lamp still lights the base', run('lightAt(R.lr, 80, 140) > 0.2'));
flatWorld(run, 320, 200, 160);
run(`
SET.daylight = 1; bot.lamp = false;
for (let bx = 8; bx < 30; bx++) placeBlock(BP_BELT, 1, bx, 20, true);
for (let x = 32; x < 120; x++) for (let y = 77; y < 80; y++) setCell(y * W + x, SAND, AMB[y], 0);
for (let x = 180; x < 280; x++) {
setCell(80 * W + x, STONE, AMB[80], F_NAT);
for (let y = 77; y < 80; y++) setCell(y * W + x, SAND, AMB[y], 0);
}
updateRoof();
`);
for (const mode of ['full', 'fast']) {
run(`SET.lighting = '${mode}'; computeLight();`);
for (const channel of ['lr', 'lg', 'lb']) {
const [belt, open, stone] = run(`[lightAt(R.${channel}, 80, 84), lightAt(R.${channel}, 152, 84), lightAt(R.${channel}, 232, 84)]`);
check(`${mode} ${channel}: a loaded belt leaves daylight below it`, Math.abs(belt - open) < 1e-6, `${belt} / ${open}`);
check(`${mode} ${channel}: sand on natural stone still shades below`, stone < open * 0.95, `${stone} / ${open}`);
}
run('setCell(76 * W + 80, WATER, AMB[76], 0); updateRoof(); computeLight();');
const a = run('lightAt(R.latt, 80, 76)');
check(`${mode}: a wet belt load transmits like air`, Math.abs(a - Math.pow(0.978, mode === 'fast' ? 4 : 2)) < 1e-6, a);
run('setCell(76 * W + 80, EMPTY, AMB[76], 0); updateRoof();');
}
for (const [name, support, depth, gap, expected] of [
['left belt', 'mat[i] = BELTL', 12, '', true],
['right belt', 'mat[i] = BELTR', 12, '', true],
['filter', 'part[i] = P_FILTER', 12, '', true],
['deep heap', 'mat[i] = BELTR', 13, '', false],
['air gap', 'mat[i] = BELTR', 3, 'EMPTY', false],
['gas gap', 'mat[i] = BELTR', 3, 'STEAM', false],
['natural ledge', 'mat[i] = STONE; flags[i] = F_NAT', 3, '', false],
['wall floor', 'mat[i] = STONE', 3, '', false],
['tube', 'part[i] = P_TR', 3, '', false],
]) {
run(`
for (let y = 40; y <= 53; y++) { const i = y * W + 300; mat[i] = SAND; part[i] = 0; flags[i] = 0; }
{ const i = (40 + ${depth}) * W + 300; mat[i] = EMPTY; mat[i + W] = BELTR; ${support}; }
${gap ? `mat[41 * W + 300] = ${gap};` : ''}
`);
check(`${name}: belt load detection`, run('onBeltForLight(40 * W + 300)') === expected);
}
run('mat[(H - 1) * W + 300] = SAND;');
check('belt load detection stops at the world bottom', run('!onBeltForLight((H - 1) * W + 300)'));
done(); done();