// Rendering functions function render() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Get visible chunks const visibleChunks = getVisibleChunks(); // Render each visible chunk for (const { chunkX, chunkY, isVisible } of visibleChunks) { // Skip rendering for chunks that are not visible if (!isVisible) continue; const key = getChunkKey(chunkX, chunkY); // Skip rendering if the chunk hasn't changed and isn't marked as dirty if (!dirtyChunks.has(key) && !worldMoved) { continue; } if (!chunks.has(key)) continue; const chunk = chunks.get(key); // Calculate screen position of chunk const screenX = (chunkX * CHUNK_SIZE - worldOffsetX) * PIXEL_SIZE; const screenY = (chunkY * CHUNK_SIZE - worldOffsetY) * PIXEL_SIZE; // Draw chunk border in debug mode if (debugMode) { ctx.strokeStyle = '#ff0000'; ctx.lineWidth = 1; ctx.strokeRect( screenX, screenY, CHUNK_SIZE * PIXEL_SIZE, CHUNK_SIZE * PIXEL_SIZE ); // Draw chunk coordinates ctx.fillStyle = '#ffffff'; ctx.font = '12px Arial'; ctx.fillText(`${chunkX},${chunkY}`, screenX + 5, screenY + 15); } // Render each pixel in the chunk for (let y = 0; y < CHUNK_SIZE; y++) { for (let x = 0; x < CHUNK_SIZE; x++) { const index = y * CHUNK_SIZE + x; const type = chunk[index]; if (type === EMPTY) continue; // Set color based on type if (type === SAND) { ctx.fillStyle = SAND_COLOR; } else if (type === WATER) { // Get water color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = WATER_COLORS[colorIndex]; } else if (type === WALL) { ctx.fillStyle = WALL_COLOR; } else if (type === DIRT) { // Get dirt color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = DIRT_COLORS[colorIndex]; } else if (type === STONE) { // Get stone color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = STONE_COLORS[colorIndex]; } else if (type === GRASS) { // Get grass color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = GRASS_COLORS[colorIndex]; } else if (type === WOOD) { // Get wood color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = WOOD_COLORS[colorIndex]; } else if (type === SEED) { ctx.fillStyle = SEED_COLOR; } else if (type === GRASS_BLADE) { // Use the same color variation as grass const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = GRASS_COLORS[colorIndex]; } else if (type === FLOWER) { // Get flower color from metadata or use a default const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); ctx.fillStyle = metadata && metadata.color ? metadata.color : FLOWER_COLORS[0]; } else if (type === TREE_SEED) { ctx.fillStyle = SEED_COLOR; } else if (type === LEAF) { // Get leaf color from metadata with variation const metadata = getMetadata(chunkX * CHUNK_SIZE + x, chunkY * CHUNK_SIZE + y); const colorIndex = metadata && metadata.colorIndex !== undefined ? metadata.colorIndex : 0; ctx.fillStyle = LEAF_COLORS[colorIndex]; } 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]; } 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 ctx.fillRect( screenX + x * PIXEL_SIZE, screenY + y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE ); } } // Remove this chunk from the dirty list after rendering dirtyChunks.delete(key); } // Reset world moved flag after rendering worldMoved = false; // Draw cursor position and update debug info if (currentMouseX !== undefined && currentMouseY !== undefined) { const worldX = Math.floor(currentMouseX / PIXEL_SIZE) + worldOffsetX; const worldY = Math.floor(currentMouseY / PIXEL_SIZE) + worldOffsetY; // Update coordinates display in debug mode if (debugMode) { document.getElementById('coords').textContent = `Chunk: ${Math.floor(worldOffsetX / CHUNK_SIZE)},${Math.floor(worldOffsetY / CHUNK_SIZE)} | ` + `Mouse: ${worldX},${worldY} | Offset: ${Math.floor(worldOffsetX)},${Math.floor(worldOffsetY)}`; // Draw cursor outline const cursorScreenX = (worldX - worldOffsetX) * PIXEL_SIZE; const cursorScreenY = (worldY - worldOffsetY) * PIXEL_SIZE; ctx.strokeStyle = '#00ff00'; ctx.lineWidth = 2; ctx.strokeRect( cursorScreenX - PIXEL_SIZE, cursorScreenY - PIXEL_SIZE, PIXEL_SIZE * 3, PIXEL_SIZE * 3 ); // Draw a dot at the exact mouse position ctx.fillStyle = '#ff0000'; ctx.beginPath(); ctx.arc(currentMouseX, currentMouseY, 3, 0, Math.PI * 2); ctx.fill(); } } }