import { grid, cols, rows } from './state.js'; import { config } from './config.js'; import { createCell } from './physics.js'; let explosionQueue = []; let isProcessingExplosions = false; export function updateExplosive(x, y, cell) { const dxs = [0, 0, -1, 1]; const dys = [1, -1, 0, 0]; for (let i = 0; i < 4; i++) { const nx = x + dxs[i]; const ny = y + dys[i]; if (nx >= 0 && nx < cols && ny >= 0 && ny < rows && grid[nx][ny]) { if (grid[nx][ny].type === 'fire' || grid[nx][ny].type === 'lava') { explode(x, y); return; } } } } export function explode(startX, startY) { if (!grid[startX][startY] || grid[startX][startY].type !== 'explosive') return; explosionQueue.push({ x: startX, y: startY }); if (isProcessingExplosions) return; isProcessingExplosions = true; while (explosionQueue.length > 0) { const origin = explosionQueue.shift(); processSingleExplosion(origin.x, origin.y); } isProcessingExplosions = false; } function processSingleExplosion(startX, startY) { if (startX < 0 || startX >= cols || startY < 0 || startY >= rows) return; // BFS to find contiguous cluster of TNT const queue = [{ x: startX, y: startY }]; const cluster = []; const visited = new Uint8Array(cols * rows); visited[startY * cols + startX] = 1; let head = 0; let minX = startX, maxX = startX, minY = startY, maxY = startY; while (head < queue.length) { const curr = queue[head++]; cluster.push(curr); if (curr.x < minX) minX = curr.x; if (curr.x > maxX) maxX = curr.x; if (curr.y < minY) minY = curr.y; if (curr.y > maxY) maxY = curr.y; const dxs = [0, 0, -1, 1]; const dys = [1, -1, 0, 0]; for (let i = 0; i < 4; i++) { const nx = curr.x + dxs[i]; const ny = curr.y + dys[i]; if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) { const idx = ny * cols + nx; if (!visited[idx]) { visited[idx] = 1; if (grid[nx][ny] && grid[nx][ny].type === 'explosive') { queue.push({ x: nx, y: ny }); } } } } } // Clear the TNT cluster for (let i = 0; i < cluster.length; i++) { const p = cluster[i]; grid[p.x][p.y] = null; } const reach = Math.round(config.explosive.radius); const totalMass = cluster.length; const forceMultiplier = config.explosive.force + Math.log(totalMass + 1) * 0.15; // Center of the explosion const centerX = Math.round((minX + maxX) / 2); const centerY = Math.round((minY + maxY) / 2); const blastMinX = Math.max(0, minX - reach); const blastMaxX = Math.min(cols - 1, maxX + reach); const blastMinY = Math.max(0, minY - reach); const blastMaxY = Math.min(rows - 1, maxY + reach); // Apply blast wave across the combined bounding area for (let ny = blastMinY; ny <= blastMaxY; ny++) { for (let nx = blastMinX; nx <= blastMaxX; nx++) { // Find distance to closest cluster block or center const dx = nx - centerX; const dy = ny - centerY; const dist = Math.sqrt(dx * dx + dy * dy); const effectiveReach = reach + Math.min(10, Math.sqrt(totalMass)); if (dist <= effectiveReach) { const target = grid[nx][ny]; if (target) { const tt = target.type; if (tt === 'explosive') { // Queue chain reaction explosionQueue.push({ x: nx, y: ny }); } else if (config.explosive.destroy[tt]) { if (tt === 'wood' || tt === 'plant' || tt === 'grass') { grid[nx][ny] = createCell(Math.random() < 0.5 ? 'fire' : 'smoke'); } else if (tt === 'dirt' || tt === 'sand') { // Fling granular particles outward with blast force const angle = Math.atan2(dy, dx); const force = (effectiveReach - dist + 1) * forceMultiplier * 0.4; target.vx = (target.vx || 0) + Math.cos(angle) * force; target.vy = (target.vy || 0) + Math.sin(angle) * force - 1.5; } else { grid[nx][ny] = null; } } else if (tt === 'water' || tt === 'acid' || tt === 'oil') { if (tt === 'water' && Math.random() < 0.35) { grid[nx][ny] = createCell('steam'); } else if (tt === 'oil' && Math.random() < 0.5) { grid[nx][ny] = createCell('fire'); } else { const angle = Math.atan2(dy, dx); const force = (effectiveReach - dist + 1) * forceMultiplier * 0.5; target.vx = (target.vx || 0) + Math.cos(angle) * force; target.vy = (target.vy || 0) + Math.sin(angle) * force - 1.0; } } } else if (Math.random() < 0.08 && !grid[nx][ny]) { // Spawn fire or smoke in empty space within blast core grid[nx][ny] = createCell(Math.random() < 0.4 ? 'fire' : 'smoke'); } } } } }