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