diff --git a/index.html b/index.html
index 663b52b..05f10b2 100644
--- a/index.html
+++ b/index.html
@@ -18,6 +18,7 @@
+
diff --git a/script.js b/script.js
index ced44b8..ecdf894 100644
--- a/script.js
+++ b/script.js
@@ -13,6 +13,7 @@ const WOOD_COLOR = '#8B5A2B';
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'];
// Element types
const EMPTY = 0;
@@ -28,6 +29,10 @@ const GRASS_BLADE = 9;
const FLOWER = 10;
const TREE_SEED = 11;
const LEAF = 12;
+const FIRE = 13;
+
+// Flammable materials
+const FLAMMABLE_MATERIALS = [GRASS, WOOD, SEED, GRASS_BLADE, FLOWER, TREE_SEED, LEAF];
// Global variables
let canvas, ctx;
@@ -45,6 +50,7 @@ let worldOffsetYBeforeDrag = 0;
let chunks = new Map(); // Map to store chunks with key "x,y"
let metadata = new Map(); // Map to store metadata for pixels
let debugMode = false;
+let fireUpdateCounter = 0;
// Initialize the simulation
window.onload = function() {
@@ -64,6 +70,7 @@ window.onload = function() {
document.getElementById('wood-btn').addEventListener('click', () => setTool(WOOD));
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('eraser-btn').addEventListener('click', () => setTool(EMPTY));
// Navigation controls
@@ -116,6 +123,8 @@ function setTool(tool) {
document.getElementById('seed-btn').classList.add('active');
} else if (tool === TREE_SEED) {
document.getElementById('tree-seed-btn').classList.add('active');
+ } else if (tool === FIRE) {
+ document.getElementById('fire-btn').classList.add('active');
} else if (tool === EMPTY) {
document.getElementById('eraser-btn').classList.add('active');
}
@@ -192,18 +201,30 @@ function draw(x, y) {
const pixelX = worldX + dx;
const pixelY = worldY + dy;
- setPixel(pixelX, pixelY, currentTool);
-
- // Add metadata for special types
- if (currentTool === SEED) {
- setMetadata(pixelX, pixelY, { type: 'regular' });
- } else if (currentTool === FLOWER) {
- setMetadata(pixelX, pixelY, {
- type: 'flower',
- color: FLOWER_COLORS[Math.floor(Math.random() * FLOWER_COLORS.length)],
- age: 0,
- height: 1
- });
+ // Special handling for fire - only set fire to flammable materials
+ if (currentTool === FIRE) {
+ const currentPixel = getPixel(pixelX, pixelY);
+ if (FLAMMABLE_MATERIALS.includes(currentPixel)) {
+ setPixel(pixelX, pixelY, FIRE);
+ setMetadata(pixelX, pixelY, {
+ lifetime: 100 + Math.floor(Math.random() * 100),
+ colorIndex: Math.floor(Math.random() * FIRE_COLORS.length)
+ });
+ }
+ } else {
+ setPixel(pixelX, pixelY, currentTool);
+
+ // Add metadata for special types
+ if (currentTool === SEED) {
+ setMetadata(pixelX, pixelY, { type: 'regular' });
+ } else if (currentTool === FLOWER) {
+ setMetadata(pixelX, pixelY, {
+ type: 'flower',
+ color: FLOWER_COLORS[Math.floor(Math.random() * FLOWER_COLORS.length)],
+ age: 0,
+ height: 1
+ });
+ }
}
}
}
@@ -377,6 +398,9 @@ function updatePhysics() {
// Get visible chunks
const visibleChunks = getVisibleChunks();
+ // Increment fire update counter
+ fireUpdateCounter++;
+
// Process each visible chunk
for (const { chunkX, chunkY } of visibleChunks) {
const chunk = getOrCreateChunk(chunkX, chunkY);
@@ -413,6 +437,8 @@ function updatePhysics() {
updateFlower(worldX, worldY);
} else if (type === TREE_SEED) {
updateTreeSeed(worldX, worldY);
+ } else if (type === FIRE) {
+ updateFire(worldX, worldY);
}
}
}
@@ -793,6 +819,92 @@ function updateWater(x, y) {
moved = true;
}
}
+
+ // Water extinguishes 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) {
+ if (getPixel(x + dir.dx, y + dir.dy) === FIRE) {
+ setPixel(x + dir.dx, y + dir.dy, EMPTY);
+ removeMetadata(x + dir.dx, y + dir.dy);
+ }
+ }
+}
+
+// Add fire update function
+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 getVisibleChunks() {
@@ -889,6 +1001,11 @@ function render() {
ctx.fillStyle = SEED_COLOR;
} else if (type === LEAF) {
ctx.fillStyle = LEAF_COLOR;
+ } else if (type === FIRE) {
+ // Get fire color from metadata
+ const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y);
+ const colorIndex = metadata ? metadata.colorIndex : 0;
+ ctx.fillStyle = FIRE_COLORS[colorIndex];
}
// Draw the pixel