1 // Load all required engine components
  2 R.Engine.define({
  3     "class":"R.components.logic.behaviors.UnalignedCollisionAvoidance",
  4     "requires":[
  5         "R.components.logic.behaviors.BaseBehavior"
  6     ]
  7 });
  8 
  9 // Add behavior options
 10 if (R.Engine.options.behaviors === undefined) {
 11     R.Engine.options.behaviors = {};
 12 }
 13 
 14 $.extend(R.Engine.options.behaviors, {
 15     "unalignedAvoidanceCollisionRadius":150,
 16     "unalignedAvoidanceFutureDistance":60
 17 });
 18 
 19 /**
 20  * @class The unaligned collision avoidance behavior component. This component will actively avoid other moving
 21  *        objects by examining their future position.
 22  * @param vehicles {Array} The vehicle list to compare against
 23  * @param [radius=150] {Number} The radius around each vehicle to use in collision detection
 24  * @param [checkLength=60] {Number} The distance in front of the vehicle to perform checking
 25  * @extends R.components.logic.behaviors.BaseBehavior
 26  * @constructor
 27  */
 28 R.components.logic.behaviors.UnalignedCollisionAvoidance = function () {
 29     "use strict";
 30     return R.components.logic.behaviors.BaseBehavior.extend(/** @scope R.components.logic.behaviors.UnalignedCollisionAvoidance.prototype */{
 31 
 32         radius:0,
 33         vehicles:null,
 34         checkLength:0,
 35 
 36         /** @private */
 37         constructor:function (vehicles, radius, checkLength) {
 38             this.base("unalignedcollision");
 39             this.vehicles = vehicles;
 40             this.radius = radius || R.Engine.options.behaviors.unalignedAvoidanceCollisionRadius;
 41             this.checkLength = checkLength || R.Engine.options.behaviors.unalignedAvoidanceFutureDistance;
 42         },
 43 
 44         /**
 45          * This method is called by the game object to run the component,
 46          * updating its state.
 47          *
 48          * @param renderContext {R.rendercontexts.AbstractRenderContext} The context the component will render within.
 49          * @param time {Number} The global engine time
 50          * @param dt {Number} The delta between the world time and the last time the world was updated
 51          *          in milliseconds.
 52          */
 53         execute:function (time, dt) {
 54             // No vehicles? nothing to do
 55             if (!this.vehicles) {
 56                 return R.math.Vector2D.ZERO;
 57             }
 58 
 59             if (!this.getGameObject() || this.getGameObject().isDestroyed()) {
 60                 return R.math.Vector2D.ZERO;
 61             }
 62 
 63             var steering = R.math.Vector2D.create(0, 0), count = 0;
 64 
 65             for (var i = 0; i < this.vehicles.length; i++) {
 66                 var other = this.vehicles[i];
 67                 if (other === this.getGameObject()) {
 68                     // If this is our game object, skip it...
 69                     continue;
 70                 }
 71 
 72                 if (other.isDestroyed() || this.getGameObject().isDestroyed()) {
 73                     return R.math.Vector2D.ZERO;
 74                 }
 75 
 76                 var oMC = other.getComponentByClass("R.components.transform.BehaviorMover2D"), oPos = R.math.Vector2D.create(other.getPosition()).add(other.getOrigin()),
 77                     oVel = R.clone(oMC.getVelocity()), gO = this.getGameObject(), mC = this.getTransformComponent(),
 78                     gPos = R.clone(gO.getPosition()).add(gO.getOrigin()),
 79                     fwd = R.clone(mC.getVelocity()).normalize(), future = R.clone(oVel).normalize().mul(this.checkLength),
 80                     diff = R.clone(oPos).add(future).sub(gPos);
 81 
 82                 var dot = diff.dot(fwd);
 83                 if (dot > 0) {
 84                     // They may meet in the future
 85                     var ray = R.clone(fwd).mul(this.checkLength), proj = R.clone(fwd).mul(dot),
 86                         pC = R.clone(proj), dist = pC.sub(diff).len();
 87 
 88                     if (dist > 0 && dist < this.radius && proj.len() < ray.len()) {
 89                         // They will merge
 90                         var force = R.clone(fwd).mul(mC.getMaxSpeed()), angle = force.getAngle();
 91                         angle += R.math.Math2D.radToDeg(diff.getSign(mC.getVelocity()) * R.math.Math2D.PI / 4);
 92                         force.setAngle(angle);
 93                         steering.add(force);
 94                         force.destroy();
 95                     }
 96 
 97                     ray.destroy();
 98                     proj.destroy();
 99                     pC.destroy();
100                 }
101 
102                 oPos.destroy();
103                 oVel.destroy();
104                 gPos.destroy();
105                 fwd.destroy();
106                 diff.destroy();
107                 future.destroy();
108             }
109 
110             if (count > 0) {
111                 steering.div(count);
112             }
113 
114             if (steering.len() > 0) {
115                 steering.normalize().mul(mC.getMaxSpeed()).sub(mC.getVelocity()).truncate(mC.getMaxForce());
116             }
117 
118             return steering;
119         }
120 
121     }, /** @scope R.components.logic.behaviors.UnalignedCollisionAvoidance.prototype */{
122         getClassName:function () {
123             return "R.components.logic.behaviors.UnalignedCollisionAvoidance";
124         }
125     });
126 };
127