6rz6
Add Medieval Village AI Emulator
a32dc8b
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;