Spaces:
Running
Running
File size: 5,745 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 |
/**
* 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 }; |