SandGame/physics.js
2026-10-22 09:05:51 -05:00

158 lines
4.9 KiB
JavaScript

import { grid, cols, rows } from './state.js';
import { config } from './config.js';
import { move, swap } from './actions.js';
import { updateDirt } from './dirt.js';
import { updateWater } from './water.js';
import { updateAcid } from './acid.js';
import { updateSteam } from './steam.js';
import { updatePlant } from './plant.js';
import { updateFire } from './fire.js';
import { updateExplosive, explode } from './explosive.js';
export function spawnCell(x, y, type) {
if (x >= 0 && x < cols && y >= 0 && y < rows) {
const target = grid[x][y];
// Manual Detonation
if (target && target.type === 'explosive' && type === 'fire') {
if (!config.time.paused) {
explode(x, y);
}
return;
}
// Check if target is a fluid/gas that can be displaced
const isDisplaceable = target && (target.type === 'water' || target.type === 'acid' || target.type === 'steam');
if (!target || (isDisplaceable && type !== target.type)) {
// If it's displaceable, try to push it aside
if (isDisplaceable) {
const neighbors = [[-1,0], [1,0], [0,-1], [0,1]];
neighbors.sort(() => Math.random() - 0.5);
for (let n of neighbors) {
const nx = x + n[0];
const ny = y + n[1];
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows && !grid[nx][ny]) {
grid[nx][ny] = target; // Move fluid here
target.updated = true;
// Add Ripple Velocity
target.vx = (target.vx || 0) + n[0] * 2;
target.vy = (target.vy || 0) + n[1] * 2 - 1;
break;
}
}
}
grid[x][y] = createCell(type);
}
}
}
function createCell(type) {
const colors = {
dirt: '#ffaa00',
plant: '#228B22',
water: '#0000FF',
fire: '#FF5500',
rock: '#808080',
wood: '#8B4513',
explosive: '#8B0000',
acid: '#7FFF00',
steam: '#E0FFFF'
};
return { type, color: colors[type] || '#FFF', updated: false };
}
export function update() {
for (let x = 0; x < cols; x++) {
for (let y = 0; y < rows; y++) {
if (grid[x][y]) grid[x][y].updated = false;
}
}
for (let y = rows - 1; y >= 0; y--) {
for (let x = 0; x < cols; x++) {
if (grid[x][y] && !grid[x][y].updated) {
updateCell(x, y);
}
}
}
}
function updateCell(x, y) {
const cell = grid[x][y];
cell.updated = true;
// Physics for tossed particles (Explosions/Velocity)
if (cell.vx || cell.vy) {
applyVelocity(x, y, cell);
// If velocity moved it, we might stop here or continue depending on logic.
// For now, if it moved, we consider it updated for this frame.
if (!grid[x][y] || grid[x][y] !== cell) return;
}
const type = cell.type;
if (type === 'dirt') {
updateDirt(x, y, cell);
} else if (type === 'water') {
updateWater(x, y, cell);
} else if (type === 'fire') {
updateFire(x, y, cell);
} else if (type === 'steam') {
updateSteam(x, y, cell);
} else if (type === 'acid') {
updateAcid(x, y, cell);
} else if (type === 'plant') {
updatePlant(x, y, cell);
} else if (type === 'explosive') {
updateExplosive(x, y, cell);
}
}
function applyVelocity(x, y, cell) {
cell.vx = cell.vx || 0;
cell.vy = cell.vy || 0;
if (cell.type === 'steam') {
cell.vy -= 0.1; // Steam rises
} else {
cell.vy += config.global.gravity;
}
cell.vx *= config.global.drag;
cell.vy *= config.global.drag;
let nextX = Math.round(x + cell.vx);
let nextY = Math.round(y + cell.vy);
// Bounce off walls
if (nextX < 0 || nextX >= cols) {
cell.vx *= -0.5;
nextX = x;
}
if (nextY < 0 || nextY >= rows) {
cell.vy *= -0.3;
nextY = y;
}
if (nextX >= 0 && nextX < cols && nextY >= 0 && nextY < rows) {
const neighbor = grid[nextX][nextY];
if (!neighbor) {
move(x, y, nextX, nextY);
} else if (neighbor.type === 'steam') {
swap(x, y, nextX, nextY);
} else if (neighbor.type === 'water' || neighbor.type === 'dirt' || neighbor.type === 'acid') {
// Collision with movable particles
neighbor.vx = (neighbor.vx || 0) + cell.vx * 0.8;
neighbor.vy = (neighbor.vy || 0) + cell.vy * 0.8;
neighbor.vx += (Math.random() - 0.5); // Scatter
neighbor.updated = true;
cell.vx *= -0.4;
cell.vy *= -0.4;
} else {
cell.vx *= -0.3;
cell.vy *= -0.3;
}
}
}