diff --git a/script.js b/script.js index adf73cd..43802ee 100644 --- a/script.js +++ b/script.js @@ -53,6 +53,7 @@ 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; +let generatedChunks = new Set(); // Set to track which chunks have been generated // Initialize the simulation window.onload = function() { @@ -94,8 +95,9 @@ window.onload = function() { canvas.addEventListener('touchmove', handleTouchMove); canvas.addEventListener('touchend', handleMouseUp); - // Initialize the first chunk + // Initialize the first chunk and generate terrain around it getOrCreateChunk(0, 0); + generateChunksAroundPlayer(); // Start the simulation loop requestAnimationFrame(simulationLoop); @@ -288,6 +290,9 @@ function moveWorld(dx, dy) { worldOffsetX += dx; worldOffsetY += dy; updateCoordinatesDisplay(); + + // Generate terrain for chunks around the current view + generateChunksAroundPlayer(); } function updateCoordinatesDisplay() { @@ -1040,6 +1045,183 @@ function toggleDebug() { document.getElementById('debug-btn').classList.toggle('active'); } +// Terrain generation functions +function generateChunksAroundPlayer() { + const centerChunkX = Math.floor(worldOffsetX / CHUNK_SIZE); + const centerChunkY = Math.floor(worldOffsetY / CHUNK_SIZE); + const radius = 3; // Generate chunks within 3 chunks of the player + + // Generate chunks in a square around the player + for (let dy = -radius; dy <= radius; dy++) { + for (let dx = -radius; dx <= radius; dx++) { + const chunkX = centerChunkX + dx; + const chunkY = centerChunkY + dy; + + // Only generate terrain for chunks at or above y=0 + if (chunkY >= 0) { + getOrCreateChunk(chunkX, chunkY); + } + } + } +} + +function generateTerrain(chunkX, chunkY, chunkData) { + // Use a seeded random number generator based on chunk coordinates + const seed = chunkX * 10000 + chunkY; + const random = createSeededRandom(seed); + + // Generate base terrain (hills) + generateHills(chunkX, chunkY, chunkData, random); + + // Add lakes + if (random() < 0.3) { // 30% chance for a lake in a chunk + generateLake(chunkX, chunkY, chunkData, random); + } + + // Add stone formations + if (random() < 0.4) { // 40% chance for stone formations + generateStoneFormation(chunkX, chunkY, chunkData, random); + } + + // Add vegetation (seeds and trees) + addVegetation(chunkX, chunkY, chunkData, random); +} + +function createSeededRandom(seed) { + // Simple seeded random function + return function() { + seed = (seed * 9301 + 49297) % 233280; + return seed / 233280; + }; +} + +function generateHills(chunkX, chunkY, chunkData, random) { + // Generate a height map for this chunk using simplex-like noise + const heightMap = new Array(CHUNK_SIZE).fill(0); + + // Base height (higher for chunks further from origin) + const baseHeight = Math.max(0, 50 - Math.sqrt(chunkX*chunkX + chunkY*chunkY) * 5); + + // Generate a smooth height map + for (let x = 0; x < CHUNK_SIZE; x++) { + // Use multiple frequencies for more natural looking terrain + const noise1 = Math.sin(x * 0.02 + chunkX * CHUNK_SIZE * 0.02 + random() * 10) * 15; + const noise2 = Math.sin(x * 0.05 + chunkX * CHUNK_SIZE * 0.05 + random() * 10) * 7; + const noise3 = Math.sin(x * 0.1 + chunkX * CHUNK_SIZE * 0.1 + random() * 10) * 3; + + // Combine noise at different frequencies + heightMap[x] = Math.floor(baseHeight + noise1 + noise2 + noise3); + + // Ensure height is positive + heightMap[x] = Math.max(0, heightMap[x]); + } + + // Fill the terrain based on the height map + for (let x = 0; x < CHUNK_SIZE; x++) { + const height = heightMap[x]; + + for (let y = CHUNK_SIZE - 1; y >= 0; y--) { + const worldY = chunkY * CHUNK_SIZE + y; + const depth = CHUNK_SIZE - 1 - y; + + if (depth < height) { + const index = y * CHUNK_SIZE + x; + + // Top layer is grass + if (depth === 0) { + chunkData[index] = GRASS; + } + // Next few layers are dirt + else if (depth < 5) { + chunkData[index] = DIRT; + } + // Deeper layers are stone + else { + chunkData[index] = STONE; + } + } + } + } +} + +function generateLake(chunkX, chunkY, chunkData, random) { + // Lake parameters + const lakeX = Math.floor(random() * (CHUNK_SIZE - 60)) + 30; + const lakeY = Math.floor(random() * (CHUNK_SIZE - 60)) + 30; + const lakeWidth = Math.floor(random() * 40) + 20; + const lakeHeight = Math.floor(random() * 20) + 10; + + // Create an elliptical lake + for (let y = 0; y < CHUNK_SIZE; y++) { + for (let x = 0; x < CHUNK_SIZE; x++) { + // Calculate distance from lake center (normalized to create an ellipse) + const dx = (x - lakeX) / lakeWidth; + const dy = (y - lakeY) / lakeHeight; + const distance = Math.sqrt(dx*dx + dy*dy); + + if (distance < 1) { + const index = y * CHUNK_SIZE + x; + + // Water in the center + if (distance < 0.8) { + chunkData[index] = WATER; + } + // Sand around the edges + else { + chunkData[index] = SAND; + } + } + } + } +} + +function generateStoneFormation(chunkX, chunkY, chunkData, random) { + // Stone formation parameters + const formationX = Math.floor(random() * (CHUNK_SIZE - 40)) + 20; + const formationWidth = Math.floor(random() * 30) + 10; + const formationHeight = Math.floor(random() * 40) + 20; + + // Create a stone hill/mountain + for (let x = formationX - formationWidth; x < formationX + formationWidth; x++) { + if (x < 0 || x >= CHUNK_SIZE) continue; + + // Calculate height at this x position (higher in the middle) + const dx = (x - formationX) / formationWidth; + const height = Math.floor(formationHeight * (1 - dx*dx)); + + for (let y = CHUNK_SIZE - 1; y >= CHUNK_SIZE - height; y--) { + if (y < 0 || y >= CHUNK_SIZE) continue; + + const index = y * CHUNK_SIZE + x; + chunkData[index] = STONE; + } + } +} + +function addVegetation(chunkX, chunkY, chunkData, random) { + // Add vegetation on grass + for (let y = 0; y < CHUNK_SIZE; y++) { + for (let x = 0; x < CHUNK_SIZE; x++) { + const index = y * CHUNK_SIZE + x; + + // Only add vegetation on grass + if (chunkData[index] === GRASS) { + // Check if there's empty space above + if (y > 0 && chunkData[(y-1) * CHUNK_SIZE + x] === EMPTY) { + // Random chance to add different types of vegetation + const roll = random(); + + if (roll < 0.01) { // 1% chance for a tree seed + chunkData[(y-1) * CHUNK_SIZE + x] = TREE_SEED; + } else if (roll < 0.05) { // 4% chance for a regular seed + chunkData[(y-1) * CHUNK_SIZE + x] = SEED; + } + } + } + } + } +} + function render() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height);