80 lines
2.7 KiB
JavaScript
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 }; |