Spaces:
Running
Running
| 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; |