File size: 8,506 Bytes
a32dc8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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;