Sloped belts and filters drawn as one belt turned to 45° (t40)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 23:50:45 -04:00
co-authored by Claude Opus 5.5
parent e19cb784d2
commit 0cf615eb3d
11 changed files with 383 additions and 79 deletions
+100
View File
@@ -0,0 +1,100 @@
// Drawing geometry against real parts, without changing transport.
const fs = require('fs');
const path = require('path');
const { load, suite, flatWorld } = require('./harness');
const { run } = load();
const { check, done } = suite();
run(fs.readFileSync(path.join(__dirname, '../src/render/slope-belts.js'), 'utf8'));
flatWorld(run, 160, 120, 112);
for (const code of ['BP_BELT_UP', 'BP_BELT_DOWN', 'BP_FILTER_UP', 'BP_FILTER_DOWN']) {
run(`
mat.fill(EMPTY); part.fill(0); blockP.fill(0); blockV.fill(0);
var artCode = ${code}, artGrade = beltGrade(artCode), artB = 12 * BW + 12;
for (let k = -2; k <= 2; k++) placeBlock(artCode, 1, 12 + k, 12 + artGrade * k, true);
prepareSlopeBelts(0, 0, W, H);
`);
check(`${code}: diagonal neighbours continue the band`, run('(slopeBeltJoins[artB] & 12) === 12'));
check(`${code}: corner seals use the same band`, run(`(() => {
const i = slopeSeal(artCode, 12, 12);
return i < 0 || slopeBeltAt(i, i % W, (i / W) | 0) >= 0;
})()`));
check(`${code}: band is as thick as a flat belt`, run(`(() => {
const g = artGrade, x = 50 - g * 1.85 * Math.SQRT1_2, y = 50 + 1.85 * Math.SQRT1_2;
return slopeBeltShape(x, y, artB) < -1.8 &&
slopeBeltShape(x - g * 1.7 * Math.SQRT1_2, y + 1.7 * Math.SQRT1_2, artB) < 0 &&
slopeBeltShape(x - g * 2.1 * Math.SQRT1_2, y + 2.1 * Math.SQRT1_2, artB) > 0;
})()`));
check(`${code}: air below the triangle gets the continuous band`, run(`(() => {
for (let y = 48; y < 57; y++) for (let x = 48; x < 56; x++) {
const i = y * W + x, own = (y >> 2) * BW + (x >> 2);
if (!blockP[own] && mat[i] === EMPTY && slopeBeltAt(i, x, y) >= 0) return true;
}
return false;
})()`));
run(`
for (let end = 0; end < 2; end++) {
const side = end ? 1 : -1;
removeBlock(12 + side, 12 + side * artGrade, true);
placeBlock(BP_BELT, 1, 12 + side, 12 + (end ? artGrade > 0 : artGrade < 0), true);
}
prepareSlopeBelts(0, 0, W, H);
`);
check(`${code}: both flat joints form elbows`, run('(slopeBeltJoins[artB] & 3) === 3'));
check(`${code}: adjoining flats do not round off at the joint`, run(`(() => {
const l = (12 + (artGrade < 0)) * BW + 11, r = (12 + (artGrade > 0)) * BW + 13;
return !(flatBeltCaps(l) & 2) && !(flatBeltCaps(r) & 1);
})()`));
check(`${code}: overhang never covers terrain, parts, machines or bars`, run(`(() => {
for (let y = 48; y < 57; y++) for (let x = 48; x < 56; x++) {
const i = y * W + x, own = (y >> 2) * BW + (x >> 2);
if (blockP[own] || mat[i] !== EMPTY || slopeBeltAt(i, x, y) < 0) continue;
mat[i] = STONE; const ground = slopeBeltAt(i, x, y); mat[i] = EMPTY;
part[i] = P_GRATE; const built = slopeBeltAt(i, x, y); part[i] = 0;
mach[i] = 1; const machine = slopeBeltAt(i, x, y); mach[i] = 0;
occ[i] = 1; const bar = slopeBeltAt(i, x, y); occ[i] = 0;
return ground < 0 && built < 0 && machine < 0 && bar < 0;
}
return false;
})()`));
}
check('free end rounds its corners and stops at the end', run(`(() => {
const b = artB, x = 48 - Math.SQRT2, y = 48 + Math.SQRT2;
blockP[b] = BP_BELT_DOWN;
return slopeBeltShape(x + 0.8, y + 0.8, b, 0) < 0 && slopeBeltShape(x - 1, y - 1, b, 0) > 0;
})()`));
check('flat belts still meet diagonal tube elbows without an end cap', run(`(() => {
blockP[artB - 1] = BP_BELT;
blockP[artB] = BP_TUBE; blockV[artB] = 4;
const joined = !(flatBeltCaps(artB - 1) & 2);
blockP[artB] = BP_BELT_DOWN;
return joined;
})()`));
check('unpowered rollers and studs stand still', run(`(() => {
slopeBeltJoins[artB] &= ~16;
for (let y = 48; y < 56; y++) for (let x = 48; x < 56; x++)
if (slopeBeltColor(artB, x, y, 0, 150, 170, 190) !== slopeBeltColor(artB, x, y, 20, 150, 170, 190)) return false;
return true;
})()`));
check('powered rollers and studs reverse with the belt', run(`(() => {
slopeBeltJoins[artB] |= 16;
let moves = false;
for (let y = 48; y < 56; y++) for (let x = 48; x < 56; x++) {
blockV[artB] = 1;
const a = slopeBeltColor(artB, x, y, 12, 150, 170, 190), stopped = slopeBeltColor(artB, x, y, 0, 150, 170, 190);
blockV[artB] = 0;
if (a !== slopeBeltColor(artB, x, y, -12, 150, 170, 190)) return false;
if (a !== stopped) moves = true;
}
return moves;
})()`));
check('drawing leaves the seed, cells and saved block data unchanged', run(`(() => {
const snapshot = () => JSON.stringify([RS, Array.from(mat), Array.from(part), Array.from(blockP), Array.from(blockV)]);
const before = snapshot();
prepareSlopeBelts(0, 0, W, H);
for (let y = 40; y < 64; y++) for (let x = 40; x < 72; x++) {
const b = slopeBeltAt(y * W + x, x, y);
if (b >= 0) slopeBeltColor(b, x, y, 12, 150, 170, 190);
}
return snapshot() === before;
})()`));
done();
+3 -1
View File
@@ -138,7 +138,9 @@ fresh();
run('placeBlock(BP_BELT_UP,1,20,32,true);for(let l=1;l<=CARRY_DEPTH;l++)setCell((131-l)*W+80,SAND,20,0);tickStamp++;carryColumn(130*W+80,1,null);');
check('a slope carries the same full 96-grain column as a flat belt',run('Array.from({length:CARRY_DEPTH},(_,k)=>mat[(129-k)*W+81]===SAND).every(Boolean)'));
run(fs.readFileSync('src/render/slope-belts.js','utf8'));
check('sloped stripes vary along the face and travel with the motor',run('(()=>{const b=32*BW+20,c=(x,y,t)=>slopeBeltColor(b,x,y,t,0,0,0);return c(80,131,0)!==c(82,129,0) && c(80,131,0)===c(82,129,4);})()'));
// The renderer prepares joins and power first; stripe pitch is measured along the rotated band.
run('prepareSlopeBelts(0,0,W,H);');
check('sloped stripes vary along the face and travel with the motor',run('(()=>{const b=32*BW+20,c=(x,y,t)=>slopeBeltColor(b,x,y,t,0,0,0);return c(81,130,0)!==c(82,129,0) && c(81,130,0)===c(82,129,4);})()'));
run(fs.readFileSync('src/ui/selection.js','utf8'));
run(fs.readFileSync('src/ui/actions.js','utf8'));
run("globalThis.GAME={pieceDir:{belt:1,filter:1},filterCfg:{m:[SAND],inv:0},wallMat:RSTONE};globalThis.hudDirty=false;globalThis.aimBlock=()=>({bx:25,by:15});globalThis.toolId=()=>'build';globalThis.playSfx=()=>{};");