feat: Add fire simulation with color variation and flammable material restrictions
This commit is contained in:
parent
0788b3067d
commit
d6d874ab99
@ -18,6 +18,7 @@
|
|||||||
<button id="wood-btn">Wood</button>
|
<button id="wood-btn">Wood</button>
|
||||||
<button id="seed-btn">Seed</button>
|
<button id="seed-btn">Seed</button>
|
||||||
<button id="tree-seed-btn">Tree Seed</button>
|
<button id="tree-seed-btn">Tree Seed</button>
|
||||||
|
<button id="fire-btn">Fire</button>
|
||||||
<button id="eraser-btn">Eraser</button>
|
<button id="eraser-btn">Eraser</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
|
117
script.js
117
script.js
@ -13,6 +13,7 @@ const WOOD_COLOR = '#8B5A2B';
|
|||||||
const SEED_COLOR = '#654321';
|
const SEED_COLOR = '#654321';
|
||||||
const FLOWER_COLORS = ['#FF0000', '#FFFF00', '#FF00FF', '#FFA500', '#FFFFFF', '#00FFFF'];
|
const FLOWER_COLORS = ['#FF0000', '#FFFF00', '#FF00FF', '#FFA500', '#FFFFFF', '#00FFFF'];
|
||||||
const LEAF_COLOR = '#228B22';
|
const LEAF_COLOR = '#228B22';
|
||||||
|
const FIRE_COLORS = ['#FF0000', '#FF3300', '#FF6600', '#FF9900', '#FFCC00', '#FFFF00'];
|
||||||
|
|
||||||
// Element types
|
// Element types
|
||||||
const EMPTY = 0;
|
const EMPTY = 0;
|
||||||
@ -28,6 +29,10 @@ const GRASS_BLADE = 9;
|
|||||||
const FLOWER = 10;
|
const FLOWER = 10;
|
||||||
const TREE_SEED = 11;
|
const TREE_SEED = 11;
|
||||||
const LEAF = 12;
|
const LEAF = 12;
|
||||||
|
const FIRE = 13;
|
||||||
|
|
||||||
|
// Flammable materials
|
||||||
|
const FLAMMABLE_MATERIALS = [GRASS, WOOD, SEED, GRASS_BLADE, FLOWER, TREE_SEED, LEAF];
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
let canvas, ctx;
|
let canvas, ctx;
|
||||||
@ -45,6 +50,7 @@ let worldOffsetYBeforeDrag = 0;
|
|||||||
let chunks = new Map(); // Map to store chunks with key "x,y"
|
let chunks = new Map(); // Map to store chunks with key "x,y"
|
||||||
let metadata = new Map(); // Map to store metadata for pixels
|
let metadata = new Map(); // Map to store metadata for pixels
|
||||||
let debugMode = false;
|
let debugMode = false;
|
||||||
|
let fireUpdateCounter = 0;
|
||||||
|
|
||||||
// Initialize the simulation
|
// Initialize the simulation
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
@ -64,6 +70,7 @@ window.onload = function() {
|
|||||||
document.getElementById('wood-btn').addEventListener('click', () => setTool(WOOD));
|
document.getElementById('wood-btn').addEventListener('click', () => setTool(WOOD));
|
||||||
document.getElementById('seed-btn').addEventListener('click', () => setTool(SEED));
|
document.getElementById('seed-btn').addEventListener('click', () => setTool(SEED));
|
||||||
document.getElementById('tree-seed-btn').addEventListener('click', () => setTool(TREE_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));
|
document.getElementById('eraser-btn').addEventListener('click', () => setTool(EMPTY));
|
||||||
|
|
||||||
// Navigation controls
|
// Navigation controls
|
||||||
@ -116,6 +123,8 @@ function setTool(tool) {
|
|||||||
document.getElementById('seed-btn').classList.add('active');
|
document.getElementById('seed-btn').classList.add('active');
|
||||||
} else if (tool === TREE_SEED) {
|
} else if (tool === TREE_SEED) {
|
||||||
document.getElementById('tree-seed-btn').classList.add('active');
|
document.getElementById('tree-seed-btn').classList.add('active');
|
||||||
|
} else if (tool === FIRE) {
|
||||||
|
document.getElementById('fire-btn').classList.add('active');
|
||||||
} else if (tool === EMPTY) {
|
} else if (tool === EMPTY) {
|
||||||
document.getElementById('eraser-btn').classList.add('active');
|
document.getElementById('eraser-btn').classList.add('active');
|
||||||
}
|
}
|
||||||
@ -192,6 +201,17 @@ function draw(x, y) {
|
|||||||
const pixelX = worldX + dx;
|
const pixelX = worldX + dx;
|
||||||
const pixelY = worldY + dy;
|
const pixelY = worldY + dy;
|
||||||
|
|
||||||
|
// 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);
|
setPixel(pixelX, pixelY, currentTool);
|
||||||
|
|
||||||
// Add metadata for special types
|
// Add metadata for special types
|
||||||
@ -207,6 +227,7 @@ function draw(x, y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchStart(e) {
|
function handleTouchStart(e) {
|
||||||
@ -377,6 +398,9 @@ function updatePhysics() {
|
|||||||
// Get visible chunks
|
// Get visible chunks
|
||||||
const visibleChunks = getVisibleChunks();
|
const visibleChunks = getVisibleChunks();
|
||||||
|
|
||||||
|
// Increment fire update counter
|
||||||
|
fireUpdateCounter++;
|
||||||
|
|
||||||
// Process each visible chunk
|
// Process each visible chunk
|
||||||
for (const { chunkX, chunkY } of visibleChunks) {
|
for (const { chunkX, chunkY } of visibleChunks) {
|
||||||
const chunk = getOrCreateChunk(chunkX, chunkY);
|
const chunk = getOrCreateChunk(chunkX, chunkY);
|
||||||
@ -413,6 +437,8 @@ function updatePhysics() {
|
|||||||
updateFlower(worldX, worldY);
|
updateFlower(worldX, worldY);
|
||||||
} else if (type === TREE_SEED) {
|
} else if (type === TREE_SEED) {
|
||||||
updateTreeSeed(worldX, worldY);
|
updateTreeSeed(worldX, worldY);
|
||||||
|
} else if (type === FIRE) {
|
||||||
|
updateFire(worldX, worldY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -793,6 +819,92 @@ function updateWater(x, y) {
|
|||||||
moved = true;
|
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() {
|
function getVisibleChunks() {
|
||||||
@ -889,6 +1001,11 @@ function render() {
|
|||||||
ctx.fillStyle = SEED_COLOR;
|
ctx.fillStyle = SEED_COLOR;
|
||||||
} else if (type === LEAF) {
|
} else if (type === LEAF) {
|
||||||
ctx.fillStyle = LEAF_COLOR;
|
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
|
// Draw the pixel
|
||||||
|
Loading…
x
Reference in New Issue
Block a user