// 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 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]]; 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; 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 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 }); } procDirty = false; } function procBottom(b) { while (b + BW < NB && isProcCode(blockP[b + BW])) b += BW; return b; } function procSlot(b) { const y = (((b / BW) | 0) + 1) * BS, x = (b % BW) * BS; if (y >= H) return -1; for (let k = 0; k < 4; k++) { const j = y * W + x + [1, 2, 0, 3][k]; if (mat[j] === EMPTY && !part[j] && !occ[j] && !mach[j]) return j; } return -1; } function procFeed(b, m, T) { const end = procBottom(b), x = (b % BW) * BS, y = ((b / BW) | 0) * BS - 1; for (;;) { const s = procState.get(b); s.busyT = tick; if (blockP[b] === BP_GRIND && PH[m] === PH_POWDER && REFINE[m]) { const from = m; 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, from, 'refine'); } else if (rnd() < REFINE_SAND) got = SAND; stats.refined = (stats.refined || 0) + 1; m = got; if (!m) return; } 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]; } if (b === end) break; b += BW; } if (m === BARSB || m === PLANK) { 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; else stats.bricks = (stats.bricks || 0) + 1; } } else setCell(procSlot(end), m, T, 0); } function procBlocksTick() { if (procDirty) rebuildProcessors(); for (const b of procBlocks) { if (b >= BW && isProcCode(blockP[b - BW])) continue; const s = procState.get(b), end = procBottom(b), y = ((b / BW) | 0) * BS - 1, x = (b % BW) * BS; s.work += blockP[b] === BP_GRIND ? 0.25 : 0.5; let jobs = Math.floor(s.work); s.work -= jobs; if (!jobs || y < 0) continue; // a tick without work keeps the last blocked state, so it doesn't flicker for (let q = b; q <= end; q += BW) procState.get(q).blocked = false; for (let k = 0; k < 4 && jobs > 0; k++) { 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 (blockP[q] === BP_GRIND && REFINE[m]) break; if (blockP[q] === BP_SAW) { 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; break; } const T = temp[i]; setCell(i, EMPTY, AMB[y], 0); procFeed(b, m, T); jobs--; } } } // ---------- Silo ---------- // One loose material, counted (not simulated grain by grain inside): grains in the roof hatch // go in, and with the gate open they come out of the bottom of the cone. const SILO_CAP = 4000, SILO_RATE = 1; function siloTick(mm) { for (let dy = 0; dy < 2; dy++) for (let dx = 3; dx <= 8; dx++) { const i = (mm.y + dy) * W + mm.x + dx, m = mat[i]; if (PH[m] !== PH_POWDER || stamp[i] === tickStamp || (mm.sn > 0 && m !== mm.sm) || (mm.sn || 0) >= SILO_CAP) continue; mm.sm = m; mm.sn = (mm.sn || 0) + 1; mm.fillT = tick; setCell(i, EMPTY, AMB[mm.y + dy], 0); } if (mm.open && mm.sn > 0) { let put = 0; for (let k = 0; k < 4 && put < SILO_RATE && mm.sn > 0; k++) { const x = mm.x + 4 + ((k + tick) & 3), j = (mm.y + 23) * W + x; if (mat[j] !== EMPTY || occ[j]) continue; setCell(j, mm.sm, AMB[mm.y + 23], 0); mm.sn--; put++; mm.pourT = tick; } if (!mm.sn) mm.sm = 0; } } // a silo with something in it is emptied before it can be taken down function siloFull(mm) { return mm && mm.type === 'silo' && mm.sn > 0; }