btree module in behavior tree for node.js
"use strict";
var Base = require('./base');
var countUnnamed = 0,
BehaviorTree;
BehaviorTree = Base.extend({
constructor: function(config) {
countUnnamed += 1;
this.title = config.title || 'btree' + (countUnnamed);
this._rootNode = config.tree;
this._object = config.object;
},
setObject: function(obj) {
this._object = obj;
},
step: function() {
if (this._started) {
console.log('the BehaviorTree "' + this.title + '" did call step but one Task did not finish on last call of step.');
}
this._started = true;
var node = BehaviorTree.getNode(this._rootNode);
this._actualNode = node;
node.setControl(this);
node.start(this._object);
node.run(this._object);
},
running: function() {
this._started = false;
},
success: function() {
this._actualNode.end(this._object);
this._started = false;
},
fail: function() {
this._actualNode.end(this._object);
this._started = false;
}
});
BehaviorTree._registeredNodes = {};
BehaviorTree.register = function(name, node) {
if (typeof name === 'string') {
this._registeredNodes[name] = node;
} else {
// name is the node
this._registeredNodes[name.title] = name;
}
};
BehaviorTree.getNode = function(name) {
var node = name instanceof BehaviorTree.Node ? name : this._registeredNodes[name];
if (!node) {
console.log('The node "' + name + '" could not be looked up. Maybe it was never registered?');
}
return node;
};
module.exports = BehaviorTree;
var Node = Base.extend({
constructor: function(config) {
// let config override instance properties
this.base(config);
},
start: function() {},
end: function() {},
run: function() {
console.log('Warning: run of ' + this.title + ' not implemented!');
this.fail();
},
setControl: function(control) {
this._control = control;
},
running: function() {
this._control.running(this);
},
success: function() {
this._control.success();
},
fail: function() {
this._control.fail();
}
});
BehaviorTree.Node = Node;
var BranchNode = BehaviorTree.Node.extend({
constructor: function(config) {
this.base(config);
this.children = this.nodes || [];
},
start: function() {
this._actualTask = 0;
},
run: function(object) {
this._object = object;
this.start();
if (this._actualTask < this.children.length) {
this._run();
}
this.end();
},
_run: function() {
var node = BehaviorTree.getNode(this.children[this._actualTask]);
this._runningNode = node;
node.setControl(this);
node.start(this._object);
node.run(this._object);
},
running: function(node) {
this._nodeRunning = node;
this._control.running(node);
},
success: function() {
this._nodeRunning = null;
this._runningNode.end(this._object);
},
fail: function() {
this._nodeRunning = null;
this._runningNode.end(this._object);
}
});
BehaviorTree.BranchNode = BranchNode;
var Priority = BehaviorTree.BranchNode.extend({
success: function() {
this.base();
this._control.success();
},
fail: function() {
this.base();
this._actualTask += 1;
if (this._actualTask < this.children.length) {
this._run(this._object);
} else {
this._control.fail();
}
}
});
BehaviorTree.Priority = Priority;
var Sequence = BehaviorTree.BranchNode.extend({
_run: function() {
if (this._nodeRunning) {
this._nodeRunning.run(this._object);
this._nodeRunning = null;
} else {
this.base();
}
},
success: function() {
this.base();
this._actualTask += 1;
if (this._actualTask < this.children.length) {
this._run(this._object);
} else {
this._control.success();
}
},
fail: function() {
this.base();
this._control.fail();
}
});
BehaviorTree.Sequence = Sequence;
var Random = BehaviorTree.BranchNode.extend({
start: function() {
this.base();
if (!this._nodeRunning) {
this._actualTask = Math.floor(Math.random() * this.children.length);
}
},
success: function() {
this.base();
this._control.success();
},
fail: function() {
this.base();
this._control.fail();
},
_run: function() {
if (!this._runningNode) {
this.base();
} else {
this._runningNode.run(this._object);
}
}
});
BehaviorTree.Random = Random;
var Task = BehaviorTree.Node.extend({});
BehaviorTree.Task = Task;
var Decorator = BehaviorTree.Node.extend({
constructor: function(config) {
// let config override instance properties
this.base(config);
if (this.node) {
this.node = BehaviorTree.getNode(this.node);
}
},
setNode: function(node) {
this.node = BehaviorTree.getNode(node);
},
start: function() {
this.node.setControl(this);
this.node.start();
},
end: function() {
this.node.end();
},
run: function(blackboard) {
this.node.run(blackboard);
},
});
BehaviorTree.Decorator = Decorator;
var InvertDecorator = BehaviorTree.Decorator.extend({
success: function() {
this._control.fail();
},
fail: function() {
this._control.success();
},
});
BehaviorTree.InvertDecorator = InvertDecorator;
var AlwaysSucceedDecorator = BehaviorTree.Decorator.extend({
success: function() {
this._control.success();
},
fail: function() {
this._control.success();
},
});
BehaviorTree.AlwaysSucceedDecorator = AlwaysSucceedDecorator;
var AlwaysFailDecorator = BehaviorTree.Decorator.extend({
success: function() {
this._control.fail();
},
fail: function() {
this._control.fail();
},
});
BehaviorTree.AlwaysFailDecorator = AlwaysFailDecorator;