Rename screw tubes to screw conveyors (t44)
Player text says Screw conveyor; code uses screw (BP_SCREW, piece id screw, screwIntake...). No compatibility code for old saves or blueprints. Suite outputs match before and after apart from names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -44,7 +44,7 @@ function canPlaceBlock(bx, by, code = BP_WALL) {
|
||||
}
|
||||
return '';
|
||||
}
|
||||
// v: wall material (loose id) for walls, direction for belts and tubes
|
||||
// v: wall material (loose id) for walls, direction for belts and screws
|
||||
function placeBlock(code, v, bx, by, free, extra) {
|
||||
const err = canPlaceBlock(bx, by, code); if (err) return err;
|
||||
if (isWall(code) && !WALL_OF[v]) return 'Pick a wall material with R';
|
||||
@@ -61,7 +61,7 @@ function placeBlock(code, v, bx, by, free, extra) {
|
||||
else if (isProcCode(code)) setCell(i, code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB, AMB[y], 0);
|
||||
else if (code === BP_GATE) setCell(i, GATE, AMB[y], 0);
|
||||
else if (code === BP_GRATE) { part[i] = P_GRATE; wakeXY(x, y); }
|
||||
else if (code === BP_TUBE) { part[i] = TUBE_PARTS[v & 7]; markPow(i, true); wakeXY(x, y); }
|
||||
else if (code === BP_SCREW) { part[i] = SCREW_PARTS[v & 7]; markPow(i, true); wakeXY(x, y); }
|
||||
else if (isFilter(code)) { part[i] = P_FILTER; wakeXY(x, y); }
|
||||
else if (code === BP_PIPE) { part[i] = P_PIPE; wakeXY(x, y); }
|
||||
else if (isPipeCode(code)) setCell(i, PIPE, AMB[y], 0);
|
||||
@@ -99,7 +99,7 @@ function blockIntegrity(b) {
|
||||
else if (isProcCode(code)) { if (m === (code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB)) n++; }
|
||||
else if (code === BP_GATE) { if (m === GATE || p === P_GOPEN) n++; }
|
||||
else if (code === BP_GRATE) { if (p === P_GRATE) n++; }
|
||||
else if (code === BP_TUBE) { if (IS_TUBE[p]) n++; }
|
||||
else if (code === BP_SCREW) { if (IS_SCREW[p]) n++; }
|
||||
else if (isFilter(code)) { if (p === P_FILTER) n++; }
|
||||
else if (isPipeCode(code)) { if (m === PIPE || p === P_PIPE) n++; }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ function copyRegion(x0, y0, x1, y1) {
|
||||
}
|
||||
function flipBlueprint(bp) {
|
||||
const bw = bp.bw;
|
||||
const flipV = (code, v) => isBelt(code) ? 1 - v : isFilter(code) ? v ^ 128 : code === BP_TUBE ? [0, 3, 2, 1, 7, 6, 5, 4][v & 7]
|
||||
const flipV = (code, v) => isBelt(code) ? 1 - v : isFilter(code) ? v ^ 128 : code === BP_SCREW ? [0, 3, 2, 1, 7, 6, 5, 4][v & 7]
|
||||
: code === BP_PUMP || code === BP_OUTLET ? (((v & 3) === 1 ? 3 : (v & 3) === 3 ? 1 : v & 3) | (v & 4)) : v;
|
||||
return {
|
||||
v: 2, bw, bh: bp.bh, name: bp.name,
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@ function enter(m, ph, j, dx, dy) {
|
||||
if (occ[j]) return 0;
|
||||
const p = part[j];
|
||||
if (p !== 0) {
|
||||
if (IS_TUBE[p]) { if (ph === PH_POWDER && TDX[p] === -dx && TDY[p] === -dy) return 0; } // liquids and gases flow through freely
|
||||
if (IS_SCREW[p]) { if (ph === PH_POWDER && SCREW_DX[p] === -dx && SCREW_DY[p] === -dy) return 0; } // liquids and gases flow through freely
|
||||
else if (p === P_GRATE) { if (ph === PH_POWDER || m === MSLAG) return 0; } // thick slag does not run through a grate
|
||||
else if (p === P_INTAKE) { if (ph !== PH_GAS) return 0; }
|
||||
else if (p === P_FILTER) { if (ph === PH_POWDER && !filterPassAt(m, j)) return 0; }
|
||||
@@ -93,7 +93,7 @@ function passable(m, ph, j, dx, dy) {
|
||||
if (occ[j]) return false;
|
||||
const p = part[j];
|
||||
if (p !== 0) {
|
||||
if (IS_TUBE[p]) { if (ph === PH_POWDER && TDX[p] === -dx && TDY[p] === -dy) return false; }
|
||||
if (IS_SCREW[p]) { if (ph === PH_POWDER && SCREW_DX[p] === -dx && SCREW_DY[p] === -dy) return false; }
|
||||
else if (p === P_GRATE) { if (ph === PH_POWDER || m === MSLAG) return false; }
|
||||
else if (p === P_INTAKE) { if (ph !== PH_GAS) return false; }
|
||||
else if (p === P_FILTER) { if (ph === PH_POWDER && !filterPassAt(m, j)) return false; }
|
||||
@@ -199,7 +199,7 @@ function simMove() {
|
||||
if (stamp[i] === tickStamp) continue;
|
||||
if (ph === PH_SOLID) continue;
|
||||
const p = part[i];
|
||||
if (IS_TUBE[p] && ph === PH_POWDER) continue; // a screw tube moves its own grains
|
||||
if (IS_SCREW[p] && ph === PH_POWDER) continue; // a screw conveyor moves its own grains
|
||||
if (ph === PH_POWDER) powder(i, x, y, m); else if (ph === PH_LIQUID) liquid(i, x, y, m); else gas(i, x, y, m);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,4 +1,4 @@
|
||||
// How belts, filters and screw tubes move what is on or in them.
|
||||
// How belts, filters and screw conveyors move what is on or in them.
|
||||
// What rests on a belt or filter rides along: the whole stack standing on the carrying surface
|
||||
// steps one cell toward d together (belts run front of the line first, so the way ahead is
|
||||
// clear). If a powder grain is blocked, the load above it stands still and the belt slips
|
||||
@@ -70,7 +70,7 @@ function beltDrain(b) {
|
||||
if (!passable(mat[a], PH_LIQUID, d, 0, 1)) return;
|
||||
move(a, d, x, (a / W) | 0, x, (d / W) | 0);
|
||||
}
|
||||
// Take a grain out of cell i and send it flying from (x, y): what leaves a belt or a tube mouth
|
||||
// Take a grain out of cell i and send it flying from (x, y): what leaves a belt or a screw mouth
|
||||
function launchGrain(i, x, y, vx, vy) {
|
||||
const m = mat[i], T = temp[i];
|
||||
mat[i] = EMPTY; flags[i] = 0; life[i] = 0; stamp[i] = tickStamp;
|
||||
@@ -145,8 +145,8 @@ function filterRun(b) {
|
||||
|
||||
// A cell a grain can be pushed into: empty, or liquid or gas that simply trades places with it
|
||||
function roomFor(j) { const t = mat[j]; return t === EMPTY || PH[t] === PH_GAS || PH[t] === PH_LIQUID; }
|
||||
// Cross one tube block, far lanes first; corners retain their liquid/gas swaps.
|
||||
function tubeSpread(c, x, y, sx, sy, intake = false) {
|
||||
// Cross one screw block, far lanes first; corners retain their liquid/gas swaps.
|
||||
function screwSpread(c, x, y, sx, sy, intake = false) {
|
||||
const p = part[c], bx = x >> 2, by = y >> 2;
|
||||
for (;;) {
|
||||
const nx = x + sx, ny = y + sy;
|
||||
@@ -158,8 +158,8 @@ function tubeSpread(c, x, y, sx, sy, intake = false) {
|
||||
return c;
|
||||
}
|
||||
// An empty screw cell draws in loose material from behind it or from its sides
|
||||
// (not from the cell it pushes into), so a tube end sitting in a pile feeds itself.
|
||||
function tubeIntake(i, p, dx, dy) {
|
||||
// (not from the cell it pushes into), so a screw end sitting in a pile feeds itself.
|
||||
function screwIntake(i, p, dx, dy) {
|
||||
if (occ[i]) return;
|
||||
const x = i % W, y = (i / W) | 0;
|
||||
const start = (tick + i) % 3;
|
||||
@@ -175,28 +175,28 @@ function tubeIntake(i, p, dx, dy) {
|
||||
if (PH[m] !== PH_POWDER) continue; // the screw only takes loose material; liquids just flow through
|
||||
if (stamp[i] === tickStamp) return;
|
||||
const pj = part[j];
|
||||
if (IS_TUBE[pj]) continue; // other tube cells move their own contents
|
||||
if (IS_SCREW[pj]) continue; // other screw cells move their own contents
|
||||
if (pj === P_GRATE || pj === P_FILTER) continue;
|
||||
move(j, i, nx, ny, x, y);
|
||||
if (diagonal) tubeSpread(i, x, y, 0, (y & 3) < 2 ? 1 : -1, true);
|
||||
else if (ox !== -dx || oy !== -dy) tubeSpread(i, x, y, -ox, -oy, true);
|
||||
if (diagonal) screwSpread(i, x, y, 0, (y & 3) < 2 ? 1 : -1, true);
|
||||
else if (ox !== -dx || oy !== -dy) screwSpread(i, x, y, -ox, -oy, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Four lanes cross a block in four beats; the casing carries every lane across the corner.
|
||||
// Prefer the next diagonal block, then either straight join, then the open head.
|
||||
function diagonalTubeNext(i, p) {
|
||||
const x = i % W, y = (i / W) | 0, dx = TDX[p], dy = TDY[p];
|
||||
function diagonalScrewNext(i, p) {
|
||||
const x = i % W, y = (i / W) | 0, dx = SCREW_DX[p], dy = SCREW_DY[p];
|
||||
const xx = x + dx, bx = x & ~3, by = y & ~3;
|
||||
if (xx < 0 || xx >= W) return -1;
|
||||
if ((xx >> 2) === (x >> 2)) return i + dx;
|
||||
const yy = y + dy * BS;
|
||||
if (yy >= 0 && yy < H && IS_TUBE[part[yy * W + xx]]) return yy * W + xx;
|
||||
if (IS_TUBE[part[y * W + xx]]) return y * W + xx;
|
||||
if (yy >= 0 && yy < H && IS_SCREW[part[yy * W + xx]]) return yy * W + xx;
|
||||
if (IS_SCREW[part[y * W + xx]]) return y * W + xx;
|
||||
if (dy > 0 && yy < H && (mat[yy * W + xx] === BELTL || mat[yy * W + xx] === BELTR)) return ((yy & ~3) - 1) * W + xx;
|
||||
const hy = by + (dy > 0 ? BS : -1), hx = bx + (y & 3);
|
||||
if (hy >= 0 && hy < H && IS_TUBE[part[hy * W + hx]]) return hy * W + hx;
|
||||
if (hy >= 0 && hy < H && IS_SCREW[part[hy * W + hx]]) return hy * W + hx;
|
||||
// An open mouth discharges along the diagonal, clear of the last block.
|
||||
return yy >= 0 && yy < H ? yy * W + xx : -1;
|
||||
}
|
||||
|
||||
+45
-45
@@ -5,7 +5,7 @@
|
||||
// its cells as solid (occ).
|
||||
const BAR_W = 4, BAR_H = 2;
|
||||
let entCurM = 0; // the bar being moved, so a filter can let its own kind of bar through
|
||||
function entSolidCell(j) { const m = mat[j], p = part[j]; return occ[j] || PH[m] === PH_SOLID || PH[m] === PH_POWDER || (p !== 0 && (IS_TUBE[p] || p === P_GRATE || (p === P_FILTER && !filterPassAt(entCurM, j)))); }
|
||||
function entSolidCell(j) { const m = mat[j], p = part[j]; return occ[j] || PH[m] === PH_SOLID || PH[m] === PH_POWDER || (p !== 0 && (IS_SCREW[p] || p === P_GRATE || (p === P_FILTER && !filterPassAt(entCurM, j)))); }
|
||||
function entSupportCount(x, y) {
|
||||
if (y + BAR_H >= H) return BAR_W;
|
||||
let n = 0;
|
||||
@@ -38,97 +38,97 @@ function spawnBar(m, x, y, vx, vy, T) {
|
||||
return e;
|
||||
}
|
||||
function removeEnt(e) { entMark(e, 0); const k = ents.indexOf(e); if (k >= 0) ents.splice(k, 1); }
|
||||
// No transient tube state: even a loaded bar derives its ride from its eight cells.
|
||||
function entTubeAt(x, y) {
|
||||
// No transient screw state: even a loaded bar derives its ride from its eight cells.
|
||||
function entScrewAt(x, y) {
|
||||
if (x < 0 || y < 0 || x + BAR_W > W || y + BAR_H > H) return 0;
|
||||
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) {
|
||||
const p = part[yy * W + xx]; if (!IS_TUBE[p]) return 0;
|
||||
const p = part[yy * W + xx]; if (!IS_SCREW[p]) return 0;
|
||||
}
|
||||
return part[(y + 1) * W + x + 1];
|
||||
}
|
||||
function entTubeFree(x, y, tubeOnly = false) {
|
||||
function entScrewFree(x, y, screwOnly = false) {
|
||||
if (x < 0 || y < 0 || x + BAR_W > W || y + BAR_H > H) return false;
|
||||
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) {
|
||||
const j = yy * W + xx, p = part[j], tube = IS_TUBE[p];
|
||||
// gas and liquid don't stop a bar, in a tube or in the open
|
||||
if (occ[j] || (mat[j] !== EMPTY && PH[mat[j]] !== PH_GAS && PH[mat[j]] !== PH_LIQUID) || (tubeOnly ? !tube : !tube && entSolidCell(j))) return false;
|
||||
const j = yy * W + xx, p = part[j], screw = IS_SCREW[p];
|
||||
// gas and liquid don't stop a bar, in a screw or in the open
|
||||
if (occ[j] || (mat[j] !== EMPTY && PH[mat[j]] !== PH_GAS && PH[mat[j]] !== PH_LIQUID) || (screwOnly ? !screw : !screw && entSolidCell(j))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Check the swept rectangle too: a snap must never jump through a grain or another bar.
|
||||
function entTubeMove(e, x, y, tubeOnly = false, mouth = false) {
|
||||
if (!entTubeFree(x, y, tubeOnly)) return false;
|
||||
function entScrewMove(e, x, y, screwOnly = false, mouth = false) {
|
||||
if (!entScrewFree(x, y, screwOnly)) return false;
|
||||
if (mouth) {
|
||||
// Go around the receiving corner, checking both legs before moving.
|
||||
const acrossY = y > e.iy ? e.iy : y, downX = y > e.iy ? x : e.ix;
|
||||
for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++) if (!entTubeFree(downX, yy)) return false;
|
||||
for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entTubeFree(xx, acrossY)) return false;
|
||||
for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++) if (!entScrewFree(downX, yy)) return false;
|
||||
for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entScrewFree(xx, acrossY)) return false;
|
||||
} else for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++)
|
||||
for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entTubeFree(xx, yy)) return false;
|
||||
for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entScrewFree(xx, yy)) return false;
|
||||
e.x = e.ix = x; e.y = e.iy = y; e.vx = e.vy = 0; return true;
|
||||
}
|
||||
function entTubeLane(y, iy) { return Math.max(y & ~3, Math.min((y & ~3) + 2, iy)); }
|
||||
function entTubeEdge(e, dx, dy, k) {
|
||||
function entScrewLane(y, iy) { return Math.max(y & ~3, Math.min((y & ~3) + 2, iy)); }
|
||||
function entScrewEdge(e, dx, dy, k) {
|
||||
const x = e.ix + (dx > 0 ? BAR_W : dx < 0 ? -1 : k), y = e.iy + (dy > 0 ? BAR_H : dy < 0 ? -1 : k);
|
||||
return x < 0 || y < 0 || x >= W || y >= H ? -1 : y * W + x;
|
||||
}
|
||||
function entTubeEnter(e, dx, dy, mouth = false) {
|
||||
function entScrewEnter(e, dx, dy, mouth = false) {
|
||||
for (let k = 0; k < (dx ? BAR_H : BAR_W); k++) {
|
||||
const i = entTubeEdge(e, dx, dy, k);
|
||||
const i = entScrewEdge(e, dx, dy, k);
|
||||
if (i < 0) continue;
|
||||
const xx = i % W, yy = (i / W) | 0;
|
||||
const p = part[i]; if (!IS_TUBE[p] || (!dx && TDX[p]) || dx * TDX[p] + dy * TDY[p] < 0) continue;
|
||||
const p = part[i]; if (!IS_SCREW[p] || (!dx && SCREW_DX[p]) || dx * SCREW_DX[p] + dy * SCREW_DY[p] < 0) continue;
|
||||
// Vertical screws require the full width; horizontal screws use the nearest two-row lane.
|
||||
const x = (xx & ~3), y = entTubeLane(yy, e.iy);
|
||||
if (entTubeMove(e, x, TDX[p] || dy ? y : e.iy, true, mouth)) return true;
|
||||
const x = (xx & ~3), y = entScrewLane(yy, e.iy);
|
||||
if (entScrewMove(e, x, SCREW_DX[p] || dy ? y : e.iy, true, mouth)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function entTubeTick(e, p) {
|
||||
function entScrewTick(e, p) {
|
||||
e.vx = e.vy = 0; e.x = e.ix; e.y = e.iy;
|
||||
const ci = (e.iy + 1) * W + e.ix + 1;
|
||||
if ((tick & 1) || netSat(ci) <= 0 || rnd() > netSat(ci)) return;
|
||||
const dx = TDX[p], dy = TDY[p];
|
||||
if (dx && dy) { entDiagonalTubeTick(e, p); return; }
|
||||
const dx = SCREW_DX[p], dy = SCREW_DY[p];
|
||||
if (dx && dy) { entDiagonalScrewTick(e, p); return; }
|
||||
let x = e.ix + dx, y = e.iy + dy, next = 0, ax = 0, ay = 0;
|
||||
for (let k = 0; k < (dx ? BAR_H : BAR_W); k++) {
|
||||
const i = entTubeEdge(e, dx, dy, k);
|
||||
const i = entScrewEdge(e, dx, dy, k);
|
||||
if (i < 0) return;
|
||||
const xx = i % W, yy = (i / W) | 0;
|
||||
const q = part[i];
|
||||
if (IS_TUBE[q] && q !== p) { next = q; ax = xx; ay = yy; break; }
|
||||
if (IS_SCREW[q] && q !== p) { next = q; ax = xx; ay = yy; break; }
|
||||
}
|
||||
if (next) {
|
||||
if (TDX[next] === -dx && TDY[next] === -dy) return;
|
||||
x = ax & ~3; y = entTubeLane(ay, e.iy);
|
||||
if (entTubeMove(e, x, y, true)) stats.moved++;
|
||||
if (SCREW_DX[next] === -dx && SCREW_DY[next] === -dy) return;
|
||||
x = ax & ~3; y = entScrewLane(ay, e.iy);
|
||||
if (entScrewMove(e, x, y, true)) stats.moved++;
|
||||
return;
|
||||
}
|
||||
if (entTubeAt(x, y)) { if (entTubeMove(e, x, y, true)) stats.moved++; return; }
|
||||
if (entScrewAt(x, y)) { if (entScrewMove(e, x, y, true)) stats.moved++; return; }
|
||||
// Clear the mouth in one move so the next tick can derive ordinary falling from position.
|
||||
x = e.ix + dx * BAR_W; y = e.iy + dy * BAR_H;
|
||||
if (!entTubeFree(x, y) || entTubeAt(x, y)) return;
|
||||
if (!entScrewFree(x, y) || entScrewAt(x, y)) return;
|
||||
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) if (part[yy * W + xx]) return;
|
||||
if (entTubeMove(e, x, y)) { e.vx = dx ? dx * 0.5 : (rnd() - 0.5) * 0.4; e.vy = dy * 0.6; stats.moved++; }
|
||||
if (entScrewMove(e, x, y)) { e.vx = dx ? dx * 0.5 : (rnd() - 0.5) * 0.4; e.vy = dy * 0.6; stats.moved++; }
|
||||
}
|
||||
// A horizontal bar stays whole: one diagonal block per four screw beats.
|
||||
function entDiagonalTubeTick(e, p) {
|
||||
function entDiagonalScrewTick(e, p) {
|
||||
if (tick & 7) return;
|
||||
const dx = TDX[p], dy = TDY[p], bx = e.ix & ~3, by = e.iy & ~3, lane = e.iy & 3;
|
||||
const dx = SCREW_DX[p], dy = SCREW_DY[p], bx = e.ix & ~3, by = e.iy & ~3, lane = e.iy & 3;
|
||||
for (const [ox, oy] of [[dx, dy], [dx, 0], [0, dy]]) {
|
||||
const x = bx + ox * BS, y = by + oy * BS + lane, q = entTubeAt(x, y);
|
||||
const x = bx + ox * BS, y = by + oy * BS + lane, q = entScrewAt(x, y);
|
||||
if (!q) continue;
|
||||
if (TDX[q] * dx + TDY[q] * dy < 0) return;
|
||||
if (entTubeMove(e, x, y, true)) stats.moved++;
|
||||
if (SCREW_DX[q] * dx + SCREW_DY[q] * dy < 0) return;
|
||||
if (entScrewMove(e, x, y, true)) stats.moved++;
|
||||
return;
|
||||
}
|
||||
const x = bx + dx * BS;
|
||||
let y = by + dy * BS + lane;
|
||||
const belt = y >= 0 && y < H ? mat[y * W + x] : EMPTY;
|
||||
if (dy > 0 && (belt === BELTL || belt === BELTR)) y = (y & ~3) - BAR_H;
|
||||
if (!entTubeFree(x, y)) return;
|
||||
if (!entScrewFree(x, y)) return;
|
||||
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) if (part[yy * W + xx]) return;
|
||||
if (entTubeMove(e, x, y, false, dy < 0)) { e.vx = dx * 0.5; e.vy = dy * 0.6; stats.moved++; }
|
||||
if (entScrewMove(e, x, y, false, dy < 0)) { e.vx = dx * 0.5; e.vy = dy * 0.6; stats.moved++; }
|
||||
}
|
||||
// Bars stay level; their highest contact follows the face one grain per belt beat.
|
||||
function entSlopeBeltTick(e) {
|
||||
@@ -158,7 +158,7 @@ function entSlopeBeltTick(e) {
|
||||
if (tick & 1) return true;
|
||||
const y = face === H ? e.iy : face - BAR_H, x = e.ix + d;
|
||||
if (entFreeAt(x, y)) { e.vx = d * 0.5; e.vy = (y - e.iy) * 0.5; e.x = e.ix = x; e.y = e.iy = y; stats.moved++; }
|
||||
else if (!entTubeEnter(e, d, 0)) entTubeEnter(e, d, Math.sign(y - e.iy), true);
|
||||
else if (!entScrewEnter(e, d, 0)) entScrewEnter(e, d, Math.sign(y - e.iy), true);
|
||||
return true;
|
||||
}
|
||||
function entTick() {
|
||||
@@ -167,8 +167,8 @@ function entTick() {
|
||||
entMark(e, 0);
|
||||
entCurM = e.m;
|
||||
let ix = e.ix; const iy = e.iy;
|
||||
const tube = entTubeAt(ix, iy);
|
||||
if (tube) entTubeTick(e, tube);
|
||||
const screw = entScrewAt(ix, iy);
|
||||
if (screw) entScrewTick(e, screw);
|
||||
else if (!entSlopeBeltTick(e)) {
|
||||
// what is underneath?
|
||||
let belt = 0, bsat = 0, transport = false, support = iy + BAR_H >= H;
|
||||
@@ -205,10 +205,10 @@ function entTick() {
|
||||
e.x += e.vx; e.y += e.vy;
|
||||
// step toward the new rounded position one cell at a time
|
||||
let tx = Math.round(e.x), ty = Math.round(e.y), cx = ix, cy = iy;
|
||||
while (cx !== tx) { const s = Math.sign(tx - cx); if (entFreeAt(cx + s, cy)) cx += s; else if (belt && entTubeEnter(e, s, 0)) { cx = e.ix; cy = e.iy; e.x = cx; e.y = cy; break; } else { e.vx = 0; e.x = cx; break; } }
|
||||
while (cy !== ty) { const s = Math.sign(ty - cy); if (entTubeAt(cx, cy)) break; if (entFreeAt(cx, cy + s)) cy += s; else if (entTubeEnter(e, 0, s)) { cx = e.ix; cy = e.iy; break; } else { e.vy = 0; e.y = cy; break; } }
|
||||
if (support && !belt && entTubeEnter(e, 0, 1)) { cx = e.ix; cy = e.iy; }
|
||||
if (!entTubeAt(cx, cy) && !entFreeAt(cx, cy)) {
|
||||
while (cx !== tx) { const s = Math.sign(tx - cx); if (entFreeAt(cx + s, cy)) cx += s; else if (belt && entScrewEnter(e, s, 0)) { cx = e.ix; cy = e.iy; e.x = cx; e.y = cy; break; } else { e.vx = 0; e.x = cx; break; } }
|
||||
while (cy !== ty) { const s = Math.sign(ty - cy); if (entScrewAt(cx, cy)) break; if (entFreeAt(cx, cy + s)) cy += s; else if (entScrewEnter(e, 0, s)) { cx = e.ix; cy = e.iy; break; } else { e.vy = 0; e.y = cy; break; } }
|
||||
if (support && !belt && entScrewEnter(e, 0, 1)) { cx = e.ix; cy = e.iy; }
|
||||
if (!entScrewAt(cx, cy) && !entFreeAt(cx, cy)) {
|
||||
// something moved into us (a grain fell in): try to step up out of it
|
||||
let freed = false;
|
||||
for (let u = 1; u < 6; u++) if (entFreeAt(cx, cy - u)) { cy -= u; e.y = cy; freed = true; break; }
|
||||
|
||||
+14
-14
@@ -1,4 +1,4 @@
|
||||
// Per-tick machine work: steam engines, fans, belts, filters, screw tubes (machinesTick).
|
||||
// Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick).
|
||||
function engineIntake(mm) {
|
||||
for (const i of mm.intake) {
|
||||
if (mat[i] === STEAM) {
|
||||
@@ -69,7 +69,7 @@ function machinesTick() {
|
||||
for (let n = 1; n < nets.length; n++) {
|
||||
const net = nets[n];
|
||||
let s = 0; for (const p of net.prods) s += p.out;
|
||||
let d = net.nb * BELT_POWER + net.nt * TUBE_POWER;
|
||||
let d = net.nb * BELT_POWER + net.nt * SCREW_POWER;
|
||||
for (const f of net.fans) if (f.on) d += FAN_POWER;
|
||||
net.supply = s; net.demand = d;
|
||||
net.sat = POWER_FREE ? 1 : d > 0 ? Math.min(1, s / d) : 0;
|
||||
@@ -91,16 +91,16 @@ function machinesTick() {
|
||||
if (filtersDirty) rebuildFilters();
|
||||
carryBeltsAndFilters(beltL, filterL, -1, BELTL, (tick & 1) === 0);
|
||||
carryBeltsAndFilters(beltR, filterR, 1, BELTR, (tick & 1) === 0);
|
||||
// tubes: every loose grain inside steps one cell along, front of the line first, on the same
|
||||
// screws: every loose grain inside steps one cell along, front of the line first, on the same
|
||||
// beat as belts (every other tick), so a block-wide screw carries as much as a block-deep belt
|
||||
if ((tick & 1) === 0) for (let p = 1; p < IS_TUBE.length; p++) {
|
||||
if (!IS_TUBE[p]) continue;
|
||||
const list = tubeLists[p], dx = TDX[p], dy = TDY[p], dj = dy * W + dx, flat = dy === 0;
|
||||
if ((tick & 1) === 0) for (let p = 1; p < IS_SCREW.length; p++) {
|
||||
if (!IS_SCREW[p]) continue;
|
||||
const list = screwLists[p], dx = SCREW_DX[p], dy = SCREW_DY[p], dj = dy * W + dx, flat = dy === 0;
|
||||
for (let k = 0; k < list.length; k++) {
|
||||
const i = list[k];
|
||||
if (part[i] !== p || occ[i]) continue;
|
||||
const m = mat[i];
|
||||
if (m === EMPTY || PH[m] !== PH_POWDER) { if (m === EMPTY || PH[m] !== PH_SOLID) tubeIntake(i, p, dx, dy); continue; }
|
||||
if (m === EMPTY || PH[m] !== PH_POWDER) { if (m === EMPTY || PH[m] !== PH_SOLID) screwIntake(i, p, dx, dy); continue; }
|
||||
if (stamp[i] === tickStamp) continue;
|
||||
if (temp[i] > 1000 && rnd() < 0.02) {
|
||||
part[i] = 0; markPow(i, false); emit({ t: 'warn', x: i % W, y: (i / W) | 0, text: 'melted' }); wakeIdx(i);
|
||||
@@ -128,26 +128,26 @@ function machinesTick() {
|
||||
if (shifted) continue;
|
||||
}
|
||||
if (x + dx < 0 || x + dx >= W || y + dy < 0 || y + dy >= H) continue;
|
||||
const dest = dx && dy ? diagonalTubeNext(i, p) : i + dj;
|
||||
const dest = dx && dy ? diagonalScrewNext(i, p) : i + dj;
|
||||
if (dest < 0) continue;
|
||||
const nx = dest % W, ny = (dest / W) | 0;
|
||||
if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
|
||||
const j = dest;
|
||||
if (occ[j]) continue;
|
||||
const pj = part[j], tube = IS_TUBE[pj];
|
||||
if (tube && TDX[pj] === -dx && TDY[pj] === -dy) continue;
|
||||
const pj = part[j], screw = IS_SCREW[pj];
|
||||
if (screw && SCREW_DX[pj] === -dx && SCREW_DY[pj] === -dy) continue;
|
||||
const t = mat[j];
|
||||
if (!roomFor(j)) continue;
|
||||
if (pj === P_GRATE || pj === P_INTAKE || (pj === P_FILTER && !filterPassAt(m, j))) continue;
|
||||
if (!tube && !pj && t === EMPTY) {
|
||||
if (!screw && !pj && t === EMPTY) {
|
||||
// out of the mouth: the screw spits it clear, so the next grain is never blocked by the last
|
||||
launchGrain(i, nx, ny, dy && !dx ? (rnd() - 0.5) * (dy < 0 ? 0.9 : 0.1) : dx * (0.42 + rnd() * 0.16), dy ? dy * (0.5 + rnd() * 0.2) : rnd() * 0.08);
|
||||
stats.moved++; continue;
|
||||
}
|
||||
move(i, j, x, y, nx, ny); stats.moved++;
|
||||
if (tube && pj !== p) {
|
||||
// turning a corner: keep going across the new tube's width so every lane fills
|
||||
tubeSpread(j, nx, ny, dx, dy);
|
||||
if (screw && pj !== p) {
|
||||
// turning a corner: keep going across the new screw's width so every lane fills
|
||||
screwSpread(j, nx, ny, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -10,7 +10,7 @@ let nextPowerBuild = 0;
|
||||
function rebuildPower() {
|
||||
powerDirty = false;
|
||||
for (const i of pcells) netId[i] = 0;
|
||||
nets = [null]; beltL = []; beltR = []; tubeLists = Array.from({ length: IS_TUBE.length }, () => []);
|
||||
nets = [null]; beltL = []; beltR = []; screwLists = Array.from({ length: IS_SCREW.length }, () => []);
|
||||
const stack = [];
|
||||
for (const s of pcells) {
|
||||
if (netId[s]) continue;
|
||||
@@ -23,11 +23,11 @@ function rebuildPower() {
|
||||
const i = stack.pop();
|
||||
const m = mat[i], p = part[i];
|
||||
if (m === BELTL) { net.nb++; beltL.push(i); } else if (m === BELTR) { net.nb++; beltR.push(i); }
|
||||
if (IS_TUBE[p]) { net.nt++; tubeLists[p].push(i); }
|
||||
if (IS_SCREW[p]) { net.nt++; screwLists[p].push(i); }
|
||||
const x = i % W, y = (i / W) | 0;
|
||||
// Corner-touching screws share power, without connecting unrelated diagonal machinery.
|
||||
if (IS_TUBE[p] && TDX[p] && TDY[p]) for (let d = -1; d <= 1; d += 2) {
|
||||
const xx = x + d * TDX[p], yy = y + d * TDY[p], j = yy * W + xx;
|
||||
if (IS_SCREW[p] && SCREW_DX[p] && SCREW_DY[p]) for (let d = -1; d <= 1; d += 2) {
|
||||
const xx = x + d * SCREW_DX[p], yy = y + d * SCREW_DY[p], j = yy * W + xx;
|
||||
if (xx >= 0 && xx < W && yy >= 0 && yy < H && part[j] === p && isPow[j] && !netId[j]) { netId[j] = id; stack.push(j); }
|
||||
}
|
||||
let j;
|
||||
@@ -45,10 +45,10 @@ function rebuildPower() {
|
||||
if (mm.type === 'fan') net.fans.push(mm); else if (mm.type === 'wheel' || mm.type === 'engine') net.prods.push(mm);
|
||||
}
|
||||
beltL.sort((a, b) => (a % W) - (b % W) || a - b); beltR.sort((a, b) => (b % W) - (a % W) || b - a);
|
||||
for (const p of TUBE_PARTS) {
|
||||
tubeLists[p].sort((a, b) => {
|
||||
if (TDX[p] && TDY[p]) return ((b % W) - (a % W)) * TDX[p];
|
||||
return (TDY[p] || TDX[p]) < 0 ? a - b : b - a;
|
||||
for (const p of SCREW_PARTS) {
|
||||
screwLists[p].sort((a, b) => {
|
||||
if (SCREW_DX[p] && SCREW_DY[p]) return ((b % W) - (a % W)) * SCREW_DX[p];
|
||||
return (SCREW_DY[p] || SCREW_DX[p]) < 0 ? a - b : b - a;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ const SOLIDMETAL = new Uint8Array(NMAT); for (const m of [COPPER, IRON, STEEL, T
|
||||
|
||||
// ---------- Flags & parts ----------
|
||||
const F_NAT = 1, F_CAST = 2, F_BURN = 4;
|
||||
const P_NONE = 0, P_TU = 1, P_TD = 2, P_TL = 3, P_TR = 4, P_GRATE = 5, P_GOPEN = 6, P_WHEEL = 7, P_INTAKE = 8, P_FILTER = 9, P_PIPE = 10, P_TUR = 11, P_TDR = 12, P_TDL = 13, P_TUL = 14;
|
||||
const TUBE_PARTS = [P_TU, P_TR, P_TD, P_TL, P_TUR, P_TDR, P_TDL, P_TUL];
|
||||
const TUBE_DIRS = [[0, -1], [1, 0], [0, 1], [-1, 0], [1, -1], [1, 1], [-1, 1], [-1, -1]];
|
||||
const IS_TUBE = new Uint8Array(15), TDX = new Int8Array(15), TDY = new Int8Array(15);
|
||||
for (let v = 0; v < TUBE_PARTS.length; v++) {
|
||||
const p = TUBE_PARTS[v]; IS_TUBE[p] = 1; TDX[p] = TUBE_DIRS[v][0]; TDY[p] = TUBE_DIRS[v][1];
|
||||
const P_NONE = 0, P_SCREW_U = 1, P_SCREW_D = 2, P_SCREW_L = 3, P_SCREW_R = 4, P_GRATE = 5, P_GOPEN = 6, P_WHEEL = 7, P_INTAKE = 8, P_FILTER = 9, P_PIPE = 10, P_SCREW_UR = 11, P_SCREW_DR = 12, P_SCREW_DL = 13, P_SCREW_UL = 14;
|
||||
const SCREW_PARTS = [P_SCREW_U, P_SCREW_R, P_SCREW_D, P_SCREW_L, P_SCREW_UR, P_SCREW_DR, P_SCREW_DL, P_SCREW_UL];
|
||||
const SCREW_DIRS = [[0, -1], [1, 0], [0, 1], [-1, 0], [1, -1], [1, 1], [-1, 1], [-1, -1]];
|
||||
const IS_SCREW = new Uint8Array(15), SCREW_DX = new Int8Array(15), SCREW_DY = new Int8Array(15);
|
||||
for (let v = 0; v < SCREW_PARTS.length; v++) {
|
||||
const p = SCREW_PARTS[v]; IS_SCREW[p] = 1; SCREW_DX[p] = SCREW_DIRS[v][0]; SCREW_DY[p] = SCREW_DIRS[v][1];
|
||||
}
|
||||
|
||||
// ---------- Tool tiers ----------
|
||||
|
||||
@@ -46,7 +46,7 @@ def(SLAG, { name: 'Slag', ph: PH_POWDER, col: [76, 66, 64], v: 34, dens: 2.4, k:
|
||||
def(MSLAG, { name: 'Molten slag', ph: PH_LIQUID, col: [232, 112, 40], v: 16, dens: 3.6, k: 0.02, cap: 1.2, hard: 0, freeze: 1000, freezeTo: SLAG, fluid: 0.5, spread: 10, liqT: 1150, desc: 'Smelting waste. Trickles down through the charge but is too thick to pass a grate, so it pools there and sets into slag. Knocking out the mold below rakes it away.' });
|
||||
def(BRICK, { name: 'Brick', col: [168, 76, 56], v: 16, k: 0.004, hard: 3, melt: 1750, meltTo: MAGMA, yld: RBRICK, desc: 'Fired clay. Insulates and survives 1750°C.' });
|
||||
def(GLASS, { name: 'Glass', col: [178, 216, 224], v: 6, k: 0.006, hard: 2, melt: 1300, meltTo: MGL, alpha: 0.45, yld: CULLET, desc: 'Clear. Lets light through.' });
|
||||
def(COPPER, { name: 'Copper', col: [200, 122, 76], v: 8, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, yld: SCRCU, desc: 'Conducts heat well. Belts, tubes, fans, engines.' });
|
||||
def(COPPER, { name: 'Copper', col: [200, 122, 76], v: 8, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, yld: SCRCU, desc: 'Conducts heat well. Belts, screw conveyors, fans, engines.' });
|
||||
def(IRON, { name: 'Iron', col: [128, 131, 138], v: 8, k: 0.035, cap: 1.5, hard: 5, melt: 1538, meltTo: MFE, yld: SCRFE, desc: 'Strong metal. Melts at 1538°C.' });
|
||||
def(STEEL, { name: 'Steel', col: [172, 180, 192], v: 6, k: 0.035, cap: 1.5, hard: 6, melt: 1450, meltTo: MST, yld: SCRST, desc: 'Iron refined by blowing air through it while molten. The best bit material.' });
|
||||
def(WATER, { name: 'Water', ph: PH_LIQUID, col: [44, 100, 194], v: 6, dens: 1, k: 0.02, cap: 4, hard: 0, boil: 100, boilTo: STEAM, fluid: 1, spread: 48, alpha: 0.78, liqT: 20, bag: true, desc: 'Boils at 100°C. Steam that rises into the open sky comes back down as rain.' });
|
||||
@@ -77,7 +77,7 @@ def(CULLET, { name: 'Glass shards', ph: PH_POWDER, col: [196, 228, 236], v: 22,
|
||||
def(SCRCU, { name: 'Copper scrap', ph: PH_POWDER, col: [192, 118, 74], v: 36, dens: 5, k: 0.035, cap: 1.5, hard: 0, stick: 0.7, melt: 1085, meltTo: MCU, bag: true, desc: 'Copper that set outside a mold. Melt it again and cast it.' });
|
||||
def(SCRFE, { name: 'Iron scrap', ph: PH_POWDER, col: [124, 126, 132], v: 30, dens: 5, k: 0.03, cap: 1.5, hard: 0, stick: 0.7, melt: 1538, meltTo: MFE, bag: true, desc: 'Iron that set outside a mold. Melt it again and cast it.' });
|
||||
def(SCRST, { name: 'Steel scrap', ph: PH_POWDER, col: [168, 176, 188], v: 28, dens: 5, k: 0.03, cap: 1.5, hard: 0, stick: 0.7, melt: 1450, meltTo: MST, bag: true, desc: 'Steel that set outside a mold. Melt it again and cast it.' });
|
||||
def(BARCU, { name: 'Copper bar', ph: PH_ITEM, col: [214, 132, 82], v: 0, dens: 8.9, k: 0, hard: 0, melt: 1085, meltTo: MCU, bag: true, desc: 'A cast bar. Belts, screw tubes, fans and engines are made from these.' });
|
||||
def(BARCU, { name: 'Copper bar', ph: PH_ITEM, col: [214, 132, 82], v: 0, dens: 8.9, k: 0, hard: 0, melt: 1085, meltTo: MCU, bag: true, desc: 'A cast bar. Belts, screw conveyors, fans and engines are made from these.' });
|
||||
def(BARFE, { name: 'Iron bar', ph: PH_ITEM, col: [140, 143, 150], v: 0, dens: 7.9, k: 0, hard: 0, melt: 1538, meltTo: MFE, bag: true, desc: 'A cast bar of iron.' });
|
||||
def(BARST, { name: 'Steel bar', ph: PH_ITEM, col: [184, 192, 204], v: 0, dens: 7.9, k: 0, hard: 0, melt: 1450, meltTo: MST, bag: true, desc: 'A cast bar of steel.' });
|
||||
def(ACORN, { name: 'Acorn', ph: PH_POWDER, col: [150, 104, 56], v: 22, dens: 0.9, k: 0.006, hard: 0, stick: 0.6, ign: 300, flame: 700, flameF: 900, fuel: 30, burnTo: ASH, ashP: 0.3, bag: true, desc: 'A tree seed. Left on dirt or grass under open sky, it sprouts and grows into a tree.' });
|
||||
|
||||
+6
-6
@@ -4,7 +4,7 @@
|
||||
// Everything you build snaps to a grid of 4x4-grain blocks. The world itself
|
||||
// stays grain-level; only built pieces snap.
|
||||
const BS = 4;
|
||||
const BP_WALL = 1, BP_BELT = 2, BP_TUBE = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12, BP_PRESS = 13, BP_RACK = 14;
|
||||
const BP_WALL = 1, BP_BELT = 2, BP_SCREW = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12, BP_PRESS = 13, BP_RACK = 14;
|
||||
const BP_SLOPE_BL = 15, BP_SLOPE_BR = 16, BP_SLOPE_TL = 17, BP_SLOPE_TR = 18;
|
||||
// The shape is independent of the direction: reversing a belt never flips its face.
|
||||
const BP_BELT_UP = 19, BP_BELT_DOWN = 20, BP_FILTER_UP = 21, BP_FILTER_DOWN = 22;
|
||||
@@ -71,8 +71,8 @@ const PIECES = [
|
||||
desc: 'A solid block. Diagonal drags make a sloped face; T flips it. For one block, T cycles square and four slope corners. R picks the material: a square costs 16 grains or 2 bars; a slope costs 8 grains or 1 bar.' },
|
||||
{ id: 'belt', code: BP_BELT, name: 'Belt', kind: 'block', cost: [[CASH, 12]], dirs: ['left', 'right'],
|
||||
desc: 'Carries loose grains and bars resting on top of it, left or right, in a layer up to a block deep. Water and other liquids drain through it. Drag diagonally to make a sloped belt that carries up or down.' },
|
||||
{ id: 'tube', code: BP_TUBE, name: 'Screw tube', kind: 'block', cost: [[CASH, 25]], dirs: ['up', 'right', 'down', 'left', 'up-right', 'down-right', 'down-left', 'up-left'],
|
||||
desc: 'A turning screw that draws in loose grains and whole bars and pushes them along, four grains wide. Water and other liquids run straight through it; the screw never carries them. Drag a line to set its direction, including diagonals. R turns a single tube. Join either end to tubes or belts. Copper softens around 1000°C.' },
|
||||
{ id: 'screw', code: BP_SCREW, name: 'Screw conveyor', kind: 'block', cost: [[CASH, 25]], dirs: ['up', 'right', 'down', 'left', 'up-right', 'down-right', 'down-left', 'up-left'],
|
||||
desc: 'A turning screw inside a tube that draws in loose grains and whole bars and pushes them along, four grains wide. Water and other liquids run straight through it; the screw never carries them. Drag a line to set its direction, including diagonals. R turns a single screw conveyor. Join either end to screw conveyors or belts. Copper softens around 1000°C.' },
|
||||
{ id: 'filter', code: BP_FILTER, name: 'Filter', kind: 'block', cost: [[CASH, 40]], dirs: ['left', 'right'], filter: true,
|
||||
desc: 'A stretch of belt with a mesh in it. Put it in a line of belts and it joins them as one belt. Pick materials and a mode: drop them through and carry everything else along, or carry them and drop the rest. Liquids drain through. Drag diagonally to make a sloped filter that carries up or down.' },
|
||||
{ id: 'shaft', code: BP_SHAFT, name: 'Shaft', kind: 'block', cost: [[LWOOD, 8]], hidden: POWER_FREE,
|
||||
@@ -100,7 +100,7 @@ const PIECES = [
|
||||
{ id: 'fan', name: 'Fan', kind: 'machine', bw: 1, bh: 1, cost: [[CASH, 60]], dirs: ['right', 'down', 'left', 'up'],
|
||||
desc: 'Blows gas and forces air into fires, making them far hotter: its stream reaches 28 grains and passes through grates but not walls. Its copper body melts at 1085°C, so keep it outside the furnace, a block clear of the wall, blowing in through a grate. Press F to switch it on or off.' },
|
||||
{ id: 'silo', name: 'Silo', kind: 'machine', bw: 3, bh: 6, cost: [[CASH, 180]],
|
||||
desc: 'A steel bin on legs for one loose material: up to 4,000 grains go in through the hatch in its roof (drop them in from a belt or tube). Its gauge shows how full it is. Press F to open the gate in its cone and let it out underneath, about 60 grains a second; F again shuts it. The first material in decides what it holds until it is empty.' },
|
||||
desc: 'A steel bin on legs for one loose material: up to 4,000 grains go in through the hatch in its roof (drop them in from a belt or screw conveyor). Its gauge shows how full it is. Press F to open the gate in its cone and let it out underneath, about 60 grains a second; F again shuts it. The first material in decides what it holds until it is empty.' },
|
||||
{ id: 'engine', name: 'Steam engine', kind: 'machine', bw: 3, bh: 2, cost: [[BARCU, 10]], dirs: ['exhaust right', 'exhaust left'], hidden: POWER_FREE,
|
||||
desc: 'Steam rising into the intake slots on its underside drives it. Condensate drips out the exhaust side.' },
|
||||
{ id: 'dock', name: 'Trade dock', kind: 'machine', bw: 6, bh: 4, cost: [], hidden: true, fixed: true,
|
||||
@@ -117,8 +117,8 @@ PIECES.forEach((p, i) => { p.index = i; PIECE_BY_ID[p.id] = p; if (p.code) PIECE
|
||||
for (let c = BP_SLOPE_BL; c <= BP_SLOPE_TR; c++) PIECE_BY_CODE[c] = PIECE_BY_ID.wall;
|
||||
for (const c of [BP_BELT_UP, BP_BELT_DOWN]) PIECE_BY_CODE[c] = PIECE_BY_ID.belt;
|
||||
for (const c of [BP_FILTER_UP, BP_FILTER_DOWN]) PIECE_BY_CODE[c] = PIECE_BY_ID.filter;
|
||||
// power per grain of belt / tube (a block is 16 grains)
|
||||
const BELT_POWER = 0.03, TUBE_POWER = 0.125, FAN_POWER = 6;
|
||||
// power per grain of belt / screw (a block is 16 grains)
|
||||
const BELT_POWER = 0.03, SCREW_POWER = 0.125, FAN_POWER = 6;
|
||||
const WHEEL_K = 0.32, ENGINE_K = 40;
|
||||
|
||||
const MOLDS = {
|
||||
|
||||
+2
-2
@@ -30,14 +30,14 @@ const MILESTONES = [
|
||||
{ id: 'copper', title: 'Molten copper', msg: 'Molten copper sinks to the bottom of the charge. Get it into a mold before it sets.', hint: 'Copper ore melts at 1085°C. An open fire loses too much heat, so wall coal and ore in with blocks and leave a small vent at the top for smoke. Mix ore and coal about half and half: Ctrl+click coal and copper ore in the bag to blow a mix. Right click blows an even mix.' },
|
||||
{ id: 'bars', title: 'Copper bars', msg: 'Bars sell for far more than the ore in them, and make metal wall blocks.', hint: 'Build an Ingot mold (Build, 3) right under the furnace with a floor of clay Grates between them: molten copper drips through, while the charge and the thick slag stay up. When the bars set, they drop out of the bottom of the mold onto whatever is under it; a belt carries them off. Leave space below, or pick up the bars with the Pneumatic nozzle. Ingot molds empty themselves: bars, junk and scrap drop below, slag is thrown away and the grate above is raked. Removing any mold returns its set metal to your bag: whole bars as bars, the rest as scrap. Copper that sets in the furnace melts again next firing.' },
|
||||
{ id: 'bit1', title: 'Copper drill bit', hint: 'Cast copper in a Bit mold. Building the furnace right on top works. A Grate over the funnel keeps the charge out. Let it set, then press F.', reward: 'Copper bit. You can now dig iron ore.' },
|
||||
{ id: 'auto', title: 'Moving parts', msg: 'Machines can now haul for you. Feed furnaces automatically, and lift ore out of your mines.', hint: 'Belts carry grains and bars resting on them. Screw tubes push loose material along (never liquids); stacked pointing up they lift it out of a mine. A Filter lets one material fall through and carries the rest along, so you can sort ore from rubble. Move 300 grains with machines.' },
|
||||
{ id: 'auto', title: 'Moving parts', msg: 'Machines can now haul for you. Feed furnaces automatically, and lift ore out of your mines.', hint: 'Belts carry grains and bars resting on them. Screw conveyors push loose material along (never liquids); stacked pointing up they lift it out of a mine. A Filter lets one material fall through and carries the rest along, so you can sort ore from rubble. Move 300 grains with machines.' },
|
||||
{ id: 'glass', title: 'Glass', hint: 'Sand melts near 1300°C. Still air will not get a fire that hot: point a Fan into the fuel. Molten glass runs through a grate like metal, so cast it in an Ingot mold for glass bricks, which sell well.', reward: 'Flask unlocked. The nozzle can suck up and blow out liquids.' },
|
||||
{ id: 'iron', title: 'Molten iron', msg: 'Iron is hard to keep molten. A brick mold right under the hot zone works best.', hint: 'Iron ore melts at 1538°C. That needs forced air, and stone slumps at 1250°C, so build the furnace from brick.' },
|
||||
{ id: 'bit2', title: 'Iron drill bit', hint: 'Cast iron in a Bit mold. A stone mold melts at iron temperatures, so build it from brick.', reward: 'Iron bit. Granite is now diggable.' },
|
||||
{ id: 'steel', title: 'Steel', msg: 'Cast it in a brick Bit mold for the best drill bit there is.', hint: 'Blow air through molten iron. Aim a Fan at a pool of it and the excess carbon burns off, turning it into steel.' },
|
||||
{ id: 'bit3', title: 'Steel drill bit', hint: 'Cast steel in a brick Bit mold.', reward: 'Steel bit. Basalt is now diggable.' },
|
||||
{ id: 'tank', title: 'Bigger bag', hint: 'Cast any metal in a Tank mold and knock it out to carry more.' },
|
||||
{ id: 'sale', title: 'First sale', msg: 'Money buys machines: belts, screw tubes and fans. Refined goods pay far more than raw.', hint: 'Blow, belt or lift goods into the trade dock near where you started. They are paid for the moment they drop through the grate in its floor.' },
|
||||
{ id: 'sale', title: 'First sale', msg: 'Money buys machines: belts, screw conveyors and fans. Refined goods pay far more than raw.', hint: 'Blow, belt or lift goods into the trade dock near where you started. They are paid for the moment they drop through the grate in its floor.' },
|
||||
{ id: 'order', title: 'First order filled', msg: 'Bigger buyers notice you as you fill more orders.', hint: 'Buyers post orders on the Market page: an amount of one good by a deadline, at well over market price. Goods dropped into the dock fill an open order first.' },
|
||||
{ id: 'alloy', title: 'An alloy', msg: 'Alloys sell for far more than the metals that went into them.', hint: 'Tin ore lies a little deeper than copper, zinc deeper again. Melt tin into a pool of molten copper and it mixes into bronze; zinc makes brass.' },
|
||||
{ id: 'precious', title: 'Precious metal', hint: 'Silver lies deep in the granite, gold deeper still. Their ore sells high even raw, and much higher as bars.' },
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
const SAVE_VERSION = 2;
|
||||
const TOFF = 64; // temperatures are saved with an offset so sub-zero snow and ice survive
|
||||
function serializeWorld() {
|
||||
// grains in flight from a tube or belt land where they are (rain in the air simply carries on)
|
||||
// grains in flight from a screw or belt land where they are (rain in the air simply carries on)
|
||||
for (const f of flyers) if (!f.bag && !f.rain) landFlyer(f, Math.max(0, Math.min(W - 1, Math.floor(f.x))), Math.max(0, Math.min(H - 1, Math.floor(f.y))));
|
||||
flyers = flyers.filter(f => f.bag || f.rain);
|
||||
const t16 = new Uint16Array(N);
|
||||
@@ -86,7 +86,7 @@ function deserializeWorld(meta, buffers) {
|
||||
const p = savedPart[i];
|
||||
if (p === P_WHEEL || p === P_INTAKE) continue;
|
||||
part[i] = p;
|
||||
if (IS_TUBE[p]) markPow(i, true);
|
||||
if (IS_SCREW[p]) markPow(i, true);
|
||||
}
|
||||
for (let i = 0; i < N; i++) { const m = mat[i]; if (m === BELTL || m === BELTR || m === SHAFT) markPow(i, true); }
|
||||
// Old machines removed from the game are refunded.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ function toolVacuum(cx, cy, r, pick) {
|
||||
const m = mat[i], ph = PH[m];
|
||||
if (!(ph === PH_POWDER || (ph === PH_LIQUID && player.flask))) return;
|
||||
if (!pick(m)) return;
|
||||
if (IS_TUBE[part[i]]) return;
|
||||
if (IS_SCREW[part[i]]) return;
|
||||
if (bagTotal >= bagCap()) { full = true; return false; }
|
||||
if (!first) first = m;
|
||||
bagAdd(m, 1); setCell(i, EMPTY, AMB[y], 0); got++;
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ let awake, hot, hotLvl, powderArea, roofDirty;
|
||||
let loadWakeOrder = null, loadWakePending = null, loadWakeIndex = 0;
|
||||
let tick = 0, tickStamp = 1;
|
||||
let machines = [null], molds = [], nets = [null], pcells = new Set(), powerDirty = true;
|
||||
let beltL = [], beltR = [], tubeLists = Array.from({ length: IS_TUBE.length }, () => []);
|
||||
let beltL = [], beltR = [], screwLists = Array.from({ length: IS_SCREW.length }, () => []);
|
||||
let brokenMachines = [];
|
||||
let ghostMachines = [], ghostBlocked = new Set();
|
||||
const EV = [];
|
||||
@@ -55,7 +55,7 @@ function alloc(w, h) {
|
||||
roofDirty = new Uint8Array(CW).fill(1);
|
||||
powderArea = new Uint8Array(NCH).fill(1); // unknown areas get one visit before being ruled out
|
||||
machines = [null]; molds = []; nets = [null]; pcells = new Set(); powerDirty = true;
|
||||
beltL = []; beltR = []; tubeLists = Array.from({ length: IS_TUBE.length }, () => []); brokenMachines = [];
|
||||
beltL = []; beltR = []; screwLists = Array.from({ length: IS_SCREW.length }, () => []); brokenMachines = [];
|
||||
ghostMachines = []; ghostBlocked = new Set();
|
||||
tick = 0; tickStamp = 1; drillPulses = []; lastPulseTick = -99; undoStack = []; undoRec = null;
|
||||
pipeNets = []; pipeNetAt = null; pipesDirty = true;
|
||||
|
||||
Reference in New Issue
Block a user