grechka-game/src3d/world/tnt.js

80 lines
2.7 KiB
JavaScript

import { TILE } from '../core/constants.js';
import { BLOCKS } from '../data/blocks.js';
import { state } from '../core/state.js';
import { getBlock, removeBlock, k } from './world-storage.js';
import { calculateDamage } from '../entities/player.js';
import { playSound } from '../audio/sound-engine.js';
import { spawnExplosion } from '../render/particles.js';
import { rebuildHotbar } from '../ui/hotbar.js';
// TNT логика: цепь + усиление
function activateTNT(b, fuse = 3.2) {
if (b.dead) return;
if (b.active) return;
b.active = true;
b.fuse = fuse;
state.activeTNT.add(k(b.gx, b.gy));
}
function explodeAt(gx, gy) {
const center = getBlock(gx, gy);
if (!center) return;
// усиление: считаем сколько TNT рядом (в радиусе 2) и активируем их «почти сразу»
let bonus = 0;
for (let x = gx - 2; x <= gx + 2; x++) {
for (let y = gy - 2; y <= gy + 2; y++) {
const b = getBlock(x, y);
if (b && !b.dead && b.t === 'tnt' && !(x === gx && y === gy)) {
bonus += 0.8;
activateTNT(b, 0.12); // цепь
}
}
}
const power = 1 + bonus; // условная мощность
const radius = 3.2 + bonus * 0.7; // радиус разрушения в тайлах
const dmgR = 150 + bonus * 60; // радиус урона в пикселях
removeBlock(gx, gy);
state.activeTNT.delete(k(gx, gy));
playSound('explode1'); // Звук взрыва
spawnExplosion(gx * TILE + TILE / 2, gy * TILE + TILE / 2, power);
for (let x = Math.floor(gx - radius); x <= Math.ceil(gx + radius); x++) {
for (let y = Math.floor(gy - radius); y <= Math.ceil(gy + radius); y++) {
const d = Math.hypot(x - gx, y - gy);
if (d > radius) continue;
const b = getBlock(x, y);
if (!b || b.dead) continue;
if (BLOCKS[b.t].fluid) continue;
if (BLOCKS[b.t].unbreakable) continue;
if (b.t === 'tnt') { activateTNT(b, 0.12); continue; }
removeBlock(x, y);
if (state.inv[b.t] !== undefined && Math.random() < 0.20) state.inv[b.t]++; // немного дропа
}
}
rebuildHotbar();
// урон
const hurt = (e) => {
const dx = (e.x + e.w / 2) - (gx * TILE + TILE / 2);
const dy = (e.y + e.h / 2) - (gy * TILE + TILE / 2);
const dist = Math.hypot(dx, dy);
if (dist < dmgR) {
const dmg = (dmgR - dist) * 0.06 * power;
if (e === state.player) {
const actualDamage = calculateDamage(dmg);
state.player.hp -= actualDamage;
} else {
e.hp -= dmg;
}
e.vx += (dx / dist || 0) * 600;
e.vy -= 320;
}
};
hurt(state.player);
state.mobs.forEach(hurt);
}
export { activateTNT, explodeAt };