From 15fb106246c889082c8973f66d80e0ecb0126576 Mon Sep 17 00:00:00 2001 From: "Kacper Kostka (aider)" Date: Sat, 5 Apr 2025 15:37:55 +0200 Subject: [PATCH] fix: Add missing `dirtyChunks` variable and update rendering logic --- js/elements/basic.js | 39 +++++++++++++++++++++--- js/main.js | 4 +-- js/physics.js | 64 ++++++++++++++++++++++++++------------- js/world.js | 72 ++++++++++++++++++++++++++++---------------- 4 files changed, 125 insertions(+), 54 deletions(-) diff --git a/js/elements/basic.js b/js/elements/basic.js index 7b0613e..e9b531e 100644 --- a/js/elements/basic.js +++ b/js/elements/basic.js @@ -4,29 +4,37 @@ function updateSand(x, y) { if (getPixel(x, y + 1) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x, y + 1, SAND); + return true; } // 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, SAND); + return true; } else if (getPixel(x + 1, y + 1) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x + 1, y + 1, SAND); + return true; } // Sand can displace water else if (getPixel(x, y + 1) === WATER) { setPixel(x, y, WATER); setPixel(x, y + 1, SAND); + return true; } + return false; } function updateWater(x, y) { + let modified = false; + // Update water color dynamically const metadata = getMetadata(x, y); if (metadata) { if (metadata.waterColorTimer === undefined) { metadata.waterColorTimer = 0; + modified = true; } metadata.waterColorTimer++; @@ -35,6 +43,7 @@ function updateWater(x, y) { if (metadata.waterColorTimer > 20 && Math.random() < 0.1) { metadata.colorIndex = Math.floor(Math.random() * 10); metadata.waterColorTimer = 0; + modified = true; } setMetadata(x, y, metadata); @@ -45,17 +54,20 @@ function updateWater(x, y) { setPixel(x, y, EMPTY); setPixel(x, y + 1, WATER); moveMetadata(x, y, x, y + 1); + return true; } // 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, WATER); moveMetadata(x, y, x - 1, y + 1); + return true; } else if (getPixel(x + 1, y + 1) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x + 1, y + 1, WATER); moveMetadata(x, y, x + 1, y + 1); + return true; } // Try to spread horizontally else { @@ -67,21 +79,25 @@ function updateWater(x, y) { if (goLeft && getPixel(x - 1, y) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x - 1, y, WATER); - moved = true; + moveMetadata(x, y, x - 1, y); + return true; } else if (!goLeft && getPixel(x + 1, y) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x + 1, y, WATER); - moved = true; + moveMetadata(x, y, x + 1, y); + return true; } // Try the other direction if first failed else if (!goLeft && getPixel(x - 1, y) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x - 1, y, WATER); - moved = true; + moveMetadata(x, y, x - 1, y); + return true; } else if (goLeft && getPixel(x + 1, y) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x + 1, y, WATER); - moved = true; + moveMetadata(x, y, x + 1, y); + return true; } } @@ -97,15 +113,18 @@ 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); + return true; } 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; + return true; } } + + return modified; } function updateDirt(x, y) { @@ -113,25 +132,30 @@ function updateDirt(x, y) { if (getPixel(x, y + 1) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x, y + 1, DIRT); + return true; } // 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); + return true; } else if (getPixel(x + 1, y + 1) === EMPTY) { setPixel(x, y, EMPTY); setPixel(x + 1, y + 1, DIRT); + return true; } // Dirt can displace water else if (getPixel(x, y + 1) === WATER) { setPixel(x, y, WATER); setPixel(x, y + 1, DIRT); + return true; } // Dirt can turn into grass if exposed to air above if (getPixel(x, y - 1) === EMPTY && Math.random() < 0.001) { setPixel(x, y, GRASS); + return true; } // Dirt can randomly spawn seeds if exposed to air above @@ -140,12 +164,17 @@ function updateDirt(x, y) { const seedRoll = Math.random(); if (seedRoll < 0.0002) { // Grass blade seed (most common) setPixel(x, y - 1, SEED); + return true; } else if (seedRoll < 0.00025) { // Flower seed (less common) setPixel(x, y - 1, SEED); // Mark this seed as a flower seed (will be handled in updateSeed) setMetadata(x, y - 1, { type: 'flower' }); + return true; } else if (seedRoll < 0.00026) { // Tree seed (rare) setPixel(x, y - 1, TREE_SEED); + return true; } } + + return false; } diff --git a/js/main.js b/js/main.js index 9244e12..4addd91 100644 --- a/js/main.js +++ b/js/main.js @@ -65,8 +65,8 @@ function simulationLoop(timestamp) { fps = Math.round(1000 / deltaTime); document.getElementById('fps').textContent = `FPS: ${fps}`; - // Update physics - updatePhysics(); + // Update physics with timestamp for rate limiting + updatePhysics(timestamp); // Render render(); diff --git a/js/physics.js b/js/physics.js index 85765b0..a9fffd8 100644 --- a/js/physics.js +++ b/js/physics.js @@ -1,5 +1,12 @@ // Physics simulation functions -function updatePhysics() { +function updatePhysics(timestamp) { + // Check if we should update physics based on the update rate + if (timestamp && lastPhysicsTime && timestamp - lastPhysicsTime < physicsUpdateRate) { + return; + } + + lastPhysicsTime = timestamp || 0; + // Get visible chunks const visibleChunks = getVisibleChunks(); @@ -12,6 +19,7 @@ function updatePhysics() { if (!isVisible) continue; const chunk = getOrCreateChunk(chunkX, chunkY); + let chunkModified = false; // Process from bottom to top, right to left for correct gravity simulation for (let y = CHUNK_SIZE - 1; y >= 0; y--) { @@ -29,28 +37,42 @@ function updatePhysics() { const worldX = chunkX * CHUNK_SIZE + x; const worldY = chunkY * CHUNK_SIZE + y; - if (type === SAND) { - updateSand(worldX, worldY); - } else if (type === WATER) { - updateWater(worldX, worldY); - } else if (type === DIRT) { - updateDirt(worldX, worldY); - } else if (type === GRASS) { - updateGrass(worldX, worldY); - } else if (type === SEED) { - updateSeed(worldX, worldY); - } else if (type === GRASS_BLADE) { - updateGrassBlade(worldX, worldY); - } else if (type === FLOWER) { - updateFlower(worldX, worldY); - } else if (type === TREE_SEED) { - updateTreeSeed(worldX, worldY); - } else if (type === FIRE) { - updateFire(worldX, worldY); - } else if (type === LAVA) { - updateLava(worldX, worldY); + // Use a lookup table for faster element updates + const updateFunctions = { + [SAND]: updateSand, + [WATER]: updateWater, + [DIRT]: updateDirt, + [GRASS]: updateGrass, + [SEED]: updateSeed, + [GRASS_BLADE]: updateGrassBlade, + [FLOWER]: updateFlower, + [TREE_SEED]: updateTreeSeed, + [FIRE]: updateFire, + [LAVA]: updateLava + }; + + const updateFunction = updateFunctions[type]; + if (updateFunction) { + const wasModified = updateFunction(worldX, worldY); + if (wasModified) { + chunkModified = true; + } } } } + + // Mark chunk as dirty if it was modified + if (chunkModified) { + dirtyChunks.add(getChunkKey(chunkX, chunkY)); + } + } + + // Adaptive physics rate based on FPS + if (fps < 30 && physicsUpdateRate < 32) { + // If FPS is low, update physics less frequently + physicsUpdateRate = Math.min(32, physicsUpdateRate + 2); + } else if (fps > 50 && physicsUpdateRate > 16) { + // If FPS is high, update physics more frequently + physicsUpdateRate = Math.max(16, physicsUpdateRate - 2); } } diff --git a/js/world.js b/js/world.js index 0a7cb0c..eb896a7 100644 --- a/js/world.js +++ b/js/world.js @@ -6,12 +6,19 @@ 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 generatedChunks = new Set(); // Set to track which chunks have been generated +let dirtyChunks = new Set(); // Set to track which chunks need rendering +let lastPhysicsTime = 0; // Last time physics was updated +let physicsUpdateRate = 16; // Update physics every 16ms (approx 60fps) +let worldMoved = false; // Track if the world has moved for rendering function moveWorld(dx, dy) { worldOffsetX += dx; worldOffsetY += dy; updateCoordinatesDisplay(); + // Mark that the world has moved for rendering + worldMoved = true; + // Generate terrain for chunks around the current view generateChunksAroundPlayer(); } @@ -383,32 +390,38 @@ function setPixel(worldX, worldY, type) { const chunk = getOrCreateChunk(chunkX, chunkY); const index = localY * CHUNK_SIZE + localX; - chunk[index] = type; - - // Assign random color index for natural elements - if (type === DIRT || type === GRASS || type === STONE || type === WOOD || type === LEAF) { - const colorIndex = Math.floor(Math.random() * 10); - setMetadata(worldX, worldY, { ...getMetadata(worldX, worldY) || {}, colorIndex }); - } - else if (type === WATER) { - const colorIndex = Math.floor(Math.random() * 10); - setMetadata(worldX, worldY, { ...getMetadata(worldX, worldY) || {}, colorIndex, waterColorTimer: 0 }); - } - else if (type === TREE_SEED) { - // Initialize tree seed metadata - setMetadata(worldX, worldY, { - age: Math.floor(Math.random() * 50), // Random initial age - growthStage: 0, - type: Math.random() < 0.8 ? 'oak' : 'pine' // 80% oak, 20% pine - }); - } - else if (type === SEED) { - // Initialize flower seed metadata - setMetadata(worldX, worldY, { - age: Math.floor(Math.random() * 30), - growthStage: 0, - flowerType: Math.floor(Math.random() * 5) // Different flower types - }); + // Only update if the pixel type is changing + if (chunk[index] !== type) { + chunk[index] = type; + + // Mark chunk as dirty for rendering + dirtyChunks.add(getChunkKey(chunkX, chunkY)); + + // Assign random color index for natural elements + if (type === DIRT || type === GRASS || type === STONE || type === WOOD || type === LEAF) { + const colorIndex = Math.floor(Math.random() * 10); + setMetadata(worldX, worldY, { ...getMetadata(worldX, worldY) || {}, colorIndex }); + } + else if (type === WATER) { + const colorIndex = Math.floor(Math.random() * 10); + setMetadata(worldX, worldY, { ...getMetadata(worldX, worldY) || {}, colorIndex, waterColorTimer: 0 }); + } + else if (type === TREE_SEED) { + // Initialize tree seed metadata + setMetadata(worldX, worldY, { + age: Math.floor(Math.random() * 50), // Random initial age + growthStage: 0, + type: Math.random() < 0.8 ? 'oak' : 'pine' // 80% oak, 20% pine + }); + } + else if (type === SEED) { + // Initialize flower seed metadata + setMetadata(worldX, worldY, { + age: Math.floor(Math.random() * 30), + growthStage: 0, + flowerType: Math.floor(Math.random() * 5) // Different flower types + }); + } } } @@ -448,6 +461,13 @@ function moveMetadata(fromX, fromY, toX, toY) { if (data) { setMetadata(toX, toY, data); removeMetadata(fromX, fromY); + + // Mark chunks as dirty for rendering + const { chunkX: fromChunkX, chunkY: fromChunkY } = getChunkCoordinates(fromX, fromY); + const { chunkX: toChunkX, chunkY: toChunkY } = getChunkCoordinates(toX, toY); + + dirtyChunks.add(getChunkKey(fromChunkX, fromChunkY)); + dirtyChunks.add(getChunkKey(toChunkX, toChunkY)); } }