Sloped belts and filters, laid by a 45° drag (t40)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+68
-14
@@ -4,7 +4,24 @@
|
||||
// clear). If a powder grain is blocked, the load above it stands still and the belt slips
|
||||
// underneath. A filter's own material drops through it instead, unless it is already full.
|
||||
const CARRY_DEPTH = 96;
|
||||
// Find the next carrying face at the same height or one grain above/below it.
|
||||
function conveyorStep(x, y, d) {
|
||||
const nx = x + d;
|
||||
if (nx < 0 || nx >= W) return 0;
|
||||
if ((x >> 2) === (nx >> 2)) return beltGrade(blockP[(y >> 2) * BW + (x >> 2)]) * d;
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
const yy = y + dy;
|
||||
if (yy < 0 || yy >= H) continue;
|
||||
const b = (yy >> 2) * BW + (nx >> 2), c = blockP[b], i = yy * W + nx;
|
||||
if ((isBelt(c) || isFilter(c)) && beltFaceY(c, nx >> 2, yy >> 2, nx) === yy &&
|
||||
(mat[i] === BELTL || mat[i] === BELTR || part[i] === P_FILTER)) return dy;
|
||||
}
|
||||
const b = (y >> 2) * BW + (x >> 2);
|
||||
return beltGrade(blockP[b]) * d;
|
||||
}
|
||||
function carryColumn(a, d, pass) {
|
||||
if (a < 0 || mat[a] === EMPTY) return;
|
||||
const sy = ((a / W) | 0) + 1, step = conveyorStep(a % W, sy, d);
|
||||
let blocked = false;
|
||||
for (let L = 0; L < CARRY_DEPTH; L++, a -= W) {
|
||||
if (a < 0) return;
|
||||
@@ -20,20 +37,30 @@ function carryColumn(a, d, pass) {
|
||||
if (blocked) continue; // filters still sift above a stopped load
|
||||
const x = a % W, nx = x + d;
|
||||
if (nx < 0 || nx >= W) return;
|
||||
const j = a + d, tj = mat[j];
|
||||
const j = a + d + step * W;
|
||||
if (j < 0 || j >= N) return;
|
||||
const tj = mat[j];
|
||||
// grains on a wet belt push through the water rather than stopping at it
|
||||
const wet = PH[tj] === PH_LIQUID && !occ[j] && stamp[j] !== tickStamp && (!part[j] || part[j] === P_FILTER || part[j] === P_GOPEN);
|
||||
if (!wet && !enter(m, PH_POWDER, j, d, 0)) {
|
||||
if (!wet && !enter(m, PH_POWDER, j, d, step)) {
|
||||
if (!pass) return;
|
||||
blocked = true; continue;
|
||||
}
|
||||
const y = (a / W) | 0, bj = j + W;
|
||||
// carried off the end with nothing under it: it leaves at belt speed and falls
|
||||
if (mat[j] === EMPTY && !part[j] && bj < N && (mat[bj] === EMPTY || PH[mat[bj]] === PH_GAS) && !occ[bj] && !part[bj]) launchGrain(a, nx, y, d * 0.5, 0);
|
||||
else move(a, j, x, y, nx, y);
|
||||
if (mat[j] === EMPTY && !part[j] && bj < N && (mat[bj] === EMPTY || PH[mat[bj]] === PH_GAS) && !occ[bj] && !part[bj]) launchGrain(a, nx, y + step, d * 0.5, step * 0.5);
|
||||
else move(a, j, x, y, nx, y + step);
|
||||
stats.moved++;
|
||||
}
|
||||
}
|
||||
// The running face grips its whole supported load, including on the ticks between steps.
|
||||
function holdSlopeColumn(a, pass) {
|
||||
for (let depth = 0; depth < CARRY_DEPTH && a >= 0; depth++, a -= W) {
|
||||
const m = mat[a];
|
||||
if (m === EMPTY || (PH[m] === PH_SOLID && !part[a])) return;
|
||||
if (PH[m] === PH_POWDER && !occ[a] && !(pass && pass[m])) stamp[a] = tickStamp;
|
||||
}
|
||||
}
|
||||
// Belts are perforated: liquid resting on one drips through to whatever open space is under it.
|
||||
function beltDrain(b) {
|
||||
const a = b - W, x = b % W;
|
||||
@@ -53,23 +80,48 @@ function launchGrain(i, x, y, vx, vy) {
|
||||
let filterR = [], filterL = [], filtersDirty = true;
|
||||
function rebuildFilters() {
|
||||
filtersDirty = false; filterR = []; filterL = [];
|
||||
for (let b = 0; b < NB; b++) if (blockP[b] === BP_FILTER) (blockV[b] & 128 ? filterR : filterL).push(b);
|
||||
for (let b = 0; b < NB; b++) if (isFilter(blockP[b])) (blockV[b] & 128 ? filterR : filterL).push(b);
|
||||
// front of the line first, so a full run moves as one
|
||||
filterR.sort((a, c) => (c % BW) - (a % BW)); filterL.sort((a, c) => (a % BW) - (c % BW));
|
||||
}
|
||||
function filterCarry(b, d) {
|
||||
if (blockP[b] !== BP_FILTER) return;
|
||||
const bx = b % BW, y = ((b / BW) | 0) * BS;
|
||||
function filterCarry(b, d, carrying = true) {
|
||||
if (!isFilter(blockP[b])) return;
|
||||
const bx = b % BW, by = (b / BW) | 0, code = blockP[b], y = by * BS;
|
||||
if (y < 1) return;
|
||||
if (!carrying && !isSlopeBelt(code)) return;
|
||||
const c = filterCfg.get(b), pass = c ? c.pass : null;
|
||||
for (let k = 0; k < BS; k++) {
|
||||
const x = d > 0 ? bx * BS + BS - 1 - k : bx * BS + k, top = y * W + x;
|
||||
if (part[top] === P_FILTER) carryColumn(top - W, d, pass);
|
||||
const x = d > 0 ? bx * BS + BS - 1 - k : bx * BS + k, top = beltFaceY(code, bx, by, x) * W + x;
|
||||
if (part[top] === P_FILTER) {
|
||||
if (carrying) carryColumn(top - W, d, pass);
|
||||
if (isSlopeBelt(code)) holdSlopeColumn(top - W, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Belts and filters share one front-first order, including their flat/slope joins.
|
||||
function carryBeltsAndFilters(belts, filters, d, bm, carrying) {
|
||||
let k = 0, f = 0;
|
||||
while (k < belts.length || f < filters.length) {
|
||||
const fx = f < filters.length ? (filters[f] % BW) * BS + (d > 0 ? BS - 1 : 0) : 0;
|
||||
if (f < filters.length && (k >= belts.length || (fx - belts[k] % W) * d > 0)) { filterCarry(filters[f++], d, carrying); continue; }
|
||||
const b = belts[k++];
|
||||
if (mat[b] !== bm || b < W) continue;
|
||||
const x = b % W, y = (b / W) | 0, code = blockP[(y >> 2) * BW + (x >> 2)];
|
||||
if (!carrying && !isSlopeBelt(code)) continue;
|
||||
if (isSlopeBelt(code) && beltFaceY(code, x >> 2, y >> 2, x) !== y) continue;
|
||||
if (slopeSealOwner(b) >= 0) continue;
|
||||
const above = mat[b - W];
|
||||
if (above === BELTL || above === BELTR || netSat(b) <= 0) continue;
|
||||
if (carrying) {
|
||||
if (PH[above] === PH_LIQUID) beltDrain(b);
|
||||
carryColumn(b - W, d, null);
|
||||
}
|
||||
if (isSlopeBelt(code)) holdSlopeColumn(b - W, null);
|
||||
}
|
||||
}
|
||||
// Change what a filter does; grains inside it that no longer pass are pushed out on top.
|
||||
function setFilter(b, cfg) {
|
||||
if (blockP[b] !== BP_FILTER) return false;
|
||||
if (!isFilter(blockP[b])) return false;
|
||||
const rec = filterRec(cfg);
|
||||
filterCfg.set(b, rec);
|
||||
const bx = b % BW, by = (b / BW) | 0;
|
||||
@@ -79,11 +131,13 @@ function setFilter(b, cfg) {
|
||||
}
|
||||
// The run of filters in a row that touch block b and do the same thing it does
|
||||
function filterRun(b) {
|
||||
if (blockP[b] !== BP_FILTER) return [];
|
||||
if (!isFilter(blockP[b])) return [];
|
||||
const f = filterInfo(b), dir = blockV[b] & 128, by = (b / BW) | 0, out = [b];
|
||||
for (const s of [-1, 1]) for (let bx = (b % BW) + s; bx >= 0 && bx < BW; bx += s) {
|
||||
const c = by * BW + bx;
|
||||
if (blockP[c] !== BP_FILTER || (blockV[c] & 128) !== dir || !sameFilter(filterInfo(c), f)) break;
|
||||
const yy = by + beltGrade(blockP[b]) * (bx - (b % BW));
|
||||
if (yy < 0 || yy >= BH) break;
|
||||
const c = yy * BW + bx;
|
||||
if (blockP[c] !== blockP[b] || (blockV[c] & 128) !== dir || !sameFilter(filterInfo(c), f)) break;
|
||||
out.push(c);
|
||||
}
|
||||
return out.sort((a, c) => a - c);
|
||||
|
||||
Reference in New Issue
Block a user