Brick press: clay lumps into green bricks that fire into Bricks (t28)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 16:55:54 -04:00
co-authored by Claude Opus 5.5
parent 08d2d65242
commit 1da33e3703
21 changed files with 135 additions and 41 deletions
+6 -1
View File
@@ -90,7 +90,12 @@ function drawProcBlocks() {
const radius = sw * (saw ? 0.38 : 0.22), teeth = saw ? 10 : 6;
ctx.save();
ctx.lineWidth = Math.max(1, sw * 0.025);
for (let wheel = 0; wheel < (saw ? 1 : 2); wheel++) {
if (blockP[b] === BP_PRESS) {
const offset = busy ? (1 - Math.cos(tick * 0.3)) * sw * 0.12 : 0;
ctx.fillStyle = '#caa488'; ctx.fillRect(sx + sw * 0.27, sy + sw * 0.76, sw * 0.46, sw * 0.16);
ctx.fillStyle = '#7d8996'; ctx.fillRect(sx + sw * 0.45, sy, sw * 0.1, sw * 0.4 + offset);
ctx.fillStyle = '#b9c0c8'; ctx.fillRect(sx + sw * 0.12, sy + sw * 0.4 + offset, sw * 0.76, sw * 0.12);
} else for (let wheel = 0; wheel < (saw ? 1 : 2); wheel++) {
const cx = sx + sw * (saw ? 0.5 : wheel ? 0.73 : 0.27), cy = sy + sw * 0.5;
const a = wheel ? -angle : angle;
ctx.fillStyle = saw ? '#b9c0c8' : '#8a8f96'; ctx.strokeStyle = '#444b54';
+1 -1
View File
@@ -115,7 +115,7 @@ function drawWorld() {
}
else if (m === GATE) { if ((x & 1) === 0 && (y & 3) !== 0) { r -= 40; g -= 24; b -= 20; } }
else if (m === CUTST) { const lx = x & 3, ly = y & 3; if (ly === 1 || ly === 3 || lx === (ly < 2 ? 3 : 1)) { r -= 34; g -= 33; b -= 30; } else if (ly === 0 || ly === 2) { r += 10; g += 10; b += 9; } }
else if (m === GRINDER || m === SAWB) {
else if (m === GRINDER || m === SAWB || m === PRESSB) {
const lx = x & 3, ly = y & 3, frame = lx === 0 || lx === 3 || ly === 0 || ly === 3;
r = frame ? 96 : 38; g = frame ? 102 : 42; b = frame ? 112 : 50;
}
+3 -3
View File
@@ -26,7 +26,7 @@ function placeBlock(code, v, bx, by, free, extra) {
if (code === BP_WALL) setCell(i, WALL_OF[v], AMB[y], 0);
else if (code === BP_BELT) { setCell(i, v === 0 ? BELTL : BELTR, AMB[y], 0); markPow(i, true); }
else if (code === BP_SHAFT) { setCell(i, SHAFT, AMB[y], 0); markPow(i, true); }
else if (isProcCode(code)) setCell(i, code === BP_GRIND ? GRINDER : SAWB, AMB[y], 0);
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] = [P_TU, P_TR, P_TD, P_TL][v & 3]; markPow(i, true); wakeXY(x, y); }
@@ -53,7 +53,7 @@ function blockIntegrity(b) {
if (code === BP_WALL) { if (PH[m] === PH_SOLID && !(flags[i] & F_NAT) && !PARTMAT[m]) n++; }
else if (code === BP_BELT) { if (m === BELTL || m === BELTR) n++; }
else if (code === BP_SHAFT) { if (m === SHAFT) n++; }
else if (isProcCode(code)) { if (m === (code === BP_GRIND ? GRINDER : SAWB)) n++; }
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 (p >= P_TU && p <= P_TR) n++; }
@@ -83,7 +83,7 @@ function removeBlock(bx, by) {
if (p === P_PIPE) { part[i] = 0; wakeIdx(i); n++; }
else if (m === PIPE) { setCell(i, EMPTY, temp[i], 0); n++; }
} else if (code === BP_BELT || code === BP_SHAFT || code === BP_GATE || isProcCode(code)) {
if (m === BELTL || m === BELTR || m === SHAFT || m === GATE || m === GRINDER || m === SAWB) { setCell(i, EMPTY, temp[i], 0); markPow(i, false); n++; }
if (m === BELTL || m === BELTR || m === SHAFT || m === GATE || m === GRINDER || m === SAWB || m === PRESSB) { setCell(i, EMPTY, temp[i], 0); markPow(i, false); n++; }
if (p === P_GOPEN) { part[i] = 0; n++; wakeIdx(i); }
} else if (p) { part[i] = 0; markPow(i, false); wakeIdx(i); n++; }
}
+2 -2
View File
@@ -10,7 +10,7 @@ const PRICE = new Float32Array(NMAT);
for (const [m, v] of [
[SAND, 0.1], [LCLAY, 0.2], [LWOOD, 0.3], [ACORN, 0.5], [COAL, 0.5], [RGRAN, 0.2], [RBAS, 0.4],
[CUORE, 1], [FEORE, 1.6], [SNORE, 2], [ZNORE, 2.2], [AGORE, 6], [AUORE, 14],
[CHARCOAL, 1.2], [RBRICK, 0.8], [CULLET, 1.2], [BARGL, 14], [RSTONE, 0.02], [GRAVEL, 0.03], [BARSB, 0.6], [PLANK, 2.4],
[CHARCOAL, 1.2], [RBRICK, 0.8], [CULLET, 1.2], [BARGL, 14], [RSTONE, 0.02], [GRAVEL, 0.03], [BARSB, 0.6], [PLANK, 2.4], [GREENB, 2], [BARBR, 9],
[SCRCU, 3], [SCRFE, 5], [SCRST, 11], [SCRSN, 3.5], [SCRZN, 3.5], [SCRAG, 18], [SCRAU, 40], [SCRBZ, 8], [SCRBS, 7],
[BARCU, 40], [BARFE, 75], [BARST, 160], [BARSN, 50], [BARZN, 50], [BARAG, 220], [BARAU, 480], [BARBZ, 110], [BARBS, 95],
]) PRICE[m] = v;
@@ -73,7 +73,7 @@ const STANDING = ['Local buyers', 'County buyers', 'Regional buyers', 'National
function standing() { return Math.min(STANDING.length - 1, Math.floor(ordersDone / 4)); }
// Builders buy the cheap bulk stuff (rubble, gravel, sand, stone blocks, planks) in big lots at a few
// times the market price, so waste rock earns a little. [what, smallest lot, largest lot]
const BUILD_GOODS = [[RSTONE, 400, 1500], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80]];
const BUILD_GOODS = [[RSTONE, 400, 1500], [GRAVEL, 300, 1000], [SAND, 300, 900], [BARSB, 20, 80], [PLANK, 20, 80], [BARBR, 20, 80]];
function makeBuildersOrder() {
const cands = BUILD_GOODS.filter(([m]) => seen[m] && !orders.some(o => o.m === m));
if (!cands.length) return null;
+3
View File
@@ -136,6 +136,9 @@ function entTick() {
// heat: the bar tends toward the temperature around it
const T = temp[Math.min(H - 1, cy + 1) * W + Math.min(W - 1, cx + 1)];
e.T += (T - e.T) * 0.04;
if (FIRETO[e.m] && e.T >= 880 && rnd() < 0.004 + (e.T - 880) * 0.00012) {
e.m = FIRETO[e.m]; discover(e.m, e.ix, e.iy, GREENB, 'fire', 880);
}
if (e.T >= MELT[e.m]) {
// melts back into 8 grains of molten metal if they fit here
let fits = true;
+1 -1
View File
@@ -5,7 +5,7 @@ function checkLedger() {
const mark = id => { if (!ledger[id]) { ledger[id] = tick || 1; emit({ t: 'milestone', id }); } };
if (stats.dug >= 15) mark('dig');
if (seen[FIRE]) { mark('fire'); if (!player.lens) { player.lens = true; } }
if (seen[BRICK] || seen[RBRICK]) mark('brick');
if (seen[BRICK] || seen[RBRICK] || seen[BARBR]) mark('brick');
if (seen[CHARCOAL]) mark('charcoal');
if (stats.grown > 0) mark('grow');
if (seen[MCU]) mark('copper');
+7 -2
View File
@@ -11,8 +11,8 @@ const EMPTY = 0, GRASS = 1, DIRT = 2, LDIRT = 3, SAND = 4, GRAVEL = 5, CLAY = 6,
ZNV = 63, ZNORE = 64, ZINC = 65, MZN = 66, SCRZN = 67, BARZN = 68,
AGV = 69, AGORE = 70, SILVER = 71, MAG = 72, SCRAG = 73, BARAG = 74,
AUV = 75, AUORE = 76, GOLD = 77, MAU = 78, SCRAU = 79, BARAU = 80,
BRONZE = 81, MBZ = 82, SCRBZ = 83, BARBZ = 84, BRASS = 85, MBS = 86, SCRBS = 87, BARBS = 88, DOCKW = 89, MSLAG = 90, PIPE = 91, BARGL = 92, CUTST = 93, BARSB = 94, GRINDER = 95, SAWB = 96, PLANK = 97;
const NMAT = 98;
BRONZE = 81, MBZ = 82, SCRBZ = 83, BARBZ = 84, BRASS = 85, MBS = 86, SCRBS = 87, BARBS = 88, DOCKW = 89, MSLAG = 90, PIPE = 91, BARGL = 92, CUTST = 93, BARSB = 94, GRINDER = 95, SAWB = 96, PLANK = 97, PRESSB = 98, GREENB = 99, BARBR = 100;
const NMAT = 101;
const MD = [];
function def(id, o) {
@@ -64,6 +64,7 @@ def(SHAFT, { name: 'Shaft', col: [150, 110, 64], v: 6, k: 0.005, hard: 2, ign: 3
def(GATE, { name: 'Gate', col: [146, 74, 58], v: 6, k: 0.004, hard: 3, melt: 1750, meltTo: MAGMA, partMat: true });
def(GRINDER, { name: 'Grinder', col: [64, 70, 80], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
def(SAWB, { name: 'Saw', col: [74, 80, 90], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
def(PRESSB, { name: 'Brick press', col: [74, 80, 90], v: 4, k: 0.035, hard: 6, melt: 1450, meltTo: MST, partMat: true });
def(SPRING, { name: 'Spring', col: [92, 112, 128], v: 30, k: 0.012, hard: 9, yld: 0, desc: 'Groundwater seeps out of this rock whenever there is room next to it.' });
def(RSTONE, { name: 'Stone rubble', ph: PH_POWDER, col: [126, 125, 130], v: 30, dens: 1.8, k: 0.01, hard: 0, melt: 1250, meltTo: MAGMA, bag: true, desc: 'Broken stone. 16 grains make a stone block.' });
def(RGRAN, { name: 'Granite rubble', ph: PH_POWDER, col: [164, 128, 124], v: 32, dens: 1.9, k: 0.01, hard: 0, melt: 1260, meltTo: MAGMA, bag: true, desc: 'Broken granite. Makes granite blocks.' });
@@ -106,6 +107,10 @@ 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(PLANK, { name: 'Plank', ph: PH_ITEM, col: [204, 158, 104], v: 0, dens: 0.6, k: 0, hard: 0, melt: 9999, bag: true, desc: 'Sawn from 8 wood chunks by a Saw. Builders buy them, and 2 make a wood wall block.' });
def(GREENB, { name: 'Green brick', ph: PH_ITEM, col: [202, 164, 136], v: 0, dens: 1.8, k: 0, hard: 0, melt: 1750, meltTo: MAGMA, bag: true, desc: 'Unfired clay pressed into shape by a Brick press. Fire it above ~900°C to make a Brick.' });
def(BARBR, { name: 'Brick', ph: PH_ITEM, col: [174, 76, 56], v: 0, dens: 1.8, k: 0, hard: 0, melt: 1750, meltTo: MAGMA, bag: true, desc: 'Fired clay brick. Builders buy them, and 2 make a brick wall block.' });
// An item that fires into another item when hot.
const FIRETO = new Uint8Array(NMAT); FIRETO[GREENB] = BARBR;
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 block', 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 });
+5 -3
View File
@@ -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;
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;
function isPipeCode(c) { return c === BP_PIPE || c === BP_PUMP || c === BP_OUTLET; }
// Filters: each filter block has a set of materials and a mode. In drop mode (inv 0)
// the chosen materials fall through and everything else rides along on top; in carry
@@ -25,8 +25,8 @@ function sameFilter(a, b) { return a.inv === b.inv && a.m.length === b.m.length
const WALL_OF = new Uint8Array(NMAT);
WALL_OF[LDIRT] = DIRT; WALL_OF[RSTONE] = STONE; WALL_OF[RGRAN] = GRANITE; WALL_OF[RBAS] = BASALT; WALL_OF[LCLAY] = CLAY;
WALL_OF[LWOOD] = WOOD; WALL_OF[RBRICK] = BRICK; WALL_OF[CULLET] = GLASS; WALL_OF[BARCU] = COPPER; WALL_OF[BARFE] = IRON; WALL_OF[BARST] = STEEL;
WALL_OF[BARSB] = CUTST; WALL_OF[PLANK] = WOOD; WALL_OF[BARGL] = GLASS;
const WALL_LIST = [RSTONE, BARSB, PLANK, LCLAY, RBRICK, LDIRT, LWOOD, CULLET, BARGL, RGRAN, RBAS, BARCU, BARFE, BARST];
WALL_OF[BARSB] = CUTST; WALL_OF[PLANK] = WOOD; WALL_OF[BARBR] = BRICK; WALL_OF[BARGL] = GLASS;
const WALL_LIST = [RSTONE, BARSB, PLANK, BARBR, LCLAY, RBRICK, LDIRT, LWOOD, CULLET, BARGL, RGRAN, RBAS, BARCU, BARFE, BARST];
const BAR_OF = new Uint8Array(NMAT); BAR_OF[COPPER] = BARCU; BAR_OF[IRON] = BARFE; BAR_OF[STEEL] = BARST;
BAR_OF[GLASS] = BARGL; BAR_OF[CUTST] = BARSB; BAR_OF[WOOD] = PLANK; BAR_OF[TIN] = BARSN; BAR_OF[ZINC] = BARZN; BAR_OF[SILVER] = BARAG; BAR_OF[GOLD] = BARAU; BAR_OF[BRONZE] = BARBZ; BAR_OF[BRASS] = BARBS;
const GRAINS_PER_BAR = 8;
@@ -59,6 +59,8 @@ const PIECES = [
desc: 'Lets a pipe line\'s liquid out of the side its arrow points to, wherever there is room for it. Press F to open or close it.' },
{ id: 'grinder', code: BP_GRIND, name: 'Grinder', kind: 'block', cost: [[CASH, 60]],
desc: 'Grinds rubble, gravel, sand and dirt resting on top into sand and the odd grain of ore. Results drop out below; keep that space clear. Wider rows grind faster. Stack under a Saw to cut stone and grind the rest.' },
{ id: 'press', code: BP_PRESS, name: 'Brick press', kind: 'block', cost: [[CASH, 60]],
desc: 'Presses clay lumps resting on top into a green brick for every 8. They drop out underneath; put a belt or an open drop under it. Fire green bricks in a fire hotter than about 900°C to make Bricks. Wider rows press faster.' },
{ id: 'saw', code: BP_SAW, name: 'Saw', kind: 'block', cost: [[CASH, 90]],
desc: 'Cuts stone rubble into stone blocks and wood chunks into planks, one from every 8 grains resting on top. They drop out underneath and pile up there: put a belt or an open drop under it to carry them away. Wider rows cut faster. Stack over a Grinder to grind everything else.' },
{ id: 'wheel', name: 'Water wheel', kind: 'machine', bw: 3, bh: 3, cost: [[LWOOD, 48]], hidden: POWER_FREE,
+12 -7
View File
@@ -15,12 +15,12 @@ const CUT_PER_BRICK = 8;
const SAW_CUTS = []; SAW_CUTS[RSTONE] = BARSB; SAW_CUTS[LWOOD] = PLANK;
// 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; }
function isProcCode(c) { return c === BP_GRIND || c === BP_SAW || c === BP_PRESS; }
function rebuildProcessors() {
procBlocks = [];
for (let b = 0; b < NB; b++) if (isProcCode(blockP[b])) {
procBlocks.push(b);
if (!procState.has(b)) procState.set(b, { work: 0, cut: { [RSTONE]: 0, [LWOOD]: 0 }, busyT: -999, blocked: false });
if (!procState.has(b)) procState.set(b, { work: 0, press: 0, cut: { [RSTONE]: 0, [LWOOD]: 0 }, busyT: -999, blocked: false });
}
procDirty = false;
}
@@ -45,15 +45,19 @@ function procFeed(b, m, T) {
} else if (blockP[b] === BP_SAW && SAW_CUTS[m]) {
if (++s.cut[m] < CUT_PER_BRICK) return;
s.cut[m] = 0; m = SAW_CUTS[m];
} else if (blockP[b] === BP_PRESS && m === LCLAY) {
if (++s.press < CUT_PER_BRICK) return;
s.press = 0; m = GREENB;
}
if (b === end) break;
b += BW;
}
if (m === BARSB || m === PLANK) {
if (m === BARSB || m === PLANK || m === GREENB) {
const e = spawnBar(m, x, (((end / BW) | 0) + 1) * BS, (rnd() - 0.5) * 0.1, 0.05);
if (e) {
e.T = T; discover(m, x, y, m === PLANK ? LWOOD : RSTONE, 'cut');
if (m === PLANK) stats.planks = (stats.planks || 0) + 1;
e.T = T; discover(m, x, y, m === GREENB ? LCLAY : m === PLANK ? LWOOD : RSTONE, m === GREENB ? 'press' : 'cut');
if (m === GREENB) stats.pressed = (stats.pressed || 0) + 1;
else if (m === PLANK) stats.planks = (stats.planks || 0) + 1;
else stats.bricks = (stats.bricks || 0) + 1;
}
} else setCell(procSlot(end), m, T, 0);
@@ -71,9 +75,10 @@ function procBlocksTick() {
const i = y * W + x + ((k + tick) & 3), m = mat[i];
if (m === EMPTY || PH[m] === PH_GAS || PH[m] === PH_SOLID || stamp[i] === tickStamp) continue;
let brick = false;
if (SAW_CUTS[m]) for (let q = b; q <= end; q += BW) {
if (SAW_CUTS[m] || m === LCLAY) for (let q = b; q <= end; q += BW) {
if (blockP[q] === BP_GRIND && REFINE[m]) break;
if (blockP[q] === BP_SAW) { brick = procState.get(q).cut[m] + 1 >= CUT_PER_BRICK; break; }
if (blockP[q] === BP_PRESS && m === LCLAY) { brick = procState.get(q).press + 1 >= CUT_PER_BRICK; break; }
if (blockP[q] === BP_SAW && SAW_CUTS[m]) { brick = procState.get(q).cut[m] + 1 >= CUT_PER_BRICK; break; }
}
if (procSlot(end) < 0 || brick && !entFreeAt(x, (((end / BW) | 0) + 1) * BS)) {
for (let q = b; q <= end; q += BW) procState.get(q).blocked = true;
+1 -1
View File
@@ -24,7 +24,7 @@ const STARTER_DESIGNS = [
const MILESTONES = [
{ id: 'dig', title: 'Break ground', msg: 'Broken material falls loose. The Pneumatic nozzle (2) picks up and blows the selection. Click or Q/E in the bag selects; Ctrl+click mixes. Hold Ctrl to see everything found. Clear returns to picking what you click. Shift: everything.', hint: 'Hold the left mouse button with the Drill (1): it sends pulses at the cursor that shake rock until it shatters. It falls as rubble, ore and coal.' },
{ id: 'fire', title: 'First fire', hint: 'Pick up coal or wood chunks with the nozzle, blow a pile out with right click, then hold the Igniter (4) on it. Fuel only burns where it touches open air.', reward: 'Thermal lens unlocked. Press H to see heat.' },
{ id: 'brick', title: 'Fired brick', hint: 'Clay turns into brick after a while above about 900°C, hotter than an open wood fire gets. Blow clay lumps into a coal fire, best walled in like a furnace, and pick up the brick rubble with the Pneumatic nozzle. Brick rubble builds brick blocks.', reward: 'Brick insulates far better than stone and survives 1750°C.' },
{ id: 'brick', title: 'Fired brick', hint: 'Clay turns into brick after a while above about 900°C, hotter than an open wood fire gets. Blow clay lumps into a coal fire, best walled in like a furnace, and pick up the brick rubble with the Pneumatic nozzle. Brick rubble builds brick blocks. A Brick press makes neat green bricks to fire.', reward: 'Brick insulates far better than stone and survives 1750°C.' },
{ id: 'charcoal', title: 'Charcoal', hint: 'Wood that gets hot without air turns into charcoal instead of burning. Bury a burning pile of wood chunks under dirt, or heat a sealed box of wood chunks from outside.', reward: 'A fuel you can grow: nearly as hot as coal.' },
{ id: 'grow', title: 'Plant a tree', msg: 'Wood is renewable. Plant rows of acorns on dirt to farm it.', hint: 'Cutting down a tree drops acorns. Leave one on dirt or grass under open sky and it sprouts, then grows into a tree in about a minute and a half.' },
{ 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.' },
+2 -1
View File
@@ -4,7 +4,7 @@ const BUILD_CATS = [
['Structure', ['wall', 'grate', 'gate']],
['Moving', ['belt', 'filter', 'tube']],
['Liquids', ['pipe', 'pump', 'outlet']],
['Machines', ['fan', 'grinder', 'saw', 'silo']],
['Machines', ['fan', 'grinder', 'saw', 'press', 'silo']],
['Molds', ['ingotmold', 'bitmold', 'tankmold']],
];
const BUILD_ORDER = BUILD_CATS.flatMap(c => c[1]);
@@ -20,6 +20,7 @@ const PIECE_ICONS = {
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"/>',
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"/>',
press: '<path d="M5 3h14l-3 5H8zM12 8v5M5 13h14v3H5z"/><rect x="8" y="18" width="8" height="3"/>',
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"/>',
ingotmold: '<path d="M3 9h18l-2 7H5z"/><path d="M9.2 9l.8 7M14.8 9l-.8 7"/>',
+4 -3
View File
@@ -32,6 +32,7 @@ function howText(e) {
case 'alloy': return `${F} melted into molten copper and the two mixed.`;
case 'char': return `${F} got hot with no air to burn in, and charred.`;
case 'refine': return `Ground out of ${f}.`;
case 'press': return `Pressed from ${f} by a Brick press.`;
case 'cut': return `Cut from ${f} by a saw.`;
default: return '';
}
@@ -92,9 +93,9 @@ function hoverInfo(ax, ay) {
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');
} else if (isProcCode(b)) {
const s = procState.get((ay >> 2) * BW + (ax >> 2)), saw = b === BP_SAW;
t1 = saw ? 'Saw' : 'Grinder';
t2.push(s && s.blocked ? saw ? 'stopped: no room underneath; a belt there carries them away' : 'stopped: clear the space under it' : s && tick - s.busyT < 30 ? saw ? 'cutting' : 'grinding' : saw ? 'idle: drop stone rubble or wood chunks on top' : 'idle: drop rubble, gravel, sand or dirt on top', saw ? `${(stats.bricks || 0).toLocaleString('en-US')} stone blocks, ${(stats.planks || 0).toLocaleString('en-US')} planks cut` : `${(stats.refined || 0).toLocaleString('en-US')} ground, ${(stats.refinedOre || 0).toLocaleString('en-US')} ore found`);
const s = procState.get((ay >> 2) * BW + (ax >> 2)), saw = b === BP_SAW, press = b === BP_PRESS;
t1 = press ? 'Brick press' : saw ? 'Saw' : 'Grinder';
t2.push(s && s.blocked ? saw || press ? 'stopped: no room underneath; a belt there carries them away' : 'stopped: clear the space under it' : s && tick - s.busyT < 30 ? press ? 'pressing' : saw ? 'cutting' : 'grinding' : press ? 'idle: drop clay lumps on top' : saw ? 'idle: drop stone rubble or wood chunks on top' : 'idle: drop rubble, gravel, sand or dirt on top', press ? `${stats.pressed || 0} green bricks pressed` : saw ? `${(stats.bricks || 0).toLocaleString('en-US')} stone blocks, ${(stats.planks || 0).toLocaleString('en-US')} planks cut` : `${(stats.refined || 0).toLocaleString('en-US')} ground, ${(stats.refinedOre || 0).toLocaleString('en-US')} ore found`);
} else if (p === P_FILTER) {
const fb = ((ay / BS) | 0) * BW + ((ax / BS) | 0), pr = filterParts(filterInfo(fb)), ar = blockV[fb] & 128 ? '→' : '←';
t1 = 'Filter ' + ar; t2.push(`drops ${pr.drops}`, `carries ${pr.rides} ${ar}`, 'F to change · I shows all');
+3 -3
View File
@@ -27,7 +27,7 @@ function renderNotebook() {
renderMarketTab(body);
} else if (nbTab === 'Materials') {
let rows = '', unknown = 0;
const list = [DIRT, LDIRT, STONE, RSTONE, BARSB, PLANK, CLAY, LCLAY, WOOD, LWOOD, ACORN, CHARCOAL, SNOW, ICE, COALV, COAL, CUV, CUORE, FEV, FEORE, SAND, GRAVEL, GRANITE, RGRAN, BASALT, RBAS,
const list = [DIRT, LDIRT, STONE, RSTONE, BARSB, PLANK, GREENB, BARBR, CLAY, LCLAY, WOOD, LWOOD, ACORN, CHARCOAL, SNOW, ICE, COALV, COAL, CUV, CUORE, FEV, FEORE, SAND, GRAVEL, GRANITE, RGRAN, BASALT, RBAS,
BRICK, RBRICK, GLASS, CULLET, COPPER, SCRCU, BARCU, IRON, SCRFE, BARFE, STEEL, SCRST, BARST, ASH, SLAG, MSLAG, WATER, STEAM, MAGMA, MCU, MFE, MST, MGL, FIRE, SMOKE, SNV, SNORE, TIN, MSN, SCRSN, BARSN, ZNV, ZNORE, ZINC, MZN, SCRZN, BARZN, AGV, AGORE, SILVER, MAG, SCRAG, BARAG, AUV, AUORE, GOLD, MAU, SCRAU, BARAU, BRONZE, MBZ, SCRBZ, BARBZ, BRASS, MBS, SCRBS, BARBS];
for (const m of list) {
if (!seen[m]) { unknown++; continue; }
@@ -58,7 +58,7 @@ function orderName(o) { return `${o.who ? o.who + ': ' : ''}${o.qty.toLocaleStri
function orderLeft(o) { return Math.max(0, o.due - tick); }
const MARKET_GROUPS = [
['Raw', [COAL, LWOOD, ACORN, SAND, LCLAY, RGRAN, RBAS, CUORE, FEORE, SNORE, ZNORE]],
['Bulk (builders)', [RSTONE, GRAVEL, BARSB, PLANK]],
['Bulk (builders)', [RSTONE, GRAVEL, BARSB, PLANK, GREENB, BARBR]],
['Refined', [CHARCOAL, RBRICK, CULLET, BARGL, SCRCU, SCRFE, SCRST, SCRSN, SCRZN, BARCU, BARSN, BARZN, BARFE, BARST]],
['Alloys', [SCRBZ, SCRBS, BARBZ, BARBS]],
['Precious', [AGORE, AUORE, SCRAG, SCRAU, BARAG, BARAU]],
@@ -182,7 +182,7 @@ function controlsHtml() {
const tools = [
['1 Resonance drill', 'Hold left click to send pulses from the bot toward the cursor: rock they shake hard enough shatters and falls as rubble, ore, coal or scrap, and trees come apart into wood chunks and acorns. Q and E set the pulse width: narrow bores a tunnel fast, wide clears a big cone slowly, medium is in between. R over a material tunes the drill to it, so only that shatters (great for pulling a vein out of the rock); R again untunes. Built blocks are never harmed. Better bits dig harder rock, faster and further.'],
['2 Pneumatic nozzle', "Left drag picks up the selection, or what you click if nothing is selected. Right click blows the selection. Click a bag name or Q/E selects one; Ctrl+click mixes. Hold Ctrl to see everything found. Clear removes the selection. Bars blow alone. Blowing out the last of a material deselects it. Shift while not flying takes everything; Shift + right click blows everything loose in the bag."],
['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, tubes, filters, grates, gates, pipes, pumps, outlets, Grinders and Saws, machines (fans and silos) and molds. Drag for a line, hold Shift while dragging for a filled rectangle. Right click takes a piece back. Your bot flies through anything you build.'],
['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, tubes, filters, grates, gates, pipes, pumps, outlets, Grinders, Saws and Brick presses, machines (fans and silos) and molds. Drag for a line, hold Shift while dragging for a filled rectangle. Right click takes a piece back. Your bot flies through anything you build.'],
['4 Igniter', 'Heats a small spot to 640°C, and its flame reaches a few grains into a pile. Enough to light coal and wood that touch air.'],
['Copy, cut and paste', 'Hold C (copy) or X (cut) and drag a box over what you built; let go and it is picked up, ready to paste. Cut takes it down and refunds it to your bag. Click to place ghosts: your bot builds them from your bag when it is close. R flips, V pastes again, right click or Esc stops. Ctrl+Z undoes building. The notebook (B) keeps saved blueprints and a few starter designs.'],
];
+1 -1
View File
@@ -34,7 +34,7 @@ const ICONS = {
ignite: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"><path d="M12 21c-4 0-6-2.6-6-5.6 0-3.4 3-5 3.6-8.4C12.5 9 14 11 14 13c1-.6 1.6-1.8 1.6-3 2 1.6 2.4 3.6 2.4 5.4 0 3-2 5.6-6 5.6z"/></svg>',
blueprint: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="11" height="11" rx="1" stroke-dasharray="2.5 2"/><rect x="10" y="10" width="11" height="11" rx="1"/></svg>',
};
const BAG_ORDER = [BARAU, BARAG, BARBZ, BARBS, BARST, BARFE, BARCU, BARSN, BARZN, BARGL, BARSB, PLANK, AUORE, AGORE, ZNORE, SNORE, RSTONE, LCLAY, RBRICK, LDIRT, LWOOD, ACORN, COAL, CHARCOAL, CUORE, FEORE, SAND, GRAVEL, RGRAN, RBAS, CULLET,
const BAG_ORDER = [BARAU, BARAG, BARBZ, BARBS, BARST, BARFE, BARCU, BARSN, BARZN, BARGL, BARSB, PLANK, GREENB, BARBR, AUORE, AGORE, ZNORE, SNORE, RSTONE, LCLAY, RBRICK, LDIRT, LWOOD, ACORN, COAL, CHARCOAL, CUORE, FEORE, SAND, GRAVEL, RGRAN, RBAS, CULLET,
SCRCU, SCRFE, SCRST, SCRSN, SCRZN, SCRAG, SCRAU, SCRBZ, SCRBS, ASH, SLAG, SNOW, WATER, MAGMA, MCU, MFE, MST, MGL, MSN, MZN, MAG, MAU, MBZ, MBS];
const GAME = {
ctrl: false,