98 lines
5.4 KiB
JavaScript
98 lines
5.4 KiB
JavaScript
// 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;
|
|
let tick = 0, tickStamp = 1;
|
|
let machines = [null], molds = [], nets = [null], pcells = new Set(), powerDirty = true;
|
|
let beltL = [], beltR = [], tubeLists = [[], [], [], [], []];
|
|
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) {
|
|
W = w; H = h; N = w * h;
|
|
fogAlloc();
|
|
CW = Math.ceil(w / 32); CH = Math.ceil(h / 32); NCH = CW * CH;
|
|
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();
|
|
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);
|
|
awake = new Uint8Array(NCH); hot = new Uint8Array(NCH); hotLvl = new Uint8Array(NCH);
|
|
machines = [null]; molds = []; nets = [null]; pcells = new Set(); powerDirty = true;
|
|
beltL = []; beltR = []; tubeLists = [[], [], [], [], []]; 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 computeAmbient() {
|
|
let sm = 0; for (let x = 0; x < W; x++) sm += surfY[x]; sm /= W;
|
|
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;
|
|
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);
|
|
hot[c] = 6; hotLvl[c] = 2;
|
|
}
|
|
function wakeIdx(i) { wakeXY(i % W, (i / W) | 0); }
|
|
function wakeHeatIdx(i) { wakeHeatXY(i % W, (i / W) | 0); }
|