shimdh
4/18/2014 - 1:24 AM

btree module in behavior tree for node.js

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;