Added Path Manager Plugin.

This commit is contained in:
Richard Davey 2016-07-13 03:08:03 +01:00
parent 4548d70369
commit a01f59d82d
7 changed files with 2915 additions and 4 deletions

View file

@ -112,6 +112,7 @@ EOL;
<script src="$path/src/geom/Circle.js"></script>
<script src="$path/src/geom/Ellipse.js"></script>
<script src="$path/src/geom/Hermite.js"></script>
<script src="$path/src/geom/Line.js"></script>
<script src="$path/src/geom/Matrix.js"></script>
<script src="$path/src/geom/Point.js"></script>
@ -203,6 +204,12 @@ EOL;
<script src="$path/src/plugins/weapon/WeaponPlugin.js"></script>
<script src="$path/src/plugins/weapon/Bullet.js"></script>
<script src="$path/src/plugins/path/PathManagerPlugin.js"></script>
<script src="$path/src/plugins/path/Path.js"></script>
<script src="$path/src/plugins/path/PathFollower.js"></script>
<script src="$path/src/plugins/path/PathPoint.js"></script>
<script src="$path/src/plugins/path/EventTarget.js"></script>
EOL;
if ($modules['rope'])
@ -296,10 +303,10 @@ EOL;
}
echo <<<EOL
<script src="$path/src/system/Device.js"></script>
<script src="$path/src/system/DOM.js"></script>
<script src="$path/src/system/Canvas.js"></script>
<script src="$path/src/system/RequestAnimationFrame.js"></script>
<script src="$path/src/utils/Device.js"></script>
<script src="$path/src/utils/DOM.js"></script>
<script src="$path/src/utils/Canvas.js"></script>
<script src="$path/src/utils/RequestAnimationFrame.js"></script>
<script src="$path/src/math/Math.js"></script>
<script src="$path/src/math/RandomDataGenerator.js"></script>

612
src/geom/Hermite.js Normal file
View file

@ -0,0 +1,612 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Pete Baron <pete@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A data representation of a Hermite Curve (see http://en.wikipedia.org/wiki/Cubic_Hermite_spline)
*
* A Hermite curve has a start and end point and tangent vectors for both of them.
* The curve will always pass through the two control points and the shape of it is controlled
* by the length and direction of the tangent vectors. At the control points the curve will
* be facing exactly in the vector direction.
*
* As these curves change speed (speed = distance between points separated by an equal change in
* 't' value - see Hermite.getPoint) this class attempts to reduce the variation by pre-calculating
* the `accuracy` number of points on the curve. The straight-line distances to these points are stored
* in the private 'points' array, and this information is used by Hermite.findT() to convert a pixel
* distance along the curve into a 'time' value.
*
* Higher `accuracy` values will result in more even movement, but require more memory for the points
* list. 5 works, but 10 seems to be an ideal value for the length of curves found in most games on
* a desktop screen. If you use very long curves (more than 400 pixels) you may need to increase
* this value further.
*
* @class Phaser.Hermite
* @constructor
* @param {number} p1x - The x coordinate of the start of the curve.
* @param {number} p1y - The y coordinate of the start of the curve.
* @param {number} p2x - The x coordinate of the end of the curve.
* @param {number} p2y - The y coordinate of the end of the curve.
* @param {number} v1x - The x component of the tangent vector for the start of the curve.
* @param {number} v1y - The y component of the tangent vector for the start of the curve.
* @param {number} v2x - The x component of the tangent vector for the end of the curve.
* @param {number} v2y - The y component of the tangent vector for the end of the curve.
* @param {number} [accuracy=10] The amount of points to pre-calculate on the curve.
*/
Phaser.Hermite = function (p1x, p1y, p2x, p2y, v1x, v1y, v2x, v2y, accuracy) {
if (accuracy === undefined) { accuracy = 10; }
/**
* @property {number} _accuracy - The amount of points to pre-calculate on the curve.
* @private
*/
this._accuracy = accuracy;
/**
* @property {number} _p1x - The x coordinate of the start of the curve.
* @private
*/
this._p1x = p1x;
/**
* @property {number} _p1y - The y coordinate of the start of the curve.
* @private
*/
this._p1y = p1y;
/**
* @property {number} _p2x - The x coordinate of the end of the curve.
* @private
*/
this._p2x = p2x;
/**
* @property {number} _p2y - The y coordinate of the end of the curve.
* @private
*/
this._p2y = p2y;
/**
* @property {number} _v1x - The x component of the tangent vector for the start of the curve.
* @private
*/
this._v1x = v1x;
/**
* @property {number} _v1y - The y component of the tangent vector for the start of the curve.
* @private
*/
this._v1y = v1y;
/**
* @property {number} _v2x - The x component of the tangent vector for the end of the curve.
* @private
*/
this._v2x = v2x;
/**
* @property {number} _v2y - The y component of the tangent vector for the end of the curve.
* @private
*/
this._v2y = v2y;
/**
* @property {array} _points - A local array of cached points.
* @private
*/
this._points = [];
/**
* @property {Phaser.Point} _temp1 - A local cached Point object.
* @private
*/
this._temp1 = new Phaser.Point;
/**
* @property {Phaser.Point} _temp2 - A local cached Point object.
* @private
*/
this._temp2 = new Phaser.Point;
this.recalculate();
};
Phaser.Hermite.prototype.constructor = Phaser.Hermite;
Phaser.Hermite.prototype = {
/**
* Performs the curve calculations.
*
* This is called automatically if you change any of the curves public properties, such as `Hermite.p1x` or `Hermite.v2y`.
*
* If you adjust any of the internal private values, then call this to update the points.
*
* @method Phaser.Hermite#recalculate
* @return {Phaser.Hermite} This object.
*/
recalculate: function () {
this._ax = (2 * this._p1x - 2 * this._p2x + this._v1x + this._v2x);
this._ay = (2 * this._p1y - 2 * this._p2y + this._v1y + this._v2y);
this._bx = (-3 * this._p1x + 3 * this._p2x - 2 * this._v1x - this._v2x);
this._by = (-3 * this._p1y + 3 * this._p2y - 2 * this._v1y - this._v2y);
this.length = this.calculateEvenPoints();
return this;
},
/**
* Calculate a number of points along the curve, based on `Hermite.accuracy`, and stores them in the private `_points` array.
*
* @method Phaser.Hermite#calculateEvenPoints
* @return {number} The total length of the curve approximated as straight line distances between the points.
*/
calculateEvenPoints: function () {
var totalLength = 0;
this._temp1.setTo(0, 0); // pnt
this._temp2.setTo(this._p1x, this._p1y); // lastPnt
this._points[0] = 0;
for (var i = 1; i <= this._accuracy; i++)
{
this.getPoint(i / this._accuracy, this._temp1);
totalLength += this._temp1.distance(this._temp2);
this._points[i] = totalLength;
this._temp2.copyFrom(this._temp1);
}
return totalLength;
},
/**
* Convert a distance along this curve into a `time` value which will be between 0 and 1.
*
* For example if this curve has a length of 100 pixels then `findT(50)` would return `0.5`.
*
* @method Phaser.Hermite#findT
* @param {integer} distance - The distance into the curve in pixels. Should be a positive integer.
* @return {number} The time (`t`) value, a float between 0 and 1.
*/
findT: function (distance) {
if (distance <= 0)
{
return 0;
}
// Find the _points which bracket the distance value
var ti = Math.floor(distance / this.length * this._accuracy);
while (ti > 0 && this._points[ti] > distance)
{
ti--;
}
while (ti < this._accuracy && this._points[ti] < distance)
{
ti++;
}
// Linear interpolation to get a more accurate fix
var dt = this._points[ti] - this._points[ti - 1];
var d = distance - this._points[ti - 1];
return ((ti - 1) / this._accuracy) + d / (dt * this._accuracy);
},
/**
* Get the X component of a point on the curve based on the `t` (time) value, which must be between 0 and 1.
*
* @method Phaser.Hermite#getX
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
* @return {number} The X component of a point on the curve based on the `t` (time) value.
*/
getX: function (t) {
if (t === undefined)
{
t = 0;
}
else
{
if (t < 0)
{
t = 0;
}
if (t > 1)
{
t = 1;
}
}
var t2 = t * t;
var t3 = t * t2;
return (t3 * this._ax + t2 * this._bx + t * this._v1x + this._p1x);
},
/**
* Get the Y component of a point on the curve based on the `t` (time) value, which must be between 0 and 1.
*
* @method Phaser.Hermite#getY
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
* @return {number} The Y component of a point on the curve based on the `t` (time) value.
*/
getY: function (t) {
if (t === undefined)
{
t = 0;
}
else
{
if (t < 0)
{
t = 0;
}
if (t > 1)
{
t = 1;
}
}
var t2 = t * t;
var t3 = t * t2;
return (t3 * this._ay + t2 * this._by + t * this._v1y + this._p1y);
},
/**
* Get a point on the curve using the `t` (time) value, which must be between 0 and 1.
*
* @method Phaser.Hermite#getPoint
* @param {number} [t=0] - The time value along the curve from which to extract a point. This is a value between 0 and 1, where 0 represents the start of the curve and 1 the end.
* @param {Phaser.Point|Object} [point] - An optional Phaser.Point, or Object containing public `x` and `y` properties. If given the resulting values will be stored in the Objects `x` and `y` properties. If omitted a new Phaser.Point object is created.
* @return {Phaser.Point} An Object with the x, y coordinate of the curve at the specified `t` value set in its `x` and `y` properties.
*/
getPoint: function (t, point) {
if (t === undefined) { t = 0; }
if (point === undefined) { point = new Phaser.Point(); }
if (t < 0)
{
t = 0;
}
if (t > 1)
{
t = 1;
}
var t2 = t * t;
var t3 = t * t2;
point.x = t3 * this._ax + t2 * this._bx + t * this._v1x + this._p1x;
point.y = t3 * this._ay + t2 * this._by + t * this._v1y + this._p1y;
return point;
},
/**
* Get a point on the curve using the distance, in pixels, along the curve.
*
* @method Phaser.Hermite#getPointWithDistance
* @param {integer} [distance=0] - The distance along the curve to get the point from, given in pixels.
* @param {Phaser.Point|Object} [point] - An optional Phaser.Point, or Object containing public `x` and `y` properties. If given the resulting values will be stored in the Objects `x` and `y` properties. If omitted a new Phaser.Point object is created.
* @return {Phaser.Point} The point on the line at the specified 'distance' along the curve.
*/
getPointWithDistance: function (distance, point) {
if (distance === undefined) { distance = 0; }
if (point === undefined) { point = new Phaser.Point(); }
if (distance <= 0)
{
point.x = this._p1x;
point.y = this._p1y;
}
else
{
this.getPoint(this.findT(distance), point);
}
return point;
},
/**
* Calculate and return the angle, in radians, of the curves tangent based on time.
*
* @method Phaser.Hermite#getAngle
* @param {number} [t=0] - The `t` (time) value at which to find the angle. Must be between 0 and 1.
* @return {number} The angle of the line at the specified `t` time value along the curve. The value is in radians.
*/
getAngle: function (t) {
if (t === undefined) { t = 0; }
this.getPoint(t - .01, this._temp1);
this.getPoint(t + .01, this._temp2);
return Math.atan2(this._temp2.y - this._temp1.y, this._temp2.x - this._temp1.x);
},
/**
* Calculate and return the angle, in radians, of the curves tangent at the given pixel distance along the curves length.
*
* @method Phaser.Hermite#getAngleWithDistance
* @param {number} [distance=0] - The distance along the curve to get the angle from, in pixels.
* @return {number} The angle of the line at the specified distance along the curve. The value is in radians.
*/
getAngleWithDistance: function (distance) {
if (distance === undefined) { distance = 0; }
if (distance <= 0)
{
return Math.atan2(this._v1y, this._v1x);
}
else
{
return this.getAngle(this.findT(distance));
}
},
/**
* Get the angle of the curves entry point.
*
* @method Phaser.Hermite#getEntryTangent
* @param {Phaser.Point|Object} point - The Phaser.Point object, or an Object with public `x` and `y` properties, in which the tangent vector values will be stored.
* @return {Phaser.Point} A Point object containing the tangent vector of this Hermite curve.
*/
getEntryTangent: function (point) {
point.x = this._v1x;
point.y = this._v1y;
return point;
}
};
Object.defineProperties(Phaser.Hermite.prototype, {
/**
* @name Phaser.Hermite#accuracy
* @property {number} accuracy - The amount of points to pre-calculate on the curve.
*/
accuracy: {
get: function () {
return this._accuracy;
},
set: function (value) {
if (value !== this._accuracy)
{
this._accuracy = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#p1x
* @property {number} p1x - The x coordinate of the start of the curve. Setting this value will recalculate the curve.
*/
p1x: {
get: function () {
return this._p1x;
},
set: function (value) {
if (value !== this._p1x)
{
this._p1x = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#p1y
* @property {number} p1y - The y coordinate of the start of the curve. Setting this value will recalculate the curve.
*/
p1y: {
get: function () {
return this._p1y;
},
set: function (value) {
if (value !== this._p1y)
{
this._p1y = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#p2x
* @property {number} p2x - The x coordinate of the end of the curve. Setting this value will recalculate the curve.
*/
p2x: {
get: function () {
return this._p2x;
},
set: function (value) {
if (value !== this._p2x)
{
this._p2x = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#p2y
* @property {number} p2y - The y coordinate of the end of the curve. Setting this value will recalculate the curve.
*/
p2y: {
get: function () {
return this._p2y;
},
set: function (value) {
if (value !== this._p2y)
{
this._p2y = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#v1x
* @property {number} v1x - The x component of the tangent vector for the start of the curve. Setting this value will recalculate the curve.
*/
v1x: {
get: function () {
return this._v1x;
},
set: function (value) {
if (value !== this._v1x)
{
this._v1x = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#v1y
* @property {number} v1y - The y component of the tangent vector for the start of the curve. Setting this value will recalculate the curve.
*/
v1y: {
get: function () {
return this._v1y;
},
set: function (value) {
if (value !== this._v1y)
{
this._v1y = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#v2x
* @property {number} v2x - The x component of the tangent vector for the end of the curve. Setting this value will recalculate the curve.
*/
v2x: {
get: function () {
return this._v2x;
},
set: function (value) {
if (value !== this._v2x)
{
this._v2x = value;
this.recalculate();
}
}
},
/**
* @name Phaser.Hermite#v2y
* @property {number} v2y - The y component of the tangent vector for the end of the curve. Setting this value will recalculate the curve.
*/
v2y: {
get: function () {
return this._v2y;
},
set: function (value) {
if (value !== this._v2y)
{
this._v2y = value;
this.recalculate();
}
}
}
});

View file

@ -0,0 +1,284 @@
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
* @author Chad Engler https://github.com/englercj @Rolnaaba
*/
/**
* Originally based on https://github.com/mrdoob/eventtarget.js/ from mr Doob.
* Currently takes inspiration from the nodejs EventEmitter, EventEmitter3, and smokesignals
*/
/**
* Mixins event emitter functionality to a class
*
* @class EventTarget
* @example
* function MyEmitter() {}
*
* Phaser.EventTarget.mixin(MyEmitter.prototype);
*
* var em = new MyEmitter();
* em.emit('eventName', 'some data', 'some more data', {}, null, ...);
*/
Phaser.EventTarget = {
/**
* Backward compat from when this used to be a function
*/
call: function callCompat(obj) {
if(obj) {
obj = obj.prototype || obj;
Phaser.EventTarget.mixin(obj);
}
},
/**
* Mixes in the properties of the EventTarget prototype onto another object
*
* @method mixin
* @param object {Object} The obj to mix into
*/
mixin: function mixin(obj) {
/**
* Return a list of assigned event listeners.
*
* @method listeners
* @param eventName {String} The events that should be listed.
* @return {Array} An array of listener functions
*/
obj.listeners = function listeners(eventName) {
this._listeners = this._listeners || {};
return this._listeners[eventName] ? this._listeners[eventName].slice() : [];
};
/**
* Emit an event to all registered event listeners.
*
* @method emit
* @alias dispatchEvent
* @param eventName {String} The name of the event.
* @return {Boolean} Indication if we've emitted an event.
*/
obj.emit = obj.dispatchEvent = function emit(eventName, data) {
this._listeners = this._listeners || {};
//backwards compat with old method ".emit({ type: 'something' })"
if(typeof eventName === 'object') {
data = eventName;
eventName = eventName.type;
}
//ensure we are using a real pixi event
if(!data || data.__isEventObject !== true) {
data = new Phaser.Event(this, eventName, data);
}
//iterate the listeners
if(this._listeners && this._listeners[eventName]) {
var listeners = this._listeners[eventName].slice(0),
length = listeners.length,
fn = listeners[0],
i;
for(i = 0; i < length; fn = listeners[++i]) {
//call the event listener
fn.call(this, data);
//if "stopImmediatePropagation" is called, stop calling sibling events
if(data.stoppedImmediate) {
return this;
}
}
//if "stopPropagation" is called then don't bubble the event
if(data.stopped) {
return this;
}
}
//bubble this event up the scene graph
if(this.parent && this.parent.emit) {
this.parent.emit.call(this.parent, eventName, data);
}
return this;
};
/**
* Register a new EventListener for the given event.
*
* @method on
* @alias addEventListener
* @param eventName {String} Name of the event.
* @param callback {Functon} fn Callback function.
*/
obj.on = obj.addEventListener = function on(eventName, fn) {
this._listeners = this._listeners || {};
(this._listeners[eventName] = this._listeners[eventName] || [])
.push(fn);
return this;
};
/**
* Add an EventListener that's only called once.
*
* @method once
* @param eventName {String} Name of the event.
* @param callback {Function} Callback function.
*/
obj.once = function once(eventName, fn) {
this._listeners = this._listeners || {};
var self = this;
function onceHandlerWrapper() {
fn.apply(self.off(eventName, onceHandlerWrapper), arguments);
}
onceHandlerWrapper._originalHandler = fn;
return this.on(eventName, onceHandlerWrapper);
};
/**
* Remove event listeners.
*
* @method off
* @alias removeEventListener
* @param eventName {String} The event we want to remove.
* @param callback {Function} The listener that we need to find.
*/
obj.off = obj.removeEventListener = function off(eventName, fn) {
this._listeners = this._listeners || {};
if(!this._listeners[eventName])
return this;
var list = this._listeners[eventName],
i = fn ? list.length : 0;
while(i-- > 0) {
if(list[i] === fn || list[i]._originalHandler === fn) {
list.splice(i, 1);
}
}
if(list.length === 0) {
delete this._listeners[eventName];
}
return this;
};
/**
* Remove all listeners or only the listeners for the specified event.
*
* @method removeAllListeners
* @param eventName {String} The event you want to remove all listeners for.
*/
obj.removeAllListeners = function removeAllListeners(eventName) {
this._listeners = this._listeners || {};
if(!this._listeners[eventName])
return this;
delete this._listeners[eventName];
return this;
};
}
};
/**
* Creates an homogenous object for tracking events so users can know what to expect.
*
* @class Event
* @extends Object
* @constructor
* @param target {Object} The target object that the event is called on
* @param name {String} The string name of the event that was triggered
* @param data {Object} Arbitrary event data to pass along
*/
Phaser.Event = function(target, name, data) {
//for duck typing in the ".on()" function
this.__isEventObject = true;
/**
* Tracks the state of bubbling propagation. Do not
* set this directly, instead use `event.stopPropagation()`
*
* @property stopped
* @type Boolean
* @private
* @readOnly
*/
this.stopped = false;
/**
* Tracks the state of sibling listener propagation. Do not
* set this directly, instead use `event.stopImmediatePropagation()`
*
* @property stoppedImmediate
* @type Boolean
* @private
* @readOnly
*/
this.stoppedImmediate = false;
/**
* The original target the event triggered on.
*
* @property target
* @type Object
* @readOnly
*/
this.target = target;
/**
* The string name of the event that this represents.
*
* @property type
* @type String
* @readOnly
*/
this.type = name;
/**
* The data that was passed in with this event.
*
* @property data
* @type Object
* @readOnly
*/
this.data = data;
//backwards compat with older version of events
this.content = data;
/**
* The timestamp when the event occurred.
*
* @property timeStamp
* @type Number
* @readOnly
*/
this.timeStamp = Date.now();
};
/**
* Stops the propagation of events up the scene graph (prevents bubbling).
*
* @method stopPropagation
*/
Phaser.Event.prototype.stopPropagation = function stopPropagation() {
this.stopped = true;
};
/**
* Stops the propagation of events to sibling listeners (no longer calls any listeners).
*
* @method stopImmediatePropagation
*/
Phaser.Event.prototype.stopImmediatePropagation = function stopImmediatePropagation() {
this.stoppedImmediate = true;
};

862
src/plugins/path/Path.js Normal file
View file

@ -0,0 +1,862 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Pete Baron <pete@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A Phaser.Path contains all the functions need to create and manipulate a single Path object.
* A Path is a list of PathPoint objects connected by Hermite curves.
*
* @class Phaser.Path
* @constructor
* @param {Phaser.Game} game - A reference to the Phaser.Game instance.
* @param {number} [type=Phaser.Path.CoordinateSystems.WORLD] - The coordinate system used by the Path.
* @param {boolean} [loops=false] - Should this Path loop or not when a PathFollower reaches the end of it?
*/
Phaser.Path = function (game, type, loops) {
if (type === undefined) { type = Phaser.Path.CoordinateSystems.WORLD; }
if (loops === undefined) { loops = false; }
/**
* @property {Phaser.Game} game - A reference to the currently running game.
*/
this.game = game;
/**
* @property {number} coordinateSystem - The coordinate system used by the Path.
*/
this.coordinateSystem = type;
/**
* @property {boolean} loops - Should this Path loop or not when a PathFollower reaches the end of it?
*/
this.loops = loops;
/**
* @property {string} cacheKey - The key of the JSON file in the cache used to define this path.
*/
this.cacheKey = '';
/**
* @property {string} key - The key of the object within the JSON data. Used if there are multiple paths per JSON file.
*/
this.key = '';
/*
* @property {Phaser.PathPoint} name - The name of this path.
*/
this.name = '';
/*
* @property {Phaser.PathPoint} type - The Phaser.Path.PathTypes of this path.
*/
this.type = Phaser.Path.PathTypes.PATH;
/*
* @property {Array} branches - A list of branches this path has.
*/
this.branches = [];
/**
* @property {array} _points - A private cache of the Points on this Path.
* @private
*/
this._points = [];
/**
* @property {Phaser.Point} _offset - Default offset for PathFollowers on this path instance.
* @private
*/
this._offset = new Phaser.Point();
/*
* @property {Phaser.PathPoint} _p1 - Used for internal calculations.
* @private
*/
this._p1 = new Phaser.PathPoint();
/*
* @property {Phaser.PathPoint} _p2 - Used for internal calculations.
* @private
*/
this._p2 = new Phaser.PathPoint();
/*
* @property {Phaser.PathPoint} origin - the origin of this path. Used mostly for BRANCH paths.
* @private
*/
this._origin = new Phaser.Point();
};
Phaser.Path.prototype.constructor = Phaser.Path;
Phaser.Path.PathTypes = {};
Phaser.Path.BranchTypes = {};
Phaser.Path.CoordinateSystems = {};
/**
* @constant
* @type {number}
*/
Phaser.Path.PathTypes.PATH = 0;
/**
* @constant
* @type {number}
*/
Phaser.Path.PathTypes.BRANCH = 1;
/**
* @constant
* @type {number}
*/
Phaser.Path.BranchTypes.ATTACHED = 0;
/**
* @constant
* @type {number}
*/
Phaser.Path.BranchTypes.JOINED = 1;
/**
* Points are relative to the World origin.
* @constant
* @type {number}
*/
Phaser.Path.CoordinateSystems.WORLD = 1;
/**
* Points are relative to the screen origin.
* @constant
* @type {number}
*/
Phaser.Path.CoordinateSystems.SCREEN = 2;
/**
* Points are relative to the first point.
* @constant
* @type {number}
*/
Phaser.Path.CoordinateSystems.OFFSET = 3;
Phaser.Path.prototype = {
/**
* Initialize a Path based on the given coordinate system.
*
* @method Phaser.Path#create
* @param {number|string} coordinateSystem - The Phaser.Path.CoordinateSystems type to use.
* @param {boolean} [loops=false] - Should this Path loop or not when a PathFollower reaches the end of it?
* @return {Phaser.Path} This Path object.
*/
create: function (coordinateSystem, loops) {
if (loops === undefined) { loops = false; }
switch (coordinateSystem)
{
default:
this.coordinateSystem = Phaser.Path.CoordinateSystems.WORLD;
break;
case 2:
case 'SCREEN_COORDINATES':
this.coordinateSystem = Phaser.Path.CoordinateSystems.SCREEN;
break;
case 3:
case 'OFFSET_COORDINATES':
this.coordinateSystem = Phaser.Path.CoordinateSystems.OFFSET;
break;
}
this.loops = loops;
this._points = [];
return this;
},
/**
* Clone this Path object. It clones the origin and points data.
*
* @method Phaser.Path#clone
* @return {Phaser.Path} The cloned Path.
*/
clone: function () {
var clone = new Phaser.Path(this.coordinateSystem, this.loops);
this.origin.clone(clone.origin);
this.points.forEach(function(p) {
clone._points.push(p.clone());
});
return clone;
},
/**
* Creates a new PathPoint object, relative to the path origin, and adds it to this path.
*
* @method Phaser.Path#addPathPoint
* @param {number} [x=0] - The x position of the PathPoint.
* @param {number} [y=0] - The y position of the PathPoint.
* @param {number} [vx=0] - The vx tangent vector value of the PathPoint.
* @param {number} [vy=0] - The vy tangent vector value of the PathPoint.
* @param {number} [speed=1] - The speed value of the PathPoint.
* @param {number} [data={}] - The data object
* @param {number} [index=null] - The index of the new path point. If not given, will add point to end of point list.
* @return {Phaser.PathPoint} The PathPoint object that was created.
*/
addPathPoint: function (x, y, vx, vy, speed, data, index) {
if (x === undefined) { x = 0; }
if (y === undefined) { y = 0; }
if (vx === undefined) { vx = 0; }
if (vy === undefined) { vy = 0; }
var pp = new Phaser.PathPoint(x - this.origin.x, y - this.origin.y, vx, vy, speed, data);
if (index !== null && index !== undefined)
{
this._points.splice(index, 0, pp);
}
else
{
this._points.push(pp);
}
return pp;
},
/**
* Remove a PathPoint from this paths point list.
*
* @method Phaser.Path#removePathPoint
* @param {number} [index] - The index of the PathPoint to remove.
* @return {Phaser.PathPoint} The removed PathPoint object.
*/
removePathPoint: function (index) {
var p = this.getPathPointReference(index);
if (p)
{
this._points.splice(index, 1);
}
return p;
},
/**
* Set a PathPoint objects position and tangent vector.
*
* @method Phaser.Path#setPathPoint
* @param {number} index - The index of the PathPoint in this paths point list.
* @param {number} x - The x coordinate of the PathPoint.
* @param {number} y - The y coordinate of the PathPoint.
* @param {number} [vx] - The x coordinate of the tangent vector to create the curve from.
* @param {number} [vy] - The y coordinate of the tangent vector to create the curve from.
* @return {Phaser.PathPoint} A reference to the PathPoint object that was updated.
*/
setPathPoint: function (index, x, y, vx, vy) {
var p = this.getPathPointReference(index);
if (p)
{
p.setTo(x, y, vx, vy);
}
return p;
},
/**
* Translate all points in a path by the given point.
*
* @method Phaser.Path#translatePoints
* @param {Phaser.Point|object} point - A Phaser.Point, or a Point-like Object with public `x` and `y` properties, that will be used to modify all points in this paths point list.
* @return {Phaser.Path} This Path object.
*/
translatePoints: function (point) {
this._points.forEach(function(pnt) {
pnt.x += point.x;
pnt.y += point.y;
});
return this;
},
/**
* Set the Path level offset which will affect all of this paths PathFollowers.
*
* @method Phaser.Path#setOffset
* @param {number} x - The x offset.
* @param {number} y - The y offset.
* @return {Phaser.Path} This Path object.
*/
setOffset: function (x, y) {
this._offset.x = x;
this._offset.y = y;
return this;
},
/**
* Get a point on the the current Path curve.
*
* @method Phaser.Path#getPointOnThisCurve
* @param {Phaser.Hermite} curve - A Phaser.Hermite curve object.
* @param {number} [t=0 .. 1.0] - The distance on the curve to get the point from. Where 0 is the start of the curve, and 1 is the end.
* @return {Phaser.Point} A point containing the x and y values at the specified distance (t) value in the curve.
*/
getPointOnThisCurve: function (curve, t) {
if (!curve)
{
return null;
}
var pnt = curve.getPoint(t);
pnt.x += this._offset.x;
pnt.y += this._offset.y;
return pnt;
},
/**
* Gets the points on the curve representing the end points of the line segments that make up the curve.
*
* @method Phaser.Path#getControlPointsOnThisCurve
* @param {Phaser.Hermite} curve - A Phaser.Hermite curve.
* @return {[Phaser.Point]} An array of points representing the end points of 10 line segments that make up the curve
*/
getControlPointsOnThisCurve: function (curve) {
var pnts = Phaser.ArrayUtils.numberArrayStep(0, 1.1, 0.1).map(function(num) {
return this.getPointOnThisCurve(curve, num);
}, this);
return pnts;
},
/**
* Get a PathPoint from this path. Automatically handles path looping.
*
* The values from the PathPoint are copied into the given PathPoint object, which must
* be a reference to a pre-existing PathPoint, as it's not returned by this method.
*
* @method Phaser.Path#getPathPoint
* @param {number} index - The index of the point in this path to get.
* @param {Phaser.PathPoint} point - A PathPoint object into which the found point object is cloned.
* @return {boolean} false if the index is past the end of the path and it doesn't loop, otherwise true.
*/
getPathPoint: function (index, point) {
var i = this.loops ? index % this._points.length : index;
// If index is in the points list range
if (this._points.length > i)
{
point.copy(this._points[i]);
switch (this.coordinateSystem)
{
case Phaser.Path.CoordinateSystems.SCREEN:
point.x -= this.game.camera.x;
point.y -= this.game.camera.y;
break;
case Phaser.Path.CoordinateSystems.OFFSET:
point.x += this.origin.x;
point.y += this.origin.y;
break;
}
return true;
}
else
{
// The path doesn't loop and the index is out of range, so fail
return false;
}
},
/**
* Get a reference to a PathPoint from this Path, handle path looping.
*
* NOTE: because this is a PathPoint reference, it does not take into account the coordinateSystem selected, it will be WORLD, or OFFSET unmodified
*
* @method Phaser.Path#getPathPointReference
* @param {number} index - The index of the point in this path to get.
* @return {Phaser.PathPoint} A reference to the PathPoint object in this Path, or null if index is out of range.
*/
getPathPointReference: function (index) {
var i = this.loops ? index % this._points.length : index;
// If index is in the points list range
if (this._points.length > i)
{
return this._points[i];
}
// The path doesn't loop and the index is out of range, fail
return null;
},
/**
* Get the curve from the given point index to the next.
*
* If the curve has been created previously, use that definition again, otherwise calculate it now.
*
* @method Phaser.Path#getCurve
* @param {number} [index=0] - The index of the point in this path to get the curve from.
* @return {Phaser.Hermite} A new Hermite object representing the curve starting at the 'index' path point.
*/
getCurve: function (index) {
if (index === undefined) { index = 0; }
// Beginning of the curve
if (!this.getPathPoint(index, this._p1))
{
return null;
}
// Has this curve been calculated already?
if (this._p1.curve)
{
return this._p1.curve;
}
// End of the curve
if (!this.getPathPoint(index + 1, this._p2))
{
if (!this._p1.branchPath)
{
return null;
}
// We joined another Path
var newPath = this._p1.branchPath;
var joinIndex = this._p1.branchPointIndex;
if (!newPath.getPathPoint(joinIndex + 1, this._p2))
{
return null;
}
}
// Create and return the new Hermite object
this._p1.curve = new Phaser.Hermite(this._p1.x, this._p1.y, this._p2.x, this._p2.y, this._p1.vx, this._p1.vy, this._p2.vx, this._p2.vy);
this.curvePointIndex = index;
return this._p1.curve;
},
/**
* Find the first matching PathPoint in this path.
* It works by taking the given PathPoint object, and then iterating through all points
* in this Path until it finds one with the same values, then returns the index to it.
*
* @method Phaser.Path#pointIndex
* @param {Phaser.PathPoint} pathPoint - The PathPoint object that will have its values compared to all the points in this Path.
* @return {number} The index of the PathPoint in this Path if an equal match is found, or -1 if no match is found.
*/
pointIndex: function (pathPoint) {
var l = this._points.length;
for (var i = 0; i < l; i++)
{
if (this.coordinateSystem === Phaser.Path.CoordinateSystems.OFFSET && i !== 0)
{
if (pathPoint.equals(this._points[i], this._points[0].x, this._points[0].y))
{
return i;
}
}
else
{
if (pathPoint.equals(this._points[i]))
{
return i;
}
}
}
return -1;
},
/**
* Is the given PathPoint index the end of this path?
*
* @method Phaser.Path#atEnd
* @param {number} index - The index of the PathPoint to test.
* @return {boolean} true if index is the last point in this path.
*/
atEnd: function (index) {
// If the path loops, the end of the path is the end of the last curve
if (this.loops)
{
return (index === this._points.length);
}
// If the path doesn't loop, the end of the path is the last point on it
return (index === this._points.length - 1);
},
/**
* The total number of PathPoints in this Path.
*
* @method Phaser.Path#numPoints
* return {number} The total number of PathPoints in this Path.
*/
numPoints: function () {
return this._points.length;
},
/*
* DATA PROCESSING
*/
/**
* Process the data associated with a point on this Path.
* Used by Phaser.PathFollower objects as they pass each control point.
*
* @method Phaser.Path#processData
* @param {Phaser.PathFollower} follower - The PathFollower that is processing the data.
* @param {number} pathPointIndex - The index of the path point to process.
* @param {boolean} reversing - Whether or not the follower is traversing the path in reverse.
* @return {Phaser.PathPoint} The PathPoint that has been processed.
*/
processData: function (follower, pathPointIndex, reversing) {
if (this.getPathPoint(pathPointIndex, this._p1))
{
// If there is a branch that can be taken from this point,
// trigger an event to decide whether to take it or stay on the current path.
// Branches are forwards facing so they are ignored when the follower is reversing.
if (this._p1.branchPath && !reversing)
{
follower.dispatchEvent({
type: Phaser.PathFollower.EVENT_BRANCH_CHOICE,
target: follower,
data: this._p1.clone()
});
}
// If there is information in the data member of this point
if (this._p1.data && this._p1.data.type)
{
switch (this._p1.data.type)
{
case Phaser.PathPoint.DATA_PAUSE:
follower.pause(this._p1.data.value);
break;
case Phaser.PathPoint.DATA_COUNTER:
// first time past, set the count
if (follower.branchCount === 0)
{
follower.branchCount = this._p1.data.value;
}
else
{
// After that decrease the count
follower.branchCount--;
if (follower.branchCount <= 0)
{
follower.branchCount = 0;
// Trigger event when counter expires
follower.dispatchEvent({
type: Phaser.PathFollower.EVENT_COUNT_FINISH,
target: follower,
data: this._p1.clone()
});
}
}
break;
}
}
// Trigger event when passing any point on the path
follower.dispatchEvent({
type: Phaser.PathFollower.EVENT_REACHED_POINT,
target: follower,
data: this._p1.clone()
});
}
return this._p1;
},
/**
* If your Path has 3 points or more, this will walk through it and auto-smooth them out.
* Note: It ignores branches.
*
* @method Phaser.Path#smooth
* @return {Phaser.Path} This Path object.
*/
smooth: function () {
if (this._points.length === 0)
{
return this;
}
var i;
var thisPoint;
var p1;
var p2;
var dx;
var dy;
for (i = 1; i < this._points.length - 1; i++)
{
thisPoint = this.getPathPointReference(i);
p1 = this.getPathPointReference(i - 1);
p2 = this.getPathPointReference(i + 1);
dx = p2.x - p1.x;
dy = p2.y - p1.y;
thisPoint.setTangent(dx, dy);
}
if (this.loops)
{
i = this._points.length - 1;
thisPoint = this.getPathPointReference(i);
p1 = this.getPathPointReference(i - 1);
p2 = this.getPathPointReference(0);
dx = p2.x - p1.x;
dy = p2.y - p1.y;
thisPoint.setTangent(dx, dy);
i = 0;
thisPoint = this.getPathPointReference(i);
p1 = this.getPathPointReference(this._points.length - 1);
p2 = this.getPathPointReference(1);
dx = p2.x - p1.x;
dy = p2.y - p1.y;
thisPoint.setTangent(dx, dy);
}
return this;
},
/**
* Draw the path on given canvas context. Used for debugging.
*
* @method Phaser.Path#debug
* @param {CanvasContext2D} ctx - The canvas context to draw the path on.
* @param {boolean} [active=false] - Whether or not to highlight the active segments of this Path or not.
* @return {Phaser.Path} This Path object.
*/
debug: function (ctx, active) {
var lineColor = '#333333';
if (active)
{
lineColor = '#ffff00';
}
if (this._points.length === 0)
{
return this;
}
this._p1.setTo(0, 0);
// Draw the lines
var lastPoint = this._points.length;
if (!this.loops)
{
lastPoint--;
}
var p = new Phaser.PathPoint();
for (var i = 0; i < lastPoint; i++)
{
var curve = this.getCurve(i);
this.getPathPoint(i, p);
ctx.lineWidth = 2;
ctx.strokeStyle = 'rgb(100, 0, 250)';
ctx.save();
// Control Points
var controlPoints = this.getControlPointsOnThisCurve(curve);
// Draw lines
ctx.beginPath();
controlPoints.forEach(function(pnt, index) {
if (!!pnt)
{
if (index === 0)
{
ctx.moveTo(pnt.x, pnt.y);
}
else
{
ctx.lineTo(pnt.x, pnt.y);
}
}
});
ctx.stroke();
ctx.closePath();
if (p.active)
{
ctx.fillStyle = '#ffffff';
ctx.strokeStyle = '#333333';
ctx.lineWidth = 1;
// Copy control points to the point object
this.getPathPointReference(i).controlPoints = controlPoints;
controlPoints.forEach(function(pnt) {
ctx.beginPath();
ctx.arc(pnt.x, pnt.y, 3, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.closePath();
});
}
ctx.restore();
}
return this;
},
/**
* Serializes this Path into a JSON object and returns it.
*
* @methods Phaser.Path#toJSON
* @return {Object} A JSON object representing this Path.
*/
toJSON: function () {
return {
name: this.name,
id: this.id,
type: this.type,
coordinateSystem: this.coordinateSystem,
loops: this.loops,
speed: 1,
pointList: this._points.map(function(p) {
return p.toJSON();
}),
};
}
};
/**
* @property {Array} - The list of PathPoints that make up this path.
* @readonly
*/
Object.defineProperty(Phaser.Path.prototype, 'points', {
get: function () {
return this._points;
}
});
/**
* @property {number} - The number of points in this path.
* @readonly
*/
Object.defineProperty(Phaser.Path.prototype, 'length', {
get: function () {
return this._points.length;
}
});
/**
* @property {Phaser.Point} - The origin of the path.
*/
Object.defineProperty(Phaser.Path.prototype, 'origin', {
get: function() {
return this._origin;
},
set: function (val) {
this._origin.setTo(val.x, val.y);
}
});

View file

@ -0,0 +1,536 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Pete Baron <pete@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* A PathFollower is a virtual entity that follows the Path.
* It is usually linked to a game object such as a Sprite and it will control either the
* position of that object, or its velocity if it is a physics object.
*
* Callbacks will be triggered when certain events happen as the follower moves. These
* may be used to aid in the creation of complex behaviours for the game objects.
*
* @class Phaser.PathFollower
* @constructor
* @param {Phaser.Path} path - The Path object which this follower is created on.
* @param {Phaser.Sprite|object} follower - The game object which this follower controls. Requires public properties: `x`, `y` for position and `rotation` for angle control (if specified).
* @param {number} [speed=1] - The current speed of this follower in pixels per frame. This value is multiplied with the Path segment speed to give the final value used.
* @param {number} [angleOffset=null] - If `null` then the PathFollower won't rotate. Otherwise it will face in the paths direction plus this offset which is given in radians.
* @param {function} [callbackAtEnd] - A callback to be invoked when the follower reaches the end of a path.
* @param {number} [physicsAdjustTime=0] - If non-zero then the follower expects to control a physics object using "arcade.moveToObject" to control velocity.
*/
Phaser.PathFollower = function (path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime) {
if (speed === undefined) { speed = 1; }
if (rotationOffset === undefined) { rotationOffset = 0; }
if (angularOffset === undefined) { angularOffset = { angle: 0, distance: 0 }; }
if (physicsAdjustTime === undefined) { physicsAdjustTime = 0; }
Phaser.EventTarget.call(this);
this.path = path;
this.follower = follower;
this._turnOffset = rotationOffset;
this.callbackAtEnd = callbackAtEnd;
this.physicsAdjustTime = physicsAdjustTime;
// offset is an x,y offset from the Path unique for this PathFollower, it is added to the Path's own offset to give a final location
this.offset = new Phaser.Point(0, 0);
if (typeof speed === 'object')
{
this.speed = Phaser.Utils.extend(true, Object.create(Phaser.PathFollower.Defaults.speed), speed);
}
else
{
this.speed = Object.create(Phaser.PathFollower.Defaults.speed);
this.speed.min = speed;
this.speed.max = speed;
}
// _angularOffset is an angular offset from the Path's tangent direction, using angle (radians) and distance (pixels)
this._angularOffset = { angle: 0, distance: 0 };
this.setAngularOffset(angularOffset.angle, angularOffset.distance);
// branchCount is used when the follower passes a counted PathPoint (see the Mummy Path example)
// it is set whenever this follower passes a counted PathPoint and the count is zero
// it decrements each time the follower passes a counted PathPoint and the count is non-zero
// when the count reaches zero it triggers EVENT_COUNT_FINISH
// NOTE: if there are multiple counted PathPoints this will not work as expected as there is only one count variable per follower!
this.branchCount = 0;
this.branchPredicate = null;
// distance along the current Path segment
this._currentDistance = 0;
// PathPoint index of the start of the current Path segment
this._currentPoint = 0;
// Hermite curve for the current Path segment
this._currentCurve = this.path.getCurve(this._currentPoint);
// initialise the _pathSpeed by taking the speed of the first point on this Path
var pp = new Phaser.PathPoint();
if (this.path.getPathPoint(0, pp))
{
this._pathSpeed = pp.speed;
}
// set up a virtualParticle if this is controlling a Physics body instead of a simple graphic object
if (this.physicsAdjustTime !== 0)
{
this.virtualParticle = new Phaser.Point(pp.x, pp.y);
}
else
{
this.virtualParticle = null;
}
// default maximum gap permitted between a physics based follower and its virtual particle, in pixels
this.maximumGap = 1000;
// process the data for the first point on this Path
this.path.processData(this, this._currentPoint, false);
// initialise the pause time to zero for this follower
this._pauseTime = 0;
this._accelerationTime = 0;
this.yoyo = false;
if (!follower.events)
{
follower.events = {};
}
follower.events.onPathPointReached = new Phaser.Signal(); // "follower has reached a PathPoint on the path"
follower.events.onPathBranchReached = new Phaser.Signal(); // "follower has reached a branch and must choose a direction" (stay on this path or changePath to the branch)
/* TODO: */
follower.events.onCountFinished = new Phaser.Signal(); // "follower passed a counted point the specified number of times" */
follower.events.onPathStart = new Phaser.Signal(); // NOTE: not "follower started a path" but "follower moved backwards to the start of the path"
follower.events.onPathYoyo = new Phaser.Signal(); // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
follower.events.onPathEnd = new Phaser.Signal(); // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
follower.events.onPathLoop = new Phaser.Signal(); // "follower reached the end of a looped path and has started at the beginning again"
follower.followerPathName = this.path.name;
Object.defineProperty(this.speed, 'avg', {
get: function() {
return (this.min + this.max) / 2;
}
});
};
// events for PathFollower
Phaser.PathFollower.EVENT_REACHED_POINT = "event_reached_point"; // "follower has reached a PathPoint on the path"
Phaser.PathFollower.EVENT_BRANCH_CHOICE = "event_branch_choice"; // "follower has reached a branch and must choose a direction" (stay on this path or changePath to the branch)
Phaser.PathFollower.EVENT_COUNT_FINISH = "event_count_finish"; // "follower passed a counted point the specified number of times"
Phaser.PathFollower.EVENT_PATH_START = "event_path_start"; // NOTE: "a path started" but "follower moved backwards to the start of the path"
Phaser.PathFollower.EVENT_PATH_END = "event_path_end"; // "follower moved to the end of the path" but NOT if the path is looped, that generates EVENT_PATH_LOOPED instead
Phaser.PathFollower.EVENT_PATH_LOOPED = "event_path_looped"; // "follower reached the end of a looped path and has started at the beginning again"
// reduce dynamic object allocations by using this temporary Point wherever possible
Phaser.PathFollower.tempPoint = new Phaser.Point();
Phaser.PathFollower.Defaults = {
speed: {
min: 1,
max: 1,
theta: null,
lambda: null,
_target: null,
_elapsed: 0,
_current: null,
_previous: null
}
};
// remove all event listeners when this PathFollower is destroyed
Phaser.PathFollower.prototype.destroy = function () {
this.follower.events.onPathPointReached.removeAll();
this.follower.events.onPathBranchReached.removeAll();
this.follower.events.onCountFinished.removeAll();
this.follower.events.onPathStart.removeAll();
this.follower.events.onPathEnd.removeAll();
this.follower.events.onPathLoop.removeAll();
};
// update this PathFollower and move the attached graphic or physics object
// @return: false if this PathFollower should be removed from the Path's list of followers
Phaser.PathFollower.prototype.update = function () {
// exit immediately if _pauseTime is non-zero and it's not that time yet
if (this._pauseTime != 0)
{
if (game.time.now < this._pauseTime)
{
return true;
}
this._pauseTime = 0;
if (this.follower.animations !== undefined)
{
// Phaser.AnimationManager doesn't check for a currentAnim before trying to set it's paused value, so I have to do it here
if (this.follower.animations.currentAnim)
{
this.follower.animations.paused = false;
}
}
}
// if the follower is a physics object following a virtual particle
var waitForFollower = false;
if (this.physicsAdjustTime && this.virtualParticle)
{
// if the distance is too great, make the virtual particle wait for the follower to catch up
if (game.physics.arcade.distanceBetween(this.follower, this.virtualParticle) >= this.maximumGap)
{
waitForFollower = true;
}
}
// advance along the path unless we're waiting for the follower to catch up
if (!waitForFollower)
{
this._currentDistance += this._calculateDistance();
}
// are we moving forwards or backwards?
var direction = (this.speed.avg * this._pathSpeed) >= 0 ? 1 : -1;
// while we're past either end of the current curve
while ((direction == 1 && this._currentDistance >= this._currentCurve.length) || (direction == -1 && this._currentDistance < 0))
{
var memCurveLength = this._currentCurve.length;
// backwards...
if (direction == -1)
{
var branchTaken = false;
// passed a point going backwards, process the data for it
var point = this.path.processData(this, this._currentPoint, true);
this.follower.events.onPathPointReached.dispatch(this.follower, point);
this.takeBranchIfAvailable();
this._currentPoint--;
// reached the start of the path moving backwards
if (this._currentPoint < 0)
{
if (this.path.loops)
{
this.follower.events.onPathLoop.dispatch(point);
this._currentPoint = this.path.numPoints() - 1;
}
else
{
if (!this.yoyo)
{
this.follower.events.onPathEnd.dispatch();
}
else
{
this.follower.events.onPathYoyo.dispatch();
var speed = {min: this.speed.min, max: this.speed.max};
this.speed.min = -speed.max;
this.speed.max = -speed.min;
this._currentPoint = 0;
this._currentCurve = this.path.getCurve(this._currentPoint);
this._currentDistance = 0;
return true;
}
}
}
if (!branchTaken)
{
// get the curve for this new point
this._currentCurve = this.path.getCurve(this._currentPoint);
// there isn't one, take a branch if there's one attached here
if (!this._currentCurve)
{
return this.takeBranchIfAvailable();
}
// move backwards to the end of the previous curve in the path
this._currentDistance += this._currentCurve.length;
}
}
else // forwards...
{
this._currentPoint++;
// reached the end of the path moving forwards
if (this.path.atEnd(this._currentPoint))
{
if (this.path.loops)
{
// the path loops
this.follower.events.onPathLoop.dispatch();
this._currentPoint = 0;
}
else
{
// if the path doesn't loop
if (!this.takeBranchIfAvailable())
{
if (!this.yoyo)
{
this.follower.events.onPathEnd.dispatch();
}
else
{
this.follower.events.onPathYoyo.dispatch();
var speed = {min: this.speed.min, max: this.speed.max};
this.speed.min = -speed.max;
this.speed.max = -speed.min;
this._currentPoint = this.path.length - 2;
this._currentCurve = this.path.getCurve(this._currentPoint);
this._currentDistance = this._currentCurve.length;
return true;
}
}
}
}
// passed a point going forwards, process the data for the next one
point = this.path.processData(this, this._currentPoint, false);
this.follower.events.onPathPointReached.dispatch(this.follower, point);
this.takeBranchIfAvailable();
// move forwards to the start of the next curve in the path
this._currentDistance -= memCurveLength;
// get the curve for this new point
this._currentCurve = this.path.getCurve(this._currentPoint);
// there isn't one, take a branch if there's one attached here
if (!this._currentCurve)
{
return this.takeBranchIfAvailable();
}
}
// update the path speed while we have a reference to the PathPoint handy
this._pathSpeed = point.speed;
}
return this.setPosition();
};
Phaser.PathFollower.prototype._calculateDistance = function () {
if (this.speed.min === this.speed.max)
{
return game.time.elapsed * this.speed.avg * this._pathSpeed;
}
else
{
this.speed._elapsed += game.time.elapsed;
this.speed._current = this.speed.current || this.speed.avg;
if (this.speed._elapsed >= this.speed.theta)
{
this.speed._current = this.speed._target;
this.speed._target = null;
this.speed._elapsed = 0;
}
if (!this.speed._target )
{
var min = Phaser.Math.clamp(this.speed._current - (this.speed._current * this.speed.lambda), this.speed.min, this.speed.max);
var max = Phaser.Math.clamp(this.speed._current + (this.speed._current * this.speed.lambda), this.speed.min, this.speed.max);
this.speed._target = game.rnd.realInRange(min, max);
}
var step = Phaser.Math.smoothstep(this.speed._elapsed,0,this.speed.theta);
return Phaser.Math.linear(this.speed._current, this.speed._target, step) * this._pathSpeed;;
}
};
// move the attached graphic or physics object to match this PathFollower
// @return: false if this PathFollower should be removed from the Path's list of followers
Phaser.PathFollower.prototype.setPosition = function () {
// if the follower object has been destroyed, kill this too
if (!this.follower)
{
return false;
}
this._currentCurve.getPointWithDistance(this._currentDistance, Phaser.PathFollower.tempPoint);
var ox = this.offset.x;
var oy = this.offset.y;
if (this._angularOffset.distance != 0)
{
var angle = (this.follower.rotation + this._angularOffset.angle);
ox += Math.cos(angle) * this._angularOffset.distance;
oy += Math.sin(angle) * this._angularOffset.distance;
}
if (this.physicsAdjustTime)
{
// move the virtual particle along the path
this.virtualParticle.x = Phaser.PathFollower.tempPoint.x + ox;
this.virtualParticle.y = Phaser.PathFollower.tempPoint.y + oy;
// move the physics body towards the virtual particle
if (this.follower.body)
{
game.physics.arcade.moveToObject(this.follower, this.virtualParticle, 100, this.physicsAdjustTime);
}
}
else
{
// move the follower along the path by directly adjusting it's x,y coordinates
this.follower.x = Phaser.PathFollower.tempPoint.x + ox;
this.follower.y = Phaser.PathFollower.tempPoint.y + oy;
}
// if this follower should turn to follow the path, and it has a rotation member
if (this._turnOffset !== undefined && this.follower.rotation !== undefined)
{
// turn to follow the path with a fixed offset of _turnOffset
this.follower.rotation = this._currentCurve.getAngleWithDistance(this._currentDistance) + this._turnOffset;
}
return true;
};
// if we've reached the end of a path or a branch, take any branch that is available rather than die
// @return: true if successful, false if no branch is available
Phaser.PathFollower.prototype.takeBranchIfAvailable = function () {
var p = new Phaser.PathPoint();
if (this.path.getPathPoint(this._currentPoint, p))
{
// kill this follower if there isn't a branch for us to take
if (!p.branchPath || !this.branchPredicate || !this.branchPredicate(p, this.path))
{
return false;
}
// changePath calls back to redo this function, exit after calling it
this.changePath(p.branchPath, p.branchPointIndex);
return true;
}
return false;
};
// follow a different path
Phaser.PathFollower.prototype.changePath = function (branchPath, branchPointIndex) {
// change to the new path
this.path = branchPath;
// get the speed of the new path
this._pathSpeed = this.path.getPathPointReference(0).speed;
// set my position on the new path
this._currentPoint = branchPointIndex;
// update the curve if we've moved past a Path point
this._currentCurve = this.path.getCurve(this._currentPoint);
// we've finished the path
if (!this._currentCurve)
{
return this.takeBranchIfAvailable();
}
// move me to the correct position on the new curve
this.setPosition();
};
// change this follower's x,y offset values
Phaser.PathFollower.prototype.setOffset = function (x, y) {
// remove any prior offset from the follower's position
this.follower.x -= this.offset.x;
this.follower.y -= this.offset.y;
// set the new offset for this PathFollower
this.offset.x = x;
this.offset.y = y;
// add the offset into the follower's position straight away
this.follower.x += this.offset.x;
this.follower.y += this.offset.y;
};
// set this follower's angular offset values
Phaser.PathFollower.prototype.setAngularOffset = function (angle, distance) {
this._angularOffset.angle = angle;
this._angularOffset.distance = distance;
};
// cause this follower to pause for 'delay' milliseconds
Phaser.PathFollower.prototype.pause = function (delay) {
this._pauseTime = game.time.now + delay;
if (this.follower.animations !== undefined)
{
if (this.follower.animations.currentAnim)
{
this.follower.animations.paused = true;
}
}
};
Object.defineProperty(Phaser.PathFollower.prototype, 'paused', {
get: function() {
return !!this._pauseTime;
},
set: function(val) {
if(!!val) {
this.pause(Number.MAX_VALUE);
} else {
this._pauseTime = game.time.now - 1;
}
},
enumerable: true,
configurable: true
});

View file

@ -0,0 +1,352 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Pete Baron <pete@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* PathManager controls a list of Paths and a list of PathFollowers.
* It is the central control for the majority of the Pathing API.
*
* @method PathManager
* @param {Phaser.Game} game - A reference to the Phaser Game instance.
* @param {[type]} parent - ?
*/
Phaser.Plugin.PathManager = function(game, parent) {
Phaser.Plugin.call(this, game, parent);
/**
* @property {array} _list - list of paths
* @private
*/
this._list = [];
/**
* @property {array} _followers - list of path followers
* @private
*/
this._followers = [];
this._branchRegistry = {};
};
Phaser.Plugin.PathManager.prototype = Object.create(Phaser.Plugin.prototype);
Phaser.Plugin.PathManager.prototype.constructor = Phaser.Plugin.PathManager;
/**
* create a new Path from JSON data
*
* JSON data format:
* required: "coordinateSystem":, "smoothness":, "loops":, "speed":, "pointList":[ {"x":, "y":}, ... ]
* optional: "branchFrom": { "path":, "point": }, "joinTo": { "path":, "point": }
*/
Phaser.Plugin.PathManager.prototype.createPathsFromJSON = function(jsonKey) {
var parse = this.game.cache.getJSON(jsonKey);
var path;
var createdPaths = [];
var branchList = [];
parse.paths.forEach(function(config) {
path = new Phaser.Path(config.coordinateSystem, config.loops);
path.name = config.name;
this.addPoints(path, config.pointList, config.speed);
this._list.push(path);
createdPaths.push(path);
config.pointList.reduce(function(list, pnt, index) {
if (pnt.branchType === Phaser.Path.BranchTypes.ATTACHED) {
list.push({
path: path.name,
branchPath: pnt.branchPath,
pointIndex: index,
type: pnt.branchType
});
} else if (pnt.branchType === Phaser.Path.BranchTypes.JOINED) {
list.push({
path: pnt.branchPath,
branchPath: path.name,
pointIndex: pnt.branchPointIndex,
type: pnt.branchType
});
}
return list;
}, branchList);
}, this);
branchList.forEach(function(branch) {
var mainPath = this.findPathByName(branch.path);
var branchPath = this.findPathByName(branch.branchPath);
var mainPathPointIndex = branch.pointIndex;
if (branch.type === Phaser.Path.BranchTypes.ATTACHED) {
this.attachBranch(branchPath, mainPath, mainPathPointIndex);
} else if (branch.type === Phaser.Path.BranchTypes.JOINED) {
this.joinBranch(branchPath, mainPath, mainPathPointIndex, false);
}
}, this);
return createdPaths;
};
Phaser.Plugin.PathManager.prototype.addPath = function(path) {
// if path has points then addPoints, otherwise don't
// this.addPoints(path, parse.pointList, parse.speed);
this._list.push(path);
return path;
};
// create a branching path and attach the start to an existing path
// when a PathFollower encounters the attachment point, it will be able to switch onto this new branch
//
// @param: count {value, optional}, make this branch counted (it won't be taken until a follower has passed it enough times)
Phaser.Plugin.PathManager.prototype.attachBranch = function(branchPath, mainPath, mainPathPointIndex, count) {
if (typeof mainPath === 'string') {
mainPath = this.findPathByName(mainPath);
}
var branchFromPoint = new Phaser.PathPoint();
if (mainPath.getPathPoint(mainPathPointIndex, branchFromPoint)) {
// move the first point of the branchPath to the branchFromPoint location
branchPath.origin = branchFromPoint;
var branchToPoint = branchPath.getPathPointReference(0);
// attach the branch (use point reference so the changes go into the path)
var branchFromPointRef = mainPath.getPathPointReference(mainPathPointIndex);
this._branchAttach(branchFromPointRef, branchPath, 0);
branchFromPointRef.branchType = Phaser.Path.BranchTypes.ATTACHED;
// attach the branch's first point back to where it branched off from (for path reversal)
branchToPoint.branchPath = mainPath;
branchToPoint.branchPointIndex = mainPathPointIndex;
// make sure this branch knows that it's using offset coordinates based on the first path point location
branchPath.coordinateSystem = Phaser.Path.CoordinateSystems.OFFSET;
branchPath.type = Phaser.Path.PathTypes.BRANCH;
// set up counted branches data
if (count !== undefined) {
branchFromPointRef.data = {
type: Phaser.PathPoint.DATA_COUNTER,
value: count
};
}
if (this._branchRegistry[branchPath.name]) {
this._branchRegistry[branchPath.name].push(branchFromPointRef);
} else {
this._branchRegistry[branchPath.name] = [branchFromPointRef];
}
}
};
// attach the end of a path to an existing path
// when a PathFollower encounters the attachment point, it will automatically switch onto the attached path
Phaser.Plugin.PathManager.prototype.joinBranch = function(branchPath, mainPath, mainPathPointIndex, addPoint) {
if (typeof addPoint === 'undefined') {
addPoint = true;
}
if (typeof mainPath === 'string') {
mainPath = this.findPathByName(mainPath);
}
var mainPathJoinPoint, branchLastPoint;
mainPathJoinPoint = new Phaser.PathPoint();
mainPath.getPathPoint(mainPathPointIndex, mainPathJoinPoint);
if (mainPathJoinPoint) {
if (addPoint) {
var newBranchPoint = new Phaser.PathPoint();
if (branchPath.getPathPoint(0, newBranchPoint)) {
// make sure the newly added last path point is relative to the previously added first path point for the branch path by subtracting it out
branchLastPoint = branchPath.addPathPoint(mainPathJoinPoint.x - newBranchPoint.x, mainPathJoinPoint.y - newBranchPoint.y, mainPathJoinPoint.vx, mainPathJoinPoint.vy, 1.0);
this._branchAttach(branchLastPoint, mainPath, mainPathPointIndex);
}
} else {
branchLastPoint = branchPath.getPathPointReference(branchPath.length - 1);
this._branchAttach(branchLastPoint, mainPath, mainPathPointIndex);
}
branchLastPoint.branchType = Phaser.Path.BranchTypes.JOINED;
}
if (this._branchRegistry[branchPath.name]) {
this._branchRegistry[branchPath.name].push(branchLastPoint);
} else {
this._branchRegistry[branchPath.name] = [branchLastPoint];
}
};
// internal function, set the branching parameters of a PathPoint
Phaser.Plugin.PathManager.prototype._branchAttach = function(attachPoint, branchingPath, branchToPointIndex) {
attachPoint.branchPath = branchingPath;
attachPoint.branchPointIndex = branchToPointIndex;
};
Phaser.Plugin.PathManager.prototype._branchDetach = function(attachedPoint) {
attachedPoint.branchPath = null;
attachedPoint.branchPointIndex = null;
};
Phaser.Plugin.PathManager.prototype.removeBranch = function(branch) {
if (typeof branch === 'string') {
branch = this.findPathByName(branch);
}
this._branchRegistry[branch.name].forEach(function(point) {
this._branchDetach(point);
}, this);
this._branchRegistry[branch.name] = null;
this.removePath(this.pathIndex(branch));
};
// @return: the Path object which is at 'index' in the list
Phaser.Plugin.PathManager.prototype.getPath = function(index) {
return this._list[index];
};
// add a list of points to a Path
Phaser.Plugin.PathManager.prototype.addPoints = function(path, pointList, speed) {
if (speed === undefined) speed = 1.0;
for (var i = 0; i < pointList.length; i++) {
path.addPathPoint(pointList[i].x, pointList[i].y, pointList[i].vx, pointList[i].vy, speed, pointList[i].data);
}
return path.numPoints();
};
// @return: the Path object matching 'name' in the list
Phaser.Plugin.PathManager.prototype.findPathByName = function(name) {
for (var i = 0; i < this._list.length; i++) {
if (this._list[i].name == name) {
return this._list[i];
}
}
return null;
};
Phaser.Plugin.PathManager.prototype.findPathByPoint = function(point) {
var l = this._list.length;
for (var i = 0; i < l; i++) {
if (this._list[i].pointIndex(point) > -1) {
return this._list[i];
}
}
};
/*
* FOLLOWERS
*
* the following functions control PathFollower objects
*
*/
// create a new PathFollower and add it to the list
// @param: physicsAdjustTime - how quickly does a physics object attempt to get back to the path's virtual particle position (milliseconds), 0 = it's not a physics object
// @return: the new PathFollower object
Phaser.Plugin.PathManager.prototype.addFollower = function(path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime) {
var f = new Phaser.PathFollower(path, follower, speed, rotationOffset, angularOffset, callbackAtEnd, physicsAdjustTime);
this._followers.push(f);
return f;
};
// update all PathFollower objects in the _followers list
// this will automatically move them along the Paths
// was called updateFollowers
Phaser.Plugin.PathManager.prototype.update = function() {
// move this to a plugin var
//var elapsedTime = 1.0;
for (var i = this._followers.length - 1; i >= 0; --i) {
var f = this._followers[i];
// when a follower's update returns false, kill it
if (!f.update(this.game.time.elpased)) {
// callback for this follower when it dies
if (f.callbackAtEnd) {
f.callbackAtEnd(f.follower);
}
// destroy the follower
f.destroy();
// remove the follower from the list
this._followers.splice(i, 1);
}
}
};
// remove all PathFollowers on this path without destroying their attached graphic objects
// (eg. a long line of enemies use a path to enter, then switch to AI control on arrival maintaining their relative positions)
Phaser.Plugin.PathManager.prototype.removeAllFollowers = function(path) {
for (var i = this._followers.length - 1; i >= 0; --i) {
var f = this._followers[i];
if (f.path == path) {
// callback for this follower when it dies
if (f.callbackAtEnd) {
f.callbackAtEnd(f.follower);
}
// destroy the follower
f.destroy();
// remove the follower from the list
this._followers.splice(i, 1);
}
}
};
Phaser.Plugin.PathManager.prototype.pathIndex = function(path) {
return this._list.indexOf(path);
};
Phaser.Plugin.PathManager.prototype.removePath = function(pathIndex) {
this.removeAllFollowers(this.getPath(pathIndex));
if (pathIndex < this._list.length) {
return this._list.splice(pathIndex, 1);
} else {
throw new Error("ERROR: Cannot remove non-existent path");
}
};
/*
* DEBUG DRAWING OF ALL PATHS
*/
// draw all paths
Phaser.Plugin.PathManager.prototype.drawPaths = function(graphics) {
for (var i = 0; i < this._list.length; i++) {
this._list[i].debug(graphics);
}
};

View file

@ -0,0 +1,258 @@
/**
* @author Richard Davey <rich@photonstorm.com>
* @author Pete Baron <pete@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
/**
* The PathPoint class contains data and functions for each point on a Path.
*
* @class Phaser.PathPoint
* @constructor
* @param {number} x - The x coordinate of the PathPoint.
* @param {number} y - The y coordinate of the PathPoint.
* @param {number} vx - The x coordinate of the tangent vector to create the curve from.
* @param {number} vy - The y coordinate of the tangent vector to create the curve from.
* @param {number} [speed=1] - The speed multiplier for PathFollowers on this Path segment.
* @param {object} [data] - The data associated with this point, e.g.: { type: PathPoint.DATA_VALUE, value: XXX }
* @param {Phaser.Path} [branchPath] - A branched path which is attached to this point.
* @param {number} [branchPointIndex] - The index where the branch is attached to on the new path.
*/
Phaser.PathPoint = function (x, y, vx, vy, speed, data, branchPath, branchPointIndex) {
if (speed === undefined) { speed = 1; }
if (data === undefined) { data = { type: 0, value: 0 }; }
if (branchPath === undefined) { branchPath = null; }
if (branchPointIndex === undefined) { branchPointIndex = 0; }
/**
* @property {number} x - The x coordinate of the PathPoint.
*/
this.x = x;
/**
* @property {number} y - The y coordinate of the PathPoint.
*/
this.y = y;
/**
* @property {number} vx - The x coordinate of the tangent vector to create the curve from.
*/
this.vx = vx;
/**
* @property {number} vy - The y coordinate of the tangent vector to create the curve from.
*/
this.vy = vy;
/**
* @property {number} speed - The speed multiplier for PathFollowers on this path segment.
*/
this.speed = speed;
/**
* @property {object} data - Data associated with this point eg: { type: PathPoint.DATA_VALUE, value: XXX }
*/
this.data = data;
/**
* @property {Phaser.Path} branchPath - A branched path which is attached at this point.
*/
this.branchPath = branchPath;
/**
* @property {number} branchPointIndex - The index where the branch is attached to on the new path.
*/
this.branchPointIndex = branchPointIndex;
/**
* @property {number} branchType - The branch type of the path this point is on. Either 0 (attached) or 1 (joined)
*/
this.branchType = 0;
/**
* @property {number} curve - Once the Hermite curve is calculated, store it to avoid recalculation later.
* @protected
*/
this.curve = null;
/**
* @property {boolean} active - Is this point a selected (or active) point?
* @warn For Path Editor use only
*/
this.active = false;
/**
* @property {array} controlPoints - A list of Phaser.Point objects representing the control points on the segment.
* @warn For Path Editor use only
*/
this.controlPoints = null;
};
Phaser.PathPoint.prototype.constructor = Phaser.PathPoint;
/**
* @constant
* @type {number}
*/
Phaser.PathPoint.DATA_NONE = 0;
/**
* @constant
* @type {number}
*/
Phaser.PathPoint.DATA_PAUSE = 1;
/**
* @constant
* @type {number}
*/
Phaser.PathPoint.DATA_VALUE = 2;
/**
* @constant
* @type {number}
*/
Phaser.PathPoint.DATA_COUNTER = 3;
Phaser.PathPoint.prototype = {
/**
* Sets the x, y and optionally vx and vy properties of this PathPoint.
*
* @method Phaser.PathPoint#setTo
* @param {number} x - The x coordinate of the PathPoint.
* @param {number} y - The y coordinate of the PathPoint.
* @param {number} [vx] - The x coordinate of the tangent vector to create the curve from.
* @param {number} [vy] - The y coordinate of the tangent vector to create the curve from.
* @return {Phaser.PathPoint} This object.
*/
setTo: function (x, y, vx, vy) {
this.x = x;
this.y = y;
if (vx !== undefined)
{
this.vx = vx;
}
if (vy !== undefined)
{
this.vy = vy;
}
// Invalidate the pre-calculated curve to force it to recalculate with these new settings
this.curve = null;
return this;
},
/**
* Sets the tangent vector properties of this PathPoint.
*
* @method Phaser.PathPoint#setTangent
* @param {number} vx - The x coordinate of the tangent vector to create the curve from.
* @param {number} vy - The y coordinate of the tangent vector to create the curve from.
* @return {Phaser.PathPoint} This object.
*/
setTangent: function (vx, vy) {
this.vx = vx;
this.vy = vy;
// Invalidate the pre-calculated curve to force it to recalculate with these new settings
this.curve = null;
return this;
},
/**
* Creates a clone of this PathPoint object.
*
* @method Phaser.PathPoint#clone
* @param {Phaser.PathPoint} [out] - An optional PathPoint object into which this object is cloned. If no object is provided a new PathPoint is created.
* @return {Phaser.PathPoint} A clone of this PathPoint.
*/
clone: function (out) {
if (out === undefined) { out = new Phaser.PathPoint(); }
return out.copy(this);
},
/**
* Copies all of the values from the given PathPoint object into this PathPoint.
* The source PathPoint is untouched by this operation.
*
* @method Phaser.PathPoint#copy
* @param {Phaser.PathPoint} source - The PathPoint object to copy the values from.
* @return {Phaser.PathPoint} This PathPoint object.
*/
copy: function (source) {
this.x = source.x;
this.y = source.y;
this.vx = source.vx;
this.vy = source.vy;
this.speed = source.speed;
this.data = source.data;
this.branchPath = source.branchPath;
this.branchPointIndex = source.branchPointIndex;
this.curve = null;
this.active = source.active;
return this;
},
/**
* Compare this PathPoint with another PathPoint object and return `true`
* if they have the same `x`, `y` and `speed` properties, after taking the optional
* offset values into consideration.
*
* @method Phaser.PathPoint#equals
* @param {Phaser.PathPoint} pathPoint - The PathPoint to compare against this PathPoint.
* @param {number} [offsetX=0] - A value to apply to the x coordinate before comparison.
* @param {number} [offsetY=0] - A value to apply to the y coordinate before comparison.
* @return {boolean} True if the two PathPoint objects match, after the offsets are applied, or false if they don't.
*/
equals: function (pathPoint, offsetX, offsetY) {
if (offsetX === undefined) { offsetX = 0; }
if (offsetY === undefined) { offsetY = 0; }
return (this.x === pathPoint.x + offsetX &&
this.y === pathPoint.y + offsetY &&
this.speed === pathPoint.speed);
},
/**
* Serializes this PathPoint into a JSON object and returns it.
*
* @method Phaser.PathPoint#toJSON
* @return {Object} A JSON object representing this PathPoint.
*/
toJSON: function () {
return {
x: this.x,
y: this.y,
vx: this.vx,
vy: this.vy,
speed: this.speed,
data: this.data,
branchPath: !!this.branchPath ? this.branchPath.name : null,
branchPointIndex: this.branchPointIndex,
branchType: this.branchType
};
}
};