/** * 🏛️ STRUCTURE SYSTEM * Generates and manages structures across the 500x500 world * - Creates buildings, ruins, landmarks across biomes * - Handles roads and paths between biomes * - Biome-aware structure placement */ class StructureSystem { constructor(worldWidth, worldHeight, biomeSystem, riverSystem, lakeSystem) { this.worldWidth = worldWidth; this.worldHeight = worldHeight; this.biomeSystem = biomeSystem; this.riverSystem = riverSystem; this.lakeSystem = lakeSystem; // Structure map (marks where structures are) this.structureMap = Array(worldHeight).fill(null).map(() => Array(worldWidth).fill(null)); // Road map (marks where roads are) this.roadMap = Array(worldHeight).fill(null).map(() => Array(worldWidth).fill(false)); // List of all structures this.structures = []; // List of all landmarks this.landmarks = []; // Road paths this.roads = []; // Structure types by biome this.structureTypes = { 'Grassland': ['farm', 'house', 'barn', 'windmill', 'well'], 'Forest': ['cabin', 'ruins', 'treehouse', 'camp', 'shrine'], 'Desert': ['pyramid', 'ruins', 'oasis_camp', 'tomb', 'pillar'], 'Mountain': ['mine', 'cave', 'tower', 'ruins', 'altar'], 'Swamp': ['hut', 'ruins', 'totem', 'bog_shrine', 'abandoned_dock'] }; // Landmark types (unique, 1-3 per world) this.landmarkTypes = [ { type: 'ancient_temple', biome: 'Forest', count: 1 }, { type: 'great_pyramid', biome: 'Desert', count: 1 }, { type: 'mountain_peak', biome: 'Mountain', count: 1 }, { type: 'abandoned_city', biome: 'Grassland', count: 1 }, { type: 'dragon_skeleton', biome: 'Swamp', count: 1 } ]; } // Generate all structures and roads generateAll() { console.log('🏛️ StructureSystem: Starting structure generation...'); // 1. Generate roads between biome centers this.generateRoads(); // 2. Generate landmarks (major points of interest) this.generateLandmarks(); // 3. Generate regular structures this.generateStructures(); console.log(`✅ Generated ${this.structures.length} structures, ${this.landmarks.length} landmarks, ${this.roads.length} roads`); } // Generate roads connecting biome centers generateRoads() { console.log('🛤️ Generating roads...'); // Find biome centers const biomeLocations = { 'Grassland': [], 'Forest': [], 'Desert': [], 'Mountain': [], 'Swamp': [] }; // Sample the world to find biome centers const sampleRate = 50; for (let x = 0; x < this.worldWidth; x += sampleRate) { for (let y = 0; y < this.worldHeight; y += sampleRate) { const biome = this.biomeSystem.getBiome(x, y); if (!biomeLocations[biome]) biomeLocations[biome] = []; biomeLocations[biome].push({ x, y }); } } // Create main roads connecting different biomes const roadPoints = []; // Central hub (spawn point) roadPoints.push({ x: 250, y: 250, name: 'Center' }); // Add one major location per biome for (const [biomeName, locations] of Object.entries(biomeLocations)) { if (locations.length > 0) { // Pick central location const centerIdx = Math.floor(locations.length / 2); roadPoints.push({ x: locations[centerIdx].x, y: locations[centerIdx].y, name: `${biomeName} Hub` }); } } // Connect road points for (let i = 0; i < roadPoints.length; i++) { const start = roadPoints[i]; // Connect to nearest 2-3 other points const distances = roadPoints .map((point, idx) => ({ idx, dist: Math.sqrt((point.x - start.x) ** 2 + (point.y - start.y) ** 2) })) .filter(d => d.idx !== i) .sort((a, b) => a.dist - b.dist); // Connect to 1-2 nearest points const connectCount = Math.min(2, distances.length); for (let j = 0; j < connectCount; j++) { const end = roadPoints[distances[j].idx]; this.createRoad(start, end); } } } // Create a road between two points createRoad(start, end) { const path = []; const dx = end.x - start.x; const dy = end.y - start.y; const steps = Math.max(Math.abs(dx), Math.abs(dy)); // Create path with some randomness (looks more natural) for (let i = 0; i <= steps; i++) { const t = i / steps; let x = Math.round(start.x + dx * t); let y = Math.round(start.y + dy * t); // Add slight curve (Perlin-like noise) const noise = Math.sin(t * Math.PI * 3) * 10; x += Math.round(noise); path.push({ x, y }); } // Mark road tiles (3 tiles wide) for (const point of path) { for (let offsetX = -1; offsetX <= 1; offsetX++) { for (let offsetY = -1; offsetY <= 1; offsetY++) { const x = point.x + offsetX; const y = point.y + offsetY; if (x >= 0 && x < this.worldWidth && y >= 0 && y < this.worldHeight) { // Don't place roads over water if (!this.riverSystem.isRiver(x, y) && !this.lakeSystem.isLake(x, y)) { this.roadMap[y][x] = true; } } } } } this.roads.push({ start: start.name || 'unknown', end: end.name || 'unknown', path }); } // Generate landmarks (unique structures) generateLandmarks() { console.log('🗿 Generating landmarks...'); for (const landmarkDef of this.landmarkTypes) { for (let i = 0; i < landmarkDef.count; i++) { const location = this.findBiomeLocation(landmarkDef.biome, 100, 100); if (location) { this.createLandmark(landmarkDef.type, location.x, location.y); } } } } // Create a single landmark createLandmark(type, x, y) { const size = 15; // Landmarks are large (15x15) // Mark area as occupied for (let dx = -size; dx <= size; dx++) { for (let dy = -size; dy <= size; dy++) { const tx = x + dx; const ty = y + dy; if (tx >= 0 && tx < this.worldWidth && ty >= 0 && ty < this.worldHeight) { this.structureMap[ty][tx] = { type: 'landmark', landmarkType: type }; } } } this.landmarks.push({ type, x, y, size, discovered: false }); } // Generate regular structures generateStructures() { console.log('🏠 Generating structures...'); const structureCount = 80; // 80 structures across the world let placed = 0; let attempts = 0; const maxAttempts = structureCount * 10; while (placed < structureCount && attempts < maxAttempts) { attempts++; // Random location const x = Math.floor(Math.random() * this.worldWidth); const y = Math.floor(Math.random() * this.worldHeight); // Check if location is valid if (this.canPlaceStructure(x, y, 20)) { const biome = this.biomeSystem.getBiome(x, y); const types = this.structureTypes[biome]; if (types && types.length > 0) { const type = types[Math.floor(Math.random() * types.length)]; this.createStructure(type, x, y, biome); placed++; } } } console.log(`✅ Placed ${placed} structures (${attempts} attempts)`); } // Check if structure can be placed at location canPlaceStructure(x, y, minDistance) { // Check if on water if (this.riverSystem.isRiver(x, y) || this.lakeSystem.isLake(x, y)) { return false; } // Check if too close to other structures for (const structure of this.structures) { const dist = Math.sqrt((structure.x - x) ** 2 + (structure.y - y) ** 2); if (dist < minDistance) { return false; } } // Check if too close to landmarks for (const landmark of this.landmarks) { const dist = Math.sqrt((landmark.x - x) ** 2 + (landmark.y - y) ** 2); if (dist < 50) { return false; } } return true; } // Create a single structure createStructure(type, x, y, biome) { const size = this.getStructureSize(type); // Mark area as occupied for (let dx = -size; dx <= size; dx++) { for (let dy = -size; dy <= size; dy++) { const tx = x + dx; const ty = y + dy; if (tx >= 0 && tx < this.worldWidth && ty >= 0 && ty < this.worldHeight) { this.structureMap[ty][tx] = { type: 'structure', structureType: type }; } } } this.structures.push({ type, x, y, size, biome, explored: false }); } // Get structure size getStructureSize(type) { const sizes = { // Small structures 'well': 2, 'camp': 3, 'totem': 2, 'pillar': 2, // Medium structures 'house': 4, 'cabin': 4, 'hut': 3, 'shrine': 4, 'altar': 4, 'tomb': 5, // Large structures 'farm': 7, 'barn': 6, 'windmill': 5, 'ruins': 6, 'mine': 5, 'tower': 4, 'pyramid': 8, 'cave': 5, 'treehouse': 4, 'oasis_camp': 5, 'bog_shrine': 4, 'abandoned_dock': 5 }; return sizes[type] || 4; } // Find a location in specific biome findBiomeLocation(biomeName, minDistance, maxAttempts) { for (let i = 0; i < maxAttempts; i++) { const x = Math.floor(Math.random() * this.worldWidth); const y = Math.floor(Math.random() * this.worldHeight); const biome = this.biomeSystem.getBiome(x, y); if (biome === biomeName && this.canPlaceStructure(x, y, minDistance)) { return { x, y }; } } return null; } // Check if tile is a road isRoad(x, y) { if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) { return false; } return this.roadMap[y][x]; } // Get structure at tile getStructure(x, y) { if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) { return null; } return this.structureMap[y][x]; } // Get statistics getStats() { return { structures: this.structures.length, landmarks: this.landmarks.length, roads: this.roads.length, roadTiles: this.roadMap.flat().filter(r => r).length }; } // Export data for saving exportData() { return { structures: this.structures, landmarks: this.landmarks, roads: this.roads, structureMap: this.structureMap, roadMap: this.roadMap }; } // Import data from save importData(data) { this.structures = data.structures || []; this.landmarks = data.landmarks || []; this.roads = data.roads || []; this.structureMap = data.structureMap || this.structureMap; this.roadMap = data.roadMap || this.roadMap; } }