// ==================== 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; }