- One helper each for wall and mold refunds, tube edges and lanes, pipe text, piece dimensions, font lookup, reach circles and surface averaging. - previews, smooth, notebook, materials, worldgen and selection split into topic files (preview-drill, preview-selection, smooth-shaders, notebook-market, notebook-blueprints, material-tables, worldgen-helpers, selection-pickers, selection-panels). - Every suite's output identical apart from timings; seeds make the same worlds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
3.6 KiB
JavaScript
62 lines
3.6 KiB
JavaScript
// World generation helpers: noise and removal of tiny hard rock specks.
|
|
// ---------- Perlin noise ----------
|
|
function makeNoise(rng) {
|
|
const perm = new Uint8Array(512), gx = new Float32Array(256), gy = new Float32Array(256);
|
|
const p = []; for (let i = 0; i < 256; i++) p.push(i);
|
|
for (let i = 255; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); const t = p[i]; p[i] = p[j]; p[j] = t; }
|
|
for (let i = 0; i < 512; i++) perm[i] = p[i & 255];
|
|
for (let i = 0; i < 256; i++) { const a = rng() * Math.PI * 2; gx[i] = Math.cos(a); gy[i] = Math.sin(a); }
|
|
return function (x, y) {
|
|
const xi = Math.floor(x), yi = Math.floor(y), xf = x - xi, yf = y - yi;
|
|
const X = xi & 255, Y = yi & 255;
|
|
const p0 = perm[Y], p1 = perm[Y + 1];
|
|
const h00 = perm[X + p0], h10 = perm[X + 1 + p0], h01 = perm[X + p1], h11 = perm[X + 1 + p1];
|
|
const n00 = gx[h00] * xf + gy[h00] * yf, n10 = gx[h10] * (xf - 1) + gy[h10] * yf;
|
|
const n01 = gx[h01] * xf + gy[h01] * (yf - 1), n11 = gx[h11] * (xf - 1) + gy[h11] * (yf - 1);
|
|
const u = xf * xf * xf * (xf * (xf * 6 - 15) + 10), v = yf * yf * yf * (yf * (yf * 6 - 15) + 10);
|
|
return (n00 + u * (n10 - n00) + v * (n01 - n00) + u * v * (n00 - n10 - n01 + n11)) * 1.41;
|
|
};
|
|
}
|
|
function fbm(n, x, y, oct) { let s = 0, a = 1, f = 1, t = 0; for (let o = 0; o < oct; o++) { s += n(x * f, y * f) * a; t += a; a *= 0.5; f *= 2; } return s / t; }
|
|
|
|
// Small specks of rock harder than the rock around them (a few grains of iron ore in stone, a
|
|
// chip of granite, a sliver of basalt) are not worth having: a bit too weak to break them leaves
|
|
// them hanging in the way. Any such piece of SPECK_MIN grains or fewer becomes the softer rock
|
|
// around it. Bigger veins and pockets stay. live: the world is in play (an older save).
|
|
const SPECK_MIN = 30;
|
|
function clearHardSpecks(live) {
|
|
const seen = new Uint8Array(N), st = new Int32Array(N), cells = new Int32Array(SPECK_MIN + 1);
|
|
let n = 0;
|
|
for (const h0 of [6, 5, 4]) {
|
|
seen.fill(0);
|
|
const mem = (i) => { const m = mat[i]; return (flags[i] & F_NAT) !== 0 && PH[m] === PH_SOLID && HARD[m] >= h0 && HARD[m] <= 6 && YLD[m] !== 0 && !PARTMAT[m]; };
|
|
const soft = (i) => { const m = mat[i]; return (flags[i] & F_NAT) !== 0 && PH[m] === PH_SOLID && HARD[m] >= 1 && HARD[m] < h0 && YLD[m] !== 0 && !PARTMAT[m]; };
|
|
for (let i0 = 0; i0 < N; i0++) {
|
|
if (seen[i0] || !mem(i0)) continue;
|
|
let sp = 0, sz = 0, x0 = W, y0 = H, x1 = 0, y1 = 0;
|
|
const hosts = new Map();
|
|
st[sp++] = i0; seen[i0] = 1;
|
|
while (sp) {
|
|
const i = st[--sp], x = i % W, y = (i / W) | 0;
|
|
if (sz <= SPECK_MIN) cells[sz] = i;
|
|
sz++;
|
|
if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y;
|
|
for (const j of [x > 0 ? i - 1 : -1, x < W - 1 ? i + 1 : -1, i - W, i + W]) {
|
|
if (j < 0 || j >= N || seen[j]) continue;
|
|
if (mem(j)) { seen[j] = 1; st[sp++] = j; }
|
|
else if (soft(j)) hosts.set(mat[j], (hosts.get(mat[j]) || 0) + 1);
|
|
}
|
|
}
|
|
if (sz > SPECK_MIN) continue;
|
|
// a speck left floating (in a cave, or dug round) takes the rock nearest round it
|
|
if (!hosts.size) for (let y = Math.max(0, y0 - 5); y <= Math.min(H - 1, y1 + 5); y++) for (let x = Math.max(0, x0 - 5); x <= Math.min(W - 1, x1 + 5); x++) { const j = y * W + x; if (soft(j)) hosts.set(mat[j], (hosts.get(mat[j]) || 0) + 1); }
|
|
let host = 0, best = 0;
|
|
for (const [m, c] of hosts) if (c > best) { best = c; host = m; }
|
|
if (!host) continue;
|
|
for (let k = 0; k < sz; k++) { const i = cells[k]; if (live) setCell(i, host, temp[i], F_NAT); else mat[i] = host; }
|
|
n++;
|
|
}
|
|
}
|
|
return n;
|
|
}
|