import VillagePathfinder from './pathfinding.js'; import { VillagerBehaviorTree } from './behavior.js'; import CrowdManager from './crowd.js'; import { DailyRoutineManager, Villager } from './routines.js'; import { EnvironmentInteractionSystem, Resource, Building } from './environment.js'; import { AIEntityManager } from './optimization.js'; /** * Main AI System for Medieval Village Simulator * * This file integrates all AI components into a cohesive system for managing * villager AI in a medieval town simulator. */ class VillageAISystem { constructor(scene) { this.scene = scene; this.pathfinder = new VillagePathfinder(); this.crowdManager = new CrowdManager(); this.routineManager = new DailyRoutineManager(); this.environmentSystem = new EnvironmentInteractionSystem(); this.entityManager = new AIEntityManager(); // Initialize systems this.initSystems(); } /** * Initialize all AI systems */ initSystems() { // Initialize pathfinding with a simple plane for now // In a real implementation, this would use a navigation mesh // Note: We're not using THREE.js directly in the AI system anymore // The nav mesh is just a placeholder object this.pathfinder.initNavMesh({}); // Create some sample resources this.createSampleResources(); // Create some sample buildings this.createSampleBuildings(); } /** * Create sample resources for the village */ createSampleResources() { const woodResource = new Resource('wood1', 'wood', [10, 0, 10], 100); const stoneResource = new Resource('stone1', 'stone', [-10, 0, -10], 100); const foodResource = new Resource('food1', 'food', [0, 0, 15], 100); this.environmentSystem.addResource('wood1', woodResource); this.environmentSystem.addResource('stone1', stoneResource); this.environmentSystem.addResource('food1', foodResource); } /** * Create sample buildings for the village */ createSampleBuildings() { // Create a proper square village layout with buildings around a central square const squareSize = 20; // Size of the central square const buildingSpacing = 8; // Distance between buildings let buildingId = 1; // Define building types and their positions around the square const buildingConfigs = [ { type: 'university', x: -squareSize, z: -squareSize, maxOccupancy: 8, useRadius: 6 }, { type: 'store', x: squareSize, z: -squareSize, maxOccupancy: 4, useRadius: 4 }, { type: 'bank', x: -squareSize, z: squareSize, maxOccupancy: 3, useRadius: 4 }, { type: 'hospital', x: squareSize, z: squareSize, maxOccupancy: 5, useRadius: 5 }, { type: 'market', x: 0, z: -squareSize - buildingSpacing, maxOccupancy: 10, useRadius: 8 }, { type: 'restaurant', x: 0, z: squareSize + buildingSpacing, maxOccupancy: 6, useRadius: 5 }, { type: 'workshop', x: -squareSize - buildingSpacing, z: 0, maxOccupancy: 4, useRadius: 4 }, { type: 'house', x: squareSize + buildingSpacing, z: 0, maxOccupancy: 3, useRadius: 3 } ]; // Add buildings around the square buildingConfigs.forEach(config => { const building = new Building(`building${buildingId}`, config.type, [config.x, 0, config.z]); building.maxOccupancy = config.maxOccupancy; building.useRadius = config.useRadius; this.environmentSystem.addBuilding(`building${buildingId}`, building); buildingId++; }); // Add houses around the perimeter const housePositions = [ [-squareSize, 0, -squareSize + buildingSpacing], [-squareSize + buildingSpacing, 0, -squareSize], [squareSize - buildingSpacing, 0, -squareSize], [squareSize, 0, -squareSize + buildingSpacing], [-squareSize, 0, squareSize - buildingSpacing], [-squareSize + buildingSpacing, 0, squareSize], [squareSize - buildingSpacing, 0, squareSize], [squareSize, 0, squareSize - buildingSpacing] ]; housePositions.forEach((pos, index) => { const house = new Building(`house${index + 1}`, 'house', pos); house.maxOccupancy = 3; house.useRadius = 3; this.environmentSystem.addBuilding(`house${index + 1}`, house); }); // Add some resources scattered around const resources = [ new Resource('wood1', 'wood', [-squareSize - 5, 0, -squareSize - 5], 100), new Resource('wood2', 'wood', [squareSize + 5, 0, squareSize + 5], 100), new Resource('stone1', 'stone', [-squareSize - 5, 0, squareSize + 5], 100), new Resource('stone2', 'stone', [squareSize + 5, 0, -squareSize - 5], 100), new Resource('food1', 'food', [0, 0, -squareSize - 10], 100), new Resource('food2', 'food', [0, 0, squareSize + 10], 100) ]; resources.forEach(resource => { this.environmentSystem.addResource(resource.id, resource); }); } /** * Create a villager and add it to the simulation * @param {string} id - Villager ID * @param {Array} position - Initial position [x, y, z] * @returns {Villager} Created villager */ createVillager(id, position) { // Convert THREE.Vector3 to array if needed let posArray = position; if (position && typeof position.x !== 'undefined') { posArray = [position.x, position.y, position.z]; } const villager = new Villager(id, posArray); // Add to systems this.crowdManager.addVillager(villager); this.entityManager.addEntity(villager); // Create a schedule for the villager const workBuilding = this.environmentSystem.findNearestBuilding(posArray, 'workshop'); const homeBuilding = this.environmentSystem.findNearestBuilding(posArray, 'house'); const marketBuilding = this.environmentSystem.findNearestBuilding(posArray, 'market'); const workLocation = workBuilding ? workBuilding.position : [0, 0, 0]; const homeLocation = homeBuilding ? homeBuilding.position : [0, 0, 0]; const socialLocations = marketBuilding ? [marketBuilding.position] : [[0, 0, 0]]; this.routineManager.createDefaultSchedule(id, workLocation, homeLocation, socialLocations); return villager; } /** * Update the AI system * @param {number} deltaTime - Time since last frame */ update(deltaTime) { // Update routine manager this.routineManager.update(deltaTime); // Update entity manager (includes optimization techniques) this.entityManager.update(deltaTime); // Update crowd manager this.crowdManager.update(deltaTime); // Update each villager for (const villager of this.crowdManager.villagers) { // Update villager with routine manager and pathfinder villager.update(this.routineManager, this.pathfinder, deltaTime); // Handle environmental interactions if (villager.state === 'work' && villager.path.length === 0) { // Try to gather resources const nearestWood = this.environmentSystem.findNearestResource(villager.position, 'wood'); if (nearestWood && this.environmentSystem.calculateDistance(villager.position, nearestWood.position) <= nearestWood.gatherRadius) { this.environmentSystem.gatherResource(villager, nearestWood); } } if (villager.state === 'socialize' && villager.path.length === 0) { // Try to use market const market = this.environmentSystem.findNearestBuilding(villager.position, 'market'); if (market) { this.environmentSystem.useBuilding(villager, market); } } // Move along path if there is one if (villager.path.length > 0) { this.pathfinder.moveAlongPath(villager, villager.path, villager.maxSpeed, deltaTime); } } } } export default VillageAISystem;