Sloped belts hold their load with moving cleats (t42); sloped filters are downhill shaking screens (t43)

The magic grip is gone: cleats every 8 face cells, 6 grains tall, carry
pockets uphill (about 71 grains/s against 90 flat) and overflow rolls
back. Sloped filters always run downhill; grains travel by gravity and
a shake, and sorting stays exact. Drawing needs another pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 19:47:59 -04:00
co-authored by Claude Opus 5.5
parent 1f8a89db6a
commit 578b55316b
28 changed files with 425 additions and 89 deletions
+16 -6
View File
@@ -36,7 +36,7 @@ function move(i, j, x, y, nx, ny) {
// Can a grain of material m (phase ph) enter cell j moving by (dx,dy)?
// 0 = no, 1 = into empty, 2 = by displacing what is there.
function enter(m, ph, j, dx, dy) {
if (occ[j]) return 0;
if (occ[j] || (ph === PH_POWDER && !cleatCarrying && cleatCells.length && (cleatMask[j] || (dy > 0 && cleatMask[j - dx - dy * W])))) return 0;
const p = part[j];
if (p !== 0) {
if (IS_SCREW[p]) { if (ph === PH_POWDER && SCREW_DX[p] === -dx && SCREW_DY[p] === -dy) return 0; } // liquids and gases flow through freely
@@ -72,20 +72,30 @@ let LSTEP = 8, LFILM = 4;
let CORNER_SEAL = true;
function powder(i, x, y, m) {
if (y >= H - 1) return;
const b = i + W;
const b = i + W, barriers = cleatCells.length;
if (enter(m, PH_POWDER, b, 0, 1)) { move(i, b, x, y, x, y + 1); return; }
// a grain at least twice as dense works its way down through a loose pile. Unlit fuel
// packs too tight for that, so a mixed furnace charge stays mixed until the fire gets in.
const t = mat[b];
if (PH[t] === PH_POWDER && DENS[m] >= DENS[t] * 2 && (!FUEL[t] || (flags[b] & F_BURN)) && stamp[b] !== tickStamp && !occ[b] && part[b] !== P_GRATE && part[b] !== P_FILTER && rnd() < (FUEL[t] ? SORTP_FUEL : SORTP)) { move(i, b, x, y, x, y + 1); awake[(y >> 5) * CW + (x >> 5)] = 40; return; }
if (PH[t] === PH_POWDER && (!barriers || (!cleatMask[i] && !cleatMask[b])) && DENS[m] >= DENS[t] * 2 && (!FUEL[t] || (flags[b] & F_BURN)) && stamp[b] !== tickStamp && !occ[b] && part[b] !== P_GRATE && part[b] !== P_FILTER && rnd() < (FUEL[t] ? SORTP_FUEL : SORTP)) { move(i, b, x, y, x, y + 1); awake[(y >> 5) * CW + (x >> 5)] = 40; return; }
// a grain lying right on a belt or filter is held by it: the surface carries it, it does not slide off the edge
if (t === BELTL || t === BELTR || part[b] === P_FILTER) return;
if ((t === BELTL || t === BELTR || part[b] === P_FILTER) &&
!isSlopeBelt(blockP[((y + 1) >> 2) * BW + (x >> 2)])) return;
// Feed a receiving screw mouth sideways instead of falling under its bottom casing.
if (t === BELTL || t === BELTR) {
const b = ((y + 1) >> 2) * BW + (x >> 2);
const d = isSlopeBelt(blockP[b]) ? (blockV[b] & 1 ? 1 : -1) : 0;
if (d && x + d >= 0 && x + d < W && IS_SCREW[part[i + d]]) {
if (enter(m, PH_POWDER, i + d, d, 0)) move(i, i + d, x, y, x + d, y);
return;
}
}
if (rnd() < STICK[m]) {
const d = (RS & 2) ? 1 : -1;
let nx = x + d;
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i + d]] !== PH_SOLID) && enter(m, PH_POWDER, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || (PH[mat[b]] !== PH_SOLID && (!barriers || !cleatMask[b])) || (PH[mat[i + d]] !== PH_SOLID && (!barriers || !cleatMask[i + d]))) && enter(m, PH_POWDER, b + d, d, 1)) { move(i, b + d, x, y, nx, y + 1); return; }
nx = x - d;
if (nx >= 0 && nx < W && (!CORNER_SEAL || PH[mat[b]] !== PH_SOLID || PH[mat[i - d]] !== PH_SOLID) && enter(m, PH_POWDER, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
if (nx >= 0 && nx < W && (!CORNER_SEAL || (PH[mat[b]] !== PH_SOLID && (!barriers || !cleatMask[b])) || (PH[mat[i - d]] !== PH_SOLID && (!barriers || !cleatMask[i - d]))) && enter(m, PH_POWDER, b - d, -d, 1)) { move(i, b - d, x, y, nx, y + 1); return; }
}
}
function passable(m, ph, j, dx, dy) {