Drill pulses pass through rubble and fluidise it; per-grain damping (t92)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+34
-24
@@ -8,14 +8,15 @@
|
||||
// medium: a cone, the all-rounder
|
||||
// wide: a broad fan that clears a big face close to the bot, but reaches only a short way
|
||||
// Tuned to one material, a pulse passes through everything else and shatters only that, and
|
||||
// loose grains it passes are jostled toward the bot, so a shattered vein pours out of the wall.
|
||||
// loose grains it passes are shaken into short hops, so piles flow and pockets drain.
|
||||
// Built blocks, machines and molds soak pulses up unharmed.
|
||||
// half: half the spread angle; mouth: half the width of the fan where it leaves the bot;
|
||||
// en: energy per ray; damp: energy kept after each shake (higher bores deeper); reach: x bit reach
|
||||
// en: energy per ray; damp: energy kept per grain (higher bores deeper); reach: x bit reach
|
||||
// Narrow/medium compensate for rubble now passing pulses; wide keeps its old squared loss.
|
||||
const PULSE_W = [
|
||||
{ name: 'narrow', half: 0.03, mouth: 3.2, rays: 13, en: 1.75, damp: 0.9, reach: 1.45 },
|
||||
{ name: 'medium', half: 0.24, mouth: 3, rays: 23, en: 1.4, damp: 0.84, reach: 1 },
|
||||
{ name: 'wide', half: 0.62, mouth: 3.4, rays: 41, en: 1.32, damp: 0.78, reach: 0.7 },
|
||||
{ name: 'narrow', half: 0.03, mouth: 3.2, rays: 13, en: 1.75, damp: 0.76, reach: 1.45 },
|
||||
{ name: 'medium', half: 0.24, mouth: 3, rays: 23, en: 1.4, damp: 0.69, reach: 1 },
|
||||
{ name: 'wide', half: 0.62, mouth: 3.4, rays: 41, en: 1.32, damp: 0.6084, reach: 0.7 },
|
||||
];
|
||||
const PULSE_EVERY = 8, PULSE_SPEED = 2.2, PULSE_K = 240; // ticks between pulses, cells per tick, shake per unit of energy
|
||||
let drillPulses = [], lastPulseTick = -99, drillWidth = 1;
|
||||
@@ -97,49 +98,58 @@ function shatterCell(i, x, y, m) {
|
||||
if ((stats.dug & 7) === 0) emit({ t: 'sfx', s: 'dig', a: HARD[m] });
|
||||
}
|
||||
// Advance every pulse in flight by one tick
|
||||
function pulseTick() {
|
||||
function pulseTick(step = 0.5) {
|
||||
if (!drillPulses.length) return;
|
||||
const tier = BIT_TIERS[player.bitTier];
|
||||
// Per-grain losses; squared old half-grain factors preserve the rock/built/liquid paths.
|
||||
const loose = 0.97 ** step, liquid = 0.970225 ** step, built = 0.0625 ** step;
|
||||
const skip = 0.986049 ** step, hard = 0.1225 ** step, tuned = 0.9216 ** step;
|
||||
const wood = 0.85 ** step, leaves = 0.97 ** step;
|
||||
let dust = 0;
|
||||
// cells inside the bot's body are not touched (it flies in its own little space)
|
||||
const bx0 = Math.floor(bot.x), by0 = Math.floor(bot.y), bx1 = Math.ceil(bot.x + bot.w) - 1, by1 = Math.ceil(bot.y + bot.h) - 1;
|
||||
for (const p of drillPulses) {
|
||||
p.r += PULSE_SPEED;
|
||||
const lim = Math.min(p.r, p.range), Wd = PULSE_W[p.w];
|
||||
const lim = Math.min(p.r, p.range), Wd = PULSE_W[p.w], damp = p.damp ** step;
|
||||
for (let k = 0; k < p.n; k++) {
|
||||
let e = p.en[k];
|
||||
if (e < 0.04) continue;
|
||||
const ray = pulseRay(p.ox, p.oy, p.ang, Wd, -1 + 2 * (k + p.jit) / p.n), ux = ray.ux, uy = ray.uy;
|
||||
let sd = p.s[k];
|
||||
let sd = p.s[k], kicks = 0;
|
||||
while (sd < lim && e >= 0.04) {
|
||||
sd += 0.5;
|
||||
sd += step;
|
||||
const x = Math.floor(ray.x + ux * sd), y = Math.floor(ray.y + uy * sd);
|
||||
if (x < 0 || y < 0 || x >= W || y >= H) { e = 0; break; }
|
||||
if (x >= bx0 && x <= bx1 && y >= by0 && y <= by1) continue;
|
||||
const i = y * W + x, m = mat[i], ph = PH[m];
|
||||
if (ph === PH_EMPTY || ph === PH_GAS) {
|
||||
// standing trees in the way are shaken apart too (not by a tuned pulse)
|
||||
if (bgm[i] && !p.tune && (sd * 2 & 1) === 0) { cutTree(i, x, y, tier, e * 0.5); e *= bgm[i] === WOOD ? 0.85 : 0.97; }
|
||||
if (bgm[i] && !p.tune) { const loss = bgm[i] === WOOD ? wood : leaves; cutTree(i, x, y, tier, e * step * 0.5); e *= loss; }
|
||||
continue;
|
||||
}
|
||||
if (ph === PH_LIQUID) { e *= 0.985; continue; }
|
||||
if (ph === PH_LIQUID) { e *= liquid; continue; }
|
||||
if (ph === PH_POWDER) {
|
||||
// loose grains are jostled back toward the bot, so a shattered pocket drains out of the
|
||||
// wall and a bore clears itself: straight back if there is room, else down or up a step
|
||||
e *= 0.94;
|
||||
if (stamp[i] !== tickStamp && rnd() < 0.3 * e) {
|
||||
const sx = Math.abs(ux) > 0.3 ? -Math.sign(ux) : 0, sy = Math.abs(uy) > 0.3 ? -Math.sign(uy) : 0;
|
||||
for (const [mx, my] of [[sx, sy], [sx, 1], [sx, -1], [0, sy]]) {
|
||||
if (!mx && !my) continue;
|
||||
e *= loose;
|
||||
// At most three one-cell hops per ray/tick; gravity settles the shaken grains.
|
||||
if (kicks < 3 && stamp[i] !== tickStamp && !occ[i] && !part[i] && rnd() < 1.1 * e * step) {
|
||||
const side = rnd() < 0.5 ? -1 : 1;
|
||||
for (let hop = 0; hop < 4; hop++) {
|
||||
const mx = hop < 2 ? side : -side, my = -(hop & 1);
|
||||
const nx = x + mx, ny = y + my;
|
||||
if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
|
||||
const j = ny * W + nx;
|
||||
if ((mat[j] === EMPTY || PH[mat[j]] === PH_GAS) && !occ[j] && !part[j]) { move(i, j, x, y, nx, ny); break; }
|
||||
if (mat[j] !== EMPTY && PH[mat[j]] !== PH_GAS) continue;
|
||||
if (occ[j] || part[j] || stamp[j] === tickStamp || !enter(m, ph, j, mx, my)) continue;
|
||||
if (my && CORNER_SEAL && PH[mat[i + mx]] === PH_SOLID && PH[mat[i - W]] === PH_SOLID) continue;
|
||||
move(i, j, x, y, nx, ny); kicks++;
|
||||
if (dust < 4) { emit({ t: 'debris', x, y, m }); dust++; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (builtCell(i, x, y)) { e *= 0.25; continue; }
|
||||
if (p.tune && YLD[m] !== p.tune) { e *= 0.993; continue; }
|
||||
if (builtCell(i, x, y)) { e *= built; continue; }
|
||||
if (p.tune && YLD[m] !== p.tune) { e *= skip; continue; }
|
||||
const h = HARD[m];
|
||||
// thin rock (a corner, a nub, a sliver with open space on two or more sides) has little
|
||||
// to hold it and shakes apart faster than a flat face, so ragged edges get cleaned up
|
||||
@@ -149,10 +159,10 @@ function pulseTick() {
|
||||
if (y > 0 && PH[mat[i - W]] !== PH_SOLID) open++;
|
||||
if (y < H - 1 && PH[mat[i + W]] !== PH_SOLID) open++;
|
||||
const thin = open > 1 && !THIN_BONUS_OFF ? 1 + (open - 1) * THIN_BONUS : 1;
|
||||
if (h > tier.hard) { e *= 0.35; continue; }
|
||||
const q = digp[i] + e * thin * tier.speed * PULSE_K / (1 + h * h * 0.6);
|
||||
if (h > tier.hard) { e *= hard; continue; }
|
||||
const q = digp[i] + e * (2 * step) * thin * tier.speed * PULSE_K / (1 + h * h * 0.6);
|
||||
if (q >= 255) { shatterCell(i, x, y, m); if (!p.tune) crumbleAround(x, y); } else { digp[i] = q; renderDirtyIdx(i); }
|
||||
e *= p.tune ? 0.96 : p.damp;
|
||||
e *= p.tune ? tuned : damp;
|
||||
}
|
||||
p.en[k] = e; p.s[k] = sd;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user