Files
novafarma/src/systems/StructureSystem.js

393 lines
12 KiB
JavaScript

/**
* 🏛️ 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;
}
}