206 lines
7.1 KiB
JavaScript
206 lines
7.1 KiB
JavaScript
// World management functions
|
|
let worldOffsetX = 0;
|
|
let worldOffsetY = 0;
|
|
let worldOffsetXBeforeDrag = 0;
|
|
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
|
|
|
|
function moveWorld(dx, dy) {
|
|
worldOffsetX += dx;
|
|
worldOffsetY += dy;
|
|
updateCoordinatesDisplay();
|
|
|
|
// Generate terrain for chunks around the current view
|
|
generateChunksAroundPlayer();
|
|
}
|
|
|
|
function updateCoordinatesDisplay() {
|
|
const chunkX = Math.floor(worldOffsetX / CHUNK_SIZE);
|
|
const chunkY = Math.floor(worldOffsetY / CHUNK_SIZE);
|
|
document.getElementById('coords').textContent = `Chunk: ${chunkX},${chunkY} | Offset: ${Math.floor(worldOffsetX)},${Math.floor(worldOffsetY)}`;
|
|
}
|
|
|
|
function getChunkKey(chunkX, chunkY) {
|
|
return `${chunkX},${chunkY}`;
|
|
}
|
|
|
|
function getOrCreateChunk(chunkX, chunkY) {
|
|
const key = getChunkKey(chunkX, chunkY);
|
|
|
|
if (!chunks.has(key)) {
|
|
// Create a new chunk with empty pixels
|
|
const chunkData = new Array(CHUNK_SIZE * CHUNK_SIZE).fill(EMPTY);
|
|
|
|
// Add floor at the bottom of the world (y = 0 and y = 1)
|
|
if (chunkY === 0 || chunkY === 1) {
|
|
// Fill the bottom row with walls
|
|
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
chunkData[(CHUNK_SIZE - 1) * CHUNK_SIZE + x] = WALL;
|
|
}
|
|
}
|
|
|
|
// Special generation for the first chunk (0,0)
|
|
if (chunkX === 0 && chunkY === 0) {
|
|
generateFirstChunk(chunkData);
|
|
}
|
|
|
|
chunks.set(key, chunkData);
|
|
}
|
|
|
|
return chunks.get(key);
|
|
}
|
|
|
|
// Generate special terrain for the first chunk
|
|
function generateFirstChunk(chunkData) {
|
|
// 1. Create a base layer of sand above the floor
|
|
const floorY = CHUNK_SIZE - 1;
|
|
const baseHeight = 10; // Base height of sand
|
|
|
|
// Create two random hill points
|
|
const hill1X = Math.floor(CHUNK_SIZE * 0.3);
|
|
const hill2X = Math.floor(CHUNK_SIZE * 0.7);
|
|
const hill1Height = baseHeight + Math.floor(Math.random() * 10) + 5; // 5-15 blocks higher
|
|
const hill2Height = baseHeight + Math.floor(Math.random() * 10) + 5;
|
|
|
|
// Generate height map for sand
|
|
const heightMap = new Array(CHUNK_SIZE).fill(0);
|
|
|
|
// Calculate heights based on distance from the two hills
|
|
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
// Distance from each hill (using a simple distance function)
|
|
const dist1 = Math.abs(x - hill1X);
|
|
const dist2 = Math.abs(x - hill2X);
|
|
|
|
// Height contribution from each hill (inverse to distance)
|
|
const h1 = hill1Height * Math.max(0, 1 - dist1 / (CHUNK_SIZE * 0.3));
|
|
const h2 = hill2Height * Math.max(0, 1 - dist2 / (CHUNK_SIZE * 0.3));
|
|
|
|
// Take the maximum height contribution
|
|
heightMap[x] = Math.floor(baseHeight + Math.max(h1, h2));
|
|
}
|
|
|
|
// Find the lowest points for water
|
|
let minHeight = Math.min(...heightMap);
|
|
|
|
// Place sand according to the height map
|
|
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
const height = heightMap[x];
|
|
for (let y = floorY - height; y < floorY; y++) {
|
|
chunkData[y * CHUNK_SIZE + x] = SAND;
|
|
}
|
|
|
|
// 3. Add grass on top of the hills
|
|
if (height > baseHeight + 3) { // Only on higher parts
|
|
chunkData[(floorY - height) * CHUNK_SIZE + x] = GRASS;
|
|
}
|
|
}
|
|
|
|
// 2. Add water in the lowest areas
|
|
for (let x = 0; x < CHUNK_SIZE; x++) {
|
|
const height = heightMap[x];
|
|
// Add water where the height is close to the minimum
|
|
if (height <= minHeight + 2) {
|
|
// Add a few layers of water
|
|
const waterDepth = 3;
|
|
for (let d = 0; d < waterDepth; d++) {
|
|
const y = floorY - height - d - 1;
|
|
if (y >= 0) {
|
|
chunkData[y * CHUNK_SIZE + x] = WATER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function getChunkCoordinates(worldX, worldY) {
|
|
const chunkX = Math.floor(worldX / CHUNK_SIZE);
|
|
const chunkY = Math.floor(worldY / CHUNK_SIZE);
|
|
const localX = ((worldX % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE;
|
|
const localY = ((worldY % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE;
|
|
|
|
return { chunkX, chunkY, localX, localY };
|
|
}
|
|
|
|
function setPixel(worldX, worldY, type) {
|
|
const { chunkX, chunkY, localX, localY } = getChunkCoordinates(worldX, worldY);
|
|
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 });
|
|
}
|
|
}
|
|
|
|
function getPixel(worldX, worldY) {
|
|
// Special case: floor at the bottom of the world (first two chunks)
|
|
const floorChunkY = Math.floor(worldY / CHUNK_SIZE);
|
|
if (worldY % CHUNK_SIZE === CHUNK_SIZE - 1 && (floorChunkY === 0 || floorChunkY === 1)) {
|
|
return WALL;
|
|
}
|
|
|
|
const { chunkX, chunkY, localX, localY } = getChunkCoordinates(worldX, worldY);
|
|
const key = getChunkKey(chunkX, chunkY);
|
|
|
|
if (!chunks.has(key)) {
|
|
return EMPTY;
|
|
}
|
|
|
|
const chunk = chunks.get(key);
|
|
const index = localY * CHUNK_SIZE + localX;
|
|
|
|
return chunk[index];
|
|
}
|
|
|
|
// Metadata functions to store additional information about pixels
|
|
function setMetadata(worldX, worldY, data) {
|
|
const key = `${worldX},${worldY}`;
|
|
metadata.set(key, data);
|
|
}
|
|
|
|
function getMetadata(worldX, worldY) {
|
|
const key = `${worldX},${worldY}`;
|
|
return metadata.get(key);
|
|
}
|
|
|
|
function removeMetadata(worldX, worldY) {
|
|
const key = `${worldX},${worldY}`;
|
|
metadata.delete(key);
|
|
}
|
|
|
|
// Move metadata when a pixel moves
|
|
function moveMetadata(fromX, fromY, toX, toY) {
|
|
const data = getMetadata(fromX, fromY);
|
|
if (data) {
|
|
setMetadata(toX, toY, data);
|
|
removeMetadata(fromX, fromY);
|
|
}
|
|
}
|
|
|
|
function getVisibleChunks() {
|
|
const visibleChunks = [];
|
|
|
|
// Calculate visible chunk range
|
|
const startChunkX = Math.floor(worldOffsetX / CHUNK_SIZE) - 1;
|
|
const endChunkX = Math.ceil((worldOffsetX + canvas.width / PIXEL_SIZE) / CHUNK_SIZE) + 1;
|
|
const startChunkY = Math.floor(worldOffsetY / CHUNK_SIZE) - 1;
|
|
const endChunkY = Math.ceil((worldOffsetY + canvas.height / PIXEL_SIZE) / CHUNK_SIZE) + 1;
|
|
|
|
for (let chunkY = startChunkY; chunkY < endChunkY; chunkY++) {
|
|
for (let chunkX = startChunkX; chunkX < endChunkX; chunkX++) {
|
|
visibleChunks.push({ chunkX, chunkY });
|
|
}
|
|
}
|
|
|
|
return visibleChunks;
|
|
}
|