/** * Phaser - GameMath * * @desc Adds a set of extra Math functions and extends a few commonly used ones. * Includes methods written by Dylan Engelman and Adam Saltsman. * * @version 1.0 - 17th March 2013 * @author Richard Davey */ var GameMath = (function () { function GameMath(game) { /** * The global random number generator seed (for deterministic behavior in recordings and saves). */ this.globalSeed = Math.random(); this._game = game; } GameMath.PI = 3.141592653589793; GameMath.PI_2 = 1.5707963267948965; GameMath.PI_4 = 0.7853981633974483; GameMath.PI_8 = 0.39269908169872413; GameMath.PI_16 = 0.19634954084936206; GameMath.TWO_PI = 6.283185307179586; GameMath.THREE_PI_2 = 4.7123889803846895; GameMath.E = 2.71828182845905; GameMath.LN10 = 2.302585092994046; GameMath.LN2 = 0.6931471805599453; GameMath.LOG10E = 0.4342944819032518; GameMath.LOG2E = 1.442695040888963387; GameMath.SQRT1_2 = 0.7071067811865476; GameMath.SQRT2 = 1.4142135623730951; GameMath.DEG_TO_RAD = 0.017453292519943294444444444444444; GameMath.RAD_TO_DEG = 57.295779513082325225835265587527; GameMath.B_16 = 65536; GameMath.B_31 = 2147483648; GameMath.B_32 = 4294967296; GameMath.B_48 = 281474976710656; GameMath.B_53 = 9007199254740992; GameMath.B_64 = 18446744073709551616; GameMath.ONE_THIRD = 0.333333333333333333333333333333333; GameMath.TWO_THIRDS = 0.666666666666666666666666666666666; GameMath.ONE_SIXTH = 0.166666666666666666666666666666666; GameMath.COS_PI_3 = 0.86602540378443864676372317075294; GameMath.SIN_2PI_3 = 0.03654595; GameMath.CIRCLE_ALPHA = 0.5522847498307933984022516322796; GameMath.ON = true; GameMath.OFF = false; GameMath.SHORT_EPSILON = 0.1; GameMath.PERC_EPSILON = 0.001; GameMath.EPSILON = 0.0001; GameMath.LONG_EPSILON = 0.00000001; GameMath.prototype.computeMachineEpsilon = //arbitrary 8 digit epsilon function () { // Machine epsilon ala Eispack var fourThirds = 4.0 / 3.0; var third = fourThirds - 1.0; var one = third + third + third; return Math.abs(1.0 - one); }; GameMath.prototype.fuzzyEqual = function (a, b, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return Math.abs(a - b) < epsilon; }; GameMath.prototype.fuzzyLessThan = function (a, b, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return a < b + epsilon; }; GameMath.prototype.fuzzyGreaterThan = function (a, b, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return a > b - epsilon; }; GameMath.prototype.fuzzyCeil = function (val, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return Math.ceil(val - epsilon); }; GameMath.prototype.fuzzyFloor = function (val, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return Math.floor(val + epsilon); }; GameMath.prototype.average = function () { var args = []; for (var _i = 0; _i < (arguments.length - 0); _i++) { args[_i] = arguments[_i + 0]; } var avg = 0; for(var i = 0; i < args.length; i++) { avg += args[i]; } return avg / args.length; }; GameMath.prototype.slam = function (value, target, epsilon) { if (typeof epsilon === "undefined") { epsilon = 0.0001; } return (Math.abs(value - target) < epsilon) ? target : value; }; GameMath.prototype.percentageMinMax = /** * ratio of value to a range */ function (val, max, min) { if (typeof min === "undefined") { min = 0; } val -= min; max -= min; if(!max) { return 0; } else { return val / max; } }; GameMath.prototype.sign = /** * a value representing the sign of the value. * -1 for negative, +1 for positive, 0 if value is 0 */ function (n) { if(n) { return n / Math.abs(n); } else { return 0; } }; GameMath.prototype.truncate = function (n) { return (n > 0) ? Math.floor(n) : Math.ceil(n); }; GameMath.prototype.shear = function (n) { return n % 1; }; GameMath.prototype.wrap = /** * wrap a value around a range, similar to modulus with a floating minimum */ function (val, max, min) { if (typeof min === "undefined") { min = 0; } val -= min; max -= min; if(max == 0) { return min; } val %= max; val += min; while(val < min) { val += max; } return val; }; GameMath.prototype.arithWrap = /** * arithmetic version of wrap... need to decide which is more efficient */ function (value, max, min) { if (typeof min === "undefined") { min = 0; } max -= min; if(max == 0) { return min; } return value - max * Math.floor((value - min) / max); }; GameMath.prototype.clamp = /** * force a value within the boundaries of two values * * if max < min, min is returned */ function (input, max, min) { if (typeof min === "undefined") { min = 0; } return Math.max(min, Math.min(max, input)); }; GameMath.prototype.snapTo = /** * Snap a value to nearest grid slice, using rounding. * * example if you have an interval gap of 5 and a position of 12... you will snap to 10. Where as 14 will snap to 15 * * @param input - the value to snap * @param gap - the interval gap of the grid * @param start - optional starting offset for gap */ function (input, gap, start) { if (typeof start === "undefined") { start = 0; } if(gap == 0) { return input; } input -= start; input = gap * Math.round(input / gap); return start + input; }; GameMath.prototype.snapToFloor = /** * Snap a value to nearest grid slice, using floor. * * example if you have an interval gap of 5 and a position of 12... you will snap to 10. As will 14 snap to 10... but 16 will snap to 15 * * @param input - the value to snap * @param gap - the interval gap of the grid * @param start - optional starting offset for gap */ function (input, gap, start) { if (typeof start === "undefined") { start = 0; } if(gap == 0) { return input; } input -= start; input = gap * Math.floor(input / gap); return start + input; }; GameMath.prototype.snapToCeil = /** * Snap a value to nearest grid slice, using ceil. * * example if you have an interval gap of 5 and a position of 12... you will snap to 15. As will 14 will snap to 15... but 16 will snap to 20 * * @param input - the value to snap * @param gap - the interval gap of the grid * @param start - optional starting offset for gap */ function (input, gap, start) { if (typeof start === "undefined") { start = 0; } if(gap == 0) { return input; } input -= start; input = gap * Math.ceil(input / gap); return start + input; }; GameMath.prototype.snapToInArray = /** * Snaps a value to the nearest value in an array. */ function (input, arr, sort) { if (typeof sort === "undefined") { sort = true; } if(sort) { arr.sort(); } if(input < arr[0]) { return arr[0]; } var i = 1; while(arr[i] < input) { i++; } var low = arr[i - 1]; var high = (i < arr.length) ? arr[i] : Number.POSITIVE_INFINITY; return ((high - input) <= (input - low)) ? high : low; }; GameMath.prototype.roundTo = /** * roundTo some place comparative to a 'base', default is 10 for decimal place * * 'place' is represented by the power applied to 'base' to get that place * * @param value - the value to round * @param place - the place to round to * @param base - the base to round in... default is 10 for decimal * * e.g. * * 2000/7 ~= 285.714285714285714285714 ~= (bin)100011101.1011011011011011 * * roundTo(2000/7,3) == 0 * roundTo(2000/7,2) == 300 * roundTo(2000/7,1) == 290 * roundTo(2000/7,0) == 286 * roundTo(2000/7,-1) == 285.7 * roundTo(2000/7,-2) == 285.71 * roundTo(2000/7,-3) == 285.714 * roundTo(2000/7,-4) == 285.7143 * roundTo(2000/7,-5) == 285.71429 * * roundTo(2000/7,3,2) == 288 -- 100100000 * roundTo(2000/7,2,2) == 284 -- 100011100 * roundTo(2000/7,1,2) == 286 -- 100011110 * roundTo(2000/7,0,2) == 286 -- 100011110 * roundTo(2000/7,-1,2) == 285.5 -- 100011101.1 * roundTo(2000/7,-2,2) == 285.75 -- 100011101.11 * roundTo(2000/7,-3,2) == 285.75 -- 100011101.11 * roundTo(2000/7,-4,2) == 285.6875 -- 100011101.1011 * roundTo(2000/7,-5,2) == 285.71875 -- 100011101.10111 * * note what occurs when we round to the 3rd space (8ths place), 100100000, this is to be assumed * because we are rounding 100011.1011011011011011 which rounds up. */ function (value, place, base) { if (typeof place === "undefined") { place = 0; } if (typeof base === "undefined") { base = 10; } var p = Math.pow(base, -place); return Math.round(value * p) / p; }; GameMath.prototype.floorTo = function (value, place, base) { if (typeof place === "undefined") { place = 0; } if (typeof base === "undefined") { base = 10; } var p = Math.pow(base, -place); return Math.floor(value * p) / p; }; GameMath.prototype.ceilTo = function (value, place, base) { if (typeof place === "undefined") { place = 0; } if (typeof base === "undefined") { base = 10; } var p = Math.pow(base, -place); return Math.ceil(value * p) / p; }; GameMath.prototype.interpolateFloat = /** * a one dimensional linear interpolation of a value. */ function (a, b, weight) { return (b - a) * weight + a; }; GameMath.prototype.radiansToDegrees = /** * convert radians to degrees */ function (angle) { return angle * GameMath.RAD_TO_DEG; }; GameMath.prototype.degreesToRadians = /** * convert degrees to radians */ function (angle) { return angle * GameMath.DEG_TO_RAD; }; GameMath.prototype.angleBetween = /** * Find the angle of a segment from (x1, y1) -> (x2, y2 ) */ function (x1, y1, x2, y2) { return Math.atan2(y2 - y1, x2 - x1); }; GameMath.prototype.normalizeAngle = /** * set an angle with in the bounds of -PI to PI */ function (angle, radians) { if (typeof radians === "undefined") { radians = true; } var rd = (radians) ? GameMath.PI : 180; return this.wrap(angle, rd, -rd); }; GameMath.prototype.nearestAngleBetween = /** * closest angle between two angles from a1 to a2 * absolute value the return for exact angle */ function (a1, a2, radians) { if (typeof radians === "undefined") { radians = true; } var rd = (radians) ? GameMath.PI : 180; a1 = this.normalizeAngle(a1, radians); a2 = this.normalizeAngle(a2, radians); if(a1 < -rd / 2 && a2 > rd / 2) { a1 += rd * 2; } if(a2 < -rd / 2 && a1 > rd / 2) { a2 += rd * 2; } return a2 - a1; }; GameMath.prototype.normalizeAngleToAnother = /** * normalizes independent and then sets dep to the nearest value respective to independent * * for instance if dep=-170 and ind=170 then 190 will be returned as an alternative to -170 */ function (dep, ind, radians) { if (typeof radians === "undefined") { radians = true; } return ind + this.nearestAngleBetween(ind, dep, radians); }; GameMath.prototype.normalizeAngleAfterAnother = /** * normalize independent and dependent and then set dependent to an angle relative to 'after/clockwise' independent * * for instance dep=-170 and ind=170, then 190 will be reutrned as alternative to -170 */ function (dep, ind, radians) { if (typeof radians === "undefined") { radians = true; } dep = this.normalizeAngle(dep - ind, radians); return ind + dep; }; GameMath.prototype.normalizeAngleBeforeAnother = /** * normalizes indendent and dependent and then sets dependent to an angle relative to 'before/counterclockwise' independent * * for instance dep = 190 and ind = 170, then -170 will be returned as an alternative to 190 */ function (dep, ind, radians) { if (typeof radians === "undefined") { radians = true; } dep = this.normalizeAngle(ind - dep, radians); return ind - dep; }; GameMath.prototype.interpolateAngles = /** * interpolate across the shortest arc between two angles */ function (a1, a2, weight, radians, ease) { if (typeof radians === "undefined") { radians = true; } if (typeof ease === "undefined") { ease = null; } a1 = this.normalizeAngle(a1, radians); a2 = this.normalizeAngleToAnother(a2, a1, radians); return (typeof ease === 'function') ? ease(weight, a1, a2 - a1, 1) : this.interpolateFloat(a1, a2, weight); }; GameMath.prototype.logBaseOf = /** * Compute the logarithm of any value of any base * * a logarithm is the exponent that some constant (base) would have to be raised to * to be equal to value. * * i.e. * 4 ^ x = 16 * can be rewritten as to solve for x * logB4(16) = x * which with this function would be * LoDMath.logBaseOf(16,4) * * which would return 2, because 4^2 = 16 */ function (value, base) { return Math.log(value) / Math.log(base); }; GameMath.prototype.GCD = /** * Greatest Common Denominator using Euclid's algorithm */ function (m, n) { var r; //make sure positive, GCD is always positive m = Math.abs(m); n = Math.abs(n); //m must be >= n if(m < n) { r = m; m = n; n = r; } //now start loop while(true) { r = m % n; if(!r) { return n; } m = n; n = r; } return 1; }; GameMath.prototype.LCM = /** * Lowest Common Multiple */ function (m, n) { return (m * n) / this.GCD(m, n); }; GameMath.prototype.factorial = /** * Factorial - N! * * simple product series * * by definition: * 0! == 1 */ function (value) { if(value == 0) { return 1; } var res = value; while(--value) { res *= value; } return res; }; GameMath.prototype.gammaFunction = /** * gamma function * * defined: gamma(N) == (N - 1)! */ function (value) { return this.factorial(value - 1); }; GameMath.prototype.fallingFactorial = /** * falling factorial * * defined: (N)! / (N - x)! * * written subscript: (N)x OR (base)exp */ function (base, exp) { return this.factorial(base) / this.factorial(base - exp); }; GameMath.prototype.risingFactorial = /** * rising factorial * * defined: (N + x - 1)! / (N - 1)! * * written superscript N^(x) OR base^(exp) */ function (base, exp) { //expanded from gammaFunction for speed return this.factorial(base + exp - 1) / this.factorial(base - 1); }; GameMath.prototype.binCoef = /** * binomial coefficient * * defined: N! / (k!(N-k)!) * reduced: N! / (N-k)! == (N)k (fallingfactorial) * reduced: (N)k / k! */ function (n, k) { return this.fallingFactorial(n, k) / this.factorial(k); }; GameMath.prototype.risingBinCoef = /** * rising binomial coefficient * * as one can notice in the analysis of binCoef(...) that * binCoef is the (N)k divided by k!. Similarly rising binCoef * is merely N^(k) / k! */ function (n, k) { return this.risingFactorial(n, k) / this.factorial(k); }; GameMath.prototype.chanceRoll = /** * Generate a random boolean result based on the chance value *
* Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed. *
* @param chance The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%) * @return true if the roll passed, or false */ function (chance) { if (typeof chance === "undefined") { chance = 50; } if(chance <= 0) { return false; } else if(chance >= 100) { return true; } else { if(Math.random() * 100 >= chance) { return false; } else { return true; } } }; GameMath.prototype.maxAdd = /** * Adds the given amount to the value, but never lets the value go over the specified maximum * * @param value The value to add the amount to * @param amount The amount to add to the value * @param max The maximum the value is allowed to be * @return The new value */ function (value, amount, max) { value += amount; if(value > max) { value = max; } return value; }; GameMath.prototype.minSub = /** * Subtracts the given amount from the value, but never lets the value go below the specified minimum * * @param value The base value * @param amount The amount to subtract from the base value * @param min The minimum the value is allowed to be * @return The new value */ function (value, amount, min) { value -= amount; if(value < min) { value = min; } return value; }; GameMath.prototype.wrapValue = /** * Adds value to amount and ensures that the result always stays between 0 and max, by wrapping the value around. *Values must be positive integers, and are passed through Math.abs
* * @param value The value to add the amount to * @param amount The amount to add to the value * @param max The maximum the value is allowed to be * @return The wrapped value */ function (value, amount, max) { var diff; value = Math.abs(value); amount = Math.abs(amount); max = Math.abs(max); diff = (value + amount) % max; return diff; }; GameMath.prototype.randomSign = /** * Randomly returns either a 1 or -1 * * @return 1 or -1 */ function () { return (Math.random() > 0.5) ? 1 : -1; }; GameMath.prototype.isOdd = /** * Returns true if the number given is odd. * * @param n The number to check * * @return True if the given number is odd. False if the given number is even. */ function (n) { if(n & 1) { return true; } else { return false; } }; GameMath.prototype.isEven = /** * Returns true if the number given is even. * * @param n The number to check * * @return True if the given number is even. False if the given number is odd. */ function (n) { if(n & 1) { return false; } else { return true; } }; GameMath.prototype.wrapAngle = /** * Keeps an angle value between -180 and +180Number between 0 and 1.
*/
function () {
return this.globalSeed = this.srand(this.globalSeed);
};
GameMath.prototype.srand = /**
* Generates a random number based on the seed provided.
*
* @param Seed A number between 0 and 1, used to generate a predictable random number (very optional).
*
* @return A Number between 0 and 1.
*/
function (Seed) {
return ((69621 * (Seed * 0x7FFFFFFF)) % 0x7FFFFFFF) / 0x7FFFFFFF;
};
GameMath.prototype.getRandom = /**
* Fetch a random entry from the given array.
* Will return null if random selection is missing, or array has no entries.
* FlxG.getRandom() is deterministic and safe for use with replays/recordings.
* HOWEVER, FlxU.getRandom() is NOT deterministic and unsafe for use with replays/recordings.
*
* @param Objects An array of objects.
* @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param Length Optional restriction on the number of values you want to randomly select from.
*
* @return The random object that was selected.
*/
function (Objects, StartIndex, Length) {
if (typeof StartIndex === "undefined") { StartIndex = 0; }
if (typeof Length === "undefined") { Length = 0; }
if(Objects != null) {
var l = Length;
if((l == 0) || (l > Objects.length - StartIndex)) {
l = Objects.length - StartIndex;
}
if(l > 0) {
return Objects[StartIndex + Math.floor(Math.random() * l)];
}
}
return null;
};
GameMath.prototype.floor = /**
* Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2.
*
* @param Value Any number.
*
* @return The rounded value of that number.
*/
function (Value) {
var n = Value | 0;
return (Value > 0) ? (n) : ((n != Value) ? (n - 1) : (n));
};
GameMath.prototype.ceil = /**
* Round up to the next whole number. E.g. ceil(1.3) == 2, and ceil(-2.3) == -3.
*
* @param Value Any number.
*
* @return The rounded value of that number.
*/
function (Value) {
var n = Value | 0;
return (Value > 0) ? ((n != Value) ? (n + 1) : (n)) : (n);
};
return GameMath;
})();
/**
* Point
*
* @desc The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
*
* @version 1.2 - 27th February 2013
* @author Richard Davey
* @todo polar, interpolate
*/
var Point = (function () {
/**
* Creates a new point. If you pass no parameters to this method, a point is created at (0,0).
* @class Point
* @constructor
* @param {Number} x One-liner. Default is ?.
* @param {Number} y One-liner. Default is ?.
**/
function Point(x, y) {
if (typeof x === "undefined") { x = 0; }
if (typeof y === "undefined") { y = 0; }
this.setTo(x, y);
}
Point.prototype.add = /**
* Adds the coordinates of another point to the coordinates of this point to create a new point.
* @method add
* @param {Point} point - The point to be added.
* @return {Point} The new Point object.
**/
function (toAdd, output) {
if (typeof output === "undefined") { output = new Point(); }
return output.setTo(this.x + toAdd.x, this.y + toAdd.y);
};
Point.prototype.addTo = /**
* Adds the given values to the coordinates of this point and returns it
* @method addTo
* @param {Number} x - The amount to add to the x value of the point
* @param {Number} y - The amount to add to the x value of the point
* @return {Point} This Point object.
**/
function (x, y) {
if (typeof x === "undefined") { x = 0; }
if (typeof y === "undefined") { y = 0; }
return this.setTo(this.x + x, this.y + y);
};
Point.prototype.subtractFrom = /**
* Adds the given values to the coordinates of this point and returns it
* @method addTo
* @param {Number} x - The amount to add to the x value of the point
* @param {Number} y - The amount to add to the x value of the point
* @return {Point} This Point object.
**/
function (x, y) {
if (typeof x === "undefined") { x = 0; }
if (typeof y === "undefined") { y = 0; }
return this.setTo(this.x - x, this.y - y);
};
Point.prototype.invert = /**
* Inverts the x and y values of this point
* @method invert
* @return {Point} This Point object.
**/
function () {
return this.setTo(this.y, this.x);
};
Point.prototype.clamp = /**
* Clamps this Point object to be between the given min and max
* @method clamp
* @param {number} The minimum value to clamp this Point to
* @param {number} The maximum value to clamp this Point to
* @return {Point} This Point object.
**/
function (min, max) {
this.clampX(min, max);
this.clampY(min, max);
return this;
};
Point.prototype.clampX = /**
* Clamps the x value of this Point object to be between the given min and max
* @method clampX
* @param {number} The minimum value to clamp this Point to
* @param {number} The maximum value to clamp this Point to
* @return {Point} This Point object.
**/
function (min, max) {
this.x = Math.max(Math.min(this.x, max), min);
return this;
};
Point.prototype.clampY = /**
* Clamps the y value of this Point object to be between the given min and max
* @method clampY
* @param {number} The minimum value to clamp this Point to
* @param {number} The maximum value to clamp this Point to
* @return {Point} This Point object.
**/
function (min, max) {
this.x = Math.max(Math.min(this.x, max), min);
this.y = Math.max(Math.min(this.y, max), min);
return this;
};
Point.prototype.clone = /**
* Creates a copy of this Point.
* @method clone
* @param {Point} output Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
* @return {Point} The new Point object.
**/
function (output) {
if (typeof output === "undefined") { output = new Point(); }
return output.setTo(this.x, this.y);
};
Point.prototype.copyFrom = /**
* Copies the point data from the source Point object into this Point object.
* @method copyFrom
* @param {Point} source - The point to copy from.
* @return {Point} This Point object. Useful for chaining method calls.
**/
function (source) {
return this.setTo(source.x, source.y);
};
Point.prototype.copyTo = /**
* Copies the point data from this Point object to the given target Point object.
* @method copyTo
* @param {Point} target - The point to copy to.
* @return {Point} The target Point object.
**/
function (target) {
return target.setTo(this.x, this.y);
};
Point.prototype.distanceTo = /**
* Returns the distance from this Point object to the given Point object.
* @method distanceFrom
* @param {Point} target - The destination Point object.
* @param {Boolean} round - Round the distance to the nearest integer (default false)
* @return {Number} The distance between this Point object and the destination Point object.
**/
function (target, round) {
if (typeof round === "undefined") { round = false; }
var dx = this.x - target.x;
var dy = this.y - target.y;
if(round === true) {
return Math.round(Math.sqrt(dx * dx + dy * dy));
} else {
return Math.sqrt(dx * dx + dy * dy);
}
};
Point.distanceBetween = /**
* Returns the distance between the two Point objects.
* @method distanceBetween
* @param {Point} pointA - The first Point object.
* @param {Point} pointB - The second Point object.
* @param {Boolean} round - Round the distance to the nearest integer (default false)
* @return {Number} The distance between the two Point objects.
**/
function distanceBetween(pointA, pointB, round) {
if (typeof round === "undefined") { round = false; }
var dx = pointA.x - pointB.x;
var dy = pointA.y - pointB.y;
if(round === true) {
return Math.round(Math.sqrt(dx * dx + dy * dy));
} else {
return Math.sqrt(dx * dx + dy * dy);
}
};
Point.prototype.distanceCompare = /**
* Returns true if the distance between this point and a target point is greater than or equal a specified distance.
* This avoids using a costly square root operation
* @method distanceCompare
* @param {Point} target - The Point object to use for comparison.
* @param {Number} distance - The distance to use for comparison.
* @return {Boolena} True if distance is >= specified distance.
**/
function (target, distance) {
if(this.distanceTo(target) >= distance) {
return true;
} else {
return false;
}
};
Point.prototype.equals = /**
* Determines whether this Point object and the given point object are equal. They are equal if they have the same x and y values.
* @method equals
* @param {Point} point - The point to compare against.
* @return {Boolean} A value of true if the object is equal to this Point object; false if it is not equal.
**/
function (toCompare) {
if(this.x === toCompare.x && this.y === toCompare.y) {
return true;
} else {
return false;
}
};
Point.prototype.interpolate = /**
* Determines a point between two specified points. The parameter f determines where the new interpolated point is located relative to the two end points specified by parameters pt1 and pt2.
* The closer the value of the parameter f is to 1.0, the closer the interpolated point is to the first point (parameter pt1). The closer the value of the parameter f is to 0, the closer the interpolated point is to the second point (parameter pt2).
* @method interpolate
* @param {Point} pointA - The first Point object.
* @param {Point} pointB - The second Point object.
* @param {Number} f - The level of interpolation between the two points. Indicates where the new point will be, along the line between pt1 and pt2. If f=1, pt1 is returned; if f=0, pt2 is returned.
* @return {Point} The new interpolated Point object.
**/
function (pointA, pointB, f) {
};
Point.prototype.offset = /**
* Offsets the Point object by the specified amount. The value of dx is added to the original value of x to create the new x value.
* The value of dy is added to the original value of y to create the new y value.
* @method offset
* @param {Number} dx - The amount by which to offset the horizontal coordinate, x.
* @param {Number} dy - The amount by which to offset the vertical coordinate, y.
* @return {Point} This Point object. Useful for chaining method calls.
**/
function (dx, dy) {
this.x += dx;
this.y += dy;
return this;
};
Point.prototype.polar = /**
* Converts a pair of polar coordinates to a Cartesian point coordinate.
* @method polar
* @param {Number} length - The length coordinate of the polar pair.
* @param {Number} angle - The angle, in radians, of the polar pair.
* @return {Point} The new Cartesian Point object.
**/
function (length, angle) {
};
Point.prototype.setTo = /**
* Sets the x and y values of this Point object to the given coordinates.
* @method set
* @param {Number} x - The horizontal position of this point.
* @param {Number} y - The vertical position of this point.
* @return {Point} This Point object. Useful for chaining method calls.
**/
function (x, y) {
this.x = x;
this.y = y;
return this;
};
Point.prototype.subtract = /**
* Subtracts the coordinates of another point from the coordinates of this point to create a new point.
* @method subtract
* @param {Point} point - The point to be subtracted.
* @param {Point} output Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
* @return {Point} The new Point object.
**/
function (point, output) {
if (typeof output === "undefined") { output = new Point(); }
return output.setTo(this.x - point.x, this.y - point.y);
};
Point.prototype.toString = /**
* Returns a string representation of this object.
* @method toString
* @return {string} a string representation of the instance.
**/
function () {
return '[{Point (x=' + this.x + ' y=' + this.y + ')}]';
};
return Point;
})();
/// GameObject and Group extend this class,
* as do the plugins. Has no size, position or graphical data.
*
* @author Adam Atomic
* @author Richard Davey
*/
var Basic = (function () {
/**
* Instantiate the basic object.
*/
function Basic(game) {
/**
* Allows you to give this object a name. Useful for debugging, but not actually used internally.
*/
this.name = '';
this._game = game;
this.ID = -1;
this.exists = true;
this.active = true;
this.visible = true;
this.alive = true;
this.isGroup = false;
this.ignoreDrawDebug = false;
}
Basic.prototype.destroy = /**
* Override this to null out iables or manually call
* destroy() on class members if necessary.
* Don't forget to call super.destroy()!
*/
function () {
};
Basic.prototype.preUpdate = /**
* Pre-update is called right before update() on each object in the game loop.
*/
function () {
};
Basic.prototype.update = /**
* Override this to update your class's position and appearance.
* This is where most of your game rules and behavioral code will go.
*/
function () {
};
Basic.prototype.postUpdate = /**
* Post-update is called right after update() on each object in the game loop.
*/
function () {
};
Basic.prototype.render = function (camera, cameraOffsetX, cameraOffsetY) {
};
Basic.prototype.kill = /**
* Handy for "killing" game objects.
* Default behavior is to flag them as nonexistent AND dead.
* However, if you want the "corpse" to remain in the game,
* like to animate an effect or whatever, you should override this,
* setting only alive to false, and leaving exists true.
*/
function () {
this.alive = false;
this.exists = false;
};
Basic.prototype.revive = /**
* Handy for bringing game objects "back to life". Just sets alive and exists back to true.
* In practice, this is most often called by FlxObject.reset().
*/
function () {
this.alive = true;
this.exists = true;
};
Basic.prototype.toString = /**
* Convert object to readable string name. Useful for debugging, save games, etc.
*/
function () {
//return FlxU.getClassName(this, true);
return "";
};
return Basic;
})();
var __extends = this.__extends || function (d, b) {
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
/// GameObject overlaps this GameObject or FlxGroup.
* If the group has a LOT of things in it, it might be faster to use FlxG.overlaps().
* WARNING: Currently tilemaps do NOT support screen space overlap checks!
*
* @param ObjectOrGroup The object or group being tested.
* @param InScreenSpace Whether to take scroll factors numbero account when checking for overlap. Default is false, or "only compare in world space."
* @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
*
* @return Whether or not the two objects overlap.
*/
function (ObjectOrGroup, InScreenSpace, Camera) {
if (typeof InScreenSpace === "undefined") { InScreenSpace = false; }
if (typeof Camera === "undefined") { Camera = null; }
if(ObjectOrGroup.isGroup) {
var results = false;
var i = 0;
var members = ObjectOrGroup.members;
while(i < length) {
if(this.overlaps(members[i++], InScreenSpace, Camera)) {
results = true;
}
}
return results;
}
/*
if (typeof ObjectOrGroup === 'FlxTilemap')
{
//Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
// we redirect the call to the tilemap overlap here.
return ObjectOrGroup.overlaps(this, InScreenSpace, Camera);
}
*/
//var object: GameObject = ObjectOrGroup;
if(!InScreenSpace) {
return (ObjectOrGroup.x + ObjectOrGroup.width > this.x) && (ObjectOrGroup.x < this.x + this.width) && (ObjectOrGroup.y + ObjectOrGroup.height > this.y) && (ObjectOrGroup.y < this.y + this.height);
}
if(Camera == null) {
Camera = this._game.camera;
}
var objectScreenPos = ObjectOrGroup.getScreenXY(null, Camera);
this.getScreenXY(this._point, Camera);
return (objectScreenPos.x + ObjectOrGroup.width > this._point.x) && (objectScreenPos.x < this._point.x + this.width) && (objectScreenPos.y + ObjectOrGroup.height > this._point.y) && (objectScreenPos.y < this._point.y + this.height);
};
GameObject.prototype.overlapsAt = /**
* Checks to see if this GameObject were located at the given position, would it overlap the GameObject or FlxGroup?
* This is distinct from overlapsPoint(), which just checks that ponumber, rather than taking the object's size numbero account.
* WARNING: Currently tilemaps do NOT support screen space overlap checks!
*
* @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here.
* @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here.
* @param ObjectOrGroup The object or group being tested.
* @param InScreenSpace Whether to take scroll factors numbero account when checking for overlap. Default is false, or "only compare in world space."
* @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
*
* @return Whether or not the two objects overlap.
*/
function (X, Y, ObjectOrGroup, InScreenSpace, Camera) {
if (typeof InScreenSpace === "undefined") { InScreenSpace = false; }
if (typeof Camera === "undefined") { Camera = null; }
if(ObjectOrGroup.isGroup) {
var results = false;
var basic;
var i = 0;
var members = ObjectOrGroup.members;
while(i < length) {
if(this.overlapsAt(X, Y, members[i++], InScreenSpace, Camera)) {
results = true;
}
}
return results;
}
/*
if (typeof ObjectOrGroup === 'FlxTilemap')
{
//Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
// we redirect the call to the tilemap overlap here.
//However, since this is overlapsAt(), we also have to invent the appropriate position for the tilemap.
//So we calculate the offset between the player and the requested position, and subtract that from the tilemap.
var tilemap: FlxTilemap = ObjectOrGroup;
return tilemap.overlapsAt(tilemap.x - (X - this.x), tilemap.y - (Y - this.y), this, InScreenSpace, Camera);
}
*/
//var object: GameObject = ObjectOrGroup;
if(!InScreenSpace) {
return (ObjectOrGroup.x + ObjectOrGroup.width > X) && (ObjectOrGroup.x < X + this.width) && (ObjectOrGroup.y + ObjectOrGroup.height > Y) && (ObjectOrGroup.y < Y + this.height);
}
if(Camera == null) {
Camera = this._game.camera;
}
var objectScreenPos = ObjectOrGroup.getScreenXY(null, Camera);
this._point.x = X - Camera.scroll.x * this.scrollFactor.x//copied from getScreenXY()
;
this._point.y = Y - Camera.scroll.y * this.scrollFactor.y;
this._point.x += (this._point.x > 0) ? 0.0000001 : -0.0000001;
this._point.y += (this._point.y > 0) ? 0.0000001 : -0.0000001;
return (objectScreenPos.x + ObjectOrGroup.width > this._point.x) && (objectScreenPos.x < this._point.x + this.width) && (objectScreenPos.y + ObjectOrGroup.height > this._point.y) && (objectScreenPos.y < this._point.y + this.height);
};
GameObject.prototype.overlapsPoint = /**
* Checks to see if a ponumber in 2D world space overlaps this GameObject object.
*
* @param Point The ponumber in world space you want to check.
* @param InScreenSpace Whether to take scroll factors numbero account when checking for overlap.
* @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
*
* @return Whether or not the ponumber overlaps this object.
*/
function (point, InScreenSpace, Camera) {
if (typeof InScreenSpace === "undefined") { InScreenSpace = false; }
if (typeof Camera === "undefined") { Camera = null; }
if(!InScreenSpace) {
return (point.x > this.x) && (point.x < this.x + this.width) && (point.y > this.y) && (point.y < this.y + this.height);
}
if(Camera == null) {
Camera = this._game.camera;
}
var X = point.x - Camera.scroll.x;
var Y = point.y - Camera.scroll.y;
this.getScreenXY(this._point, Camera);
return (X > this._point.x) && (X < this._point.x + this.width) && (Y > this._point.y) && (Y < this._point.y + this.height);
};
GameObject.prototype.onScreen = /**
* Check and see if this object is currently on screen.
*
* @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
*
* @return Whether the object is on screen or not.
*/
function (Camera) {
if (typeof Camera === "undefined") { Camera = null; }
if(Camera == null) {
Camera = this._game.camera;
}
this.getScreenXY(this._point, Camera);
return (this._point.x + this.width > 0) && (this._point.x < Camera.width) && (this._point.y + this.height > 0) && (this._point.y < Camera.height);
};
GameObject.prototype.getScreenXY = /**
* Call this to figure out the on-screen position of the object.
*
* @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera.
* @param Point Takes a Point object and assigns the post-scrolled X and Y values of this object to it.
*
* @return The Point you passed in, or a new Point if you didn't pass one, containing the screen X and Y position of this object.
*/
function (point, Camera) {
if (typeof point === "undefined") { point = null; }
if (typeof Camera === "undefined") { Camera = null; }
if(point == null) {
point = new Point();
}
if(Camera == null) {
Camera = this._game.camera;
}
point.x = this.x - Camera.scroll.x * this.scrollFactor.x;
point.y = this.y - Camera.scroll.y * this.scrollFactor.y;
point.x += (point.x > 0) ? 0.0000001 : -0.0000001;
point.y += (point.y > 0) ? 0.0000001 : -0.0000001;
return point;
};
Object.defineProperty(GameObject.prototype, "solid", {
get: /**
* Whether the object collides or not. For more control over what directions
* the object will collide from, use collision constants (like LEFT, FLOOR, etc)
* to set the value of allowCollisions directly.
*/
function () {
return (this.allowCollisions & GameObject.ANY) > GameObject.NONE;
},
set: /**
* @private
*/
function (Solid) {
if(Solid) {
this.allowCollisions = GameObject.ANY;
} else {
this.allowCollisions = GameObject.NONE;
}
},
enumerable: true,
configurable: true
});
GameObject.prototype.getMidpoint = /**
* Retrieve the midponumber of this object in world coordinates.
*
* @Point Allows you to pass in an existing Point object if you're so inclined. Otherwise a new one is created.
*
* @return A Point object containing the midponumber of this object in world coordinates.
*/
function (point) {
if (typeof point === "undefined") { point = null; }
if(point == null) {
point = new Point();
}
point.x = this.x + this.width * 0.5;
point.y = this.y + this.height * 0.5;
return point;
};
GameObject.prototype.reset = /**
* Handy for reviving game objects.
* Resets their existence flags and position.
*
* @param X The new X position of this object.
* @param Y The new Y position of this object.
*/
function (X, Y) {
this.revive();
this.touching = GameObject.NONE;
this.wasTouching = GameObject.NONE;
this.x = X;
this.y = Y;
this.last.x = X;
this.last.y = Y;
this.velocity.x = 0;
this.velocity.y = 0;
};
GameObject.prototype.isTouching = /**
* Handy for checking if this object is touching a particular surface.
* For slightly better performance you can just & the value directly numbero touching.
* However, this method is good for readability and accessibility.
*
* @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
*
* @return Whether the object is touching an object in (any of) the specified direction(s) this frame.
*/
function (Direction) {
return (this.touching & Direction) > GameObject.NONE;
};
GameObject.prototype.justTouched = /**
* Handy for checking if this object is just landed on a particular surface.
*
* @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc).
*
* @return Whether the object just landed on (any of) the specified surface(s) this frame.
*/
function (Direction) {
return ((this.touching & Direction) > GameObject.NONE) && ((this.wasTouching & Direction) <= GameObject.NONE);
};
GameObject.prototype.hurt = /**
* Reduces the "health" variable of this sprite by the amount specified in Damage.
* Calls kill() if health drops to or below zero.
*
* @param Damage How much health to take away (use a negative number to give a health bonus).
*/
function (Damage) {
this.health = this.health - Damage;
if(this.health <= 0) {
this.kill();
}
};
GameObject.prototype.destroy = function () {
};
Object.defineProperty(GameObject.prototype, "x", {
get: function () {
return this.bounds.x;
},
set: function (value) {
this.bounds.x = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GameObject.prototype, "y", {
get: function () {
return this.bounds.y;
},
set: function (value) {
this.bounds.y = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GameObject.prototype, "rotation", {
get: function () {
return this._angle;
},
set: function (value) {
this._angle = this._game.math.wrap(value, 360, 0);
},
enumerable: true,
configurable: true
});
Object.defineProperty(GameObject.prototype, "angle", {
get: function () {
return this._angle;
},
set: function (value) {
this._angle = this._game.math.wrap(value, 360, 0);
},
enumerable: true,
configurable: true
});
Object.defineProperty(GameObject.prototype, "width", {
get: function () {
return this.bounds.width;
},
enumerable: true,
configurable: true
});
Object.defineProperty(GameObject.prototype, "height", {
get: function () {
return this.bounds.height;
},
enumerable: true,
configurable: true
});
return GameObject;
})(Basic);
/// Basics.
* NOTE: Although Group extends Basic, it will not automatically
* add itself to the global collisions quad tree, it will only add its members.
*
* @author Adam Atomic
* @author Richard Davey
*/
var Group = (function (_super) {
__extends(Group, _super);
function Group(game, MaxSize) {
if (typeof MaxSize === "undefined") { MaxSize = 0; }
_super.call(this, game);
this.isGroup = true;
this.members = [];
this.length = 0;
this._maxSize = MaxSize;
this._marker = 0;
this._sortIndex = null;
}
Group.ASCENDING = -1;
Group.DESCENDING = 1;
Group.prototype.destroy = /**
* Override this function to handle any deleting or "shutdown" type operations you might need,
* such as removing traditional Flash children like Basic objects.
*/
function () {
if(this.members != null) {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if(basic != null) {
basic.destroy();
}
}
this.members.length = 0;
}
this._sortIndex = null;
};
Group.prototype.update = /**
* Automatically goes through and calls update on everything you added.
*/
function () {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && basic.exists && basic.active) {
basic.preUpdate();
basic.update();
basic.postUpdate();
}
}
};
Group.prototype.render = /**
* Automatically goes through and calls render on everything you added.
*/
function (camera, cameraOffsetX, cameraOffsetY) {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && basic.exists && basic.visible) {
basic.render(camera, cameraOffsetX, cameraOffsetY);
}
}
};
Object.defineProperty(Group.prototype, "maxSize", {
get: /**
* The maximum capacity of this group. Default is 0, meaning no max capacity, and the group can just grow.
*/
function () {
return this._maxSize;
},
set: /**
* @private
*/
function (Size) {
this._maxSize = Size;
if(this._marker >= this._maxSize) {
this._marker = 0;
}
if((this._maxSize == 0) || (this.members == null) || (this._maxSize >= this.members.length)) {
return;
}
//If the max size has shrunk, we need to get rid of some objects
var basic;
var i = this._maxSize;
var l = this.members.length;
while(i < l) {
basic = this.members[i++];
if(basic != null) {
basic.destroy();
}
}
this.length = this.members.length = this._maxSize;
},
enumerable: true,
configurable: true
});
Group.prototype.add = /**
* Adds a new Basic subclass (Basic, FlxBasic, Enemy, etc) to the group.
* Group will try to replace a null member of the array first.
* Failing that, Group will add it to the end of the member array,
* assuming there is room for it, and doubling the size of the array if necessary.
*
* WARNING: If the group has a maxSize that has already been met, * the object will NOT be added to the group!
* * @param Object The object you want to add to the group. * * @return The sameBasic object that was passed in.
*/
function (Object) {
//Don't bother adding an object twice.
if(this.members.indexOf(Object) >= 0) {
return Object;
}
//First, look for a null entry where we can add the object.
var i = 0;
var l = this.members.length;
while(i < l) {
if(this.members[i] == null) {
this.members[i] = Object;
if(i >= this.length) {
this.length = i + 1;
}
return Object;
}
i++;
}
//Failing that, expand the array (if we can) and add the object.
if(this._maxSize > 0) {
if(this.members.length >= this._maxSize) {
return Object;
} else if(this.members.length * 2 <= this._maxSize) {
this.members.length *= 2;
} else {
this.members.length = this._maxSize;
}
} else {
this.members.length *= 2;
}
//If we made it this far, then we successfully grew the group,
//and we can go ahead and add the object at the first open slot.
this.members[i] = Object;
this.length = i + 1;
return Object;
};
Group.prototype.recycle = /**
* Recycling is designed to help you reuse game objects without always re-allocating or "newing" them.
*
* If you specified a maximum size for this group (like in Emitter), * then recycle will employ what we're calling "rotating" recycling. * Recycle() will first check to see if the group is at capacity yet. * If group is not yet at capacity, recycle() returns a new object. * If the group IS at capacity, then recycle() just returns the next object in line.
* *If you did NOT specify a maximum size for this group, * then recycle() will employ what we're calling "grow-style" recycling. * Recycle() will return either the first object with exists == false, * or, finding none, add a new object to the array, * doubling the size of the array if necessary.
* *WARNING: If this function needs to create a new object, * and no object class was provided, it will return null * instead of a valid object!
* * @param ObjectClass The class type you want to recycle (e.g. FlxBasic, EvilRobot, etc). Do NOT "new" the class in the parameter! * * @return A reference to the object that was created. Don't forget to cast it back to the Class you want (e.g. myObject = myGroup.recycle(myObjectClass) as myObjectClass;). */ function (ObjectClass) { if (typeof ObjectClass === "undefined") { ObjectClass = null; } var basic; if(this._maxSize > 0) { if(this.length < this._maxSize) { if(ObjectClass == null) { return null; } return this.add(new ObjectClass()); } else { basic = this.members[this._marker++]; if(this._marker >= this._maxSize) { this._marker = 0; } return basic; } } else { basic = this.getFirstAvailable(ObjectClass); if(basic != null) { return basic; } if(ObjectClass == null) { return null; } return this.add(new ObjectClass()); } }; Group.prototype.remove = /** * Removes an object from the group. * * @param Object TheBasic you want to remove.
* @param Splice Whether the object should be cut from the array entirely or not.
*
* @return The removed object.
*/
function (Object, Splice) {
if (typeof Splice === "undefined") { Splice = false; }
var index = this.members.indexOf(Object);
if((index < 0) || (index >= this.members.length)) {
return null;
}
if(Splice) {
this.members.splice(index, 1);
this.length--;
} else {
this.members[index] = null;
}
return Object;
};
Group.prototype.replace = /**
* Replaces an existing Basic with a new one.
*
* @param OldObject The object you want to replace.
* @param NewObject The new object you want to use instead.
*
* @return The new object.
*/
function (OldObject, NewObject) {
var index = this.members.indexOf(OldObject);
if((index < 0) || (index >= this.members.length)) {
return null;
}
this.members[index] = NewObject;
return NewObject;
};
Group.prototype.sort = /**
* Call this function to sort the group according to a particular value and order.
* For example, to sort game objects for Zelda-style overlaps you might call
* myGroup.sort("y",Group.ASCENDING) at the bottom of your
* FlxState.update() override. To sort all existing objects after
* a big explosion or bomb attack, you might call myGroup.sort("exists",Group.DESCENDING).
*
* @param Index The string name of the member variable you want to sort on. Default value is "y".
* @param Order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING.
*/
function (Index, Order) {
if (typeof Index === "undefined") { Index = "y"; }
if (typeof Order === "undefined") { Order = Group.ASCENDING; }
this._sortIndex = Index;
this._sortOrder = Order;
this.members.sort(this.sortHandler);
};
Group.prototype.setAll = /**
* Go through and set the specified variable to the specified value on all members of the group.
*
* @param VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor".
* @param Value The value you want to assign to that variable.
* @param Recurse Default value is true, meaning if setAll() encounters a member that is a group, it will call setAll() on that group rather than modifying its variable.
*/
function (VariableName, Value, Recurse) {
if (typeof Recurse === "undefined") { Recurse = true; }
var basic;
var i = 0;
while(i < length) {
basic = this.members[i++];
if(basic != null) {
if(Recurse && (basic.isGroup == true)) {
basic['setAll'](VariableName, Value, Recurse);
} else {
basic[VariableName] = Value;
}
}
}
};
Group.prototype.callAll = /**
* Go through and call the specified function on all members of the group.
* Currently only works on functions that have no required parameters.
*
* @param FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()".
* @param Recurse Default value is true, meaning if callAll() encounters a member that is a group, it will call callAll() on that group rather than calling the group's function.
*/
function (FunctionName, Recurse) {
if (typeof Recurse === "undefined") { Recurse = true; }
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if(basic != null) {
if(Recurse && (basic.isGroup == true)) {
basic['callAll'](FunctionName, Recurse);
} else {
basic[FunctionName]();
}
}
}
};
Group.prototype.forEach = function (callback, Recurse) {
if (typeof Recurse === "undefined") { Recurse = false; }
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if(basic != null) {
if(Recurse && (basic.isGroup == true)) {
basic.forEach(callback, true);
} else {
callback.call(this, basic);
}
}
}
};
Group.prototype.getFirstAvailable = /**
* Call this function to retrieve the first object with exists == false in the group.
* This is handy for recycling in general, e.g. respawning enemies.
*
* @param ObjectClass An optional parameter that lets you narrow the results to instances of this particular class.
*
* @return A Basic currently flagged as not existing.
*/
function (ObjectClass) {
if (typeof ObjectClass === "undefined") { ObjectClass = null; }
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && !basic.exists && ((ObjectClass == null) || (typeof basic === ObjectClass))) {
return basic;
}
}
return null;
};
Group.prototype.getFirstNull = /**
* Call this function to retrieve the first index set to 'null'.
* Returns -1 if no index stores a null object.
*
* @return An int indicating the first null slot in the group.
*/
function () {
var basic;
var i = 0;
var l = this.members.length;
while(i < l) {
if(this.members[i] == null) {
return i;
} else {
i++;
}
}
return -1;
};
Group.prototype.getFirstExtant = /**
* Call this function to retrieve the first object with exists == true in the group.
* This is handy for checking if everything's wiped out, or choosing a squad leader, etc.
*
* @return A Basic currently flagged as existing.
*/
function () {
var basic;
var i = 0;
while(i < length) {
basic = this.members[i++];
if((basic != null) && basic.exists) {
return basic;
}
}
return null;
};
Group.prototype.getFirstAlive = /**
* Call this function to retrieve the first object with dead == false in the group.
* This is handy for checking if everything's wiped out, or choosing a squad leader, etc.
*
* @return A Basic currently flagged as not dead.
*/
function () {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && basic.exists && basic.alive) {
return basic;
}
}
return null;
};
Group.prototype.getFirstDead = /**
* Call this function to retrieve the first object with dead == true in the group.
* This is handy for checking if everything's wiped out, or choosing a squad leader, etc.
*
* @return A Basic currently flagged as dead.
*/
function () {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && !basic.alive) {
return basic;
}
}
return null;
};
Group.prototype.countLiving = /**
* Call this function to find out how many members of the group are not dead.
*
* @return The number of Basics flagged as not dead. Returns -1 if group is empty.
*/
function () {
var count = -1;
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if(basic != null) {
if(count < 0) {
count = 0;
}
if(basic.exists && basic.alive) {
count++;
}
}
}
return count;
};
Group.prototype.countDead = /**
* Call this function to find out how many members of the group are dead.
*
* @return The number of Basics flagged as dead. Returns -1 if group is empty.
*/
function () {
var count = -1;
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if(basic != null) {
if(count < 0) {
count = 0;
}
if(!basic.alive) {
count++;
}
}
}
return count;
};
Group.prototype.getRandom = /**
* Returns a member at random from the group.
*
* @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param Length Optional restriction on the number of values you want to randomly select from.
*
* @return A Basic from the members list.
*/
function (StartIndex, Length) {
if (typeof StartIndex === "undefined") { StartIndex = 0; }
if (typeof Length === "undefined") { Length = 0; }
if(Length == 0) {
Length = this.length;
}
return this._game.math.getRandom(this.members, StartIndex, Length);
};
Group.prototype.clear = /**
* Remove all instances of Basic subclass (FlxBasic, FlxBlock, etc) from the list.
* WARNING: does not destroy() or kill() any of these objects!
*/
function () {
this.length = this.members.length = 0;
};
Group.prototype.kill = /**
* Calls kill on the group's members and then on the group itself.
*/
function () {
var basic;
var i = 0;
while(i < this.length) {
basic = this.members[i++];
if((basic != null) && basic.exists) {
basic.kill();
}
}
};
Group.prototype.sortHandler = /**
* Helper function for the sort process.
*
* @param Obj1 The first object being sorted.
* @param Obj2 The second object being sorted.
*
* @return An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2).
*/
function (Obj1, Obj2) {
if(Obj1[this._sortIndex] < Obj2[this._sortIndex]) {
return this._sortOrder;
} else if(Obj1[this._sortIndex] > Obj2[this._sortIndex]) {
return -this._sortOrder;
}
return 0;
};
return Group;
})(Basic);
/// Sprite to have slightly more specialized behavior
* common to many game scenarios. You can override and extend this class
* just like you would Sprite. While Emitter
* used to work with just any old sprite, it now requires a
* Particle based class.
*
* @author Adam Atomic
* @author Richard Davey
*/
var Particle = (function (_super) {
__extends(Particle, _super);
/**
* Instantiate a new particle. Like Sprite, all meaningful creation
* happens during loadGraphic() or makeGraphic() or whatever.
*/
function Particle(game) {
_super.call(this, game);
this.lifespan = 0;
this.friction = 500;
}
Particle.prototype.update = /**
* The particle's main update logic. Basically it checks to see if it should
* be dead yet, and then has some special bounce behavior if there is some gravity on it.
*/
function () {
//lifespan behavior
if(this.lifespan <= 0) {
return;
}
this.lifespan -= this._game.time.elapsed;
if(this.lifespan <= 0) {
this.kill();
}
//simpler bounce/spin behavior for now
if(this.touching) {
if(this.angularVelocity != 0) {
this.angularVelocity = -this.angularVelocity;
}
}
if(this.acceleration.y > 0)//special behavior for particles with gravity
{
if(this.touching & GameObject.FLOOR) {
this.drag.x = this.friction;
if(!(this.wasTouching & GameObject.FLOOR)) {
if(this.velocity.y < -this.elasticity * 10) {
if(this.angularVelocity != 0) {
this.angularVelocity *= -this.elasticity;
}
} else {
this.velocity.y = 0;
this.angularVelocity = 0;
}
}
} else {
this.drag.x = 0;
}
}
};
Particle.prototype.onEmit = /**
* Triggered whenever this object is launched by a Emitter.
* You can override this to add custom behavior like a sound or AI or something.
*/
function () {
};
return Particle;
})(Sprite);
/// Emitter is a lightweight particle emitter.
* It can be used for one-time explosions or for
* continuous fx like rain and fire. Emitter
* is not optimized or anything; all it does is launch
* Particle objects out at set intervals
* by setting their positions and velocities accordingly.
* It is easy to use and relatively efficient,
* relying on Group's RECYCLE POWERS.
*
* @author Adam Atomic
* @author Richard Davey
*/
var Emitter = (function (_super) {
__extends(Emitter, _super);
/**
* Creates a new FlxEmitter object at a specific position.
* Does NOT automatically generate or attach particles!
*
* @param X The X position of the emitter.
* @param Y The Y position of the emitter.
* @param Size Optional, specifies a maximum capacity for this emitter.
*/
function Emitter(game, X, Y, Size) {
if (typeof X === "undefined") { X = 0; }
if (typeof Y === "undefined") { Y = 0; }
if (typeof Size === "undefined") { Size = 0; }
_super.call(this, game, Size);
this.x = X;
this.y = Y;
this.width = 0;
this.height = 0;
this.minParticleSpeed = new Point(-100, -100);
this.maxParticleSpeed = new Point(100, 100);
this.minRotation = -360;
this.maxRotation = 360;
this.gravity = 0;
this.particleClass = null;
this.particleDrag = new Point();
this.frequency = 0.1;
this.lifespan = 3;
this.bounce = 0;
this._quantity = 0;
this._counter = 0;
this._explode = true;
this.on = false;
this._point = new Point();
}
Emitter.prototype.destroy = /**
* Clean up memory.
*/
function () {
this.minParticleSpeed = null;
this.maxParticleSpeed = null;
this.particleDrag = null;
this.particleClass = null;
this._point = null;
_super.prototype.destroy.call(this);
};
Emitter.prototype.makeParticles = /**
* This function generates a new array of particle sprites to attach to the emitter.
*
* @param Graphics If you opted to not pre-configure an array of FlxSprite objects, you can simply pass in a particle image or sprite sheet.
* @param Quantity The number of particles to generate when using the "create from image" option.
* @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations.
* @param Multiple Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!).
* @param Collide Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box.
*
* @return This FlxEmitter instance (nice for chaining stuff together, if you're into that).
*/
function (Graphics, Quantity, BakedRotations, Multiple, Collide) {
if (typeof Quantity === "undefined") { Quantity = 50; }
if (typeof BakedRotations === "undefined") { BakedRotations = 16; }
if (typeof Multiple === "undefined") { Multiple = false; }
if (typeof Collide === "undefined") { Collide = 0.8; }
this.maxSize = Quantity;
var totalFrames = 1;
/*
if(Multiple)
{
var sprite:Sprite = new Sprite(this._game);
sprite.loadGraphic(Graphics,true);
totalFrames = sprite.frames;
sprite.destroy();
}
*/
var randomFrame;
var particle;
var i = 0;
while(i < Quantity) {
if(this.particleClass == null) {
particle = new Particle(this._game);
} else {
particle = new this.particleClass(this._game);
}
if(Multiple) {
/*
randomFrame = this._game.math.random()*totalFrames;
if(BakedRotations > 0)
particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame);
else
{
particle.loadGraphic(Graphics,true);
particle.frame = randomFrame;
}
*/
} else {
/*
if (BakedRotations > 0)
particle.loadRotatedGraphic(Graphics,BakedRotations);
else
particle.loadGraphic(Graphics);
*/
if(Graphics) {
particle.loadGraphic(Graphics);
}
}
if(Collide > 0) {
particle.width *= Collide;
particle.height *= Collide;
//particle.centerOffsets();
} else {
particle.allowCollisions = GameObject.NONE;
}
particle.exists = false;
this.add(particle);
i++;
}
return this;
};
Emitter.prototype.update = /**
* Called automatically by the game loop, decides when to launch particles and when to "die".
*/
function () {
if(this.on) {
if(this._explode) {
this.on = false;
var i = 0;
var l = this._quantity;
if((l <= 0) || (l > this.length)) {
l = this.length;
}
while(i < l) {
this.emitParticle();
i++;
}
this._quantity = 0;
} else {
this._timer += this._game.time.elapsed;
while((this.frequency > 0) && (this._timer > this.frequency) && this.on) {
this._timer -= this.frequency;
this.emitParticle();
if((this._quantity > 0) && (++this._counter >= this._quantity)) {
this.on = false;
this._quantity = 0;
}
}
}
}
_super.prototype.update.call(this);
};
Emitter.prototype.kill = /**
* Call this function to turn off all the particles and the emitter.
*/
function () {
this.on = false;
_super.prototype.kill.call(this);
};
Emitter.prototype.start = /**
* Call this function to start emitting particles.
*
* @param Explode Whether the particles should all burst out at once.
* @param Lifespan How long each particle lives once emitted. 0 = forever.
* @param Frequency Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds.
* @param Quantity How many particles to launch. 0 = "all of the particles".
*/
function (Explode, Lifespan, Frequency, Quantity) {
if (typeof Explode === "undefined") { Explode = true; }
if (typeof Lifespan === "undefined") { Lifespan = 0; }
if (typeof Frequency === "undefined") { Frequency = 0.1; }
if (typeof Quantity === "undefined") { Quantity = 0; }
this.revive();
this.visible = true;
this.on = true;
this._explode = Explode;
this.lifespan = Lifespan;
this.frequency = Frequency;
this._quantity += Quantity;
this._counter = 0;
this._timer = 0;
};
Emitter.prototype.emitParticle = /**
* This function can be used both internally and externally to emit the next particle.
*/
function () {
var particle = this.recycle(Particle);
particle.lifespan = this.lifespan;
particle.elasticity = this.bounce;
particle.reset(this.x - (particle.width >> 1) + this._game.math.random() * this.width, this.y - (particle.height >> 1) + this._game.math.random() * this.height);
particle.visible = true;
if(this.minParticleSpeed.x != this.maxParticleSpeed.x) {
particle.velocity.x = this.minParticleSpeed.x + this._game.math.random() * (this.maxParticleSpeed.x - this.minParticleSpeed.x);
} else {
particle.velocity.x = this.minParticleSpeed.x;
}
if(this.minParticleSpeed.y != this.maxParticleSpeed.y) {
particle.velocity.y = this.minParticleSpeed.y + this._game.math.random() * (this.maxParticleSpeed.y - this.minParticleSpeed.y);
} else {
particle.velocity.y = this.minParticleSpeed.y;
}
particle.acceleration.y = this.gravity;
if(this.minRotation != this.maxRotation) {
particle.angularVelocity = this.minRotation + this._game.math.random() * (this.maxRotation - this.minRotation);
} else {
particle.angularVelocity = this.minRotation;
}
if(particle.angularVelocity != 0) {
particle.angle = this._game.math.random() * 360 - 180;
}
particle.drag.x = this.particleDrag.x;
particle.drag.y = this.particleDrag.y;
particle.onEmit();
};
Emitter.prototype.setSize = /**
* A more compact way of setting the width and height of the emitter.
*
* @param Width The desired width of the emitter (particles are spawned randomly within these dimensions).
* @param Height The desired height of the emitter.
*/
function (Width, Height) {
this.width = Width;
this.height = Height;
};
Emitter.prototype.setXSpeed = /**
* A more compact way of setting the X velocity range of the emitter.
*
* @param Min The minimum value for this range.
* @param Max The maximum value for this range.
*/
function (Min, Max) {
if (typeof Min === "undefined") { Min = 0; }
if (typeof Max === "undefined") { Max = 0; }
this.minParticleSpeed.x = Min;
this.maxParticleSpeed.x = Max;
};
Emitter.prototype.setYSpeed = /**
* A more compact way of setting the Y velocity range of the emitter.
*
* @param Min The minimum value for this range.
* @param Max The maximum value for this range.
*/
function (Min, Max) {
if (typeof Min === "undefined") { Min = 0; }
if (typeof Max === "undefined") { Max = 0; }
this.minParticleSpeed.y = Min;
this.maxParticleSpeed.y = Max;
};
Emitter.prototype.setRotation = /**
* A more compact way of setting the angular velocity constraints of the emitter.
*
* @param Min The minimum value for this range.
* @param Max The maximum value for this range.
*/
function (Min, Max) {
if (typeof Min === "undefined") { Min = 0; }
if (typeof Max === "undefined") { Max = 0; }
this.minRotation = Min;
this.maxRotation = Max;
};
Emitter.prototype.at = /**
* Change the emitter's midpoint to match the midpoint of a FlxObject.
*
* @param Object The FlxObject that you want to sync up with.
*/
function (Object) {
Object.getMidpoint(this._point);
this.x = this._point.x - (this.width >> 1);
this.y = this._point.y - (this.height >> 1);
};
return Emitter;
})(Group);
/// QuadTree for how to use it, IF YOU DARE.
*/
var LinkedList = (function () {
/**
* Creates a new link, and sets object and next to null.
*/
function LinkedList() {
this.object = null;
this.next = null;
}
LinkedList.prototype.destroy = /**
* Clean up memory.
*/
function () {
this.object = null;
if(this.next != null) {
this.next.destroy();
}
this.next = null;
};
return LinkedList;
})();
/// myFunction(Object1:GameObject,Object2:GameObject) that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true.
* @param ProcessCallback A function with the form myFunction(Object1:GameObject,Object2:GameObject):bool that is called whenever two objects are found to overlap in world space. The NotifyCallback is only called if this function returns true. See GameObject.separate().
*/
function (ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, ProcessCallback) {
if (typeof ObjectOrGroup2 === "undefined") { ObjectOrGroup2 = null; }
if (typeof NotifyCallback === "undefined") { NotifyCallback = null; }
if (typeof ProcessCallback === "undefined") { ProcessCallback = null; }
//console.log('quadtree load', QuadTree.divisions, ObjectOrGroup1, ObjectOrGroup2);
this.add(ObjectOrGroup1, QuadTree.A_LIST);
if(ObjectOrGroup2 != null) {
this.add(ObjectOrGroup2, QuadTree.B_LIST);
QuadTree._useBothLists = true;
} else {
QuadTree._useBothLists = false;
}
QuadTree._notifyCallback = NotifyCallback;
QuadTree._processingCallback = ProcessCallback;
//console.log('use both', QuadTree._useBothLists);
//console.log('------------ end of load');
};
QuadTree.prototype.add = /**
* Call this function to add an object to the root of the tree.
* This function will recursively add all group members, but
* not the groups themselves.
*
* @param ObjectOrGroup GameObjects are just added, Groups are recursed and their applicable members added accordingly.
* @param List A uint flag indicating the list to which you want to add the objects. Options are QuadTree.A_LIST and QuadTree.B_LIST.
*/
function (ObjectOrGroup, List) {
QuadTree._list = List;
if(ObjectOrGroup.isGroup == true) {
var i = 0;
var basic;
var members = ObjectOrGroup['members'];
var l = ObjectOrGroup['length'];
while(i < l) {
basic = members[i++];
if((basic != null) && basic.exists) {
if(basic.isGroup) {
this.add(basic, List);
} else {
QuadTree._object = basic;
if(QuadTree._object.exists && QuadTree._object.allowCollisions) {
QuadTree._objectLeftEdge = QuadTree._object.x;
QuadTree._objectTopEdge = QuadTree._object.y;
QuadTree._objectRightEdge = QuadTree._object.x + QuadTree._object.width;
QuadTree._objectBottomEdge = QuadTree._object.y + QuadTree._object.height;
this.addObject();
}
}
}
}
} else {
QuadTree._object = ObjectOrGroup;
//console.log('add - not group:', ObjectOrGroup.name);
if(QuadTree._object.exists && QuadTree._object.allowCollisions) {
QuadTree._objectLeftEdge = QuadTree._object.x;
QuadTree._objectTopEdge = QuadTree._object.y;
QuadTree._objectRightEdge = QuadTree._object.x + QuadTree._object.width;
QuadTree._objectBottomEdge = QuadTree._object.y + QuadTree._object.height;
//console.log('object properties', QuadTree._objectLeftEdge, QuadTree._objectTopEdge, QuadTree._objectRightEdge, QuadTree._objectBottomEdge);
this.addObject();
}
}
};
QuadTree.prototype.addObject = /**
* Internal function for recursively navigating and creating the tree
* while adding objects to the appropriate nodes.
*/
function () {
//console.log('addObject');
//If this quad (not its children) lies entirely inside this object, add it here
if(!this._canSubdivide || ((this._leftEdge >= QuadTree._objectLeftEdge) && (this._rightEdge <= QuadTree._objectRightEdge) && (this._topEdge >= QuadTree._objectTopEdge) && (this._bottomEdge <= QuadTree._objectBottomEdge))) {
//console.log('add To List');
this.addToList();
return;
}
//See if the selected object fits completely inside any of the quadrants
if((QuadTree._objectLeftEdge > this._leftEdge) && (QuadTree._objectRightEdge < this._midpointX)) {
if((QuadTree._objectTopEdge > this._topEdge) && (QuadTree._objectBottomEdge < this._midpointY)) {
//console.log('Adding NW tree');
if(this._northWestTree == null) {
this._northWestTree = new QuadTree(this._leftEdge, this._topEdge, this._halfWidth, this._halfHeight, this);
}
this._northWestTree.addObject();
return;
}
if((QuadTree._objectTopEdge > this._midpointY) && (QuadTree._objectBottomEdge < this._bottomEdge)) {
//console.log('Adding SW tree');
if(this._southWestTree == null) {
this._southWestTree = new QuadTree(this._leftEdge, this._midpointY, this._halfWidth, this._halfHeight, this);
}
this._southWestTree.addObject();
return;
}
}
if((QuadTree._objectLeftEdge > this._midpointX) && (QuadTree._objectRightEdge < this._rightEdge)) {
if((QuadTree._objectTopEdge > this._topEdge) && (QuadTree._objectBottomEdge < this._midpointY)) {
//console.log('Adding NE tree');
if(this._northEastTree == null) {
this._northEastTree = new QuadTree(this._midpointX, this._topEdge, this._halfWidth, this._halfHeight, this);
}
this._northEastTree.addObject();
return;
}
if((QuadTree._objectTopEdge > this._midpointY) && (QuadTree._objectBottomEdge < this._bottomEdge)) {
//console.log('Adding SE tree');
if(this._southEastTree == null) {
this._southEastTree = new QuadTree(this._midpointX, this._midpointY, this._halfWidth, this._halfHeight, this);
}
this._southEastTree.addObject();
return;
}
}
//If it wasn't completely contained we have to check out the partial overlaps
if((QuadTree._objectRightEdge > this._leftEdge) && (QuadTree._objectLeftEdge < this._midpointX) && (QuadTree._objectBottomEdge > this._topEdge) && (QuadTree._objectTopEdge < this._midpointY)) {
if(this._northWestTree == null) {
this._northWestTree = new QuadTree(this._leftEdge, this._topEdge, this._halfWidth, this._halfHeight, this);
}
//console.log('added to north west partial tree');
this._northWestTree.addObject();
}
if((QuadTree._objectRightEdge > this._midpointX) && (QuadTree._objectLeftEdge < this._rightEdge) && (QuadTree._objectBottomEdge > this._topEdge) && (QuadTree._objectTopEdge < this._midpointY)) {
if(this._northEastTree == null) {
this._northEastTree = new QuadTree(this._midpointX, this._topEdge, this._halfWidth, this._halfHeight, this);
}
//console.log('added to north east partial tree');
this._northEastTree.addObject();
}
if((QuadTree._objectRightEdge > this._midpointX) && (QuadTree._objectLeftEdge < this._rightEdge) && (QuadTree._objectBottomEdge > this._midpointY) && (QuadTree._objectTopEdge < this._bottomEdge)) {
if(this._southEastTree == null) {
this._southEastTree = new QuadTree(this._midpointX, this._midpointY, this._halfWidth, this._halfHeight, this);
}
//console.log('added to south east partial tree');
this._southEastTree.addObject();
}
if((QuadTree._objectRightEdge > this._leftEdge) && (QuadTree._objectLeftEdge < this._midpointX) && (QuadTree._objectBottomEdge > this._midpointY) && (QuadTree._objectTopEdge < this._bottomEdge)) {
if(this._southWestTree == null) {
this._southWestTree = new QuadTree(this._leftEdge, this._midpointY, this._halfWidth, this._halfHeight, this);
}
//console.log('added to south west partial tree');
this._southWestTree.addObject();
}
};
QuadTree.prototype.addToList = /**
* Internal function for recursively adding objects to leaf lists.
*/
function () {
//console.log('Adding to List');
var ot;
if(QuadTree._list == QuadTree.A_LIST) {
//console.log('A LIST');
if(this._tailA.object != null) {
ot = this._tailA;
this._tailA = new LinkedList();
ot.next = this._tailA;
}
this._tailA.object = QuadTree._object;
} else {
//console.log('B LIST');
if(this._tailB.object != null) {
ot = this._tailB;
this._tailB = new LinkedList();
ot.next = this._tailB;
}
this._tailB.object = QuadTree._object;
}
if(!this._canSubdivide) {
return;
}
if(this._northWestTree != null) {
this._northWestTree.addToList();
}
if(this._northEastTree != null) {
this._northEastTree.addToList();
}
if(this._southEastTree != null) {
this._southEastTree.addToList();
}
if(this._southWestTree != null) {
this._southWestTree.addToList();
}
};
QuadTree.prototype.execute = /**
* QuadTree's other main function. Call this after adding objects
* using QuadTree.load() to compare the objects that you loaded.
*
* @return Whether or not any overlaps were found.
*/
function () {
//console.log('quadtree execute');
var overlapProcessed = false;
var iterator;
if(this._headA.object != null) {
//console.log('---------------------------------------------------');
//console.log('headA iterator');
iterator = this._headA;
while(iterator != null) {
QuadTree._object = iterator.object;
if(QuadTree._useBothLists) {
QuadTree._iterator = this._headB;
} else {
QuadTree._iterator = iterator.next;
}
if(QuadTree._object.exists && (QuadTree._object.allowCollisions > 0) && (QuadTree._iterator != null) && (QuadTree._iterator.object != null) && QuadTree._iterator.object.exists && this.overlapNode()) {
//console.log('headA iterator overlapped true');
overlapProcessed = true;
}
iterator = iterator.next;
}
}
//Advance through the tree by calling overlap on each child
if((this._northWestTree != null) && this._northWestTree.execute()) {
//console.log('NW quadtree execute');
overlapProcessed = true;
}
if((this._northEastTree != null) && this._northEastTree.execute()) {
//console.log('NE quadtree execute');
overlapProcessed = true;
}
if((this._southEastTree != null) && this._southEastTree.execute()) {
//console.log('SE quadtree execute');
overlapProcessed = true;
}
if((this._southWestTree != null) && this._southWestTree.execute()) {
//console.log('SW quadtree execute');
overlapProcessed = true;
}
return overlapProcessed;
};
QuadTree.prototype.overlapNode = /**
* An private for comparing an object against the contents of a node.
*
* @return Whether or not any overlaps were found.
*/
function () {
//console.log('overlapNode');
//Walk the list and check for overlaps
var overlapProcessed = false;
var checkObject;
while(QuadTree._iterator != null) {
if(!QuadTree._object.exists || (QuadTree._object.allowCollisions <= 0)) {
//console.log('break 1');
break;
}
checkObject = QuadTree._iterator.object;
if((QuadTree._object === checkObject) || !checkObject.exists || (checkObject.allowCollisions <= 0)) {
//console.log('break 2');
QuadTree._iterator = QuadTree._iterator.next;
continue;
}
//calculate bulk hull for QuadTree._object
QuadTree._objectHullX = (QuadTree._object.x < QuadTree._object.last.x) ? QuadTree._object.x : QuadTree._object.last.x;
QuadTree._objectHullY = (QuadTree._object.y < QuadTree._object.last.y) ? QuadTree._object.y : QuadTree._object.last.y;
QuadTree._objectHullWidth = QuadTree._object.x - QuadTree._object.last.x;
QuadTree._objectHullWidth = QuadTree._object.width + ((QuadTree._objectHullWidth > 0) ? QuadTree._objectHullWidth : -QuadTree._objectHullWidth);
QuadTree._objectHullHeight = QuadTree._object.y - QuadTree._object.last.y;
QuadTree._objectHullHeight = QuadTree._object.height + ((QuadTree._objectHullHeight > 0) ? QuadTree._objectHullHeight : -QuadTree._objectHullHeight);
//calculate bulk hull for checkObject
QuadTree._checkObjectHullX = (checkObject.x < checkObject.last.x) ? checkObject.x : checkObject.last.x;
QuadTree._checkObjectHullY = (checkObject.y < checkObject.last.y) ? checkObject.y : checkObject.last.y;
QuadTree._checkObjectHullWidth = checkObject.x - checkObject.last.x;
QuadTree._checkObjectHullWidth = checkObject.width + ((QuadTree._checkObjectHullWidth > 0) ? QuadTree._checkObjectHullWidth : -QuadTree._checkObjectHullWidth);
QuadTree._checkObjectHullHeight = checkObject.y - checkObject.last.y;
QuadTree._checkObjectHullHeight = checkObject.height + ((QuadTree._checkObjectHullHeight > 0) ? QuadTree._checkObjectHullHeight : -QuadTree._checkObjectHullHeight);
//check for intersection of the two hulls
if((QuadTree._objectHullX + QuadTree._objectHullWidth > QuadTree._checkObjectHullX) && (QuadTree._objectHullX < QuadTree._checkObjectHullX + QuadTree._checkObjectHullWidth) && (QuadTree._objectHullY + QuadTree._objectHullHeight > QuadTree._checkObjectHullY) && (QuadTree._objectHullY < QuadTree._checkObjectHullY + QuadTree._checkObjectHullHeight)) {
//console.log('intersection!');
//Execute callback functions if they exist
if((QuadTree._processingCallback == null) || QuadTree._processingCallback(QuadTree._object, checkObject)) {
overlapProcessed = true;
}
if(overlapProcessed && (QuadTree._notifyCallback != null)) {
QuadTree._notifyCallback(QuadTree._object, checkObject);
}
}
QuadTree._iterator = QuadTree._iterator.next;
}
return overlapProcessed;
};
return QuadTree;
})(Rectangle);
/// GameObject overlaps another.
* Can be called with one object and one group, or two groups, or two objects,
* whatever floats your boat! For maximum performance try bundling a lot of objects
* together using a FlxGroup (or even bundling groups together!).
*
* NOTE: does NOT take objects' scrollfactor into account, all overlaps are checked in world space.
* * @param ObjectOrGroup1 The first object or group you want to check. * @param ObjectOrGroup2 The second object or group you want to check. If it is the same as the first, flixel knows to just do a comparison within that group. * @param NotifyCallback A function with twoGameObject parameters - e.g. myOverlapFunction(Object1:GameObject,Object2:GameObject) - that is called if those two objects overlap.
* @param ProcessCallback A function with two GameObject parameters - e.g. myOverlapFunction(Object1:GameObject,Object2:GameObject) - that is called if those two objects overlap. If a ProcessCallback is provided, then NotifyCallback will only be called if ProcessCallback returns true for those objects!
*
* @return Whether any overlaps were detected.
*/
function (ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, ProcessCallback) {
if (typeof ObjectOrGroup1 === "undefined") { ObjectOrGroup1 = null; }
if (typeof ObjectOrGroup2 === "undefined") { ObjectOrGroup2 = null; }
if (typeof NotifyCallback === "undefined") { NotifyCallback = null; }
if (typeof ProcessCallback === "undefined") { ProcessCallback = null; }
if(ObjectOrGroup1 == null) {
ObjectOrGroup1 = this.group;
}
if(ObjectOrGroup2 == ObjectOrGroup1) {
ObjectOrGroup2 = null;
}
QuadTree.divisions = this.worldDivisions;
var quadTree = new QuadTree(this.bounds.x, this.bounds.y, this.bounds.width, this.bounds.height);
quadTree.load(ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, ProcessCallback);
var result = quadTree.execute();
quadTree.destroy();
quadTree = null;
return result;
};
World.separate = /**
* The main collision resolution in flixel.
*
* @param Object1 Any Sprite.
* @param Object2 Any other Sprite.
*
* @return Whether the objects in fact touched and were separated.
*/
function separate(Object1, Object2) {
var separatedX = World.separateX(Object1, Object2);
var separatedY = World.separateY(Object1, Object2);
return separatedX || separatedY;
};
World.separateX = /**
* The X-axis component of the object separation process.
*
* @param Object1 Any Sprite.
* @param Object2 Any other Sprite.
*
* @return Whether the objects in fact touched and were separated along the X axis.
*/
function separateX(Object1, Object2) {
//can't separate two immovable objects
var obj1immovable = Object1.immovable;
var obj2immovable = Object2.immovable;
if(obj1immovable && obj2immovable) {
return false;
}
//If one of the objects is a tilemap, just pass it off.
/*
if (typeof Object1 === 'FlxTilemap')
{
return Object1.overlapsWithCallback(Object2, separateX);
}
if (typeof Object2 === 'FlxTilemap')
{
return Object2.overlapsWithCallback(Object1, separateX, true);
}
*/
//First, get the two object deltas
var overlap = 0;
var obj1delta = Object1.x - Object1.last.x;
var obj2delta = Object2.x - Object2.last.x;
if(obj1delta != obj2delta) {
//Check if the X hulls actually overlap
var obj1deltaAbs = (obj1delta > 0) ? obj1delta : -obj1delta;
var obj2deltaAbs = (obj2delta > 0) ? obj2delta : -obj2delta;
var obj1rect = new Rectangle(Object1.x - ((obj1delta > 0) ? obj1delta : 0), Object1.last.y, Object1.width + ((obj1delta > 0) ? obj1delta : -obj1delta), Object1.height);
var obj2rect = new Rectangle(Object2.x - ((obj2delta > 0) ? obj2delta : 0), Object2.last.y, Object2.width + ((obj2delta > 0) ? obj2delta : -obj2delta), Object2.height);
if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height)) {
var maxOverlap = obj1deltaAbs + obj2deltaAbs + GameObject.OVERLAP_BIAS;
//If they did overlap (and can), figure out by how much and flip the corresponding flags
if(obj1delta > obj2delta) {
overlap = Object1.x + Object1.width - Object2.x;
if((overlap > maxOverlap) || !(Object1.allowCollisions & GameObject.RIGHT) || !(Object2.allowCollisions & GameObject.LEFT)) {
overlap = 0;
} else {
Object1.touching |= GameObject.RIGHT;
Object2.touching |= GameObject.LEFT;
}
} else if(obj1delta < obj2delta) {
overlap = Object1.x - Object2.width - Object2.x;
if((-overlap > maxOverlap) || !(Object1.allowCollisions & GameObject.LEFT) || !(Object2.allowCollisions & GameObject.RIGHT)) {
overlap = 0;
} else {
Object1.touching |= GameObject.LEFT;
Object2.touching |= GameObject.RIGHT;
}
}
}
}
//Then adjust their positions and velocities accordingly (if there was any overlap)
if(overlap != 0) {
var obj1v = Object1.velocity.x;
var obj2v = Object2.velocity.x;
if(!obj1immovable && !obj2immovable) {
overlap *= 0.5;
Object1.x = Object1.x - overlap;
Object2.x += overlap;
var obj1velocity = Math.sqrt((obj2v * obj2v * Object2.mass) / Object1.mass) * ((obj2v > 0) ? 1 : -1);
var obj2velocity = Math.sqrt((obj1v * obj1v * Object1.mass) / Object2.mass) * ((obj1v > 0) ? 1 : -1);
var average = (obj1velocity + obj2velocity) * 0.5;
obj1velocity -= average;
obj2velocity -= average;
Object1.velocity.x = average + obj1velocity * Object1.elasticity;
Object2.velocity.x = average + obj2velocity * Object2.elasticity;
} else if(!obj1immovable) {
Object1.x = Object1.x - overlap;
Object1.velocity.x = obj2v - obj1v * Object1.elasticity;
} else if(!obj2immovable) {
Object2.x += overlap;
Object2.velocity.x = obj1v - obj2v * Object2.elasticity;
}
return true;
} else {
return false;
}
};
World.separateY = /**
* The Y-axis component of the object separation process.
*
* @param Object1 Any Sprite.
* @param Object2 Any other Sprite.
*
* @return Whether the objects in fact touched and were separated along the Y axis.
*/
function separateY(Object1, Object2) {
//can't separate two immovable objects
var obj1immovable = Object1.immovable;
var obj2immovable = Object2.immovable;
if(obj1immovable && obj2immovable) {
return false;
}
//If one of the objects is a tilemap, just pass it off.
/*
if (typeof Object1 === 'FlxTilemap')
{
return Object1.overlapsWithCallback(Object2, separateY);
}
if (typeof Object2 === 'FlxTilemap')
{
return Object2.overlapsWithCallback(Object1, separateY, true);
}
*/
//First, get the two object deltas
var overlap = 0;
var obj1delta = Object1.y - Object1.last.y;
var obj2delta = Object2.y - Object2.last.y;
if(obj1delta != obj2delta) {
//Check if the Y hulls actually overlap
var obj1deltaAbs = (obj1delta > 0) ? obj1delta : -obj1delta;
var obj2deltaAbs = (obj2delta > 0) ? obj2delta : -obj2delta;
var obj1rect = new Rectangle(Object1.x, Object1.y - ((obj1delta > 0) ? obj1delta : 0), Object1.width, Object1.height + obj1deltaAbs);
var obj2rect = new Rectangle(Object2.x, Object2.y - ((obj2delta > 0) ? obj2delta : 0), Object2.width, Object2.height + obj2deltaAbs);
if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height)) {
var maxOverlap = obj1deltaAbs + obj2deltaAbs + GameObject.OVERLAP_BIAS;
//If they did overlap (and can), figure out by how much and flip the corresponding flags
if(obj1delta > obj2delta) {
overlap = Object1.y + Object1.height - Object2.y;
if((overlap > maxOverlap) || !(Object1.allowCollisions & GameObject.DOWN) || !(Object2.allowCollisions & GameObject.UP)) {
overlap = 0;
} else {
Object1.touching |= GameObject.DOWN;
Object2.touching |= GameObject.UP;
}
} else if(obj1delta < obj2delta) {
overlap = Object1.y - Object2.height - Object2.y;
if((-overlap > maxOverlap) || !(Object1.allowCollisions & GameObject.UP) || !(Object2.allowCollisions & GameObject.DOWN)) {
overlap = 0;
} else {
Object1.touching |= GameObject.UP;
Object2.touching |= GameObject.DOWN;
}
}
}
}
//Then adjust their positions and velocities accordingly (if there was any overlap)
if(overlap != 0) {
var obj1v = Object1.velocity.y;
var obj2v = Object2.velocity.y;
if(!obj1immovable && !obj2immovable) {
overlap *= 0.5;
Object1.y = Object1.y - overlap;
Object2.y += overlap;
var obj1velocity = Math.sqrt((obj2v * obj2v * Object2.mass) / Object1.mass) * ((obj2v > 0) ? 1 : -1);
var obj2velocity = Math.sqrt((obj1v * obj1v * Object1.mass) / Object2.mass) * ((obj1v > 0) ? 1 : -1);
var average = (obj1velocity + obj2velocity) * 0.5;
obj1velocity -= average;
obj2velocity -= average;
Object1.velocity.y = average + obj1velocity * Object1.elasticity;
Object2.velocity.y = average + obj2velocity * Object2.elasticity;
} else if(!obj1immovable) {
Object1.y = Object1.y - overlap;
Object1.velocity.y = obj2v - obj1v * Object1.elasticity;
//This is special case code that handles cases like horizontal moving platforms you can ride
if(Object2.active && Object2.moves && (obj1delta > obj2delta)) {
Object1.x += Object2.x - Object2.last.x;
}
} else if(!obj2immovable) {
Object2.y += overlap;
Object2.velocity.y = obj1v - obj2v * Object2.elasticity;
//This is special case code that handles cases like horizontal moving platforms you can ride
if(Object1.active && Object1.moves && (obj1delta < obj2delta)) {
Object2.x += Object1.x - Object1.last.x;
}
}
return true;
} else {
return false;
}
};
return World;
})();
/// Tilemap that helps expand collision opportunities and control.
* You can use Tilemap.setTileProperties() to alter the collision properties and
* callback functions and filters for this object to do things like one-way tiles or whatever.
*
* @author Adam Atomic
* @author Richard Davey
*/
var Tile = (function (_super) {
__extends(Tile, _super);
/**
* Instantiate this new tile object. This is usually called from FlxTilemap.loadMap().
*
* @param Tilemap A reference to the tilemap object creating the tile.
* @param Index The actual core map data index for this tile type.
* @param Width The width of the tile.
* @param Height The height of the tile.
* @param Visible Whether the tile is visible or not.
* @param AllowCollisions The collision flags for the object. By default this value is ANY or NONE depending on the parameters sent to loadMap().
*/
function Tile(game, Tilemap, Index, Width, Height, Visible, AllowCollisions) {
_super.call(this, game, 0, 0, Width, Height);
this.immovable = true;
this.moves = false;
this.callback = null;
this.filter = null;
this.tilemap = Tilemap;
this.index = Index;
this.visible = Visible;
this.allowCollisions = AllowCollisions;
this.mapIndex = 0;
}
Tile.prototype.destroy = /**
* Clean up memory.
*/
function () {
_super.prototype.destroy.call(this);
this.callback = null;
this.tilemap = null;
};
return Tile;
})(GameObject);