feat: Add new materials (dirt, stone, grass, wood) with unique behaviors

This commit is contained in:
Kacper Kostka (aider) 2025-04-04 11:24:14 +02:00
parent a6f0baf02c
commit 6ad564eea0
2 changed files with 102 additions and 1 deletions

View File

@ -12,6 +12,10 @@
<div class="tools"> <div class="tools">
<button id="sand-btn" class="active">Sand</button> <button id="sand-btn" class="active">Sand</button>
<button id="water-btn">Water</button> <button id="water-btn">Water</button>
<button id="dirt-btn">Dirt</button>
<button id="stone-btn">Stone</button>
<button id="grass-btn">Grass</button>
<button id="wood-btn">Wood</button>
<button id="eraser-btn">Eraser</button> <button id="eraser-btn">Eraser</button>
</div> </div>
<div class="navigation"> <div class="navigation">

View File

@ -6,12 +6,20 @@ const WATER_SPREAD = 3;
const SAND_COLOR = '#e6c588'; const SAND_COLOR = '#e6c588';
const WATER_COLOR = '#4a80f5'; const WATER_COLOR = '#4a80f5';
const WALL_COLOR = '#888888'; const WALL_COLOR = '#888888';
const DIRT_COLOR = '#8B4513';
const STONE_COLOR = '#A9A9A9';
const GRASS_COLOR = '#7CFC00';
const WOOD_COLOR = '#8B5A2B';
// Element types // Element types
const EMPTY = 0; const EMPTY = 0;
const SAND = 1; const SAND = 1;
const WATER = 2; const WATER = 2;
const WALL = 3; const WALL = 3;
const DIRT = 4;
const STONE = 5;
const GRASS = 6;
const WOOD = 7;
// Global variables // Global variables
let canvas, ctx; let canvas, ctx;
@ -41,6 +49,10 @@ window.onload = function() {
// Tool selection // Tool selection
document.getElementById('sand-btn').addEventListener('click', () => setTool(SAND)); document.getElementById('sand-btn').addEventListener('click', () => setTool(SAND));
document.getElementById('water-btn').addEventListener('click', () => setTool(WATER)); document.getElementById('water-btn').addEventListener('click', () => setTool(WATER));
document.getElementById('dirt-btn').addEventListener('click', () => setTool(DIRT));
document.getElementById('stone-btn').addEventListener('click', () => setTool(STONE));
document.getElementById('grass-btn').addEventListener('click', () => setTool(GRASS));
document.getElementById('wood-btn').addEventListener('click', () => setTool(WOOD));
document.getElementById('eraser-btn').addEventListener('click', () => setTool(EMPTY)); document.getElementById('eraser-btn').addEventListener('click', () => setTool(EMPTY));
// Navigation controls // Navigation controls
@ -81,6 +93,14 @@ function setTool(tool) {
document.getElementById('sand-btn').classList.add('active'); document.getElementById('sand-btn').classList.add('active');
} else if (tool === WATER) { } else if (tool === WATER) {
document.getElementById('water-btn').classList.add('active'); document.getElementById('water-btn').classList.add('active');
} else if (tool === DIRT) {
document.getElementById('dirt-btn').classList.add('active');
} else if (tool === STONE) {
document.getElementById('stone-btn').classList.add('active');
} else if (tool === GRASS) {
document.getElementById('grass-btn').classList.add('active');
} else if (tool === WOOD) {
document.getElementById('wood-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');
} }
@ -317,7 +337,7 @@ function updatePhysics() {
const index = y * CHUNK_SIZE + x; const index = y * CHUNK_SIZE + x;
const type = chunk[index]; const type = chunk[index];
if (type === EMPTY) continue; if (type === EMPTY || type === STONE || type === WOOD) continue;
const worldX = chunkX * CHUNK_SIZE + x; const worldX = chunkX * CHUNK_SIZE + x;
const worldY = chunkY * CHUNK_SIZE + y; const worldY = chunkY * CHUNK_SIZE + y;
@ -326,6 +346,10 @@ function updatePhysics() {
updateSand(worldX, worldY); updateSand(worldX, worldY);
} else if (type === WATER) { } else if (type === WATER) {
updateWater(worldX, worldY); updateWater(worldX, worldY);
} else if (type === DIRT) {
updateDirt(worldX, worldY);
} else if (type === GRASS) {
updateGrass(worldX, worldY);
} }
} }
} }
@ -354,6 +378,71 @@ function updateSand(x, y) {
} }
} }
function updateDirt(x, y) {
// Try to move down
if (getPixel(x, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x, y + 1, DIRT);
}
// 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, DIRT);
}
else if (getPixel(x + 1, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + 1, y + 1, DIRT);
}
// Dirt can displace water
else if (getPixel(x, y + 1) === WATER) {
setPixel(x, y, WATER);
setPixel(x, y + 1, DIRT);
}
// Dirt can turn into grass if exposed to air above
if (getPixel(x, y - 1) === EMPTY && Math.random() < 0.001) {
setPixel(x, y, GRASS);
}
}
function updateGrass(x, y) {
// Grass behaves like dirt for physics
if (getPixel(x, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x, y + 1, GRASS);
}
else if (getPixel(x - 1, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x - 1, y + 1, GRASS);
}
else if (getPixel(x + 1, y + 1) === EMPTY) {
setPixel(x, y, EMPTY);
setPixel(x + 1, y + 1, GRASS);
}
else if (getPixel(x, y + 1) === WATER) {
setPixel(x, y, WATER);
setPixel(x, y + 1, GRASS);
}
// Grass can spread to nearby dirt
if (Math.random() < 0.0005) {
const directions = [
{dx: -1, dy: 0}, {dx: 1, dy: 0},
{dx: 0, dy: -1}, {dx: 0, dy: 1}
];
const dir = directions[Math.floor(Math.random() * directions.length)];
if (getPixel(x + dir.dx, y + dir.dy) === DIRT) {
setPixel(x + dir.dx, y + dir.dy, GRASS);
}
}
// Grass dies if covered (no air above)
if (getPixel(x, y - 1) !== EMPTY && Math.random() < 0.05) {
setPixel(x, y, DIRT);
}
}
function updateWater(x, y) { function updateWater(x, y) {
// Try to move down // Try to move down
if (getPixel(x, y + 1) === EMPTY) { if (getPixel(x, y + 1) === EMPTY) {
@ -472,6 +561,14 @@ function render() {
ctx.fillStyle = WATER_COLOR; ctx.fillStyle = WATER_COLOR;
} else if (type === WALL) { } else if (type === WALL) {
ctx.fillStyle = WALL_COLOR; ctx.fillStyle = WALL_COLOR;
} else if (type === DIRT) {
ctx.fillStyle = DIRT_COLOR;
} else if (type === STONE) {
ctx.fillStyle = STONE_COLOR;
} else if (type === GRASS) {
ctx.fillStyle = GRASS_COLOR;
} else if (type === WOOD) {
ctx.fillStyle = WOOD_COLOR;
} }
// Draw the pixel // Draw the pixel