sandsim/js/elements/fire.js
Kacper Kostka (aider) a86acfff3a refactor: Spread game code into modular files for better maintainability
This commit reorganizes the game's source code into multiple files within a `js/` directory, creating a more modular and maintainable structure. The changes include:

- Created separate files for different game components:
  - `constants.js`: Game constants and element types
  - `world.js`: World management functions
  - `terrain.js`: Terrain generation logic
  - `physics.js`: Physics simulation
  - `render.js`: Rendering functions
  - `input.js`: Input handling
  - `main.js`: Main game initialization and loop
  - Element-specific files in `js/elements/`:
    - `basic.js`: Sand, water, dirt behaviors
    - `plants.js`: Grass, seeds, flowers
    - `trees.js`: Tree growth and leaf generation
    - `fire.js`: Fire and lava behaviors

- Updated `index.html` to load modules in the correct order
- Removed the monolithic `script.js`

The modular approach improves code readability, makes future extensions easier, and separates concerns more effectively.
2025-04-04 12:15:30 +02:00

167 lines
5.7 KiB
JavaScript

// Fire and lava element behaviors
let fireUpdateCounter = 0;
function updateFire(x, y) {
const metadata = getMetadata(x, y);
if (!metadata) {
// Initialize metadata if it doesn't exist
setMetadata(x, y, {
lifetime: 100 + Math.floor(Math.random() * 100),
colorIndex: Math.floor(Math.random() * FIRE_COLORS.length)
});
return;
}
// Decrease lifetime
metadata.lifetime--;
// Randomly change color for flickering effect
if (Math.random() < 0.2) {
metadata.colorIndex = Math.floor(Math.random() * FIRE_COLORS.length);
}
// Update metadata
setMetadata(x, y, metadata);
// Fire rises upward occasionally
if (Math.random() < 0.3 && getPixel(x, y - 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x, y - 1, FIRE);
moveMetadata(x, y, x, y - 1);
return;
}
// Fire can also move slightly to the sides
if (Math.random() < 0.1) {
const direction = Math.random() > 0.5 ? 1 : -1;
if (getPixel(x + direction, y - 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + direction, y - 1, FIRE);
moveMetadata(x, y, x + direction, y - 1);
return;
}
}
// Fire spreads to nearby flammable materials (less frequently to reduce performance impact)
if (fireUpdateCounter % 3 === 0 && Math.random() < 0.3) {
const directions = [
{dx: -1, dy: 0}, {dx: 1, dy: 0},
{dx: 0, dy: -1}, {dx: 0, dy: 1},
{dx: -1, dy: -1}, {dx: 1, dy: -1},
{dx: -1, dy: 1}, {dx: 1, dy: 1}
];
const dir = directions[Math.floor(Math.random() * directions.length)];
const nearbyType = getPixel(x + dir.dx, y + dir.dy);
if (FLAMMABLE_MATERIALS.includes(nearbyType)) {
setPixel(x + dir.dx, y + dir.dy, FIRE);
setMetadata(x + dir.dx, y + dir.dy, {
lifetime: 100 + Math.floor(Math.random() * 100),
colorIndex: Math.floor(Math.random() * FIRE_COLORS.length)
});
}
}
// Fire burns out after its lifetime
if (metadata.lifetime <= 0) {
setPixel(x, y, EMPTY);
removeMetadata(x, y);
}
}
function updateLava(x, y) {
const metadata = getMetadata(x, y);
if (!metadata) {
// Initialize metadata if it doesn't exist
setMetadata(x, y, {
colorIndex: Math.floor(Math.random() * LAVA_COLORS.length)
});
} else {
// Randomly change color for flowing effect
if (Math.random() < 0.1) {
metadata.colorIndex = Math.floor(Math.random() * LAVA_COLORS.length);
setMetadata(x, y, metadata);
}
}
// Lava moves slower than water
if (Math.random() < 0.7) {
// Try to move down
if (getPixel(x, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x, y + 1, LAVA);
moveMetadata(x, y, x, y + 1);
}
// Try to move down-left or down-right
else if (getPixel(x - 1, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x - 1, y + 1, LAVA);
moveMetadata(x, y, x - 1, y + 1);
}
else if (getPixel(x + 1, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + 1, y + 1, LAVA);
moveMetadata(x, y, x + 1, y + 1);
}
// Try to spread horizontally (slower than water)
else if (Math.random() < 0.3) {
// Randomly choose direction first
const goLeft = Math.random() > 0.5;
if (goLeft && getPixel(x - 1, y) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x - 1, y, LAVA);
moveMetadata(x, y, x - 1, y);
} else if (!goLeft && getPixel(x + 1, y) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + 1, y, LAVA);
moveMetadata(x, y, x + 1, y);
}
// Try the other direction if first failed
else if (!goLeft && getPixel(x - 1, y) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x - 1, y, LAVA);
moveMetadata(x, y, x - 1, y);
} else if (goLeft && getPixel(x + 1, y) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + 1, y, LAVA);
moveMetadata(x, y, x + 1, y);
}
}
}
// Lava sets nearby flammable materials on fire
const directions = [
{dx: -1, dy: 0}, {dx: 1, dy: 0},
{dx: 0, dy: -1}, {dx: 0, dy: 1},
{dx: -1, dy: -1}, {dx: 1, dy: -1},
{dx: -1, dy: 1}, {dx: 1, dy: 1}
];
for (const dir of directions) {
const nearbyType = getPixel(x + dir.dx, y + dir.dy);
// Set flammable materials on fire
if (FLAMMABLE_MATERIALS.includes(nearbyType)) {
setPixel(x + dir.dx, y + dir.dy, FIRE);
setMetadata(x + dir.dx, y + dir.dy, {
lifetime: 100 + Math.floor(Math.random() * 100),
colorIndex: Math.floor(Math.random() * FIRE_COLORS.length)
});
}
// Lava can melt sand into glass (stone)
else if (nearbyType === SAND && Math.random() < 0.05) {
setPixel(x + dir.dx, y + dir.dy, STONE);
}
// Lava can burn dirt
else if (nearbyType === DIRT && Math.random() < 0.02) {
setPixel(x + dir.dx, y + dir.dy, EMPTY);
}
}
}