// World state: the per-cell typed arrays, alloc(), ambient temperature by depth, waking. // ---------- World state ---------- let W = 0, H = 0, N = 0, CW = 0, CH = 0, NCH = 0; let mat, temp, life, flags, stamp, part, mach, digp, noiseB, airS, netId, isPow, occ; let surfY, dirtY, graniteY, basaltY, magmaY, roofY, AMB, worldSeed = 1; let surfBase = 175, underD = 465; // surface height and underground depth: layers are fractions of underD // shallowD: in newer worlds the top 30% of the layering (dirt, coal, copper, tin, the top of the iron) keeps // a fixed depth in cells whatever the world size, and only the deeper layers stretch. 0 = plain fractions. // lapse: how fast the air cools with height above the ground, per cell. let shallowD = 0, lapse = 0.36; let BW = 0, BH = 0, NB = 0, blockP, blockV; // block registry: piece code + variant per 4x4 block let ents = []; // loose objects (bars) let bgm; // background layer: trees. Liquids, powders and gases pass in front of it. let flyers = []; // grains in flight, blown out of the bot let ghostBlocks = new Map(), ghostMolds = []; 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 = [], screwLists = Array.from({ length: IS_SCREW.length }, () => []); let brokenMachines = []; let ghostMachines = [], ghostBlocked = new Set(); const EV = []; const bot = { x: 0, y: 0, vx: 0, vy: 0, w: 5, h: 5, lamp: true, face: 1, thrust: 0 }; let bag = new Int32Array(NMAT), bagTotal = 0; const player = { bitTier: 0, tankTier: 0, lens: false, flask: false }; let seen = new Uint8Array(NMAT); let ledger = {}; const stats = { dug: 0, moved: 0, placed: 0, ignitions: 0, cast: 0, bars: 0 }; function emit(e) { if (EV.length < 400) EV.push(e); } function bagCap() { return TANK_TIERS[player.tankTier].cap; } // Things count as discovered only when they happen near the player (or in the bag). // how: 'dig', 'pickup', 'melt', 'smelt', 'freeze', 'boil', 'ignite', 'burn', 'fire', 'blow', 'condense' function discover(m, x, y, from, how, T) { if (seen[m]) return; if (x !== undefined) { const dx = x - bot.x, dy = y - bot.y; if (dx * dx + dy * dy > 110 * 110) return; } seen[m] = 1; emit({ t: 'discover', m, from: from || 0, how: how || '', T: T || 0 }); } function alloc(w, h) { resetDevConsole(); W = w; H = h; N = w * h; fogAlloc(); CW = Math.ceil(w / 32); CH = Math.ceil(h / 32); NCH = CW * CH; cleatMask = new Uint8Array(N); cleatCells = []; 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); slopePhase = 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); AMB = new Float32Array(h); loadWakeOrder = loadWakePending = null; loadWakeIndex = 0; cleatChunks = new Uint8Array(NCH); awake = new Uint8Array(NCH); hot = new Uint8Array(NCH); hotLvl = new Uint8Array(NCH); 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 = []; 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; } // 0 at the base surface height, 1 at the bottom of the world function depthU(y) { const d = y - surfBase; if (!shallowD) return d / underD; return d <= shallowD ? d / shallowD * 0.3 : 0.3 + (d - shallowD) / (underD - shallowD) * 0.7; } // the y at a fraction u of the underground depth (the inverse of depthU) function depthY(u) { return Math.round(surfBase + (!shallowD ? u * underD : u <= 0.3 ? u / 0.3 * shallowD : shallowD + (u - 0.3) / 0.7 * (underD - shallowD))); } function meanSurface() { let s = 0; for (let x = 0; x < W; x++) s += surfY[x]; return s / W; } function computeAmbient() { const sm = meanSurface(); for (let y = 0; y < H; y++) { let a = 18; if (y > sm) a += Math.min(60, (y - sm) * 0.04); else if (y < sm - 20) a = Math.max(-30, a - (sm - 20 - y) * lapse); // thin cold air high above the ground const u = depthU(y); // geothermal: warms slowly, then steeply toward the magma sea if (u > 0.53) { const k = Math.min(1, (u - 0.53) / (0.905 - 0.53)); a += Math.pow(k, 2.2) * 1200; } AMB[y] = a; } } function fillNoise() { for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) noiseB[y * W + x] = hash2(x, y, worldSeed) & 255; } // ---------- Waking ---------- function wakeXY(x, y) { const cx = x >> 5, cy = y >> 5, c = cy * CW + cx; roofDirty[cx] = 1; awake[c] = 3; const lx = x & 31, ly = y & 31; if (lx === 0) { if (cx > 0) awake[c - 1] = 3; } else if (lx === 31 && cx < CW - 1) awake[c + 1] = 3; if (ly === 0) { if (cy > 0) awake[c - CW] = 3; } else if (ly === 31 && cy < CH - 1) awake[c + CW] = 3; } function wakeHeatXY(x, y) { const c = (y >> 5) * CW + (x >> 5); roofDirty[x >> 5] = 1; hot[c] = 6; hotLvl[c] = 2; } function wakeIdx(i) { wakeXY(i % W, (i / W) | 0); } function wakeHeatIdx(i) { wakeHeatXY(i % W, (i / W) | 0); }