phaser/Phaser/core/Group.ts

1006 lines
30 KiB
TypeScript
Raw Normal View History

2013-05-28 20:38:37 +00:00
/// <reference path="../Game.ts" />
/// <reference path="../Statics.ts" />
2013-04-12 16:19:56 +00:00
/**
2013-04-18 15:49:08 +00:00
* Phaser - Group
*
* This class is used for organising, updating and sorting game objects.
2013-04-18 13:16:18 +00:00
*/
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
module Phaser {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
export class Group {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
constructor(game: Game, maxSize?: number = 0) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this.game = game;
this.type = Phaser.Types.GROUP;
this.exists = true;
this.visible = true;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
this.members = [];
this.length = 0;
2013-05-28 20:38:37 +00:00
this._maxSize = maxSize;
2013-04-18 13:16:18 +00:00
this._marker = 0;
this._sortIndex = null;
2013-05-28 20:38:37 +00:00
this.cameraBlacklist = [];
2013-04-12 16:19:56 +00:00
this.ID = this.game.world.getNextGroupID();
2013-04-12 16:19:56 +00:00
}
/**
* Internal tracker for the maximum capacity of the group.
* Default is 0, or no max capacity.
*/
private _maxSize: number;
/**
* Internal helper variable for recycling objects a la <code>Emitter</code>.
*/
private _marker: number;
/**
* Helper for sort.
*/
private _sortIndex: string = '';
/**
* Helper for sort.
*/
private _sortOrder: number;
2013-05-28 20:38:37 +00:00
/**
* Temp vars to help avoid gc spikes
*/
private _member;
private _length: number;
private _i: number;
private _prevAlpha: number;
private _count: number;
/**
* This keeps track of the z value of any game object added to this Group
*/
private _zCounter: number = 0;
2013-05-28 20:38:37 +00:00
/**
* Reference to the main game object
*/
public game: Game;
/**
* The type of game object.
*/
public type: number;
/**
* The unique Group ID
*/
public ID: number = -1;
/**
* The z value of this Group (within its parent Group, if any)
*/
public z: number = -1;
/**
* The Group this Group is a child of (if any).
*/
public group: Group = null;
2013-05-28 20:38:37 +00:00
/**
* If this Group exists or not. Can be set to false to skip certain loop checks.
*/
public exists: bool;
/**
* Controls if this Group (and all of its contents) are rendered or skipped during the core game loop.
*/
public visible: bool;
2013-04-18 13:16:18 +00:00
/**
* Use with <code>sort()</code> to sort in ascending order.
*/
public static ASCENDING: number = -1;
/**
* Use with <code>sort()</code> to sort in descending order.
*/
public static DESCENDING: number = 1;
/**
2013-05-28 20:38:37 +00:00
* Array of all the objects that exist in this group.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public members;
2013-04-18 13:16:18 +00:00
/**
* The number of entries in the members array.
* For performance and safety you should check this variable
* instead of members.length unless you really know what you're doing!
*/
public length: number;
/**
* You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children.
* This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one.
* If this value is set it will call a canvas context save and restore before and after the render pass.
* Set to null to disable.
2013-04-18 13:16:18 +00:00
*/
public globalCompositeOperation: string = null;
2013-04-18 13:16:18 +00:00
/**
* You can set an alpha value on this Group that will be applied before the render method is called on this Groups children.
* This is useful if you wish to alpha a whole group of children as it saves doing it one-by-one.
* Set to 0 to disable.
2013-04-18 13:16:18 +00:00
*/
public alpha: number = 0;
2013-04-18 13:16:18 +00:00
/**
* An Array of Cameras to which this Group, or any of its children, won't render
* @type {Array}
2013-04-18 13:16:18 +00:00
*/
public cameraBlacklist: number[];
2013-04-18 13:16:18 +00:00
/**
* Gets the next z index value for children of this Group
*/
public getNextZIndex(): number {
return this._zCounter++;
}
2013-04-18 13:16:18 +00:00
/**
* Override this function to handle any deleting or "shutdown" type operations you might need,
* such as removing traditional Flash children like Basic objects.
*/
public destroy() {
if (this.members != null)
{
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.destroy();
2013-04-18 13:16:18 +00:00
}
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
this.members.length = 0;
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
this._sortIndex = null;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
2013-05-28 20:38:37 +00:00
* Calls update on all members of this Group who have a status of active=true and exists=true
* You can also call Object.update directly, which will bypass the active/exists check.
*/
public update() {
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null && this._member.exists && this._member.active)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.preUpdate();
this._member.update();
2013-05-28 20:38:37 +00:00
this._member.postUpdate();
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
2013-04-18 13:16:18 +00:00
/**
2013-05-28 20:38:37 +00:00
* Calls render on all members of this Group who have a status of visible=true and exists=true
* You can also call Object.render directly, which will bypass the visible/exists check.
*/
public render(camera: Camera) {
2013-05-28 20:38:37 +00:00
if (camera.isHidden(this) == true)
{
return;
}
2013-04-12 16:19:56 +00:00
if (this.globalCompositeOperation)
{
2013-05-28 20:38:37 +00:00
this.game.stage.context.save();
this.game.stage.context.globalCompositeOperation = this.globalCompositeOperation;
}
if (this.alpha > 0)
{
2013-05-28 20:38:37 +00:00
this._prevAlpha = this.game.stage.context.globalAlpha;
this.game.stage.context.globalAlpha = this.alpha;
}
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null && this._member.exists && this._member.visible && camera.isHidden(this._member) == false)
2013-04-18 13:16:18 +00:00
{
if (this._member.type == Types.GROUP)
{
this._member.render(camera);
}
else
{
this.game.renderer.renderGameObject(this._member);
}
}
}
if (this.alpha > 0)
{
this.game.stage.context.globalAlpha = this._prevAlpha;
}
if (this.globalCompositeOperation)
{
this.game.stage.context.restore();
}
}
/**
* Calls render on all members of this Group regardless of their visible status and also ignores the camera blacklist.
* Use this when the Group objects render to hidden canvases for example.
*/
public directRender(camera: Camera) {
if (this.globalCompositeOperation)
{
this.game.stage.context.save();
this.game.stage.context.globalCompositeOperation = this.globalCompositeOperation;
}
if (this.alpha > 0)
{
this._prevAlpha = this.game.stage.context.globalAlpha;
this.game.stage.context.globalAlpha = this.alpha;
}
this._i = 0;
while (this._i < this.length)
{
this._member = this.members[this._i++];
if (this._member != null && this._member.exists)
{
if (this._member.type == Types.GROUP)
{
this._member.directRender(camera);
}
else
{
this.game.renderer.renderGameObject(this._member);
}
2013-04-18 13:16:18 +00:00
}
}
if (this.alpha > 0)
{
2013-05-28 20:38:37 +00:00
this.game.stage.context.globalAlpha = this._prevAlpha;
}
if (this.globalCompositeOperation)
{
2013-05-28 20:38:37 +00:00
this.game.stage.context.restore();
}
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* The maximum capacity of this group. Default is 0, meaning no max capacity, and the group can just grow.
*/
public get maxSize(): number {
return this._maxSize;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* @private
*/
public set maxSize(Size: number) {
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
this._maxSize = Size;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
if (this._marker >= this._maxSize)
2013-04-12 16:19:56 +00:00
{
2013-04-18 13:16:18 +00:00
this._marker = 0;
2013-04-12 16:19:56 +00:00
}
2013-05-28 20:38:37 +00:00
if (this._maxSize == 0 || this.members == null || (this._maxSize >= this.members.length))
2013-04-18 13:16:18 +00:00
{
return;
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
//If the max size has shrunk, we need to get rid of some objects
2013-05-28 20:38:37 +00:00
this._i = this._maxSize;
this._length = this.members.length;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this._length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.destroy();
2013-04-12 16:19:56 +00:00
}
}
2013-04-18 13:16:18 +00:00
this.length = this.members.length = this._maxSize;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* Adds a new Game Object to the group.
2013-04-18 13:16:18 +00:00
* 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.
*
* <p>WARNING: If the group has a maxSize that has already been met,
* the object will NOT be added to the group!</p>
*
* @param {Basic} Object The object you want to add to the group.
* @return {Basic} The same object that was passed in.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public add(object): any {
2013-04-18 13:16:18 +00:00
// Is this object already in another Group?
// You can't add a Group to itself or an object to the same Group twice
if (object.group && (object.group.ID == this.ID || (object.type == Types.GROUP && object.ID == this.ID)))
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
return object;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
// First, look for a null entry where we can add the object.
2013-05-28 20:38:37 +00:00
this._i = 0;
this._length = this.members.length;
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this._length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (this.members[this._i] == null)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this.members[this._i] = object;
2013-04-18 13:16:18 +00:00
this.setObjectIDs(object);
2013-05-28 20:38:37 +00:00
if (this._i >= this.length)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this.length = this._i + 1;
2013-04-18 13:16:18 +00:00
}
2013-05-28 20:38:37 +00:00
return object;
2013-04-18 13:16:18 +00:00
}
2013-05-28 20:38:37 +00:00
this._i++;
2013-04-18 13:16:18 +00:00
}
// Failing that, expand the array (if we can) and add the object.
2013-04-18 13:16:18 +00:00
if (this._maxSize > 0)
{
if (this.members.length >= this._maxSize)
{
2013-05-28 20:38:37 +00:00
return object;
2013-04-18 13:16:18 +00:00
}
else if (this.members.length * 2 <= this._maxSize)
{
this.members.length *= 2;
}
else
{
this.members.length = this._maxSize;
}
2013-04-12 16:19:56 +00:00
}
else
{
2013-04-18 13:16:18 +00:00
this.members.length *= 2;
2013-04-12 16:19:56 +00:00
}
// 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.
2013-05-28 20:38:37 +00:00
this.members[this._i] = object;
this.length = this._i + 1;
2013-04-12 16:19:56 +00:00
this.setObjectIDs(object);
2013-05-28 20:38:37 +00:00
return object;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
/**
* Create a new Sprite within this Group at the specified position.
*
* @param x {number} X position of the new sprite.
* @param y {number} Y position of the new sprite.
* @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite
* @param [bodyType] {number} The physics body type of the object (defaults to BODY_DISABLED)
* @returns {Sprite} The newly created sprite object.
*/
public addNewSprite(x: number, y: number, key?: string = '', bodyType?: number = Phaser.Types.BODY_DISABLED): Sprite {
return <Sprite> this.add(new Sprite(this.game, x, y, key, bodyType));
}
/**
* Sets all of the game object properties needed to exist within this Group.
*/
private setObjectIDs(object, zIndex: number = -1) {
// If the object is already in another Group, inform that Group it has left
if (object.group !== null)
{
object.group.remove(object);
}
object.group = this;
if (zIndex == -1)
{
zIndex = this.getNextZIndex();
}
object.z = zIndex;
if (object['events'])
{
object['events'].onAddedToGroup.dispatch(object, this, object.z);
}
}
2013-04-18 13:16:18 +00:00
/**
* Recycling is designed to help you reuse game objects without always re-allocating or "newing" them.
2013-05-04 16:18:45 +00:00
*
2013-04-18 13:16:18 +00:00
* <p>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.</p>
2013-05-04 16:18:45 +00:00
*
2013-04-18 13:16:18 +00:00
* <p>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.</p>
2013-05-04 16:18:45 +00:00
*
2013-04-18 13:16:18 +00:00
* <p>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!</p>
2013-05-04 16:18:45 +00:00
*
* @param {class} ObjectClass The class type you want to recycle (e.g. Basic, EvilRobot, etc). Do NOT "new" the class in the parameter!
2013-05-04 16:18:45 +00:00
*
* @return {any} 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;).
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public recycle(objectClass = null) {
2013-04-18 13:16:18 +00:00
if (this._maxSize > 0)
2013-04-12 16:19:56 +00:00
{
2013-04-18 13:16:18 +00:00
if (this.length < this._maxSize)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (objectClass == null)
2013-04-18 13:16:18 +00:00
{
return null;
}
2013-05-28 20:38:37 +00:00
return this.add(new objectClass(this.game));
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
else
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._marker++];
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
if (this._marker >= this._maxSize)
{
this._marker = 0;
}
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
else
{
2013-05-28 20:38:37 +00:00
this._member = this.getFirstAvailable(objectClass);
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (objectClass == null)
2013-04-12 16:19:56 +00:00
{
2013-04-18 13:16:18 +00:00
return null;
2013-04-12 16:19:56 +00:00
}
2013-05-28 20:38:37 +00:00
return this.add(new objectClass(this.game));
2013-04-12 16:19:56 +00:00
}
}
2013-04-18 13:16:18 +00:00
/**
* Removes an object from the group.
2013-05-04 16:18:45 +00:00
*
* @param {Basic} object The Game Object you want to remove.
* @param {boolean} splice Whether the object should be cut from the array entirely or not.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} The removed object.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public remove(object, splice: bool = false) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = this.members.indexOf(object);
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._i < 0 || (this._i >= this.members.length))
2013-04-12 16:19:56 +00:00
{
return null;
}
if (splice)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this.members.splice(this._i, 1);
2013-04-18 13:16:18 +00:00
this.length--;
}
else
{
2013-05-28 20:38:37 +00:00
this.members[this._i] = null;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
if (object['events'])
{
object['events'].onRemovedFromGroup.dispatch(object, this);
}
object.group = null;
object.z = -1;
return object;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* Replaces an existing game object in this Group with a new one.
2013-05-04 16:18:45 +00:00
*
* @param {Basic} oldObject The object you want to replace.
* @param {Basic} newObject The new object you want to use instead.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} The new object.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public replace(oldObject, newObject) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = this.members.indexOf(oldObject);
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._i < 0 || (this._i >= this.members.length))
2013-04-18 13:16:18 +00:00
{
return null;
}
2013-04-12 16:19:56 +00:00
this.setObjectIDs(newObject, this.members[this._i].z);
// Null the old object
this.remove(this.members[this._i]);
2013-05-28 20:38:37 +00:00
this.members[this._i] = newObject;
2013-04-12 16:19:56 +00:00
return newObject;
2013-04-12 16:19:56 +00:00
}
public swap(child1, child2, sort?: bool = true): bool {
if (child1.group.ID != this.ID || child2.group.ID != this.ID || child1 === child2)
{
return false;
}
var tempZ: number = child1.z;
child1.z = child2.z;
child2.z = tempZ;
if (sort)
{
this.sort();
}
return true;
}
2013-04-18 13:16:18 +00:00
/**
* 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
* <code>myGroup.sort("y",Group.ASCENDING)</code> at the bottom of your
* <code>State.update()</code> override. To sort all existing objects after
2013-04-18 13:16:18 +00:00
* a big explosion or bomb attack, you might call <code>myGroup.sort("exists",Group.DESCENDING)</code>.
2013-05-04 16:18:45 +00:00
*
* @param {string} index The <code>string</code> name of the member variable you want to sort on. Default value is "y".
* @param {number} order A <code>Group</code> constant that defines the sort order. Possible values are <code>Group.ASCENDING</code> and <code>Group.DESCENDING</code>. Default value is <code>Group.ASCENDING</code>.
2013-04-18 13:16:18 +00:00
*/
public sort(index: string = 'z', order: number = Group.ASCENDING) {
2013-04-18 13:16:18 +00:00
this._sortIndex = index;
this._sortOrder = order;
this.members.sort((a, b) => this.sortHandler(a, b));
}
/**
* Helper function for the sort process.
*
* @param {Basic} Obj1 The first object being sorted.
* @param {Basic} Obj2 The second object being sorted.
*
* @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2).
*/
public sortHandler(obj1, obj2): number {
if (obj1[this._sortIndex] < obj2[this._sortIndex])
{
return this._sortOrder;
}
else if (obj1[this._sortIndex] > obj2[this._sortIndex])
{
return -this._sortOrder;
}
return 0;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* Go through and set the specified variable to the specified value on all members of the group.
2013-05-04 16:18:45 +00:00
*
* @param {string} VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor".
* @param {Object} Value The value you want to assign to that variable.
* @param {boolean} Recurse Default value is true, meaning if <code>setAll()</code> encounters a member that is a group, it will call <code>setAll()</code> on that group rather than modifying its variable.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public setAll(variableName: string, value: Object, recurse: bool = true) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (recurse && this._member.type == Phaser.Types.GROUP)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.setAll(variableName, value, recurse);
2013-04-18 13:16:18 +00:00
}
else
{
2013-05-28 20:38:37 +00:00
this._member[variableName] = value;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
}
2013-04-18 13:16:18 +00:00
/**
* Go through and call the specified function on all members of the group.
* Currently only works on functions that have no required parameters.
2013-05-04 16:18:45 +00:00
*
* @param {string} FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()".
* @param {boolean} Recurse Default value is true, meaning if <code>callAll()</code> encounters a member that is a group, it will call <code>callAll()</code> on that group rather than calling the group's function.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public callAll(functionName: string, recurse: bool = true) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (recurse && this._member.type == Phaser.Types.GROUP)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.callAll(functionName, recurse);
2013-04-18 13:16:18 +00:00
}
else
{
2013-05-28 20:38:37 +00:00
this._member[functionName]();
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
}
/**
* @param {function} callback
* @param {boolean} recursive
*/
public forEach(callback, recursive: bool = false) {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (recursive && this._member.type == Phaser.Types.GROUP)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.forEach(callback, true);
2013-04-18 13:16:18 +00:00
}
else
{
2013-05-28 20:38:37 +00:00
callback.call(this, this._member);
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
/**
* @param {any} context
* @param {function} callback
* @param {boolean} recursive
*/
public forEachAlive(context, callback, recursive: bool = false) {
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-05-28 20:38:37 +00:00
if (this._member != null && this._member.alive)
{
2013-05-28 20:38:37 +00:00
if (recursive && this._member.type == Phaser.Types.GROUP)
{
2013-05-28 20:38:37 +00:00
this._member.forEachAlive(context, callback, true);
}
else
{
2013-05-28 20:38:37 +00:00
callback.call(context, this._member);
}
}
}
}
2013-04-18 13:16:18 +00:00
/**
* 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.
2013-05-04 16:18:45 +00:00
*
* @param {any} [ObjectClass] An optional parameter that lets you narrow the results to instances of this particular class.
2013-05-04 16:18:45 +00:00
*
* @return {any} A <code>Basic</code> currently flagged as not existing.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public getFirstAvailable(objectClass = null) {
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if ((this._member != null) && !this._member.exists && ((objectClass == null) || (typeof this._member === objectClass)))
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
return null;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* Call this function to retrieve the first index set to 'null'.
* Returns -1 if no index stores a null object.
2013-05-04 16:18:45 +00:00
*
* @return {number} An <code>int</code> indicating the first null slot in the group.
2013-04-18 13:16:18 +00:00
*/
public getFirstNull(): number {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (this.members[this._i] == null)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._i;
2013-04-18 13:16:18 +00:00
}
else
{
2013-05-28 20:38:37 +00:00
this._i++;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
return -1;
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* 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.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} A <code>Basic</code> currently flagged as existing.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public getFirstExtant() {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null && this._member.exists)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
return null;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* 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.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} A <code>Basic</code> currently flagged as not dead.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public getFirstAlive() {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if ((this._member != null) && this._member.exists && this._member.alive)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
return null;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* 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.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} A <code>Basic</code> currently flagged as dead.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public getFirstDead() {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if ((this._member != null) && !this._member.alive)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
return this._member;
2013-04-18 13:16:18 +00:00
}
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
return null;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* Call this function to find out how many members of the group are not dead.
2013-05-04 16:18:45 +00:00
*
* @return {number} The number of <code>Basic</code>s flagged as not dead. Returns -1 if group is empty.
2013-04-18 13:16:18 +00:00
*/
public countLiving(): number {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._count = -1;
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (this._count < 0)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._count = 0;
2013-04-18 13:16:18 +00:00
}
2013-05-28 20:38:37 +00:00
if (this._member.exists && this._member.alive)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._count++;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
2013-05-28 20:38:37 +00:00
return this._count;
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* Call this function to find out how many members of the group are dead.
2013-05-04 16:18:45 +00:00
*
* @return {number} The number of <code>Basic</code>s flagged as dead. Returns -1 if group is empty.
2013-04-18 13:16:18 +00:00
*/
public countDead(): number {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._count = -1;
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if (this._member != null)
2013-04-12 16:19:56 +00:00
{
2013-05-28 20:38:37 +00:00
if (this._count < 0)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._count = 0;
2013-04-18 13:16:18 +00:00
}
2013-05-28 20:38:37 +00:00
if (!this._member.alive)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._count++;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
return this._count;
2013-04-18 13:16:18 +00:00
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* Returns a member at random from the group.
2013-05-04 16:18:45 +00:00
*
* @param {number} StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array.
* @param {number} Length Optional restriction on the number of values you want to randomly select from.
2013-05-04 16:18:45 +00:00
*
* @return {Basic} A <code>Basic</code> from the members list.
2013-04-18 13:16:18 +00:00
*/
2013-05-28 20:38:37 +00:00
public getRandom(startIndex: number = 0, length: number = 0) {
2013-04-18 13:16:18 +00:00
2013-05-28 20:38:37 +00:00
if (length == 0)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
length = this.length;
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
return this.game.math.getRandom(this.members, startIndex, length);
2013-04-12 16:19:56 +00:00
}
2013-04-18 13:16:18 +00:00
/**
* Remove all instances of <code>Basic</code> subclass (Basic, Block, etc) from the list.
2013-04-18 13:16:18 +00:00
* WARNING: does not destroy() or kill() any of these objects!
*/
public clear() {
this.length = this.members.length = 0;
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
/**
* Calls kill on the group's members and then on the group itself.
*/
public kill() {
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
this._i = 0;
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
while (this._i < this.length)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member = this.members[this._i++];
2013-04-12 16:19:56 +00:00
2013-05-28 20:38:37 +00:00
if ((this._member != null) && this._member.exists)
2013-04-18 13:16:18 +00:00
{
2013-05-28 20:38:37 +00:00
this._member.kill();
2013-04-18 13:16:18 +00:00
}
}
2013-04-12 16:19:56 +00:00
2013-04-18 13:16:18 +00:00
}
2013-04-12 16:19:56 +00:00
}
}