feat: Add lava element with dynamic colors and interactions

This commit is contained in:
Kacper Kostka (aider) 2025-04-04 11:48:06 +02:00
parent d6d874ab99
commit c858da4c3e
2 changed files with 116 additions and 1 deletions

View File

@ -19,6 +19,7 @@
<button id="seed-btn">Seed</button>
<button id="tree-seed-btn">Tree Seed</button>
<button id="fire-btn">Fire</button>
<button id="lava-btn">Lava</button>
<button id="eraser-btn">Eraser</button>
</div>
<div class="navigation">

116
script.js
View File

@ -14,6 +14,7 @@ const SEED_COLOR = '#654321';
const FLOWER_COLORS = ['#FF0000', '#FFFF00', '#FF00FF', '#FFA500', '#FFFFFF', '#00FFFF'];
const LEAF_COLOR = '#228B22';
const FIRE_COLORS = ['#FF0000', '#FF3300', '#FF6600', '#FF9900', '#FFCC00', '#FFFF00'];
const LAVA_COLORS = ['#FF0000', '#FF3300', '#FF4500', '#FF6600', '#FF8C00'];
// Element types
const EMPTY = 0;
@ -30,6 +31,7 @@ const FLOWER = 10;
const TREE_SEED = 11;
const LEAF = 12;
const FIRE = 13;
const LAVA = 14;
// Flammable materials
const FLAMMABLE_MATERIALS = [GRASS, WOOD, SEED, GRASS_BLADE, FLOWER, TREE_SEED, LEAF];
@ -71,6 +73,7 @@ window.onload = function() {
document.getElementById('seed-btn').addEventListener('click', () => setTool(SEED));
document.getElementById('tree-seed-btn').addEventListener('click', () => setTool(TREE_SEED));
document.getElementById('fire-btn').addEventListener('click', () => setTool(FIRE));
document.getElementById('lava-btn').addEventListener('click', () => setTool(LAVA));
document.getElementById('eraser-btn').addEventListener('click', () => setTool(EMPTY));
// Navigation controls
@ -125,6 +128,8 @@ function setTool(tool) {
document.getElementById('tree-seed-btn').classList.add('active');
} else if (tool === FIRE) {
document.getElementById('fire-btn').classList.add('active');
} else if (tool === LAVA) {
document.getElementById('lava-btn').classList.add('active');
} else if (tool === EMPTY) {
document.getElementById('eraser-btn').classList.add('active');
}
@ -439,6 +444,8 @@ function updatePhysics() {
updateTreeSeed(worldX, worldY);
} else if (type === FIRE) {
updateFire(worldX, worldY);
} else if (type === LAVA) {
updateLava(worldX, worldY);
}
}
}
@ -820,7 +827,7 @@ function updateWater(x, y) {
}
}
// Water extinguishes fire
// Water extinguishes fire and turns lava into stone
const directions = [
{dx: -1, dy: 0}, {dx: 1, dy: 0},
{dx: 0, dy: -1}, {dx: 0, dy: 1},
@ -832,6 +839,13 @@ function updateWater(x, y) {
if (getPixel(x + dir.dx, y + dir.dy) === FIRE) {
setPixel(x + dir.dx, y + dir.dy, EMPTY);
removeMetadata(x + dir.dx, y + dir.dy);
} else if (getPixel(x + dir.dx, y + dir.dy) === LAVA) {
// Water turns lava into stone
setPixel(x + dir.dx, y + dir.dy, STONE);
removeMetadata(x + dir.dx, y + dir.dy);
// Water is consumed in the process
setPixel(x, y, EMPTY);
return;
}
}
}
@ -907,6 +921,101 @@ function updateFire(x, y) {
}
}
// Add lava update function
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);
}
}
}
function getVisibleChunks() {
const visibleChunks = [];
@ -1006,6 +1115,11 @@ function render() {
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
const colorIndex = metadata ? metadata.colorIndex : 0;
ctx.fillStyle = FIRE_COLORS[colorIndex];
} else if (type === LAVA) {
// Get lava color from metadata
const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
const colorIndex = metadata ? metadata.colorIndex : 0;
ctx.fillStyle = LAVA_COLORS[colorIndex];
}
// Draw the pixel