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 };