6rz6
Add Medieval Village AI Emulator
a32dc8b
// No direct THREE.js import needed
/**
* Daily Routine System for Villager AI
*
* This implementation manages villager daily routines including work, rest, and social interactions.
* It uses a schedule-based approach with some randomness to create natural-looking behavior.
*/
class DailyRoutineManager {
constructor() {
this.schedules = new Map(); // Map of villager IDs to schedules
this.currentTime = 0; // In-game time in hours (0-24)
}
/**
* Set a schedule for a villager
* @param {string} villagerId - Villager identifier
* @param {Array} schedule - Array of schedule entries
*/
setSchedule(villagerId, schedule) {
this.schedules.set(villagerId, schedule);
}
/**
* Update the routine manager
* @param {number} deltaTime - Time since last update in hours
*/
update(deltaTime) {
this.currentTime += deltaTime;
if (this.currentTime >= 24) {
this.currentTime -= 24; // Wrap around to next day
}
}
/**
* Get current activity for a villager
* @param {string} villagerId - Villager identifier
* @returns {Object} Current activity
*/
getCurrentActivity(villagerId) {
const schedule = this.schedules.get(villagerId);
if (!schedule) return null;
// Find the current activity based on time
for (let i = schedule.length - 1; i >= 0; i--) {
if (this.currentTime >= schedule[i].startTime) {
return schedule[i];
}
}
// If no activity found, return the last one (for times before first activity)
return schedule[schedule.length - 1];
}
/**
* Create a default schedule for a villager
* @param {string} villagerId - Villager identifier
* @param {Object} workLocation - Work location
* @param {Object} homeLocation - Home location
* @param {Array} socialLocations - Social locations
*/
createDefaultSchedule(villagerId, workLocation, homeLocation, socialLocations) {
const schedule = [
{ startTime: 0, activity: 'sleep', location: homeLocation },
{ startTime: 7, activity: 'eat', location: homeLocation },
{ startTime: 8, activity: 'work', location: workLocation },
{ startTime: 12, activity: 'eat', location: workLocation },
{ startTime: 13, activity: 'work', location: workLocation },
{ startTime: 17, activity: 'socialize', location: socialLocations[0] },
{ startTime: 19, activity: 'eat', location: homeLocation },
{ startTime: 20, activity: 'sleep', location: homeLocation }
];
this.setSchedule(villagerId, schedule);
}
}
/**
* Villager class with routine-based behavior
*/
class Villager {
constructor(id, position) {
this.id = id;
this.position = position;
this.velocity = [0, 0, 0]; // Simple array instead of THREE.Vector3
this.maxSpeed = 2;
this.energy = 100;
this.hunger = 0;
this.socialNeed = 0;
this.state = 'idle'; // idle, working, sleeping, eating, socializing
this.path = [];
this.avoidanceRadius = 2;
this.avoidanceWeight = 1.5;
this.steeringWeight = 1.0;
}
/**
* Calculate distance between two points
* @param {Array} pos1 - First position [x, y, z]
* @param {Array} pos2 - Second position [x, y, z]
* @returns {number} Distance between points
*/
calculateDistance(pos1, pos2) {
return Math.sqrt(
Math.pow(pos1[0] - pos2[0], 2) +
Math.pow(pos1[1] - pos2[1], 2) +
Math.pow(pos1[2] - pos2[2], 2)
);
}
/**
* Update villager state
* @param {DailyRoutineManager} routineManager - Routine manager
* @param {VillagePathfinder} pathfinder - Pathfinding system
* @param {number} deltaTime - Time since last update
*/
update(routineManager, pathfinder, deltaTime) {
// Update needs
this.updateNeeds(deltaTime);
// Get current activity
const activity = routineManager.getCurrentActivity(this.id);
// Update state based on activity
if (activity) {
this.state = activity.activity;
// If we have a location for this activity, pathfind to it
if (activity.location && this.calculateDistance(this.position, activity.location) > 1) {
this.path = pathfinder.findPath(this.position, activity.location);
} else {
this.path = [];
}
}
// Perform state-specific actions
switch (this.state) {
case 'sleep':
this.sleep(deltaTime);
break;
case 'work':
this.work(deltaTime);
break;
case 'eat':
this.eat(deltaTime);
break;
case 'socialize':
this.socialize(deltaTime);
break;
}
}
/**
* Update villager needs over time
* @param {number} deltaTime - Time since last update
*/
updateNeeds(deltaTime) {
this.energy = Math.max(0, this.energy - 0.1 * deltaTime);
this.hunger = Math.min(100, this.hunger + 0.05 * deltaTime);
this.socialNeed = Math.min(100, this.socialNeed + 0.03 * deltaTime);
}
/**
* Check if villager is tired
* @returns {boolean} True if tired
*/
isTired() {
return this.energy < 20;
}
/**
* Check if villager should work
* @returns {boolean} True if should work
*/
shouldWork() {
return this.energy > 30 && this.hunger < 80;
}
/**
* Check if villager should socialize
* @returns {boolean} True if should socialize
*/
shouldSocialize() {
return this.energy > 40 && this.socialNeed > 50;
}
/**
* Sleep action
* @param {number} deltaTime - Time since last update
*/
sleep(deltaTime) {
this.energy = Math.min(100, this.energy + 0.5 * deltaTime);
}
/**
* Work action
* @param {number} deltaTime - Time since last update
*/
work(deltaTime) {
this.energy = Math.max(0, this.energy - 0.2 * deltaTime);
}
/**
* Eat action
* @param {number} deltaTime - Time since last update
*/
eat(deltaTime) {
this.hunger = Math.max(0, this.hunger - 0.8 * deltaTime);
}
/**
* Socialize action
* @param {number} deltaTime - Time since last update
*/
socialize(deltaTime) {
this.socialNeed = Math.max(0, this.socialNeed - 0.6 * deltaTime);
}
/**
* Idle action
*/
idle() {
// Do nothing
}
}
export { DailyRoutineManager, Villager };