/** * Behavior Tree Implementation for Villager AI * * This implementation uses a behavior tree approach to manage complex villager behaviors * including decision-making for daily routines, work, rest, and social interactions. */ // Base Node class for behavior tree class Node { constructor() { this.status = 'INVALID'; // INVALID, SUCCESS, FAILURE, RUNNING } execute(agent, deltaTime) { throw new Error('Execute method must be implemented'); } } // Composite node that can have children class CompositeNode extends Node { constructor() { super(); this.children = []; } addChild(child) { this.children.push(child); } } // Selector node - tries children in order until one succeeds class SelectorNode extends CompositeNode { constructor() { super(); this.currentChildIndex = 0; } execute(agent, deltaTime) { // If we were running a child previously, continue with it if (this.status === 'RUNNING' && this.currentChildIndex < this.children.length) { const result = this.children[this.currentChildIndex].execute(agent, deltaTime); if (result === 'RUNNING') { return 'RUNNING'; } else if (result === 'SUCCESS') { this.status = 'SUCCESS'; this.currentChildIndex = 0; return 'SUCCESS'; } // If child failed, try next child } // Try children in order for (let i = 0; i < this.children.length; i++) { const result = this.children[i].execute(agent, deltaTime); if (result === 'RUNNING') { this.status = 'RUNNING'; this.currentChildIndex = i; return 'RUNNING'; } else if (result === 'SUCCESS') { this.status = 'SUCCESS'; this.currentChildIndex = 0; return 'SUCCESS'; } // If child failed, try next child } // All children failed this.status = 'FAILURE'; this.currentChildIndex = 0; return 'FAILURE'; } } // Sequence node - executes children in order until one fails class SequenceNode extends CompositeNode { constructor() { super(); this.currentChildIndex = 0; } execute(agent, deltaTime) { // If we were running a child previously, continue with it if (this.status === 'RUNNING' && this.currentChildIndex < this.children.length) { const result = this.children[this.currentChildIndex].execute(agent, deltaTime); if (result === 'RUNNING') { return 'RUNNING'; } else if (result === 'FAILURE') { this.status = 'FAILURE'; this.currentChildIndex = 0; return 'FAILURE'; } // If child succeeded, try next child } // Try children in order for (let i = this.currentChildIndex; i < this.children.length; i++) { const result = this.children[i].execute(agent, deltaTime); if (result === 'RUNNING') { this.status = 'RUNNING'; this.currentChildIndex = i; return 'RUNNING'; } else if (result === 'FAILURE') { this.status = 'FAILURE'; this.currentChildIndex = 0; return 'FAILURE'; } // If child succeeded, continue to next child } // All children succeeded this.status = 'SUCCESS'; this.currentChildIndex = 0; return 'SUCCESS'; } } // Leaf node for specific actions class ActionNode extends Node { constructor(action) { super(); this.action = action; } execute(agent, deltaTime) { return this.action(agent, deltaTime); } } // Condition node to check specific conditions class ConditionNode extends Node { constructor(condition) { super(); this.condition = condition; } execute(agent, deltaTime) { return this.condition(agent, deltaTime) ? 'SUCCESS' : 'FAILURE'; } } // Villager behavior tree implementation class VillagerBehaviorTree { constructor() { this.root = this.createBehaviorTree(); } createBehaviorTree() { // Root selector - decides what the villager should do const root = new SelectorNode(); // Check if it's time to sleep const sleepSequence = new SequenceNode(); sleepSequence.addChild(new ConditionNode((agent) => agent.isTired())); sleepSequence.addChild(new ActionNode((agent) => agent.sleep())); // Check if it's time to work const workSequence = new SequenceNode(); workSequence.addChild(new ConditionNode((agent) => agent.shouldWork())); workSequence.addChild(new ActionNode((agent) => agent.work())); // Check if it's time to socialize const socializeSequence = new SequenceNode(); socializeSequence.addChild(new ConditionNode((agent) => agent.shouldSocialize())); socializeSequence.addChild(new ActionNode((agent) => agent.socialize())); // Default action - idle const idleAction = new ActionNode((agent) => agent.idle()); // Add all sequences to root root.addChild(sleepSequence); root.addChild(workSequence); root.addChild(socializeSequence); root.addChild(idleAction); return root; } update(agent, deltaTime) { this.root.execute(agent, deltaTime); } } export { VillagerBehaviorTree, Node, CompositeNode, SelectorNode, SequenceNode, ActionNode, ConditionNode };