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