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.
167 lines
5.7 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
}
|