Remove the Refiner and Stone cutter; old saves refund them on load (t4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-24
@@ -1,4 +1,4 @@
|
||||
// Machine overlays: fans, engines, refiner, cutter, silo and the rest, drawn on top of the world.
|
||||
// Machine overlays: fans, engines, silo and the rest, drawn on top of the world.
|
||||
// ---------- Overlays ----------
|
||||
function cellRect(x, y, w, h) { const p = worldToScreenPx(x, y); return [p.x, p.y, w * R.zp, h * R.zp]; }
|
||||
// Yellow and black hazard stripes on both top corners of a hopper
|
||||
@@ -63,29 +63,6 @@ function drawMachines() {
|
||||
ctx.beginPath(); ctx.moveTo(cx + dx * sw * 0.55, cy + dy * sh * 0.55); ctx.lineTo(cx + dx * sw * 0.9, cy + dy * sh * 0.9); ctx.stroke();
|
||||
} else if (mm.type === 'silo') {
|
||||
drawSilo(ctx, mm, sx, sy, zp);
|
||||
} else if (mm.type === 'cutter') {
|
||||
// hazard rims on the hopper, and a toothed saw blade in the floor that spins while it cuts
|
||||
const busy = tick - (mm.busyT || -99) < 12;
|
||||
drawHazardRims(ctx, sx, sy, sw, zp);
|
||||
const cx = sx + 4 * zp, cy = sy + 6.5 * zp, rr = zp * 1.4, a0 = busy ? R.frame * 0.35 : 0;
|
||||
ctx.fillStyle = '#c9cfd4'; ctx.beginPath();
|
||||
for (let t = 0; t < 16; t++) { const a = a0 + t * Math.PI / 8, r2 = t & 1 ? rr : rr * 0.78; ctx.lineTo(cx + Math.cos(a) * r2, cy + Math.sin(a) * r2); }
|
||||
ctx.closePath(); ctx.fill();
|
||||
ctx.fillStyle = '#3a4048'; ctx.beginPath(); ctx.arc(cx, cy, rr * 0.3, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.fillStyle = mm.blocked ? '#c4553f' : busy ? '#e0b43c' : '#4a5058';
|
||||
ctx.fillRect(sx + 2 * zp, sy + 7.6 * zp, 4 * zp, zp * 0.4);
|
||||
} else if (mm.type === 'refiner') {
|
||||
// hopper rim, two toothed grinding rollers in the floor (they turn while it works), the chute
|
||||
const busy = tick - (mm.busyT || -99) < 12;
|
||||
drawHazardRims(ctx, sx, sy, sw, zp);
|
||||
for (let k = 0; k < 2; k++) {
|
||||
const cx = sx + (k ? 7.5 : 4.5) * zp, cy = sy + 11 * zp, rr = zp * 1.15, a0 = (busy ? R.frame * 0.25 : 0) * (k ? -1 : 1);
|
||||
ctx.fillStyle = '#2c3036'; ctx.beginPath(); ctx.arc(cx, cy, rr, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.strokeStyle = '#9aa4ae'; ctx.lineWidth = Math.max(1, zp * 0.3);
|
||||
ctx.beginPath(); for (let t = 0; t < 6; t++) { const a = a0 + t * Math.PI / 3; ctx.moveTo(cx + Math.cos(a) * rr * 0.35, cy + Math.sin(a) * rr * 0.35); ctx.lineTo(cx + Math.cos(a) * rr, cy + Math.sin(a) * rr); } ctx.stroke();
|
||||
}
|
||||
ctx.fillStyle = mm.blocked ? '#c4553f' : busy ? '#e0b43c' : '#4a5058';
|
||||
ctx.beginPath(); ctx.moveTo(sx + 4 * zp, sy + 12 * zp); ctx.lineTo(sx + 8 * zp, sy + 12 * zp); ctx.lineTo(sx + 7 * zp, sy + 12.8 * zp); ctx.lineTo(sx + 5 * zp, sy + 12.8 * zp); ctx.closePath(); ctx.fill();
|
||||
} else if (mm.type === 'engine') {
|
||||
ctx.fillStyle = 'rgba(16,18,22,0.4)'; ctx.fillRect(sx + zp, sy + zp, sw - 2 * zp, sh - 3 * zp);
|
||||
const fx = sx + sw * (mm.dir ? 0.3 : 0.7), fy = sy + sh * 0.42, fr = sh * 0.3;
|
||||
|
||||
@@ -57,10 +57,6 @@ function machinesTick() {
|
||||
const inst = mm.flow * WHEEL_K;
|
||||
mm.out += (inst - mm.out) * 0.04; mm.flow = 0;
|
||||
mm.angle += Math.min(0.35, mm.out * 0.012);
|
||||
} else if (mm.type === 'refiner') {
|
||||
refinerTick(mm);
|
||||
} else if (mm.type === 'cutter') {
|
||||
cutterTick(mm);
|
||||
} else if (mm.type === 'silo') {
|
||||
siloTick(mm);
|
||||
} else if (mm.type === 'engine') {
|
||||
|
||||
@@ -76,9 +76,6 @@ function machineFootprint(type, x, y) {
|
||||
}
|
||||
} else if (type === 'fan') {
|
||||
for (let dy = 0; dy < 4; dy++) for (let dx = 0; dx < 4; dx++) out.push([x + dx, y + dy, 'hull']);
|
||||
} else if (type === 'cutter') {
|
||||
// a hopper 4 grains wide over the saw floor; bricks drop out underneath
|
||||
for (let dy = 0; dy < 8; dy++) for (let dx = 0; dx < 8; dx++) out.push([x + dx, y + dy, dx < 2 || dx >= 6 || dy >= 5 ? 'hull' : 'void']);
|
||||
} else if (type === 'silo') {
|
||||
// roof with a hatch, the body, a cone narrowing to the gate, and two legs; the gaps beside
|
||||
// the cone are left open
|
||||
@@ -92,9 +89,6 @@ function machineFootprint(type, x, y) {
|
||||
else k = dx >= 4 && dx <= 7 ? 'void' : dx === 3 || dx === 8 ? 'hull' : null; // the gate
|
||||
if (k) out.push([x + dx, y + dy, k]);
|
||||
}
|
||||
} else if (type === 'refiner') {
|
||||
// an open hopper 8 grains wide over the grinding floor; what it lets out drops from under the floor
|
||||
for (let dy = 0; dy < 12; dy++) for (let dx = 0; dx < 12; dx++) out.push([x + dx, y + dy, dx < 2 || dx >= 10 || dy >= 10 ? 'hull' : 'void']);
|
||||
} else if (type === 'dock') {
|
||||
// an open-topped hopper: 2-grain walls and floor around a 20x14 bin
|
||||
for (let dy = 0; dy < 16; dy++) for (let dx = 0; dx < 24; dx++) out.push([x + dx, y + dy, dx < 2 || dx >= 22 || dy >= 14 ? 'hull' : 'void']);
|
||||
|
||||
@@ -106,7 +106,7 @@ metalFamily({ solid: BRASS, molten: MBS, scrap: SCRBS, bar: BARBS, name: 'Brass'
|
||||
desc: 'Copper with zinc melted in. Golden and valuable.' });
|
||||
def(BARGL, { name: 'Glass brick', ph: PH_ITEM, col: [176, 218, 228], v: 0, dens: 2.5, k: 0, hard: 0, melt: 1300, meltTo: MGL, bag: true, desc: 'Cast from molten glass in an Ingot mold: 8 grains of glass. Sells well, and 2 make a glass wall block.' });
|
||||
def(CUTST, { name: 'Cut stone', col: [168, 162, 150], v: 6, k: 0.012, hard: 4, melt: 1450, meltTo: MAGMA, yld: BARSB, desc: 'Dressed stone blocks laid in courses. Neater than rough stone, and it stands 200°C more heat before it slumps.' });
|
||||
def(BARSB, { name: 'Stone brick', ph: PH_ITEM, col: [172, 166, 154], v: 0, dens: 2.6, k: 0, hard: 0, melt: 1450, meltTo: MAGMA, bag: true, desc: 'A block of stone cut square by a Stone cutter, from 8 grains of rubble. Builders buy them, and 2 make a cut stone wall block.' });
|
||||
def(BARSB, { name: 'Stone brick', ph: PH_ITEM, col: [172, 166, 154], v: 0, dens: 2.6, k: 0, hard: 0, melt: 1450, meltTo: MAGMA, bag: true, desc: 'A block of stone cut square by a Saw, from 8 grains of rubble. Builders buy them, and 2 make a cut stone wall block.' });
|
||||
def(PIPE, { name: 'Pipe', col: [176, 112, 72], v: 4, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, partMat: true });
|
||||
def(DOCKW, { name: 'Trade dock', col: [70, 82, 96], v: 6, k: 0.01, cap: 2, hard: 9, yld: 0, partMat: true, desc: 'The claim office\'s trade dock. Whatever drops through the grate in its floor is sold.' });
|
||||
def(HULL, { name: 'Machine', col: [84, 92, 102], v: 6, k: 0.03, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, partMat: true });
|
||||
|
||||
@@ -65,12 +65,8 @@ const PIECES = [
|
||||
desc: 'Makes power when liquid falls through it. More flow, more power.' },
|
||||
{ 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: 'cutter', hidden: true, name: 'Stone cutter', kind: 'machine', bw: 2, bh: 2, cost: [[CASH, 90]],
|
||||
desc: 'A saw under a hopper. Stone rubble dropped in is cut into stone bricks, one from every 8 grains, which drop out underneath. Builders buy stone bricks, and they make neat cut stone walls that stand more heat than rough stone. Anything else falls straight through.' },
|
||||
{ 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.' },
|
||||
{ id: 'refiner', hidden: true, name: 'Refiner', kind: 'machine', bw: 3, bh: 3, cost: [[CASH, 120]],
|
||||
desc: 'A crushing sieve. Drop stone, granite or basalt rubble, gravel, sand or dirt into its hopper: it grinds each grain, keeps the odd grain of ore it finds (clay, from dirt) and lets about half as much fine sand out of the chute underneath. Ore and anything else it cannot grind falls straight through. Keep the space under the chute clear, or a belt running under it.' },
|
||||
{ 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,
|
||||
|
||||
+5
-67
@@ -1,8 +1,8 @@
|
||||
// Processing machines: the Refiner, the Stone cutter and the Silo.
|
||||
// ---------- Refiner ----------
|
||||
// Processing: Grinder and Saw blocks, and the Silo.
|
||||
// ---------- Grinding and cutting ----------
|
||||
// Grinding a grain of rock, gravel, sand or dirt turns up the odd grain of ore: [chance, what],
|
||||
// tried in turn. If none turns up, half the time a grain of fine sand comes out, else nothing:
|
||||
// the rest is ground to dust and washed away, so a refiner halves a pile of waste rock.
|
||||
// the rest is ground to dust and washed away, so a Grinder halves a pile of waste rock.
|
||||
const REFINE = [];
|
||||
REFINE[RSTONE] = [[0.025, CUORE], [0.02, COAL], [0.015, FEORE], [0.006, SNORE]];
|
||||
REFINE[RGRAN] = [[0.02, FEORE], [0.012, ZNORE], [0.01, CUORE], [0.002, AGORE]];
|
||||
@@ -10,70 +10,8 @@ REFINE[RBAS] = [[0.03, FEORE], [0.01, ZNORE], [0.003, AUORE]];
|
||||
REFINE[GRAVEL] = [[0.03, FEORE], [0.02, CUORE], [0.01, SNORE], [0.003, ZNORE]];
|
||||
REFINE[SAND] = [[0.01, SNORE], [0.008, FEORE], [0.0015, AUORE]];
|
||||
REFINE[LDIRT] = [[0.08, LCLAY], [0.01, COAL], [0.008, CUORE], [0.003, ACORN]];
|
||||
const REFINE_SAND = 0.5, REFINE_RATE = 0.5; // grains ground per tick
|
||||
// a free cell under the refiner's chute, or -1 if the way out is blocked
|
||||
function refinerSlot(mm) {
|
||||
const y = mm.y + 12;
|
||||
if (y >= H) return -1;
|
||||
for (const dx of [5, 6, 4, 7]) { const j = y * W + mm.x + dx; if (mat[j] === EMPTY && !part[j] && !occ[j] && !mach[j]) return j; }
|
||||
return -1;
|
||||
}
|
||||
function refinerTick(mm) {
|
||||
mm.work = (mm.work || 0) + REFINE_RATE;
|
||||
if (mm.work < 1) return;
|
||||
let jobs = Math.floor(mm.work); mm.work -= jobs;
|
||||
const y = mm.y + 9;
|
||||
mm.blocked = false;
|
||||
for (let k = 0; k < 8 && jobs > 0; k++) {
|
||||
const x = mm.x + 2 + ((k + tick) & 7), i = y * W + x, m = mat[i];
|
||||
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
|
||||
const out = refinerSlot(mm);
|
||||
if (out < 0) { mm.blocked = true; break; }
|
||||
jobs--; mm.busyT = tick;
|
||||
const T = temp[i];
|
||||
if (PH[m] === PH_POWDER && REFINE[m]) {
|
||||
setCell(i, EMPTY, AMB[y], 0);
|
||||
let got = 0;
|
||||
for (const [chance, o] of REFINE[m]) if (rnd() < chance) { got = o; break; }
|
||||
if (got) { stats.refinedOre = (stats.refinedOre || 0) + 1; discover(got, x, y, m, 'refine'); }
|
||||
else if (rnd() < REFINE_SAND) got = SAND;
|
||||
if (got) setCell(out, got, T, 0);
|
||||
stats.refined = (stats.refined || 0) + 1;
|
||||
} else {
|
||||
// ore, coal, liquids: nothing to grind, so they go straight through
|
||||
setCell(out, m, T, 0); setCell(i, EMPTY, AMB[y], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---------- Stone cutter ----------
|
||||
const CUT_PER_BRICK = 8, CUT_RATE = 0.5;
|
||||
function cutterTick(mm) {
|
||||
mm.work = (mm.work || 0) + CUT_RATE;
|
||||
if (mm.work < 1) return;
|
||||
let jobs = Math.floor(mm.work); mm.work -= jobs;
|
||||
const y = mm.y + 4;
|
||||
mm.blocked = false;
|
||||
for (let k = 0; k < 4 && jobs > 0; k++) {
|
||||
const x = mm.x + 2 + ((k + tick) & 3), i = y * W + x, m = mat[i];
|
||||
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
|
||||
if (m === RSTONE) {
|
||||
if ((mm.cut || 0) + 1 >= CUT_PER_BRICK) {
|
||||
// a brick is ready: it needs room under the machine
|
||||
if (!entFreeAt(mm.x + 2, mm.y + 8)) { mm.blocked = true; break; }
|
||||
mm.cut = 0;
|
||||
const e = spawnBar(BARSB, mm.x + 2, mm.y + 8, (rnd() - 0.5) * 0.1, 0.05);
|
||||
if (e) { e.T = temp[i]; discover(BARSB, x, y, RSTONE, 'cut'); stats.bricks = (stats.bricks || 0) + 1; }
|
||||
} else mm.cut = (mm.cut || 0) + 1;
|
||||
setCell(i, EMPTY, AMB[y], 0);
|
||||
} else {
|
||||
// anything else drops through, out of the bottom
|
||||
const j = (mm.y + 8) * W + mm.x + 3 + (k & 1);
|
||||
if (mm.y + 8 >= H || mat[j] !== EMPTY || part[j] || occ[j]) { mm.blocked = true; break; }
|
||||
setCell(j, m, temp[i], 0); setCell(i, EMPTY, AMB[y], 0);
|
||||
}
|
||||
jobs--; mm.busyT = tick;
|
||||
}
|
||||
}
|
||||
const REFINE_SAND = 0.5;
|
||||
const CUT_PER_BRICK = 8;
|
||||
// Block hoppers share an outlet down a vertical stack; only its top spends work.
|
||||
let procState = new Map(), procBlocks = [], procDirty = true;
|
||||
function isProcCode(c) { return c === BP_GRIND || c === BP_SAW; }
|
||||
|
||||
+7
-1
@@ -74,7 +74,13 @@ function deserializeWorld(meta, buffers) {
|
||||
if (p >= P_TU && p <= P_TR) markPow(i, true);
|
||||
}
|
||||
for (let i = 0; i < N; i++) { const m = mat[i]; if (m === BELTL || m === BELTR || m === SHAFT) markPow(i, true); }
|
||||
for (const m of meta.machines) { const mm = addMachine(m.type, m.bx, m.by, m.dir); mm.on = m.on !== false; if (m.type === 'silo') { mm.sm = m.sm || 0; mm.sn = m.sn || 0; mm.open = !!m.open; } }
|
||||
// Old machines removed from the game are refunded.
|
||||
const removedMachines = { refiner: 120, cutter: 90 };
|
||||
for (const m of meta.machines) {
|
||||
if (!Object.hasOwn(PIECE_BY_ID, m.type)) { money += Object.hasOwn(removedMachines, m.type) ? removedMachines[m.type] : 0; continue; }
|
||||
const mm = addMachine(m.type, m.bx, m.by, m.dir); mm.on = m.on !== false;
|
||||
if (m.type === 'silo') { mm.sm = m.sm || 0; mm.sn = m.sn || 0; mm.open = !!m.open; }
|
||||
}
|
||||
molds = [];
|
||||
for (const m of meta.molds) molds.push(newMoldRecord(m.kind, m.x, m.y, m.wall));
|
||||
for (const [m, x, y, T] of meta.ents || []) { const e = { m, x, y, ix: x, iy: y, vx: 0, vy: 0, T, rest: 0 }; ents.push(e); entMark(e, 1); }
|
||||
|
||||
@@ -19,8 +19,6 @@ const PIECE_ICONS = {
|
||||
pump: '<circle cx="11" cy="14" r="6"/><path d="M11 14l3.5-2.5M11 14l-1 4.2M11 14l-4-1.2"/><path d="M15.5 9.5L20 5M16 5h4v4"/>',
|
||||
outlet: '<path d="M2 7h12a4 4 0 0 1 4 4v3"/><path d="M2 11h10a2 2 0 0 1 2 2v1"/><path d="M15 18v1.5M18 20v1.5M12 20v1.5"/>',
|
||||
fan: '<circle cx="12" cy="12" r="1.8"/><path d="M12 10.2c-.4-3.6 1.8-6 4-4.8 2 1.2.4 3.8-4 4.8zM13.8 12c3.6-.4 6 1.8 4.8 4-1.2 2-3.8.4-4.8-4zM12 13.8c.4 3.6-1.8 6-4 4.8-2-1.2-.4-3.8 4-4.8zM10.2 12c-3.6.4-6-1.8-4.8-4 1.2-2 3.8-.4 4.8 4z"/>',
|
||||
refiner: '<path d="M3 4h18l-5 7H8z"/><circle cx="9" cy="15.5" r="2.8"/><circle cx="15" cy="15.5" r="2.8"/><path d="M12 20v2"/>',
|
||||
cutter: '<path d="M5 3h14l-3 5H8z"/><circle cx="12" cy="15" r="5"/><circle cx="12" cy="15" r="1.2"/><path d="M12 9.2v1.6M17.8 15h-1.6M12 20.8v-1.6M6.2 15h1.6"/>',
|
||||
grinder: '<path d="M3 4h18l-5 7H8z"/><circle cx="9" cy="15.5" r="2.8"/><circle cx="15" cy="15.5" r="2.8"/><path d="M12 20v2"/>',
|
||||
saw: '<path d="M5 3h14l-3 5H8z"/><circle cx="12" cy="15" r="5"/><circle cx="12" cy="15" r="1.2"/><path d="M12 9.2v1.6M17.8 15h-1.6M12 20.8v-1.6M6.2 15h1.6"/>',
|
||||
silo: '<path d="M6 5.5h12v11H6z"/><path d="M6 5.5l1-2h10l1 2M6 16.5l4 3h4l4-3M7 16.5v5M17 16.5v5"/><path d="M15.5 8v6"/>',
|
||||
|
||||
@@ -91,8 +91,6 @@ function hoverInfo(ax, ay) {
|
||||
if (mm.type === 'wheel' || mm.type === 'engine') t2.push(`making ${mm.out.toFixed(1)} power`);
|
||||
if (mm.type === 'fan') { t2.push(mm.on ? 'on' : 'off · press F'); if (mm.hot > 760) { t2.push(`${mm.hot}°C · melts at 1085°C`); warn = 'too hot'; } }
|
||||
if (mm.type === 'silo') t2.push(mm.sn ? `holds ${mm.sn.toLocaleString('en-US')} / ${SILO_CAP.toLocaleString('en-US')} ${MD[mm.sm].name.toLowerCase()}` : `empty (holds ${SILO_CAP.toLocaleString('en-US')} grains of one material)`, mm.open ? 'gate open · F shuts it' : 'gate shut · F opens it');
|
||||
if (mm.type === 'cutter') t2.push(mm.blocked ? 'stopped: clear the space under it' : tick - (mm.busyT || -999) < 30 ? 'cutting' : 'idle: drop stone rubble in', `${(stats.bricks || 0).toLocaleString('en-US')} bricks cut`);
|
||||
if (mm.type === 'refiner') t2.push(mm.blocked ? 'stopped: clear the space under its chute' : tick - (mm.busyT || -999) < 30 ? 'grinding' : 'idle: drop rubble, gravel, sand or dirt in', `${(stats.refined || 0).toLocaleString('en-US')} ground, ${(stats.refinedOre || 0).toLocaleString('en-US')} ore found`);
|
||||
} else if (isProcCode(b)) {
|
||||
const s = procState.get((ay >> 2) * BW + (ax >> 2)), saw = b === BP_SAW;
|
||||
t1 = saw ? 'Saw' : 'Grinder';
|
||||
|
||||
Reference in New Issue
Block a user