Bar rack blocks: store 50 bars each, F lets them out oldest first (t1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"sim/heat.js",
|
||||
"sim/machines.js",
|
||||
"sim/processors.js",
|
||||
"sim/racks.js",
|
||||
"sim/pipes.js",
|
||||
"sim/machine-tick.js",
|
||||
"sim/conveyors.js",
|
||||
|
||||
@@ -76,6 +76,7 @@ function drawMachines() {
|
||||
ctx.restore();
|
||||
}
|
||||
drawProcBlocks();
|
||||
drawRackBlocks();
|
||||
}
|
||||
// Crushing rollers and saw blades sit over the shared steel housings in both looks.
|
||||
function drawProcBlocks() {
|
||||
@@ -210,3 +211,32 @@ function drawSiloPixel(ctx, mm, sx, sy, zp) {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// Shelves show each block's fill and materials; the bottom gate belongs to the column.
|
||||
function drawRackBlocks() {
|
||||
const ctx = R.ctx;
|
||||
for (const [b, s] of rackState) {
|
||||
const x = (b % BW) * BS, y = ((b / BW) | 0) * BS;
|
||||
const [sx, sy, sw] = cellRect(x, y, BS, BS);
|
||||
if (sx > R.canvas.width || sy > R.canvas.height || sx + sw < 0 || sy + sw < 0) continue;
|
||||
if (SET.fog && fogSeen(x + 2, y + 2) < 64) continue;
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#202831'; ctx.fillRect(sx + sw * 0.1, sy + sw * 0.1, sw * 0.8, sw * 0.8);
|
||||
ctx.fillStyle = '#a5adb5';
|
||||
for (let row = 0; row < 2; row++) ctx.fillRect(sx + sw * 0.1, sy + sw * (0.47 + row * 0.4), sw * 0.8, sw * 0.05);
|
||||
const slots = Math.ceil(s.n / 5);
|
||||
let run = 0, count = 0;
|
||||
for (let k = 0; k < slots; k++) {
|
||||
while (run + 1 < s.q.length && k * 5 >= count + s.q[run][1]) count += s.q[run++][1];
|
||||
const m = s.q[run][0];
|
||||
ctx.fillStyle = `rgb(${COLR[m]},${COLG[m]},${COLB[m]})`;
|
||||
ctx.fillRect(sx + sw * (0.14 + k % 5 * 0.145), sy + sw * (0.71 - Math.floor(k / 5) * 0.4), sw * 0.12, sw * 0.12);
|
||||
}
|
||||
if (b + BW >= NB || blockP[b + BW] !== BP_RACK) {
|
||||
ctx.fillStyle = s.open ? '#e0b43c' : '#c9d1d8';
|
||||
ctx.fillRect(sx + sw * 0.1, sy + sw * 0.94, sw * (s.open ? 0.2 : 0.8), sw * 0.06);
|
||||
if (s.open) ctx.fillRect(sx + sw * 0.7, sy + sw * 0.94, sw * 0.2, sw * 0.06);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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 || m === PRESSB) {
|
||||
else if (m === GRINDER || m === SAWB || m === PRESSB || m === RACKB) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ function interactAt(x, y) {
|
||||
// ingot molds empty themselves: F passes them by
|
||||
if (mo && mo.kind !== 'ingot') { const r = collectMold(mo); if (!r.startsWith('New') && !r.startsWith('Knocked')) emit({ t: 'sfx', s: 'ui' }); return r; }
|
||||
const pb = ((y / BS) | 0) * BW + ((x / BS) | 0);
|
||||
if (blockP[pb] === BP_RACK) {
|
||||
const top = rackTop(pb), open = !rackState.get(top).open;
|
||||
for (let b = top; b < NB && blockP[b] === BP_RACK; b += BW) { const s = rackState.get(b); s.open = open; s.wait = 0; }
|
||||
return open ? 'Rack open.' : 'Rack closed.';
|
||||
}
|
||||
if (blockP[pb] === BP_PUMP || blockP[pb] === BP_OUTLET) {
|
||||
blockV[pb] ^= 4; emit({ t: 'sfx', s: 'ui' });
|
||||
return blockP[pb] === BP_PUMP ? (blockV[pb] & 4 ? 'Pump off.' : 'Pump on.') : (blockV[pb] & 4 ? 'Outlet closed.' : 'Outlet open.');
|
||||
|
||||
+15
-2
@@ -26,6 +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 (code === BP_RACK) setCell(i, RACKB, 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); }
|
||||
@@ -38,6 +39,12 @@ function placeBlock(code, v, bx, by, free, extra) {
|
||||
blockP[by * BW + bx] = code; blockV[by * BW + bx] = v;
|
||||
if (code === BP_FILTER) { blockV[by * BW + bx] = v & 128; filterCfg.set(by * BW + bx, filterRec(extra)); filtersDirty = true; }
|
||||
if (isProcCode(code)) procDirty = true;
|
||||
if (code === BP_RACK) {
|
||||
const b = by * BW + bx, s = newRack();
|
||||
s.open = !!(rackState.get(b - BW)?.open || rackState.get(b + BW)?.open);
|
||||
rackState.set(b, s);
|
||||
for (let q = rackTop(b); q <= rackBottom(b); q += BW) rackState.get(q).open = s.open;
|
||||
}
|
||||
stats.placed++;
|
||||
undoNote({ t: 'block+', bx, by, code });
|
||||
return '';
|
||||
@@ -53,6 +60,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 (code === BP_RACK) { if (m === RACKB) 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++; }
|
||||
@@ -82,8 +90,8 @@ function removeBlock(bx, by) {
|
||||
} else if (isPipeCode(code)) {
|
||||
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 || m === PRESSB) { setCell(i, EMPTY, temp[i], 0); markPow(i, false); n++; }
|
||||
} else if (code === BP_BELT || code === BP_SHAFT || code === BP_GATE || code === BP_RACK || isProcCode(code)) {
|
||||
if (m === BELTL || m === BELTR || m === SHAFT || m === GATE || m === GRINDER || m === SAWB || m === PRESSB || m === RACKB) { 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++; }
|
||||
}
|
||||
@@ -91,6 +99,11 @@ function removeBlock(bx, by) {
|
||||
else if (code !== BP_WALL && n >= 8) refundAt(PIECE_BY_CODE[code].cost, 1, bx * BS + 2, by * BS + 2);
|
||||
if (code === BP_FILTER) { filtersDirty = true; filterCfg.delete(b); }
|
||||
if (isProcCode(code)) { procDirty = true; procState.delete(b); }
|
||||
if (code === BP_RACK) {
|
||||
const s = rackState.get(b);
|
||||
rackState.delete(b);
|
||||
if (s) for (const [m, count] of s.q) giveOrDrop(m, count, bot.x, bot.y);
|
||||
}
|
||||
blockP[b] = 0; blockV[b] = 0;
|
||||
if (isPipeCode(code)) {
|
||||
pipesDirty = true;
|
||||
|
||||
@@ -153,6 +153,7 @@ function entTick() {
|
||||
}
|
||||
e.ix = cx; e.iy = cy;
|
||||
}
|
||||
if (rackIntake(e)) { ents.splice(k, 1); continue; }
|
||||
const cx = e.ix, cy = e.iy;
|
||||
// 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)];
|
||||
|
||||
@@ -46,6 +46,7 @@ function fanBlow(mm, sat) {
|
||||
}
|
||||
function machinesTick() {
|
||||
procBlocksTick();
|
||||
rackTick();
|
||||
if (powerDirty && tick >= nextPowerBuild) { rebuildPower(); nextPowerBuild = tick + 6; }
|
||||
if (brokenMachines.length) {
|
||||
const ids = brokenMachines; brokenMachines = [];
|
||||
|
||||
@@ -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, PRESSB = 98, GREENB = 99, BARBR = 100;
|
||||
const NMAT = 101;
|
||||
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, RACKB = 101;
|
||||
const NMAT = 102;
|
||||
|
||||
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(RACKB, { name: 'Bar rack', 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.' });
|
||||
|
||||
+3
-1
@@ -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;
|
||||
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;
|
||||
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
|
||||
@@ -61,6 +61,8 @@ const PIECES = [
|
||||
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: 'rack', name: 'Bar rack', kind: 'block', code: BP_RACK, cost: [[CASH, 40]],
|
||||
desc: 'Stores bars of any kind, 50 per block. Stack blocks into a column for more room. Bars that land on top go in; F opens the gate at the bottom and lets them out one at a time, oldest first, onto a belt below.' },
|
||||
{ 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,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Each rack block owns its bars; only the bottom of a column releases them.
|
||||
const RACK_CAP = 50;
|
||||
let rackState = new Map();
|
||||
function newRack() { return { q: [], n: 0, open: false, wait: 0 }; }
|
||||
function rackTop(b) { while (b >= BW && blockP[b - BW] === BP_RACK) b -= BW; return b; }
|
||||
function rackBottom(b) { while (b + BW < NB && blockP[b + BW] === BP_RACK) b += BW; return b; }
|
||||
function rackPush(s, m, n) {
|
||||
const last = s.q[s.q.length - 1];
|
||||
if (last && last[0] === m) last[1] += n; else s.q.push([m, n]);
|
||||
s.n += n;
|
||||
}
|
||||
function rackShift(s, n) {
|
||||
s.q[0][1] -= n; s.n -= n;
|
||||
if (!s.q[0][1]) s.q.shift();
|
||||
}
|
||||
// Called with the moving bar unmarked, after its ordinary collision handling.
|
||||
function rackIntake(e) {
|
||||
const y = e.iy + BAR_H;
|
||||
if (e.vy !== 0 || Math.abs(e.vx) >= 0.05 || y >= H || y % BS) return false;
|
||||
for (let x = Math.max(0, e.ix); x < Math.min(W, e.ix + BAR_W); x += BS - x % BS) {
|
||||
const b = (y / BS) * BW + Math.floor(x / BS), left = (b % BW) * BS;
|
||||
if (blockP[b] !== BP_RACK || b >= BW && blockP[b - BW] === BP_RACK || Math.min(e.ix + BAR_W, left + BS) - Math.max(e.ix, left) < BAR_W / 2) continue;
|
||||
for (let q = rackBottom(b); q >= b; q -= BW) {
|
||||
const s = rackState.get(q);
|
||||
if (s.n < RACK_CAP) { rackPush(s, e.m, 1); return true; }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function rackRefill(b) {
|
||||
const top = rackTop(b);
|
||||
for (let q = b; q > top; q -= BW) {
|
||||
const lower = rackState.get(q);
|
||||
for (let above = q - BW; above >= top && lower.n < RACK_CAP; above -= BW) {
|
||||
const upper = rackState.get(above);
|
||||
while (lower.n < RACK_CAP && upper.n) {
|
||||
const n = Math.min(RACK_CAP - lower.n, upper.q[0][1]);
|
||||
rackPush(lower, upper.q[0][0], n); rackShift(upper, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function rackTick() {
|
||||
for (const [b, s] of rackState) {
|
||||
if (b + BW < NB && blockP[b + BW] === BP_RACK) continue;
|
||||
if (!s.open) continue;
|
||||
if (!s.n) rackRefill(b);
|
||||
if (!s.n) continue;
|
||||
if (s.wait < 20) s.wait++;
|
||||
if (s.wait < 20) continue;
|
||||
const x = (b % BW) * BS, y = (((b / BW) | 0) + 1) * BS;
|
||||
if (!entFreeAt(x, y) || !spawnBar(s.q[0][0], x, y, 0, 0)) continue;
|
||||
rackShift(s, 1); s.wait = 0;
|
||||
rackRefill(b);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ function serializeWorld() {
|
||||
v: SAVE_VERSION, seed: worldSeed, W, H, tick, surfBase, underD, shallowD, lapse, tOff: TOFF,
|
||||
saplings: saplings.map(s => [s.x, s.y, s.age, s.th, s.seed]),
|
||||
economy: { money, debt, earned, recent: Array.from(mkRecent, v => Math.round(v)), log: shipLog, orders, ordersDone, nextOrderT, orderSeq },
|
||||
racks: [...rackState].map(([b, s]) => [b, s.q.map(r => r.slice()), s.open]),
|
||||
filters: [...filterCfg].map(([b, c]) => [b, c.m, c.inv]),
|
||||
weather: { moist: (() => { const m = Float32Array.from(weather.moist); for (const f of flyers) if (f.rain) m[Math.min(m.length - 1, Math.max(0, (f.x / WBAND) | 0))] += 1; return Array.from(m, v => Math.round(v)); })(), raining: Array.from(weather.raining), shower: weather.shower, next: weather.next, wind: weather.wind, seen: weather.seen, base: weather.base },
|
||||
bot: { x: bot.x, y: bot.y, lamp: bot.lamp },
|
||||
@@ -38,6 +39,20 @@ function deserializeWorld(meta, buffers) {
|
||||
mat.set(buffers.mat); life.set(buffers.life); flags.set(buffers.flags);
|
||||
for (let i = 0; i < N; i++) if (mat[i] === SPRING) mat[i] = STONE; // springs are gone: older worlds keep plain rock there
|
||||
blockP.set(buffers.blockP); blockV.set(buffers.blockV); filtersDirty = true; rebuildProcessors();
|
||||
for (let b = 0; b < NB; b++) if (blockP[b] === BP_RACK) rackState.set(b, newRack());
|
||||
for (const entry of Array.isArray(meta.racks) ? meta.racks : []) {
|
||||
if (!Array.isArray(entry)) continue;
|
||||
const [b, q, open] = entry;
|
||||
if (!Number.isInteger(b) || !rackState.has(b) || !Array.isArray(q)) continue;
|
||||
const s = newRack();
|
||||
for (const r of q) {
|
||||
if (!Array.isArray(r) || r.length !== 2) continue;
|
||||
const [m, n] = r;
|
||||
if (!Number.isInteger(m) || m <= 0 || m >= NMAT || PH[m] !== PH_ITEM || !Number.isInteger(n) || n <= 0 || n > RACK_CAP - s.n) continue;
|
||||
rackPush(s, m, n);
|
||||
}
|
||||
s.open = open === true; rackState.set(b, s);
|
||||
}
|
||||
// pipes used to be solid copper blocks; now they are thin and let things past
|
||||
for (let b = 0; b < NB; b++) if (blockP[b] === BP_PIPE) for (const i of blockCellList(b % BW, (b / BW) | 0)) if (mat[i] === PIPE) { mat[i] = EMPTY; part[i] = P_PIPE; }
|
||||
for (const [b, m, inv] of meta.filters || []) if (blockP[b] === BP_FILTER) filterCfg.set(b, filterRec({ m, inv }));
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ function alloc(w, h) {
|
||||
mat = new Uint8Array(N); temp = new Float32Array(N); life = new Uint8Array(N); flags = new Uint8Array(N);
|
||||
stamp = new Uint8Array(N); part = new Uint8Array(N); mach = new Uint16Array(N); digp = new Uint8Array(N);
|
||||
noiseB = new Uint8Array(N); bgm = new Uint8Array(N); airS = new Uint16Array(N); netId = new Uint16Array(N); isPow = new Uint8Array(N); occ = new Uint8Array(N);
|
||||
BW = Math.ceil(w / BS); BH = Math.ceil(h / BS); NB = BW * BH; blockP = new Uint8Array(NB); blockV = new Uint8Array(NB); filtersDirty = true; filterCfg = new Map(); procDirty = true; procState.clear();
|
||||
BW = Math.ceil(w / BS); BH = Math.ceil(h / BS); NB = BW * BH; blockP = new Uint8Array(NB); blockV = new Uint8Array(NB); filtersDirty = true; filterCfg = new Map(); procDirty = true; procState.clear(); rackState.clear();
|
||||
ents = []; flyers = []; ghostBlocks = new Map(); ghostMolds = []; treeBases = []; saplings = [];
|
||||
surfBase = Math.round(Math.min(h * 0.27, 175)); underD = h - surfBase; shallowD = 0; lapse = 0.36;
|
||||
surfY = new Int16Array(w); roofY = new Int16Array(w); dirtY = new Int16Array(w); graniteY = new Int16Array(w); basaltY = new Int16Array(w); magmaY = new Int16Array(w);
|
||||
|
||||
@@ -4,7 +4,7 @@ const BUILD_CATS = [
|
||||
['Structure', ['wall', 'grate', 'gate']],
|
||||
['Moving', ['belt', 'filter', 'tube']],
|
||||
['Liquids', ['pipe', 'pump', 'outlet']],
|
||||
['Machines', ['fan', 'grinder', 'saw', 'press', 'silo']],
|
||||
['Machines', ['fan', 'grinder', 'saw', 'press', 'rack', 'silo']],
|
||||
['Molds', ['ingotmold', 'bitmold', 'tankmold']],
|
||||
];
|
||||
const BUILD_ORDER = BUILD_CATS.flatMap(c => c[1]);
|
||||
@@ -22,6 +22,7 @@ const PIECE_ICONS = {
|
||||
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"/>',
|
||||
rack: '<path d="M4 3v18M20 3v18M4 11h16M4 20h16"/><path d="M7 6h10v3H7zM7 15h10v3H7z"/>',
|
||||
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"/>',
|
||||
bitmold: '<path d="M3 9h10l7 3-7 3H3z"/><path d="M6.5 9v6M9.5 9v6"/>',
|
||||
|
||||
@@ -92,6 +92,18 @@ 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');
|
||||
} else if (b === BP_RACK) {
|
||||
const top = rackTop((ay >> 2) * BW + (ax >> 2)), counts = new Map();
|
||||
let n = 0, capacity = 0;
|
||||
for (let q = top; q < NB && blockP[q] === BP_RACK; q += BW) {
|
||||
const s = rackState.get(q); capacity += RACK_CAP; n += s.n;
|
||||
for (const [m, count] of s.q) counts.set(m, (counts.get(m) || 0) + count);
|
||||
}
|
||||
t1 = 'Bar rack'; t2.push(`${n} / ${capacity} bars`);
|
||||
t2.push(...[...counts].slice(0, 4).map(([m, count]) => `${count} ${MD[m].name.toLowerCase()}`));
|
||||
if (counts.size > 4) t2.push('+ more');
|
||||
t2.push(rackState.get(top).open ? 'open: letting bars out' : 'closed: F lets bars out');
|
||||
if (n === capacity) t2.push('full');
|
||||
} else if (isProcCode(b)) {
|
||||
const s = procState.get((ay >> 2) * BW + (ax >> 2)), saw = b === BP_SAW, press = b === BP_PRESS;
|
||||
t1 = press ? 'Brick press' : saw ? 'Saw' : 'Grinder';
|
||||
|
||||
+1
-1
@@ -184,7 +184,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, 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.'],
|
||||
['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, tubes, filters, grates, gates, pipes, pumps, outlets, Grinders, Saws, Brick presses and Bar racks, 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.'],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user