125 lines
5.2 KiB
JavaScript
125 lines
5.2 KiB
JavaScript
|
|
import { grid, cols, rows } from './state.js';
|
||
|
|
import { config } from './config.js';
|
||
|
|
|
||
|
|
export function updateExplosive(x, y, cell) {
|
||
|
|
// Check neighbors for fire
|
||
|
|
const neighbors = [[0, 1], [0, -1], [1, 0], [-1, 0]];
|
||
|
|
for (let n of neighbors) {
|
||
|
|
const nx = x + n[0], ny = y + n[1];
|
||
|
|
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows && grid[nx][ny]) {
|
||
|
|
if (grid[nx][ny].type === 'fire') {
|
||
|
|
explode(x, y);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function explode(startX, startY) {
|
||
|
|
// BFS to find connected explosives (Cluster Logic)
|
||
|
|
let queue = [{x: startX, y: startY}];
|
||
|
|
let visited = new Set([`${startX},${startY}`]);
|
||
|
|
let cluster = [];
|
||
|
|
let perimeter = []; // "Skin" of the explosive
|
||
|
|
|
||
|
|
// Ensure start is explosive
|
||
|
|
if (!grid[startX][startY] || grid[startX][startY].type !== 'explosive') return;
|
||
|
|
|
||
|
|
let head = 0;
|
||
|
|
while(head < queue.length) {
|
||
|
|
let curr = queue[head++];
|
||
|
|
cluster.push(curr);
|
||
|
|
|
||
|
|
let isPerimeter = false;
|
||
|
|
const neighbors = [[0, 1], [0, -1], [1, 0], [-1, 0]];
|
||
|
|
|
||
|
|
for (let n of neighbors) {
|
||
|
|
const nx = curr.x + n[0];
|
||
|
|
const ny = curr.y + n[1];
|
||
|
|
const key = `${nx},${ny}`;
|
||
|
|
|
||
|
|
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
|
||
|
|
const neighbor = grid[nx][ny];
|
||
|
|
if (neighbor && neighbor.type === 'explosive') {
|
||
|
|
if (!visited.has(key)) {
|
||
|
|
visited.add(key);
|
||
|
|
queue.push({x: nx, y: ny});
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Neighbor is not explosive (or is empty), so this is an edge
|
||
|
|
isPerimeter = true;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// Edge of screen
|
||
|
|
isPerimeter = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isPerimeter) {
|
||
|
|
perimeter.push(curr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Calculate Force based on Mass
|
||
|
|
const totalMass = cluster.length;
|
||
|
|
const forceMultiplier = config.explosive.force + Math.log(totalMass) * 0.1;
|
||
|
|
const reach = config.explosive.radius;
|
||
|
|
|
||
|
|
// Remove Cluster
|
||
|
|
for (let p of cluster) {
|
||
|
|
grid[p.x][p.y] = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Apply Blast from Perimeter (Skin)
|
||
|
|
for (let p of perimeter) {
|
||
|
|
for (let i = -reach; i <= reach; i++) {
|
||
|
|
for (let j = -reach; j <= reach; j++) {
|
||
|
|
const nx = p.x + i;
|
||
|
|
const ny = p.y + j;
|
||
|
|
|
||
|
|
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
|
||
|
|
const dist = Math.sqrt(i*i + j*j);
|
||
|
|
if (dist > 0 && dist <= reach) {
|
||
|
|
const target = grid[nx][ny];
|
||
|
|
|
||
|
|
if (target) {
|
||
|
|
if (target.type === 'explosive') {
|
||
|
|
explode(nx, ny); // Trigger separate cluster
|
||
|
|
} else if (target.type === 'wood' && config.explosive.destroy.wood) {
|
||
|
|
grid[nx][ny] = { type: 'fire', color: '#FF5500', updated: true };
|
||
|
|
} else if (target.type === 'water' || target.type === 'acid' || target.type === 'steam') {
|
||
|
|
if (target.type === 'water' && config.explosive.destroy.water) {
|
||
|
|
grid[nx][ny] = null;
|
||
|
|
} else if (target.type === 'acid' && config.explosive.destroy.acid) {
|
||
|
|
grid[nx][ny] = null;
|
||
|
|
} else {
|
||
|
|
if (target.type === 'water' && Math.random() < 0.2) {
|
||
|
|
target.type = 'steam';
|
||
|
|
target.color = '#E0FFFF';
|
||
|
|
}
|
||
|
|
const angle = Math.atan2(j, i);
|
||
|
|
const force = (reach - dist + 1) * forceMultiplier * 0.5;
|
||
|
|
target.vx = (target.vx || 0) + Math.cos(angle) * force;
|
||
|
|
target.vy = (target.vy || 0) + Math.sin(angle) * force;
|
||
|
|
target.updated = true;
|
||
|
|
}
|
||
|
|
} else if (target.type === 'dirt' && config.explosive.destroy.dirt) {
|
||
|
|
const angle = Math.atan2(j, i);
|
||
|
|
const force = (reach - dist + 1) * (forceMultiplier * 0.3);
|
||
|
|
target.vx = (target.vx || 0) + Math.cos(angle) * force;
|
||
|
|
target.vy = (target.vy || 0) + Math.sin(angle) * force;
|
||
|
|
target.updated = true;
|
||
|
|
} else if (target.type !== 'fire' && config.explosive.destroy[target.type]) {
|
||
|
|
grid[nx][ny] = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Math.random() < 0.05 && !grid[nx][ny]) {
|
||
|
|
grid[nx][ny] = { type: 'fire', color: '#FF5500', updated: true, lifespan: Math.random() * 10 + 5 };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|