grechka-game/srcv3/world/tnt3d.js

86 lines
2.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==================== TNT В 3D — GrechkaCraft v3 ====================
// Взрыв сферический: сносит блоки в радиусе по всем трём осям.
import { BLOCKS } from '../data/blocks.js';
import { state } from '../core/state.js';
import { getBlock, setBlock, removeBlock } from '../world/world-storage3d.js';
import { playSound } from '../audio/sound-engine.js';
export function activateTNT3D(b, fuseSeconds) {
b.active = true;
b.fuse = fuseSeconds;
playSound('click');
}
export function explodeTNT3D(b, st) {
const TILE = st.TILE;
const cx = b.gx, cy = b.gy, cz = b.gz || 0;
const radius = 3.2;
const r2 = radius * radius;
// убрать сам блок
removeBlock(cx, cy, cz);
const toRemove = [];
const r = Math.ceil(radius);
for (let dx = -r; dx <= r; dx++) {
for (let dy = -r; dy <= r; dy++) {
for (let dz = -r; dz <= r; dz++) {
const d2 = dx * dx + dy * dy + dz * dz;
if (d2 > r2) continue;
const blk = getBlock(cx + dx, cy + dy, cz + dz);
if (!blk) continue;
const def = BLOCKS[blk.t];
if (!def) continue;
if (def.unbreakable) continue;
// взрывная сила падает с расстоянием
if (d2 > r2 * 0.55 && blk.t !== 'dirt' && blk.t !== 'sand' && blk.t !== 'grass') continue;
toRemove.push({ gx: cx + dx, gy: cy + dy, gz: cz + dz, t: blk.t });
}
}
}
for (const rem of toRemove) removeBlock(rem.gx, rem.gy, rem.gz);
// частицы
const parts = st.parts || (st.parts = []);
const n = 30;
for (let i = 0; i < n; i++) {
const a = Math.random() * Math.PI * 2;
const p = Math.acos(2 * Math.random() - 1);
const sp = 60 + Math.random() * 180;
parts.push({
x: (cx + 0.5) * TILE,
y: (cy + 0.5) * TILE,
z: (cz + 0.5) * TILE,
vx: Math.sin(p) * Math.cos(a) * sp,
vy: Math.cos(p) * sp,
vz: Math.sin(p) * Math.sin(a) * sp,
life: 0.6 + Math.random() * 0.5,
c: Math.random() > 0.5 ? '#ff7a1a' : '#f1c40f',
});
}
// урон мобам и игроку в радиусе
const dmg = (e) => {
const ex = (e.x + e.w / 2) / TILE, ey = (e.y + e.h / 2) / TILE, ez = ((e.z || 0) + e.d / 2) / TILE;
const d = Math.hypot(ex - cx, ey - cy, ez - cz);
if (d < radius + 1) {
const f = 1 - d / (radius + 1);
e.hp -= 14 * f;
e.vx += (ex - cx) * 220;
e.vy -= 300 * f;
e.vz = (e.vz || 0) + (ez - cz) * 220;
}
};
for (const m of st.mobs) dmg(m);
const p = st.player;
dmg(p);
if (p.hp <= 0 && p.invuln <= 0) { p.hp = 0; p.invuln = 1.5; }
playSound('explode1');
// взрывные частицы убрать по таймеру
st.explosionFlash = 0.4;
setBlock(cx, cy, cz, 'air');
void setBlock;
}