1 /**
  2  * The Render Engine
  3  *
  4  * StateMachine component
  5  *
  6  * @author: Brett Fattori (brettf@renderengine.com)
  7  *
  8  * @author: $Author: bfattori $
  9  * @version: $Revision: 1555 $
 10  *
 11  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 12  *
 13  * Permission is hereby granted, free of charge, to any person obtaining a copy
 14  * of this software and associated documentation files (the "Software"), to deal
 15  * in the Software without restriction, including without limitation the rights
 16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 17  * copies of the Software, and to permit persons to whom the Software is
 18  * furnished to do so, subject to the following conditions:
 19  *
 20  * The above copyright notice and this permission notice shall be included in
 21  * all copies or substantial portions of the Software.
 22  *
 23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 29  * THE SOFTWARE.
 30  *
 31  */
 32 
 33 // The class this file defines and its required classes
 34 R.Engine.define({
 35     "class":"R.components.logic.StateMachine",
 36     "requires":[],
 37     "includes":[
 38         "/libs/machine.js"
 39     ]
 40 });
 41 
 42 /**
 43  * @class A component that uses a behavior tree to process state changes.  This is
 44  *    based on Mary Rose Cook's excellent Machine.js (which is linked in) so for now
 45  *    look to:
 46  *    <blockquote>
 47  *       http://machinejs.maryrosecook.com/
 48  *    </blockquote>
 49  *    The states, themselves, should exist on your {@link R.engine.GameObject} for
 50  *    which this component is used.
 51  *
 52  * @param name {String} The name of the component
 53  * @param [states=null] {Object} A states control object, if <code>null</code> assumes the state
 54  *    methods exist on the host.
 55  * @param [priority=1.0] {Number} The priority of this component
 56  * @extends R.components.Logic
 57  * @constructor
 58  * @description Creates a state machine which is used to drive the behaviors of your game object.
 59  */
 60 R.components.logic.StateMachine = function () {
 61     "use strict";
 62     return R.components.Logic.extend(/** @scope R.components.logic.StateMachine.prototype */{
 63 
 64         machine:null,
 65         states:null,
 66         updateInterval:0,
 67         lastUpdate:0,
 68 
 69         /** @private */
 70         constructor:function (name, states, priority) {
 71             if (R.isNumber(states)) {
 72                 priority = states;
 73                 states = null;
 74             }
 75 
 76             this.base(name, priority);
 77             this.states = states;
 78             this.machine = null;
 79             this.updateInterval = 0;
 80             this.lastUpdate = 0;
 81         },
 82 
 83         /**
 84          * Set the behavior tree for the state machine.  This is also used to configure how
 85          * often the machine is updated.  By tweaking the speed at which decisions are made,
 86          * it is possible to simulate faster or slower "behavior" or "thought" processing.
 87          *
 88          * @param stateTree {Object} The behavior tree object
 89          * @param [updateInterval=1000] {Number} The number of milliseconds between state changes
 90          */
 91         setBehaviorTree:function (stateTree, updateInterval) {
 92             this.updateInterval = updateInterval || R.components.logic.StateMachine.DEFAULT_INTERVAL;
 93             this.machine = new MachineJS();
 94 
 95             // Create the state machine on the game object
 96             this.getGameObject().setObjectDataModel(R.components.logic.StateMachine.MACHINE_STATE,
 97                 this.machine.generateTree(stateTree, this.getGameObject(), this.states));
 98         },
 99 
100         /**
101          * Set the interval at which the machine's state is updated.
102          * @param updateInterval {Number} The number of milliseconds between state changes
103          */
104         setUpdateInterval:function (updateInterval) {
105             this.updateInterval = updateInterval;
106             this.lastUpdate = 0;
107         },
108 
109         /**
110          * Update the state machine for each step of the engine.
111          *
112          * @param renderContext {R.rendercontexts.AbstractRenderContext} The rendering context
113          * @param time {Number} The engine time in milliseconds
114          * @param dt {Number} The delta between the world time and the last time the world was updated
115          *          in milliseconds.
116          */
117         execute:function (renderContext, time, dt) {
118             if (time - this.lastUpdate > this.updateInterval) {
119                 // Transition to the next state
120                 var state = this.getGameObject()
121                     .getObjectDataModel(R.components.logic.StateMachine.MACHINE_STATE);
122 
123                 state = state.tick();
124                 this.lastUpdate = time;
125             }
126         }
127 
128     }, /** @scope R.components.logic.StateMachine.prototype */{
129         getClassName:function () {
130             return "R.components.logic.StateMachine";
131         },
132 
133         /**
134          * The default time between state changes (1000 milliseconds)
135          * @type {Number}
136          */
137         DEFAULT_INTERVAL:1000,
138 
139         /**
140          * The machine state data model location.
141          * @type {String}
142          */
143         MACHINE_STATE:"MachineState"
144     });
145 };