diff --git a/Docs/Phaser Logo/phaser space paint 08.png b/Docs/Phaser Logo/PNG/Phaser Logo Print Quality.png similarity index 100% rename from Docs/Phaser Logo/phaser space paint 08.png rename to Docs/Phaser Logo/PNG/Phaser Logo Print Quality.png diff --git a/Docs/Phaser Logo/PNG/Phaser Logo Web Quality.png b/Docs/Phaser Logo/PNG/Phaser Logo Web Quality.png new file mode 100644 index 000000000..24f62a61a Binary files /dev/null and b/Docs/Phaser Logo/PNG/Phaser Logo Web Quality.png differ diff --git a/Docs/Phaser Logo/PNG/Phaser Logo iPad Resolution.png b/Docs/Phaser Logo/PNG/Phaser Logo iPad Resolution.png new file mode 100644 index 000000000..04ff2f9b2 Binary files /dev/null and b/Docs/Phaser Logo/PNG/Phaser Logo iPad Resolution.png differ diff --git a/Docs/Phaser Logo/PNG/Phaser-Logo-Small.png b/Docs/Phaser Logo/PNG/Phaser-Logo-Small.png new file mode 100644 index 000000000..a37a23af6 Binary files /dev/null and b/Docs/Phaser Logo/PNG/Phaser-Logo-Small.png differ diff --git a/Docs/Phaser Logo/phaser_logo.png b/Docs/Phaser Logo/Pixel Art/Phaser Logo.png similarity index 100% rename from Docs/Phaser Logo/phaser_logo.png rename to Docs/Phaser Logo/Pixel Art/Phaser Logo.png diff --git a/Docs/Phaser Logo/Pixel Art/Phaser-Logo-Sizes.png b/Docs/Phaser Logo/Pixel Art/Phaser-Logo-Sizes.png new file mode 100644 index 000000000..49f3d2ddb Binary files /dev/null and b/Docs/Phaser Logo/Pixel Art/Phaser-Logo-Sizes.png differ diff --git a/Docs/Phaser Logo/phaser logo sprite.gif b/Docs/Phaser Logo/Pixel Art/phaser logo sprite.gif similarity index 100% rename from Docs/Phaser Logo/phaser logo sprite.gif rename to Docs/Phaser Logo/Pixel Art/phaser logo sprite.gif diff --git a/Docs/Phaser Logo/phaser vector final.eps b/Docs/Phaser Logo/Vector/Phaser Logo.eps similarity index 100% rename from Docs/Phaser Logo/phaser vector final.eps rename to Docs/Phaser Logo/Vector/Phaser Logo.eps diff --git a/Docs/Phaser Logo/phaser vector final.fla b/Docs/Phaser Logo/Vector/Phaser Logo.fla similarity index 100% rename from Docs/Phaser Logo/phaser vector final.fla rename to Docs/Phaser Logo/Vector/Phaser Logo.fla diff --git a/Docs/Phaser Logo/PHASER logotype vector 02.fla b/Docs/Phaser Logo/concepts/PHASER logotype vector 02.fla similarity index 100% rename from Docs/Phaser Logo/PHASER logotype vector 02.fla rename to Docs/Phaser Logo/concepts/PHASER logotype vector 02.fla diff --git a/Docs/Phaser Logo/phaser vector final.png b/Docs/Phaser Logo/concepts/phaser vector final.png similarity index 100% rename from Docs/Phaser Logo/phaser vector final.png rename to Docs/Phaser Logo/concepts/phaser vector final.png diff --git a/Docs/Phaser Logo/phaserWIP4.png b/Docs/Phaser Logo/concepts/phaserWIP4.png similarity index 100% rename from Docs/Phaser Logo/phaserWIP4.png rename to Docs/Phaser Logo/concepts/phaserWIP4.png diff --git a/Docs/Phaser Logo/phaserSprite.gif b/Docs/Phaser Logo/phaserSprite.gif deleted file mode 100644 index efa9c38f4..000000000 Binary files a/Docs/Phaser Logo/phaserSprite.gif and /dev/null differ diff --git a/Phaser/Game.js b/Phaser/Game.js index 3eec63d53..d41be4617 100644 --- a/Phaser/Game.js +++ b/Phaser/Game.js @@ -238,10 +238,6 @@ var Phaser; } }; - Game.prototype.emptyCallback = function () { - // Called by onUpdateCallback etc - }; - /** * Game loop method will be called when it's running. */ diff --git a/Phaser/Phaser.csproj b/Phaser/Phaser.csproj index f802d96cb..419ada30b 100644 --- a/Phaser/Phaser.csproj +++ b/Phaser/Phaser.csproj @@ -2,7 +2,7 @@ Debug - {A90BE60F-CAEA-4747-904A-CDB097BA2459} + {BB30C59B-5B34-4F7C-B5CC-8D49EA280EDA} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library bin @@ -17,6 +17,122 @@ + + + + + + + Behaviour.ts + + + + RandomDrift.ts + + + Initialize.ts + + + + Life.ts + + + Mass.ts + + + + + Position.ts + + + Radius.ts + + + + Rate.ts + + + + Velocity.ts + + + + NumericalIntegration.ts + + + ParticleManager.ts + + + ParticlePool.ts + + + + ParticleUtils.ts + + + + + Polar2D.ts + + + Span.ts + + + + + PointZone.ts + + + Zone.ts + + + + + AABB.ts + + + + + + + ProjAABBConcave.ts + + + ProjAABBConvex.ts + + + ProjAABBFull.ts + + + + Body.ts + + + Circle.ts + + + + + ProjCircle45Deg.ts + + + + ProjCircleConcave.ts + + + + ProjCircleConvex.ts + + + ProjCircleFull.ts + + + PhysicsManager.ts + + + + TileMapCell.ts + CanvasUtils.ts @@ -285,10 +401,6 @@ TilemapLayer.ts - - - Body.ts - Events.ts @@ -398,6 +510,7 @@ Game.ts + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/Phaser/_definitions.ts b/Phaser/_definitions.ts index 45d1c0843..0f4854610 100644 --- a/Phaser/_definitions.ts +++ b/Phaser/_definitions.ts @@ -78,8 +78,6 @@ /// /// -/// - /// /// /// @@ -111,6 +109,38 @@ /// /// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + /// /// /// diff --git a/Phaser/core/Group.js b/Phaser/core/Group.js index 633d450a0..ba646ba5e 100644 --- a/Phaser/core/Group.js +++ b/Phaser/core/Group.js @@ -35,6 +35,7 @@ var Phaser; this.modified = false; this.game = game; this.type = Phaser.Types.GROUP; + this.active = true; this.exists = true; this.visible = true; @@ -95,7 +96,9 @@ var Phaser; this._member = this.members[this._i++]; if (this._member != null && this._member.exists && this._member.active) { - this._member.preUpdate(); + if (this._member.type != Phaser.Types.GROUP) { + this._member.preUpdate(); + } this._member.update(); } } diff --git a/Phaser/gameobjects/GameObjectFactory.js b/Phaser/gameobjects/GameObjectFactory.js index 359ba73dd..5dfdd3e6c 100644 --- a/Phaser/gameobjects/GameObjectFactory.js +++ b/Phaser/gameobjects/GameObjectFactory.js @@ -84,6 +84,19 @@ var Phaser; return this.game.sound.add(key, volume, loop); }; + GameObjectFactory.prototype.circle = function (x, y, radius) { + return new Phaser.Physics.Circle(this.game, x, y, radius); + }; + + GameObjectFactory.prototype.aabb = function (x, y, width, height) { + return new Phaser.Physics.AABB(this.game, x, y, Math.floor(width / 2), Math.floor(height / 2)); + }; + + GameObjectFactory.prototype.cell = function (x, y, width, height, state) { + if (typeof state === "undefined") { state = Phaser.Physics.TileMapCell.TID_FULL; } + return new Phaser.Physics.TileMapCell(this.game, x, y, width, height).SetState(state); + }; + /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -125,10 +138,9 @@ var Phaser; * * @return {Particle} The newly created particle object. */ - GameObjectFactory.prototype.particle = function () { - return new Phaser.ArcadeParticle(this.game); - }; - + //public particle(): Phaser.ArcadeParticle { + // return new Phaser.ArcadeParticle(this.game); + //} /** * Create a new Emitter. * @@ -137,13 +149,9 @@ var Phaser; * @param size {number} Optional, size of this emitter. * @return {Emitter} The newly created emitter object. */ - GameObjectFactory.prototype.emitter = function (x, y, size) { - if (typeof x === "undefined") { x = 0; } - if (typeof y === "undefined") { y = 0; } - if (typeof size === "undefined") { size = 0; } - return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); - }; - + //public emitter(x: number = 0, y: number = 0, size: number = 0): Phaser.ArcadeEmitter { + // return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); + //} /** * Create a new ScrollZone object with image key, position and size. * @@ -242,10 +250,9 @@ var Phaser; * @param emitter The Emitter to add to the Game World * @return {Phaser.Emitter} The Emitter object */ - GameObjectFactory.prototype.existingEmitter = function (emitter) { - return this._world.group.add(emitter); - }; - + //public existingEmitter(emitter: Phaser.ArcadeEmitter): Phaser.ArcadeEmitter { + // return this._world.group.add(emitter); + //} /** * Add an existing ScrollZone to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. diff --git a/Phaser/gameobjects/GameObjectFactory.ts b/Phaser/gameobjects/GameObjectFactory.ts index e9fa70281..bbc64b1c9 100644 --- a/Phaser/gameobjects/GameObjectFactory.ts +++ b/Phaser/gameobjects/GameObjectFactory.ts @@ -89,6 +89,18 @@ module Phaser { return this.game.sound.add(key, volume, loop); } + public circle(x: number, y: number, radius: number): Phaser.Physics.Circle { + return new Phaser.Physics.Circle(this.game, x, y, radius); + } + + public aabb(x: number, y: number, width: number, height:number): Phaser.Physics.AABB { + return new Phaser.Physics.AABB(this.game, x, y, Math.floor(width / 2), Math.floor(height / 2)); + } + + public cell(x: number, y: number, width: number, height: number, state: number = Phaser.Physics.TileMapCell.TID_FULL): Phaser.Physics.TileMapCell { + return new Phaser.Physics.TileMapCell(this.game, x, y, width, height).SetState(state); + } + /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -130,9 +142,9 @@ module Phaser { * * @return {Particle} The newly created particle object. */ - public particle(): Phaser.ArcadeParticle { - return new Phaser.ArcadeParticle(this.game); - } + //public particle(): Phaser.ArcadeParticle { + // return new Phaser.ArcadeParticle(this.game); + //} /** * Create a new Emitter. @@ -142,9 +154,9 @@ module Phaser { * @param size {number} Optional, size of this emitter. * @return {Emitter} The newly created emitter object. */ - public emitter(x: number = 0, y: number = 0, size: number = 0): Phaser.ArcadeEmitter { - return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); - } + //public emitter(x: number = 0, y: number = 0, size: number = 0): Phaser.ArcadeEmitter { + // return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); + //} /** * Create a new ScrollZone object with image key, position and size. @@ -237,9 +249,9 @@ module Phaser { * @param emitter The Emitter to add to the Game World * @return {Phaser.Emitter} The Emitter object */ - public existingEmitter(emitter: Phaser.ArcadeEmitter): Phaser.ArcadeEmitter { - return this._world.group.add(emitter); - } + //public existingEmitter(emitter: Phaser.ArcadeEmitter): Phaser.ArcadeEmitter { + // return this._world.group.add(emitter); + //} /** * Add an existing ScrollZone to the current world. diff --git a/Phaser/gameobjects/TransformManager.js b/Phaser/gameobjects/TransformManager.js index 540e04305..ec88cc7e1 100644 --- a/Phaser/gameobjects/TransformManager.js +++ b/Phaser/gameobjects/TransformManager.js @@ -144,8 +144,7 @@ var Phaser; TransformManager.prototype.centerOn = function (x, y) { this.parent.x = x + (this.parent.x - this.center.x); this.parent.y = y + (this.parent.y - this.center.y); - - this.setCache(); + //this.setCache(); }; /** @@ -164,10 +163,8 @@ var Phaser; this._size.y = this.parent.height; this._origin.x = this.origin.x; this._origin.y = this.origin.y; - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); + this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._prevRotation = this.rotation; if (this.parent.texture && this.parent.texture.renderRotation) { @@ -188,6 +185,9 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + this._flippedX = this.parent.texture.flippedX; + this._flippedY = this.parent.texture.flippedY; }; /** @@ -213,9 +213,7 @@ var Phaser; this._dirty = true; } - if (this.rotation != this._prevRotation) { - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + if (this.rotation != this._prevRotation || this._dirty) { this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); @@ -243,27 +241,35 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + // Translate + this.local.data[2] = this.parent.x; + this.local.data[5] = this.parent.y; } - if (this.parent.texture.flippedX) { - this.local.data[0] = this._sc.y * -this.scale.x; - this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; - } else { - this.local.data[0] = this._sc.y * this.scale.x; - this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + if (this._dirty || this._flippedX != this.parent.texture.flippedX) { + this._flippedX = this.parent.texture.flippedX; + + if (this._flippedX) { + this.local.data[0] = this._sc.y * -this.scale.x; + this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; + } else { + this.local.data[0] = this._sc.y * this.scale.x; + this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + } } - if (this.parent.texture.flippedY) { - this.local.data[4] = this._sc.y * -this.scale.y; - this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; - } else { - this.local.data[4] = this._sc.y * this.scale.y; - this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; - } + if (this._dirty || this._flippedY != this.parent.texture.flippedY) { + this._flippedY = this.parent.texture.flippedY; - // Translate - this.local.data[2] = this.parent.x; - this.local.data[5] = this.parent.y; + if (this._flippedY) { + this.local.data[4] = this._sc.y * -this.scale.y; + this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; + } else { + this.local.data[4] = this._sc.y * this.scale.y; + this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; + } + } }; return TransformManager; })(); diff --git a/Phaser/gameobjects/TransformManager.ts b/Phaser/gameobjects/TransformManager.ts index b826be285..b72fe5a1a 100644 --- a/Phaser/gameobjects/TransformManager.ts +++ b/Phaser/gameobjects/TransformManager.ts @@ -57,6 +57,8 @@ module Phaser.Components { private _angle: number; private _distance: number; private _prevRotation: number; + private _flippedX: boolean; + private _flippedY: boolean; /** * Reference to Phaser.Game @@ -197,7 +199,7 @@ module Phaser.Components { this.parent.x = x + (this.parent.x - this.center.x); this.parent.y = y + (this.parent.y - this.center.y); - this.setCache(); + //this.setCache(); } @@ -218,10 +220,8 @@ module Phaser.Components { this._size.y = this.parent.height; this._origin.x = this.origin.x; this._origin.y = this.origin.y; - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); + this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._prevRotation = this.rotation; if (this.parent.texture && this.parent.texture.renderRotation) @@ -246,6 +246,9 @@ module Phaser.Components { this._pos.x = this.parent.x; this._pos.y = this.parent.y; + this._flippedX = this.parent.texture.flippedX; + this._flippedY = this.parent.texture.flippedY; + } /** @@ -274,10 +277,8 @@ module Phaser.Components { } // 2) Rotation change - if (this.rotation != this._prevRotation) + if (this.rotation != this._prevRotation || this._dirty) { - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); @@ -297,7 +298,7 @@ module Phaser.Components { this._dirty = true; } - // If it has moved, update the edges and center + // 3) If it has moved (or any of the above) then update the edges and center if (this._dirty || this.parent.x != this._pos.x || this.parent.y != this._pos.y) { this.center.x = this.parent.x + this._distance * this._scA.y; @@ -310,34 +311,44 @@ module Phaser.Components { this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + // Translate + this.local.data[2] = this.parent.x; + this.local.data[5] = this.parent.y; } // Scale and Skew - if (this.parent.texture.flippedX) + if (this._dirty || this._flippedX != this.parent.texture.flippedX) { - this.local.data[0] = this._sc.y * -this.scale.x; - this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; - } - else - { - this.local.data[0] = this._sc.y * this.scale.x; - this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + this._flippedX = this.parent.texture.flippedX; + + if (this._flippedX) + { + this.local.data[0] = this._sc.y * -this.scale.x; + this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; + } + else + { + this.local.data[0] = this._sc.y * this.scale.x; + this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + } } - if (this.parent.texture.flippedY) + if (this._dirty || this._flippedY != this.parent.texture.flippedY) { - this.local.data[4] = this._sc.y * -this.scale.y; - this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; - } - else - { - this.local.data[4] = this._sc.y * this.scale.y; - this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; - } + this._flippedY = this.parent.texture.flippedY; - // Translate - this.local.data[2] = this.parent.x; - this.local.data[5] = this.parent.y; + if (this._flippedY) + { + this.local.data[4] = this._sc.y * -this.scale.y; + this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; + } + else + { + this.local.data[4] = this._sc.y * this.scale.y; + this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; + } + } } diff --git a/Phaser/particles/Emitter.js b/Phaser/particles/Emitter.js index f6638f36a..a66d52aea 100644 --- a/Phaser/particles/Emitter.js +++ b/Phaser/particles/Emitter.js @@ -1,327 +1,305 @@ -/// -var __extends = this.__extends || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; -/** -* Phaser - ArcadeEmitter -* -* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for -* continuous effects like rain and fire. All it really does is launch Particle objects out -* at set intervals, and fixes their positions and velocities accorindgly. -*/ var Phaser; (function (Phaser) { - var ArcadeEmitter = (function (_super) { - __extends(ArcadeEmitter, _super); - /** - * Creates a new Emitter object at a specific position. - * Does NOT automatically generate or attach particles! - * - * @param x {number} The X position of the emitter. - * @param y {number} The Y position of the emitter. - * @param [size] {number} Specifies a maximum capacity for this emitter. - */ - function ArcadeEmitter(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 Phaser.Vec2(-100, -100); - this.maxParticleSpeed = new Phaser.Vec2(100, 100); - this.minRotation = -360; - this.maxRotation = 360; - this.gravity = 0; - this.particleClass = null; - this.particleDrag = new Phaser.Vec2(); - this.frequency = 0.1; - this.lifespan = 3; - this.bounce = 0; - this._quantity = 0; - this._counter = 0; - this._explode = true; - this.on = false; - - this.exists = true; - this.active = true; - this.visible = true; - } - /** - * Clean up memory. - */ - ArcadeEmitter.prototype.destroy = function () { - this.minParticleSpeed = null; - this.maxParticleSpeed = null; - this.particleDrag = null; - this.particleClass = null; - this._point = null; - _super.prototype.destroy.call(this); - }; - - /** - * 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 Sprite objects, you can simply pass in a particle image or sprite sheet. - * @param quantity {number} The number of particles to generate when using the "create from image" option. - * @param multiple {boolean} 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 {number} 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 Emitter instance (nice for chaining stuff together, if you're into that). - */ - ArcadeEmitter.prototype.makeParticles = function (graphics, quantity, multiple, collide) { - if (typeof quantity === "undefined") { quantity = 50; } - if (typeof multiple === "undefined") { multiple = false; } - if (typeof collide === "undefined") { collide = 0; } - this.maxSize = quantity; - - var totalFrames = 1; - - /* - if(Multiple) - { - var sprite:Sprite = new Sprite(this.game); - sprite.loadGraphic(Graphics,true); - totalFrames = sprite.frames; - sprite.destroy(); - } + /// + (function (Particles) { + var Emitter = (function () { + /** + * You can use this emit particles. + * + * It will dispatch follow events: + * Proton.PARTICLE_CREATED + * Proton.PARTICLE_UPDATA + * Proton.PARTICLE_DEAD + * + * @class Proton.Emitter + * @constructor + * @param {Object} pObj the parameters object; + * for example {damping:0.01,bindEmitter:false} */ - var randomFrame; - var particle; - var i = 0; + function Emitter(pObj) { + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; - while (i < quantity) { - if (this.particleClass == null) { - particle = new Phaser.ArcadeParticle(this.game); - } else { - particle = new this.particleClass(this.game); - } + /** + * The friction coefficient for all particle emit by This; + * @property damping + * @type {Number} + * @default 0.006 + */ + this.damping = .006; - if (multiple) { - /* - randomFrame = this.game.math.random()*totalFrames; - */ - } else { - if (graphics) { - particle.texture.loadImage(graphics); - } - } + /** + * If bindEmitter the particles can bind this emitter's property; + * @property bindEmitter + * @type {Boolean} + * @default true + */ + this.bindEmitter = true; - if (collide > 0) { - //particle.body.allowCollisions = Types.ANY; - particle.body.type = Phaser.Types.BODY_DYNAMIC; - particle.width *= collide; - particle.height *= collide; - } else { - //particle.body.allowCollisions = Types.NONE; - } + /** + * The number of particles per second emit (a [particle]/b [s]); + * @property rate + * @type {Proton.Rate} + * @default Proton.Rate(1, .1) + */ + this.rate = new Phaser.Particles.Initializers.Rate(1, .1); - particle.exists = false; - - // Center the origin for rotation assistance - //particle.transform.origin.setTo(particle.body.bounds.halfWidth, particle.body.bounds.halfHeight); - this.add(particle); - - i++; + //Emitter._super_.call(this, pObj); + /** + * The emitter's id; + * @property id + * @type {String} id + */ + this.id = 'emitter_' + Emitter.ID++; } + /** + * start emit particle + * @method emit + * @param {Number} emitTime begin emit time; + * @param {String} life the life of this emitter + */ + Emitter.prototype.emit = function (emitTime, life) { + this.emitTime = 0; + this.emitTotalTimes = Particles.ParticleUtils.initValue(emitTime, Infinity); - return this; - }; + if (life == true || life == 'life' || life == 'destroy') { + if (emitTime == 'once') + this.life = 1; +else + this.life = this.emitTotalTimes; + } else if (!isNaN(life)) { + this.life = life; + } - ArcadeEmitter.prototype.preUpdate = function () { - }; - ArcadeEmitter.prototype.postUpdate = function () { - }; + this.rate.init(); + }; - /** - * Called automatically by the game loop, decides when to launch particles and when to "die". - */ - ArcadeEmitter.prototype.update = function () { - if (this.on) { - if (this._explode) { - this.on = false; + /** + * stop emiting + * @method stopEmit + */ + Emitter.prototype.stopEmit = function () { + this.emitTotalTimes = -1; + this.emitTime = 0; + }; - var i = 0; - var l = this._quantity; + /** + * remove current all particles + * @method removeAllParticles + */ + Emitter.prototype.removeAllParticles = function () { + for (var i = 0; i < this.particles.length; i++) + this.particles[i].dead = true; + }; - if ((l <= 0) || (l > this.length)) { - l = this.length; - } + /** + * create single particle; + * + * can use emit({x:10},new Gravity(10),{'particleUpdate',fun}) or emit([{x:10},new Initialize],new Gravity(10),{'particleUpdate',fun}) + * @method removeAllParticles + */ + Emitter.prototype.createParticle = function (initialize, behaviour) { + if (typeof initialize === "undefined") { initialize = null; } + if (typeof behaviour === "undefined") { behaviour = null; } + var particle = Particles.ParticleManager.pool.get(); + this.setupParticle(particle, initialize, behaviour); - while (i < l) { - this.emitParticle(); - i++; - } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_CREATED, + // particle: particle + //})); + return particle; + }; - this._quantity = 0; + /** + * add initialize to this emitter + * @method addSelfInitialize + */ + Emitter.prototype.addSelfInitialize = function (pObj) { + if (pObj['init']) { + pObj.init(this); } else { - this._timer += this.game.time.elapsed; + //this.initAll(); + } + }; - while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) { - this._timer -= this.frequency; - this.emitParticle(); + /** + * add the Initialize to particles; + * + * you can use initializes array:for example emitter.addInitialize(initialize1,initialize2,initialize3); + * @method addInitialize + * @param {Proton.Initialize} initialize like this new Proton.Radius(1, 12) + */ + Emitter.prototype.addInitialize = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.initializes.push(arguments[i]); + } + }; - if ((this._quantity > 0) && (++this._counter >= this._quantity)) { - this.on = false; - this._quantity = 0; + /** + * remove the Initialize + * @method removeInitialize + * @param {Proton.Initialize} initialize a initialize + */ + Emitter.prototype.removeInitialize = function (initializer) { + var index = this.initializes.indexOf(initializer); + if (index > -1) { + this.initializes.splice(index, 1); + } + }; + + /** + * remove all Initializes + * @method removeInitializers + */ + Emitter.prototype.removeInitializers = function () { + Particles.ParticleUtils.destroyArray(this.initializes); + }; + + /** + * add the Behaviour to particles; + * + * you can use Behaviours array:emitter.addBehaviour(Behaviour1,Behaviour2,Behaviour3); + * @method addBehaviour + * @param {Proton.Behaviour} behaviour like this new Proton.Color('random') + */ + Emitter.prototype.addBehaviour = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.behaviours.push(arguments[i]); + if (arguments[i].hasOwnProperty("parents")) + arguments[i].parents.push(this); + } + }; + + /** + * remove the Behaviour + * @method removeBehaviour + * @param {Proton.Behaviour} behaviour a behaviour + */ + Emitter.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) + this.behaviours.splice(index, 1); + }; + + /** + * remove all behaviours + * @method removeAllBehaviours + */ + Emitter.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + Emitter.prototype.integrate = function (time) { + var damping = 1 - this.damping; + Particles.ParticleManager.integrator.integrate(this, time, damping); + var length = this.particles.length, i; + for (i = 0; i < length; i++) { + var particle = this.particles[i]; + particle.update(time, i); + Particles.ParticleManager.integrator.integrate(particle, time, damping); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_UPDATE, + // particle: particle + //})); + } + }; + + Emitter.prototype.emitting = function (time) { + if (this.emitTotalTimes == 1) { + var length = this.rate.getValue(99999), i; + for (i = 0; i < length; i++) { + this.createParticle(); + } + + this.emitTotalTimes = 0; + } else if (!isNaN(this.emitTotalTimes)) { + this.emitTime += time; + if (this.emitTime < this.emitTotalTimes) { + var length = this.rate.getValue(time), i; + for (i = 0; i < length; i++) { + this.createParticle(); } } } - } + }; - _super.prototype.update.call(this); - }; + Emitter.prototype.update = function (time) { + this.age += time; + if (this.age >= this.life || this.dead) { + this.destroy(); + } - /** - * Call this function to turn off all the particles and the emitter. - */ - ArcadeEmitter.prototype.kill = function () { - this.on = false; - this.alive = false; - this.exists = false; - }; + this.emitting(time); + this.integrate(time); + var particle; + var length = this.particles.length, k; + for (k = length - 1; k >= 0; k--) { + particle = this.particles[k]; + if (particle.dead) { + Particles.ParticleManager.pool.set(particle); + this.particles.splice(k, 1); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_DEAD, + // particle: particle + //})); + } + } + }; - /** - * Handy for bringing game objects "back to life". Just sets alive and exists back to true. - * In practice, this is most often called by Object.reset(). - */ - ArcadeEmitter.prototype.revive = function () { - this.alive = true; - this.exists = true; - }; + Emitter.prototype.setupParticle = function (particle, initialize, behaviour) { + var initializes = this.initializes; + var behaviours = this.behaviours; - /** - * Call this function to start emitting particles. - * - * @param explode {boolean} Whether the particles should all burst out at once. - * @param lifespan {number} How long each particle lives once emitted. 0 = forever. - * @param frequency {number} 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 {number} How many particles to launch. 0 = "all of the particles". - */ - ArcadeEmitter.prototype.start = 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(); + if (initialize) { + if (initialize instanceof Array) + initializes = initialize; +else + initializes = [initialize]; + } - this.visible = true; - this.on = true; + if (behaviour) { + if (behaviour instanceof Array) + behaviours = behaviour; +else + behaviours = [behaviour]; + } - this._explode = explode; - this.lifespan = lifespan; - this.frequency = frequency; - this._quantity += quantity; + //Proton.InitializeUtil.initialize(this, particle, initializes); + particle.addBehaviours(behaviours); + particle.parent = this; + this.particles.push(particle); + }; - this._counter = 0; - this._timer = 0; - }; + /** + * Destory this Emitter + * @method destory + */ + Emitter.prototype.destroy = function () { + this.dead = true; + this.emitTotalTimes = -1; + if (this.particles.length == 0) { + this.removeInitializers(); + this.removeAllBehaviours(); - /** - * This function can be used both internally and externally to emit the next particle. - */ - ArcadeEmitter.prototype.emitParticle = function () { - var particle = this.recycle(Phaser.ArcadeParticle); - - particle.lifespan = this.lifespan; - - //particle.body.bounce.setTo(this.bounce, this.bounce); - Phaser.SpriteUtils.reset(particle, this.x - (particle.width >> 1) + this.game.rnd.integer * this.width, this.y - (particle.height >> 1) + this.game.rnd.integer * this.height); - particle.visible = true; - - if (this.minParticleSpeed.x != this.maxParticleSpeed.x) { - particle.body.velocity.x = this.minParticleSpeed.x + this.game.rnd.integer * (this.maxParticleSpeed.x - this.minParticleSpeed.x); - } else { - particle.body.velocity.x = this.minParticleSpeed.x; - } - - if (this.minParticleSpeed.y != this.maxParticleSpeed.y) { - particle.body.velocity.y = this.minParticleSpeed.y + this.game.rnd.integer * (this.maxParticleSpeed.y - this.minParticleSpeed.y); - } else { - particle.body.velocity.y = this.minParticleSpeed.y; - } - - if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) { - particle.body.angularVelocity = this.minRotation + this.game.rnd.integer * (this.maxRotation - this.minRotation); - } else { - particle.body.angularVelocity = this.minRotation; - } - - if (particle.body.angularVelocity != 0) { - particle.rotation = this.game.rnd.integer * 360 - 180; - } - - //particle.body.drag.x = this.particleDrag.x; - //particle.body.drag.y = this.particleDrag.y; - particle.onEmit(); - }; - - /** - * A more compact way of setting the width and height of the emitter. - * - * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). - * @param height {number} The desired height of the emitter. - */ - ArcadeEmitter.prototype.setSize = function (width, height) { - this.width = width; - this.height = height; - }; - - /** - * A more compact way of setting the X velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setXSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.x = min; - this.maxParticleSpeed.x = max; - }; - - /** - * A more compact way of setting the Y velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setYSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.y = min; - this.maxParticleSpeed.y = max; - }; - - /** - * A more compact way of setting the angular velocity constraints of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setRotation = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minRotation = min; - this.maxRotation = max; - }; - - /** - * Change the emitter's midpoint to match the midpoint of a Object. - * - * @param Object {object} The Object that you want to sync up with. - */ - ArcadeEmitter.prototype.at = function (object) { - //this.x = object.body.bounds.halfWidth - (this.width >> 1); - //this.y = object.body.bounds.halfHeight - (this.height >> 1); - }; - return ArcadeEmitter; - })(Phaser.Group); - Phaser.ArcadeEmitter = ArcadeEmitter; + if (this.parent) + this.parent.removeEmitter(this); + } + }; + Emitter.ID = 0; + return Emitter; + })(); + Particles.Emitter = Emitter; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); diff --git a/Phaser/particles/Emitter.ts b/Phaser/particles/Emitter.ts index 0d412e00a..dcef242d3 100644 --- a/Phaser/particles/Emitter.ts +++ b/Phaser/particles/Emitter.ts @@ -1,462 +1,333 @@ /// -/** -* Phaser - ArcadeEmitter -* -* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for -* continuous effects like rain and fire. All it really does is launch Particle objects out -* at set intervals, and fixes their positions and velocities accorindgly. -*/ +module Phaser.Particles { -module Phaser { - - export class ArcadeEmitter extends Phaser.Group { + export class Emitter { /** - * Creates a new Emitter object at a specific position. - * Does NOT automatically generate or attach particles! + * You can use this emit particles. * - * @param x {number} The X position of the emitter. - * @param y {number} The Y position of the emitter. - * @param [size] {number} Specifies a maximum capacity for this emitter. + * It will dispatch follow events: + * Proton.PARTICLE_CREATED + * Proton.PARTICLE_UPDATA + * Proton.PARTICLE_DEAD + * + * @class Proton.Emitter + * @constructor + * @param {Object} pObj the parameters object; + * for example {damping:0.01,bindEmitter:false} */ - constructor(game: Phaser.Game, x: number = 0, y: number = 0, size: number = 0) { - super(game, size); + constructor(pObj) { - this.x = x; - this.y = y; - this.width = 0; - this.height = 0; - this.minParticleSpeed = new Vec2(-100, -100); - this.maxParticleSpeed = new Vec2(100, 100); - this.minRotation = -360; - this.maxRotation = 360; - this.gravity = 0; - this.particleClass = null; - this.particleDrag = new Vec2(); - this.frequency = 0.1; - this.lifespan = 3; - this.bounce = 0; - this._quantity = 0; - this._counter = 0; - this._explode = true; - this.on = false; + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; + /** + * The friction coefficient for all particle emit by This; + * @property damping + * @type {Number} + * @default 0.006 + */ + this.damping = .006; + /** + * If bindEmitter the particles can bind this emitter's property; + * @property bindEmitter + * @type {Boolean} + * @default true + */ + this.bindEmitter = true; + /** + * The number of particles per second emit (a [particle]/b [s]); + * @property rate + * @type {Proton.Rate} + * @default Proton.Rate(1, .1) + */ + this.rate = new Phaser.Particles.Initializers.Rate(1, .1); + //Emitter._super_.call(this, pObj); + /** + * The emitter's id; + * @property id + * @type {String} id + */ + this.id = 'emitter_' + Emitter.ID++; + } - this.exists = true; - this.active = true; - this.visible = true; + static ID = 0; + id; + initializes = []; + particles = []; + behaviours = []; + emitTime = 0; + emitTotalTimes = -1; + damping; + bindEmitter; + rate; + life; + age; + dead; + parent; + /** + * start emit particle + * @method emit + * @param {Number} emitTime begin emit time; + * @param {String} life the life of this emitter + */ + emit(emitTime, life) { + this.emitTime = 0; + this.emitTotalTimes = ParticleUtils.initValue(emitTime, Infinity); + + if (life == true || life == 'life' || life == 'destroy') + { + if (emitTime == 'once') + this.life = 1; + else + this.life = this.emitTotalTimes; + + } else if (!isNaN(life)) + { + this.life = life; + } + + this.rate.init(); } /** - * The X position of the top left corner of the emitter in world space. + * stop emiting + * @method stopEmit */ - public x: number; + stopEmit() { + this.emitTotalTimes = -1; + this.emitTime = 0; + } /** - * The Y position of the top left corner of emitter in world space. + * remove current all particles + * @method removeAllParticles */ - public y: number; - - /** - * The width of the emitter. Particles can be randomly generated from anywhere within this box. - */ - public width: number; - - /** - * The height of the emitter. Particles can be randomly generated from anywhere within this box. - */ - public height: number; + removeAllParticles() { + for (var i = 0; i < this.particles.length; i++) + this.particles[i].dead = true; + } /** + * create single particle; * + * can use emit({x:10},new Gravity(10),{'particleUpdate',fun}) or emit([{x:10},new Initialize],new Gravity(10),{'particleUpdate',fun}) + * @method removeAllParticles */ - public alive: boolean; + createParticle(initialize=null, behaviour=null) { + var particle = ParticleManager.pool.get(); + this.setupParticle(particle, initialize, behaviour); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_CREATED, + // particle: particle + //})); + return particle; + } /** + * add initialize to this emitter + * @method addSelfInitialize + */ + addSelfInitialize(pObj) { + if (pObj['init']) + { + pObj.init(this); + } else + { + //this.initAll(); + } + } + + /** + * add the Initialize to particles; * + * you can use initializes array:for example emitter.addInitialize(initialize1,initialize2,initialize3); + * @method addInitialize + * @param {Proton.Initialize} initialize like this new Proton.Radius(1, 12) */ - public active: boolean; - - /** - * The minimum possible velocity of a particle. - * The default value is (-100,-100). - */ - public minParticleSpeed: Phaser.Vec2; - - /** - * The maximum possible velocity of a particle. - * The default value is (100,100). - */ - public maxParticleSpeed: Phaser.Vec2; - - /** - * The X and Y drag component of particles launched from the emitter. - */ - public particleDrag: Phaser.Vec2; - - /** - * The minimum possible angular velocity of a particle. The default value is -360. - * NOTE: rotating particles are more expensive to draw than non-rotating ones! - */ - public minRotation: number; - - /** - * The maximum possible angular velocity of a particle. The default value is 360. - * NOTE: rotating particles are more expensive to draw than non-rotating ones! - */ - public maxRotation: number; - - /** - * Sets the acceleration.y member of each particle to this value on launch. - */ - public gravity: number; - - /** - * Determines whether the emitter is currently emitting particles. - * It is totally safe to directly toggle this. - */ - public on: boolean; - - /** - * How often a particle is emitted (if emitter is started with Explode == false). - */ - public frequency: number; - - /** - * How long each particle lives once it is emitted. - * Set lifespan to 'zero' for particles to live forever. - */ - public lifespan: number; - - /** - * How much each particle should bounce. 1 = full bounce, 0 = no bounce. - */ - public bounce: number; - - /** - * Set your own particle class type here. - * Default is Particle. - */ - public particleClass; - - /** - * Internal helper for deciding how many particles to launch. - */ - private _quantity: number; - - /** - * Internal helper for the style of particle emission (all at once, or one at a time). - */ - private _explode: boolean; - - /** - * Internal helper for deciding when to launch particles or kill them. - */ - private _timer: number; - - /** - * Internal counter for figuring out how many particles to launch. - */ - private _counter: number; - - /** - * Internal point object, handy for reusing for memory mgmt purposes. - */ - private _point: Phaser.Vec2; - - /** - * Clean up memory. - */ - public destroy() { - this.minParticleSpeed = null; - this.maxParticleSpeed = null; - this.particleDrag = null; - this.particleClass = null; - this._point = null; - super.destroy(); + addInitialize() { + var length = arguments.length, i; + for (i = 0; i < length; i++) + { + this.initializes.push(arguments[i]); + } } /** - * 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 Sprite objects, you can simply pass in a particle image or sprite sheet. - * @param quantity {number} The number of particles to generate when using the "create from image" option. - * @param multiple {boolean} 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 {number} 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 Emitter instance (nice for chaining stuff together, if you're into that). + * remove the Initialize + * @method removeInitialize + * @param {Proton.Initialize} initialize a initialize */ - public makeParticles(graphics, quantity: number = 50, multiple: boolean = false, collide: number = 0): Phaser.ArcadeEmitter { - - this.maxSize = quantity; - - var totalFrames: number = 1; - - /* - if(Multiple) + removeInitialize(initializer) { + var index = this.initializes.indexOf(initializer); + if (index > -1) { - var sprite:Sprite = new Sprite(this.game); - sprite.loadGraphic(Graphics,true); - totalFrames = sprite.frames; - sprite.destroy(); + this.initializes.splice(index, 1); } - */ - - var randomFrame: number; - var particle: Phaser.ArcadeParticle; - var i: number = 0; - - while (i < quantity) - { - if (this.particleClass == null) - { - particle = new Phaser.ArcadeParticle(this.game); - } - else - { - particle = new this.particleClass(this.game); - } - - if (multiple) - { - /* - randomFrame = this.game.math.random()*totalFrames; - */ - } - else - { - if (graphics) - { - particle.texture.loadImage(graphics); - } - } - - if (collide > 0) - { - //particle.body.allowCollisions = Types.ANY; - particle.body.type = Phaser.Types.BODY_DYNAMIC; - particle.width *= collide; - particle.height *= collide; - } - else - { - //particle.body.allowCollisions = Types.NONE; - } - - particle.exists = false; - // Center the origin for rotation assistance - //particle.transform.origin.setTo(particle.body.bounds.halfWidth, particle.body.bounds.halfHeight); - - this.add(particle); - - i++; - } - - return this; } - public preUpdate() { } - public postUpdate() { } + /** + * remove all Initializes + * @method removeInitializers + */ + removeInitializers() { + ParticleUtils.destroyArray(this.initializes); + } /** - * Called automatically by the game loop, decides when to launch particles and when to "die". + * add the Behaviour to particles; + * + * you can use Behaviours array:emitter.addBehaviour(Behaviour1,Behaviour2,Behaviour3); + * @method addBehaviour + * @param {Proton.Behaviour} behaviour like this new Proton.Color('random') */ - public update() { - - if (this.on) + addBehaviour() { + var length = arguments.length, i; + for (i = 0; i < length; i++) { - if (this._explode) + this.behaviours.push(arguments[i]); + if (arguments[i].hasOwnProperty("parents")) + arguments[i].parents.push(this); + } + } + + /** + * remove the Behaviour + * @method removeBehaviour + * @param {Proton.Behaviour} behaviour a behaviour + */ + removeBehaviour(behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) + this.behaviours.splice(index, 1); + } + + /** + * remove all behaviours + * @method removeAllBehaviours + */ + removeAllBehaviours() { + ParticleUtils.destroyArray(this.behaviours); + } + + integrate(time) { + var damping = 1 - this.damping; + ParticleManager.integrator.integrate(this, time, damping); + var length = this.particles.length, i; + for (i = 0; i < length; i++) + { + var particle = this.particles[i]; + particle.update(time, i); + ParticleManager.integrator.integrate(particle, time, damping); + + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_UPDATE, + // particle: particle + //})); + } + } + + emitting(time) { + if (this.emitTotalTimes == 1) + { + var length = this.rate.getValue(99999), i; + for (i = 0; i < length; i++) { - this.on = false; - - var i: number = 0; - var l: number = this._quantity; - - if ((l <= 0) || (l > this.length)) - { - l = this.length; - } - - while (i < l) - { - this.emitParticle(); - i++; - } - - this._quantity = 0; + this.createParticle(); } - else + + this.emitTotalTimes = 0; + } else if (!isNaN(this.emitTotalTimes)) + { + this.emitTime += time; + if (this.emitTime < this.emitTotalTimes) { - this._timer += this.game.time.elapsed; - - while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) + var length = this.rate.getValue(time), i; + for (i = 0; i < length; i++) { - this._timer -= this.frequency; - this.emitParticle(); - - if ((this._quantity > 0) && (++this._counter >= this._quantity)) - { - this.on = false; - this._quantity = 0; - } + this.createParticle(); } } } - - super.update(); - } - /** - * Call this function to turn off all the particles and the emitter. - */ - public kill() { - this.on = false; - this.alive = false; - this.exists = false; - } - /** - * Handy for bringing game objects "back to life". Just sets alive and exists back to true. - * In practice, this is most often called by Object.reset(). - */ - public revive() { - this.alive = true; - this.exists = true; - } - - /** - * Call this function to start emitting particles. - * - * @param explode {boolean} Whether the particles should all burst out at once. - * @param lifespan {number} How long each particle lives once emitted. 0 = forever. - * @param frequency {number} 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 {number} How many particles to launch. 0 = "all of the particles". - */ - public start(explode: boolean = true, lifespan: number = 0, frequency: number = 0.1, quantity: number = 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; - - } - - /** - * This function can be used both internally and externally to emit the next particle. - */ - public emitParticle() { - - var particle: ArcadeParticle = this.recycle(ArcadeParticle); - - particle.lifespan = this.lifespan; - //particle.body.bounce.setTo(this.bounce, this.bounce); - SpriteUtils.reset(particle, this.x - (particle.width >> 1) + this.game.rnd.integer * this.width, this.y - (particle.height >> 1) + this.game.rnd.integer * this.height); - particle.visible = true; - - if (this.minParticleSpeed.x != this.maxParticleSpeed.x) + update(time) { + this.age += time; + if (this.age >= this.life || this.dead) { - particle.body.velocity.x = this.minParticleSpeed.x + this.game.rnd.integer * (this.maxParticleSpeed.x - this.minParticleSpeed.x); - } - else - { - particle.body.velocity.x = this.minParticleSpeed.x; + this.destroy(); } - if (this.minParticleSpeed.y != this.maxParticleSpeed.y) + this.emitting(time); + this.integrate(time); + var particle; + var length = this.particles.length, k; + for (k = length - 1; k >= 0; k--) { - particle.body.velocity.y = this.minParticleSpeed.y + this.game.rnd.integer * (this.maxParticleSpeed.y - this.minParticleSpeed.y); + particle = this.particles[k]; + if (particle.dead) + { + ParticleManager.pool.set(particle); + this.particles.splice(k, 1); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_DEAD, + // particle: particle + //})); + } } - else + } + + setupParticle(particle, initialize, behaviour) { + + var initializes = this.initializes; + var behaviours = this.behaviours; + + if (initialize) { - particle.body.velocity.y = this.minParticleSpeed.y; + if (initialize instanceof Array) + initializes = initialize; + else + initializes = [initialize]; } - //particle.body.acceleration.y = this.gravity; - - if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) + if (behaviour) { - particle.body.angularVelocity = this.minRotation + this.game.rnd.integer * (this.maxRotation - this.minRotation); - } - else - { - particle.body.angularVelocity = this.minRotation; + if (behaviour instanceof Array) + behaviours = behaviour; + else + behaviours = [behaviour]; } - if (particle.body.angularVelocity != 0) - { - particle.rotation = this.game.rnd.integer * 360 - 180; - } - - //particle.body.drag.x = this.particleDrag.x; - //particle.body.drag.y = this.particleDrag.y; - particle.onEmit(); - + //Proton.InitializeUtil.initialize(this, particle, initializes); + particle.addBehaviours(behaviours); + particle.parent = this; + this.particles.push(particle); } /** - * A more compact way of setting the width and height of the emitter. - * - * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). - * @param height {number} The desired height of the emitter. + * Destory this Emitter + * @method destory */ - public setSize(width: number, height: number) { - this.width = width; - this.height = height; + destroy() { + this.dead = true; + this.emitTotalTimes = -1; + if (this.particles.length == 0) + { + this.removeInitializers(); + this.removeAllBehaviours(); + + if (this.parent) + this.parent.removeEmitter(this); + } } - /** - * A more compact way of setting the X velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setXSpeed(min: number = 0, max: number = 0) { - this.minParticleSpeed.x = min; - this.maxParticleSpeed.x = max; - } - /** - * A more compact way of setting the Y velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setYSpeed(min: number = 0, max: number = 0) { - this.minParticleSpeed.y = min; - this.maxParticleSpeed.y = max; - } - - /** - * A more compact way of setting the angular velocity constraints of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - public setRotation(min: number = 0, max: number = 0) { - this.minRotation = min; - this.maxRotation = max; - } - - /** - * Change the emitter's midpoint to match the midpoint of a Object. - * - * @param Object {object} The Object that you want to sync up with. - */ - public at(object: Sprite) { - //this.x = object.body.bounds.halfWidth - (this.width >> 1); - //this.y = object.body.bounds.halfHeight - (this.height >> 1); - } } } \ No newline at end of file diff --git a/Phaser/particles/NumericalIntegration.js b/Phaser/particles/NumericalIntegration.js new file mode 100644 index 000000000..e758718e6 --- /dev/null +++ b/Phaser/particles/NumericalIntegration.js @@ -0,0 +1,30 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var NumericalIntegration = (function () { + function NumericalIntegration(type) { + this.type = Particles.ParticleUtils.initValue(type, Particles.ParticleManager.EULER); + } + NumericalIntegration.prototype.integrate = function (particles, time, damping) { + this.eulerIntegrate(particles, time, damping); + }; + + NumericalIntegration.prototype.eulerIntegrate = function (particle, time, damping) { + if (!particle.sleep) { + particle.old.p.copy(particle.p); + particle.old.v.copy(particle.v); + particle.a.multiplyScalar(1 / particle.mass); + particle.v.add(particle.a.multiplyScalar(time)); + particle.p.add(particle.old.v.multiplyScalar(time)); + if (damping) + particle.v.multiplyScalar(damping); + particle.a.clear(); + } + }; + return NumericalIntegration; + })(); + Particles.NumericalIntegration = NumericalIntegration; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/NumericalIntegration.ts b/Phaser/particles/NumericalIntegration.ts new file mode 100644 index 000000000..53a556b15 --- /dev/null +++ b/Phaser/particles/NumericalIntegration.ts @@ -0,0 +1,33 @@ +/// + +module Phaser.Particles { + + export class NumericalIntegration { + + constructor(type) { + this.type = ParticleUtils.initValue(type, ParticleManager.EULER); + } + + type; + + integrate(particles, time, damping) { + this.eulerIntegrate(particles, time, damping); + } + + eulerIntegrate(particle, time, damping) { + if (!particle.sleep) + { + particle.old.p.copy(particle.p); + particle.old.v.copy(particle.v); + particle.a.multiplyScalar(1 / particle.mass); + particle.v.add(particle.a.multiplyScalar(time)); + particle.p.add(particle.old.v.multiplyScalar(time)); + if (damping) + particle.v.multiplyScalar(damping); + particle.a.clear(); + } + } + + } + +} diff --git a/Phaser/particles/Particle.js b/Phaser/particles/Particle.js index 612ae7329..4e4d67468 100644 --- a/Phaser/particles/Particle.js +++ b/Phaser/particles/Particle.js @@ -1,52 +1,157 @@ -/// -var __extends = this.__extends || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; -/** -* Phaser - ArcadeParticle -* -* This is a simple particle class that extends a Sprite to have a slightly more -* specialised behaviour. It is used exclusively by the Emitter class and can be extended as required. -*/ var Phaser; (function (Phaser) { - var ArcadeParticle = (function (_super) { - __extends(ArcadeParticle, _super); - /** - * Instantiate a new particle. Like Sprite, all meaningful creation - * happens during loadGraphic() or makeGraphic() or whatever. - */ - function ArcadeParticle(game) { - _super.call(this, game); - - this.body.type = Phaser.Types.BODY_DYNAMIC; - this.lifespan = 0; - } - /** - * The particle's main update logic. Basically it checks to see if it should be dead yet. - */ - ArcadeParticle.prototype.update = function () { - if (this.lifespan <= 0) { - return; + /// + (function (Particles) { + var Particle = (function () { + /** + * the Particle class + * + * @class Proton.Particle + * @constructor + * @param {Object} pObj the parameters object; + * for example {life:3,dead:false} + */ + function Particle() { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + /** + * The particle's id; + * @property id + * @type {String} id + */ + this.id = 'particle_' + Particle.ID++; + this.reset(true); } + Particle.prototype.getDirection = function () { + return Math.atan2(this.v.x, -this.v.y) * (180 / Math.PI); + }; - this.lifespan -= this.game.time.elapsed; + Particle.prototype.reset = function (init) { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + if (init) { + this.transform = {}; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + } else { + Particles.ParticleUtils.destroyObject(this.transform); + this.p.setTo(0, 0); + this.v.setTo(0, 0); + this.a.setTo(0, 0); + this.old.p.setTo(0, 0); + this.old.v.setTo(0, 0); + this.old.a.setTo(0, 0); + this.removeAllBehaviours(); + } - if (this.lifespan <= 0) { - this.kill(); - } - }; + this.transform.rgb = { + r: 255, + g: 255, + b: 255 + }; + return this; + }; - /** - * Triggered whenever this object is launched by a Emitter. - * You can override this to add custom behavior like a sound or AI or something. - */ - ArcadeParticle.prototype.onEmit = function () { - }; - return ArcadeParticle; - })(Phaser.Sprite); - Phaser.ArcadeParticle = ArcadeParticle; + Particle.prototype.update = function (time, index) { + if (!this.sleep) { + this.age += time; + var length = this.behaviours.length, i; + for (i = 0; i < length; i++) { + if (this.behaviours[i]) + this.behaviours[i].applyBehaviour(this, time, index); + } + } + + if (this.age >= this.life) { + this.destroy(); + } else { + var scale = this.easing(this.age / this.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + Particle.prototype.addBehaviour = function (behaviour) { + this.behaviours.push(behaviour); + if (behaviour.hasOwnProperty('parents')) + behaviour.parents.push(this); + behaviour.initialize(this); + }; + + Particle.prototype.addBehaviours = function (behaviours) { + var length = behaviours.length, i; + for (i = 0; i < length; i++) { + this.addBehaviour(behaviours[i]); + } + }; + + Particle.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) { + var behaviour = this.behaviours.splice(index, 1); + behaviour.parents = null; + } + }; + + Particle.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + /** + * Destory this particle + * @method destory + */ + Particle.prototype.destroy = function () { + this.removeAllBehaviours(); + this.energy = 0; + this.dead = true; + this.parent = null; + }; + Particle.ID = 0; + return Particle; + })(); + Particles.Particle = Particle; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); diff --git a/Phaser/particles/Particle.ts b/Phaser/particles/Particle.ts index b34d6248c..eb953dcc5 100644 --- a/Phaser/particles/Particle.ts +++ b/Phaser/particles/Particle.ts @@ -1,63 +1,171 @@ /// -/** -* Phaser - ArcadeParticle -* -* This is a simple particle class that extends a Sprite to have a slightly more -* specialised behaviour. It is used exclusively by the Emitter class and can be extended as required. -*/ +module Phaser.Particles { -module Phaser { - - export class ArcadeParticle extends Phaser.Sprite { + export class Particle { /** - * Instantiate a new particle. Like Sprite, all meaningful creation - * happens during loadGraphic() or makeGraphic() or whatever. + * the Particle class + * + * @class Proton.Particle + * @constructor + * @param {Object} pObj the parameters object; + * for example {life:3,dead:false} */ - constructor(game: Phaser.Game) { - - super(game); - - this.body.type = Phaser.Types.BODY_DYNAMIC; - this.lifespan = 0; - + constructor() { + /** + * The particle's id; + * @property id + * @type {String} id + */ + this.id = 'particle_' + Particle.ID++; + this.reset(true); } - /** - * How long this particle lives before it disappears. - * NOTE: this is a maximum, not a minimum; the object - * could get recycled before its lifespan is up. - */ - public lifespan: number; + static ID = 0; - /** - * The particle's main update logic. Basically it checks to see if it should be dead yet. - */ - public update() { + id; + life = Infinity; + age = 0; + energy = 1; + dead = false; + sleep = false; + target = null; + sprite = null; + parent = null; + mass = 1; + radius = 10; + alpha = 1; + scale = 1; + rotation = 0; + color = null; + easing = Phaser.Easing.Linear.None; + transform; + p = new Phaser.Vec2(); + v = new Phaser.Vec2(); + a = new Phaser.Vec2(); + old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + behaviours = []; - // Lifespan behavior - if (this.lifespan <= 0) + getDirection() { + return Math.atan2(this.v.x, -this.v.y) * (180 / Math.PI); + } + + reset(init) { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + if (init) { - return; + this.transform = {} + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + } else + { + ParticleUtils.destroyObject(this.transform); + this.p.setTo(0, 0); + this.v.setTo(0, 0); + this.a.setTo(0, 0); + this.old.p.setTo(0, 0); + this.old.v.setTo(0, 0); + this.old.a.setTo(0, 0); + this.removeAllBehaviours(); } - this.lifespan -= this.game.time.elapsed; + this.transform.rgb = { + r: 255, + g: 255, + b: 255 + } + return this; + } - if (this.lifespan <= 0) + update(time, index) { + if (!this.sleep) { - this.kill(); + this.age += time; + var length = this.behaviours.length, i; + for (i = 0; i < length; i++) + { + if (this.behaviours[i]) + this.behaviours[i].applyBehaviour(this, time, index) + } + } + + if (this.age >= this.life) + { + this.destroy(); + } else + { + var scale = this.easing(this.age / this.life); + this.energy = Math.max(1 - scale, 0); } } - /** - * Triggered whenever this object is launched by a Emitter. - * You can override this to add custom behavior like a sound or AI or something. - */ - public onEmit() { + addBehaviour(behaviour) { + this.behaviours.push(behaviour); + if (behaviour.hasOwnProperty('parents')) + behaviour.parents.push(this); + behaviour.initialize(this); } + addBehaviours(behaviours) { + var length = behaviours.length, i; + for (i = 0; i < length; i++) + { + this.addBehaviour(behaviours[i]); + } + } + + removeBehaviour(behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) + { + var behaviour = this.behaviours.splice(index, 1); + behaviour.parents = null; + } + } + + removeAllBehaviours() { + ParticleUtils.destroyArray(this.behaviours); + } + + /** + * Destory this particle + * @method destory + */ + destroy() { + this.removeAllBehaviours(); + this.energy = 0; + this.dead = true; + this.parent = null; + } + + } } \ No newline at end of file diff --git a/Phaser/particles/ParticleManager.js b/Phaser/particles/ParticleManager.js new file mode 100644 index 000000000..ece4bbd54 --- /dev/null +++ b/Phaser/particles/ParticleManager.js @@ -0,0 +1,127 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleManager = (function () { + function ParticleManager(proParticleCount, integrationType) { + this.PARTICLE_CREATED = 'partilcleCreated'; + this.PARTICLE_UPDATE = 'partilcleUpdate'; + this.PARTICLE_SLEEP = 'particleSleep'; + this.PARTICLE_DEAD = 'partilcleDead'; + this.PROTON_UPDATE = 'protonUpdate'; + this.PROTON_UPDATE_AFTER = 'protonUpdateAfter'; + this.EMITTER_ADDED = 'emitterAdded'; + this.EMITTER_REMOVED = 'emitterRemoved'; + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + this.amendChangeTabsBug = true; + this.TextureBuffer = {}; + this.TextureCanvasBuffer = {}; + this.proParticleCount = Particles.ParticleUtils.initValue(proParticleCount, ParticleManager.POOL_MAX); + this.integrationType = Particles.ParticleUtils.initValue(integrationType, ParticleManager.EULER); + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + + ParticleManager.pool = new Phaser.Particles.ParticlePool(proParticleCount); + ParticleManager.integrator = new Phaser.Particles.NumericalIntegration(this.integrationType); + } + /** + * add a type of Renderer + * + * @method addRender + * @param {Renderer} render + */ + ParticleManager.prototype.addRender = function (render) { + render.proton = this; + this.renderers.push(render.proton); + }; + + /** + * add the Emitter + * + * @method addEmitter + * @param {Emitter} emitter + */ + ParticleManager.prototype.addEmitter = function (emitter) { + this.emitters.push(emitter); + emitter.parent = this; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_ADDED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.removeEmitter = function (emitter) { + var index = this.emitters.indexOf(emitter); + this.emitters.splice(index, 1); + emitter.parent = null; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_REMOVED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.update = function () { + if (!this.oldTime) + this.oldTime = new Date().getTime(); + + var time = new Date().getTime(); + this.elapsed = (time - this.oldTime) / 1000; + + //if (ParticleUtils.amendChangeTabsBug) + // this.amendChangeTabsBugHandler(); + this.oldTime = time; + if (this.elapsed > 0) { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].update(this.elapsed); + } + } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PROTON_UPDATE_AFTER + //})); + }; + + ParticleManager.prototype.amendChangeTabsBugHandler = function () { + if (this.elapsed > .5) { + this.oldTime = new Date().getTime(); + this.elapsed = 0; + } + }; + + ParticleManager.prototype.getParticleNumber = function () { + var total = 0; + for (var i = 0; i < this.emitters.length; i++) { + total += this.emitters[i].particles.length; + } + return total; + }; + + ParticleManager.prototype.destroy = function () { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].destory(); + delete this.emitters[i]; + } + + this.emitters = []; + this.time = 0; + this.oldTime = 0; + ParticleManager.pool.release(); + }; + ParticleManager.POOL_MAX = 1000; + ParticleManager.TIME_STEP = 60; + + ParticleManager.MEASURE = 100; + ParticleManager.EULER = 'euler'; + ParticleManager.RK2 = 'runge-kutta2'; + ParticleManager.RK4 = 'runge-kutta4'; + ParticleManager.VERLET = 'verlet'; + return ParticleManager; + })(); + Particles.ParticleManager = ParticleManager; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/ParticleManager.ts b/Phaser/particles/ParticleManager.ts new file mode 100644 index 000000000..9a7fdbf96 --- /dev/null +++ b/Phaser/particles/ParticleManager.ts @@ -0,0 +1,151 @@ +/// + +module Phaser.Particles { + + export class ParticleManager { + + constructor(proParticleCount, integrationType) { + + this.proParticleCount = ParticleUtils.initValue(proParticleCount, ParticleManager.POOL_MAX); + this.integrationType = ParticleUtils.initValue(integrationType, ParticleManager.EULER); + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + + ParticleManager.pool = new Phaser.Particles.ParticlePool(proParticleCount); + ParticleManager.integrator = new Phaser.Particles.NumericalIntegration(this.integrationType); + + } + + //the max particle number in pool + static POOL_MAX = 1000; + static TIME_STEP = 60; + //1:100 + static MEASURE = 100; + static EULER = 'euler'; + static RK2 = 'runge-kutta2'; + static RK4 = 'runge-kutta4'; + static VERLET = 'verlet'; + + PARTICLE_CREATED = 'partilcleCreated'; + PARTICLE_UPDATE = 'partilcleUpdate'; + PARTICLE_SLEEP = 'particleSleep'; + PARTICLE_DEAD = 'partilcleDead'; + PROTON_UPDATE = 'protonUpdate'; + PROTON_UPDATE_AFTER = 'protonUpdateAfter'; + EMITTER_ADDED = 'emitterAdded'; + EMITTER_REMOVED = 'emitterRemoved'; + + proParticleCount; + integrationType; + emitters = []; + renderers = []; + time = 0; + oldTime = 0; + static pool; + static integrator; + + amendChangeTabsBug = true; + TextureBuffer = {}; + TextureCanvasBuffer = {}; + elapsed; + + /** + * add a type of Renderer + * + * @method addRender + * @param {Renderer} render + */ + addRender(render) { + render.proton = this; + this.renderers.push(render.proton); + } + + /** + * add the Emitter + * + * @method addEmitter + * @param {Emitter} emitter + */ + addEmitter(emitter) { + this.emitters.push(emitter); + emitter.parent = this; + + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_ADDED, + // emitter: emitter + //})); + } + + removeEmitter(emitter) { + var index = this.emitters.indexOf(emitter); + this.emitters.splice(index, 1); + emitter.parent = null; + + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_REMOVED, + // emitter: emitter + //})); + } + + update() { + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PROTON_UPDATE + //})); + + if (!this.oldTime) + this.oldTime = new Date().getTime(); + + var time = new Date().getTime(); + this.elapsed = (time - this.oldTime) / 1000; + //if (ParticleUtils.amendChangeTabsBug) + // this.amendChangeTabsBugHandler(); + this.oldTime = time; + if (this.elapsed > 0) + { + for (var i = 0; i < this.emitters.length; i++) + { + this.emitters[i].update(this.elapsed); + } + } + + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PROTON_UPDATE_AFTER + //})); + } + + amendChangeTabsBugHandler() { + + if (this.elapsed > .5) + { + this.oldTime = new Date().getTime(); + this.elapsed = 0; + } + } + + getParticleNumber() { + var total = 0; + for (var i = 0; i < this.emitters.length; i++) + { + total += this.emitters[i].particles.length; + } + return total; + } + + destroy() { + for (var i = 0; i < this.emitters.length; i++) + { + this.emitters[i].destory(); + delete this.emitters[i]; + } + + this.emitters = []; + this.time = 0; + this.oldTime = 0; + ParticleManager.pool.release(); + } + + } + +} diff --git a/Phaser/particles/ParticlePool.js b/Phaser/particles/ParticlePool.js new file mode 100644 index 000000000..f8939cb53 --- /dev/null +++ b/Phaser/particles/ParticlePool.js @@ -0,0 +1,71 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticlePool = (function () { + function ParticlePool(num, releaseTime) { + if (typeof releaseTime === "undefined") { releaseTime = 0; } + this.poolList = []; + this.timeoutID = 0; + this.proParticleCount = Particles.ParticleUtils.initValue(num, 0); + this.releaseTime = Particles.ParticleUtils.initValue(releaseTime, -1); + this.poolList = []; + this.timeoutID = 0; + + for (var i = 0; i < this.proParticleCount; i++) { + this.add(); + } + + if (this.releaseTime > 0) { + // TODO - Hook to game clock so Pause works + this.timeoutID = setTimeout(this.release, this.releaseTime / 1000); + } + } + ParticlePool.prototype.create = function (newTypeParticleClass) { + if (typeof newTypeParticleClass === "undefined") { newTypeParticleClass = null; } + if (newTypeParticleClass) { + return new newTypeParticleClass(); + } else { + return new Phaser.Particles.Particle(); + } + }; + + ParticlePool.prototype.getCount = function () { + return this.poolList.length; + }; + + ParticlePool.prototype.add = function () { + return this.poolList.push(this.create()); + }; + + ParticlePool.prototype.get = function () { + if (this.poolList.length === 0) { + return this.create(); + } else { + return this.poolList.pop().reset(); + } + }; + + ParticlePool.prototype.set = function (particle) { + if (this.poolList.length < Particles.ParticleManager.POOL_MAX) { + return this.poolList.push(particle); + } + }; + + ParticlePool.prototype.release = function () { + for (var i = 0; i < this.poolList.length; i++) { + if (this.poolList[i]['destroy']) { + this.poolList[i].destroy(); + } + + delete this.poolList[i]; + } + + this.poolList = []; + }; + return ParticlePool; + })(); + Particles.ParticlePool = ParticlePool; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/ParticlePool.ts b/Phaser/particles/ParticlePool.ts new file mode 100644 index 000000000..557860f3f --- /dev/null +++ b/Phaser/particles/ParticlePool.ts @@ -0,0 +1,92 @@ +/// + +module Phaser.Particles { + + export class ParticlePool { + + constructor(num, releaseTime = 0) { + + this.proParticleCount = ParticleUtils.initValue(num, 0); + this.releaseTime = ParticleUtils.initValue(releaseTime, -1); + this.poolList = []; + this.timeoutID = 0; + + for (var i = 0; i < this.proParticleCount; i++) + { + this.add(); + } + + if (this.releaseTime > 0) + { + // TODO - Hook to game clock so Pause works + this.timeoutID = setTimeout(this.release, this.releaseTime / 1000); + } + + } + + proParticleCount: number; + releaseTime: number; + poolList = []; + timeoutID: number = 0; + + create(newTypeParticleClass = null) { + + if (newTypeParticleClass) + { + return new newTypeParticleClass; + } + else + { + return new Phaser.Particles.Particle(); + } + + } + + getCount() { + return this.poolList.length; + } + + add() { + return this.poolList.push(this.create()); + } + + get() { + + if (this.poolList.length === 0) + { + return this.create(); + } + else + { + return this.poolList.pop().reset(); + } + + } + + set(particle) { + + if (this.poolList.length < ParticleManager.POOL_MAX) + { + return this.poolList.push(particle); + } + + } + + release() { + + for (var i = 0; i < this.poolList.length; i++) + { + if (this.poolList[i]['destroy']) + { + this.poolList[i].destroy(); + } + + delete this.poolList[i]; + } + + this.poolList = []; + } + + } + +} diff --git a/Phaser/particles/ParticleUtils.js b/Phaser/particles/ParticleUtils.js new file mode 100644 index 000000000..fed67edb5 --- /dev/null +++ b/Phaser/particles/ParticleUtils.js @@ -0,0 +1,180 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleUtils = (function () { + function ParticleUtils() { + } + ParticleUtils.initValue = function (value, defaults) { + var value = (value != null && value != undefined) ? value : defaults; + return value; + }; + + ParticleUtils.isArray = function (value) { + return typeof value === 'object' && value.hasOwnProperty('length'); + }; + + ParticleUtils.destroyArray = function (array) { + array.length = 0; + }; + + ParticleUtils.destroyObject = function (obj) { + for (var o in obj) { + delete obj[o]; + } + }; + + ParticleUtils.setSpanValue = function (a, b, c) { + if (typeof b === "undefined") { b = null; } + if (typeof c === "undefined") { c = null; } + if (a instanceof Phaser.Particles.Span) { + return a; + } else { + if (!b) { + return new Phaser.Particles.Span(a); + } else { + if (!c) { + return new Phaser.Particles.Span(a, b); + } else { + return new Phaser.Particles.Span(a, b, c); + } + } + } + }; + + ParticleUtils.getSpanValue = function (pan) { + if (pan instanceof Phaser.Particles.Span) { + return pan.getValue(); + } else { + return pan; + } + }; + + ParticleUtils.randomAToB = function (a, b, INT) { + if (typeof INT === "undefined") { INT = null; } + if (!INT) { + return a + Math.random() * (b - a); + } else { + return Math.floor(Math.random() * (b - a)) + a; + } + }; + + ParticleUtils.randomFloating = function (center, f, INT) { + return ParticleUtils.randomAToB(center - f, center + f, INT); + }; + + ParticleUtils.randomZone = function (display) { + }; + + ParticleUtils.degreeTransform = function (a) { + return a * Math.PI / 180; + }; + + ParticleUtils.randomColor = //static toColor16 getRGB(num) { + // return "#" + num.toString(16); + //} + function () { + return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6); + }; + + ParticleUtils.setEasingByName = function (name) { + switch (name) { + case 'easeLinear': + return Phaser.Easing.Linear.None; + break; + + case 'easeInQuad': + return Phaser.Easing.Quadratic.In; + break; + + case 'easeOutQuad': + return Phaser.Easing.Quadratic.Out; + break; + + case 'easeInOutQuad': + return Phaser.Easing.Quadratic.InOut; + break; + + case 'easeInCubic': + return Phaser.Easing.Cubic.In; + break; + + case 'easeOutCubic': + return Phaser.Easing.Cubic.Out; + break; + + case 'easeInOutCubic': + return Phaser.Easing.Cubic.InOut; + break; + + case 'easeInQuart': + return Phaser.Easing.Quartic.In; + break; + + case 'easeOutQuart': + return Phaser.Easing.Quartic.Out; + break; + + case 'easeInOutQuart': + return Phaser.Easing.Quartic.InOut; + break; + + case 'easeInSine': + return Phaser.Easing.Sinusoidal.In; + break; + + case 'easeOutSine': + return Phaser.Easing.Sinusoidal.Out; + break; + + case 'easeInOutSine': + return Phaser.Easing.Sinusoidal.InOut; + break; + + case 'easeInExpo': + return Phaser.Easing.Exponential.In; + break; + + case 'easeOutExpo': + return Phaser.Easing.Exponential.Out; + break; + + case 'easeInOutExpo': + return Phaser.Easing.Exponential.InOut; + break; + + case 'easeInCirc': + return Phaser.Easing.Circular.In; + break; + + case 'easeOutCirc': + return Phaser.Easing.Circular.Out; + break; + + case 'easeInOutCirc': + return Phaser.Easing.Circular.InOut; + break; + + case 'easeInBack': + return Phaser.Easing.Back.In; + break; + + case 'easeOutBack': + return Phaser.Easing.Back.Out; + break; + + case 'easeInOutBack': + return Phaser.Easing.Back.InOut; + break; + + default: + return Phaser.Easing.Linear.None; + break; + } + }; + return ParticleUtils; + })(); + Particles.ParticleUtils = ParticleUtils; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/ParticleUtils.ts b/Phaser/particles/ParticleUtils.ts new file mode 100644 index 000000000..a1333c9d4 --- /dev/null +++ b/Phaser/particles/ParticleUtils.ts @@ -0,0 +1,201 @@ +/// + +module Phaser.Particles { + + export class ParticleUtils { + + static initValue(value, defaults) { + var value = (value != null && value != undefined) ? value : defaults; + return value; + } + + static isArray(value) { + return typeof value === 'object' && value.hasOwnProperty('length'); + } + + static destroyArray(array) { + array.length = 0; + } + + static destroyObject(obj) { + + for (var o in obj) + { + delete obj[o]; + } + + } + + static setSpanValue(a, b= null, c= null) { + + if (a instanceof Phaser.Particles.Span) + { + return a; + } + else + { + if (!b) + { + return new Phaser.Particles.Span(a); + } + else + { + if (!c) + { + return new Phaser.Particles.Span(a, b); + } + else + { + return new Phaser.Particles.Span(a, b, c); + } + } + } + } + + static getSpanValue(pan) { + + if (pan instanceof Phaser.Particles.Span) + { + return pan.getValue(); + } + else + { + return pan; + } + + } + + static randomAToB(a, b, INT = null) { + + if (!INT) + { + return a + Math.random() * (b - a); + } + else + { + return Math.floor(Math.random() * (b - a)) + a; + } + + } + + static randomFloating(center, f, INT) { + return ParticleUtils.randomAToB(center - f, center + f, INT); + } + + static randomZone(display) { + + } + + static degreeTransform(a) { + return a * Math.PI / 180; + } + + //static toColor16 getRGB(num) { + // return "#" + num.toString(16); + //} + + static randomColor() { + return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6); + } + + static setEasingByName(name) { + + switch (name) + { + case 'easeLinear': + return Phaser.Easing.Linear.None; + break; + + case 'easeInQuad': + return Phaser.Easing.Quadratic.In; + break; + + case 'easeOutQuad': + return Phaser.Easing.Quadratic.Out; + break; + + case 'easeInOutQuad': + return Phaser.Easing.Quadratic.InOut; + break; + + case 'easeInCubic': + return Phaser.Easing.Cubic.In; + break; + + case 'easeOutCubic': + return Phaser.Easing.Cubic.Out; + break; + + case 'easeInOutCubic': + return Phaser.Easing.Cubic.InOut; + break; + + case 'easeInQuart': + return Phaser.Easing.Quartic.In; + break; + + case 'easeOutQuart': + return Phaser.Easing.Quartic.Out; + break; + + case 'easeInOutQuart': + return Phaser.Easing.Quartic.InOut; + break; + + case 'easeInSine': + return Phaser.Easing.Sinusoidal.In; + break; + + case 'easeOutSine': + return Phaser.Easing.Sinusoidal.Out; + break; + + case 'easeInOutSine': + return Phaser.Easing.Sinusoidal.InOut; + break; + + case 'easeInExpo': + return Phaser.Easing.Exponential.In; + break; + + case 'easeOutExpo': + return Phaser.Easing.Exponential.Out; + break; + + case 'easeInOutExpo': + return Phaser.Easing.Exponential.InOut; + break; + + case 'easeInCirc': + return Phaser.Easing.Circular.In; + break; + + case 'easeOutCirc': + return Phaser.Easing.Circular.Out; + break; + + case 'easeInOutCirc': + return Phaser.Easing.Circular.InOut; + break; + + case 'easeInBack': + return Phaser.Easing.Back.In; + break; + + case 'easeOutBack': + return Phaser.Easing.Back.Out; + break; + + case 'easeInOutBack': + return Phaser.Easing.Back.InOut; + break; + + default: + return Phaser.Easing.Linear.None; + break; + } + } + + } + +} diff --git a/Phaser/particles/Polar2D.js b/Phaser/particles/Polar2D.js new file mode 100644 index 000000000..288c4666d --- /dev/null +++ b/Phaser/particles/Polar2D.js @@ -0,0 +1,71 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Polar2D = (function () { + function Polar2D(r, tha) { + this.r = Math.abs(r) || 0; + this.tha = tha || 0; + } + Polar2D.prototype.set = function (r, tha) { + this.r = r; + this.tha = tha; + return this; + }; + + Polar2D.prototype.setR = function (r) { + this.r = r; + return this; + }; + + Polar2D.prototype.setTha = function (tha) { + this.tha = tha; + return this; + }; + + Polar2D.prototype.copy = function (p) { + this.r = p.r; + this.tha = p.tha; + return this; + }; + + Polar2D.prototype.toVector = function () { + return new Phaser.Vec2(this.getX(), this.getY()); + }; + + Polar2D.prototype.getX = function () { + return this.r * Math.sin(this.tha); + }; + + Polar2D.prototype.getY = function () { + return -this.r * Math.cos(this.tha); + }; + + Polar2D.prototype.normalize = function () { + this.r = 1; + return this; + }; + + Polar2D.prototype.equals = function (v) { + return ((v.r === this.r) && (v.tha === this.tha)); + }; + + Polar2D.prototype.toArray = function () { + return [this.r, this.tha]; + }; + + Polar2D.prototype.clear = function () { + this.r = 0.0; + this.tha = 0.0; + return this; + }; + + Polar2D.prototype.clone = function () { + return new Polar2D(this.r, this.tha); + }; + return Polar2D; + })(); + Particles.Polar2D = Polar2D; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/Polar2D.ts b/Phaser/particles/Polar2D.ts new file mode 100644 index 000000000..bd4ebf88e --- /dev/null +++ b/Phaser/particles/Polar2D.ts @@ -0,0 +1,82 @@ +/// + +module Phaser.Particles { + + export class Polar2D { + + constructor(r, tha) { + this.r = Math.abs(r) || 0; + this.tha = tha || 0; + } + + r; + tha; + + set(r, tha) { + + this.r = r; + this.tha = tha; + return this; + + } + + setR(r) { + + this.r = r; + return this; + + } + + setTha(tha) { + + this.tha = tha; + return this; + + } + + copy(p) { + + this.r = p.r; + this.tha = p.tha; + return this; + + } + + toVector() { + return new Phaser.Vec2(this.getX(), this.getY()); + } + + getX() { + return this.r * Math.sin(this.tha); + } + + getY() { + return -this.r * Math.cos(this.tha); + } + + normalize() { + + this.r = 1; + return this; + } + + equals(v) { + return ((v.r === this.r) && (v.tha === this.tha)); + } + + toArray() { + return [this.r, this.tha]; + } + + clear() { + this.r = 0.0; + this.tha = 0.0; + return this; + } + + clone() { + return new Polar2D(this.r, this.tha); + } + } + +} \ No newline at end of file diff --git a/Phaser/particles/Span.js b/Phaser/particles/Span.js new file mode 100644 index 000000000..85b07c060 --- /dev/null +++ b/Phaser/particles/Span.js @@ -0,0 +1,41 @@ +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Span = (function () { + function Span(a, b, center) { + if (typeof b === "undefined") { b = null; } + if (typeof center === "undefined") { center = null; } + this.isArray = false; + + if (Particles.ParticleUtils.isArray(a)) { + this.isArray = true; + this.a = a; + } else { + this.a = Particles.ParticleUtils.initValue(a, 1); + this.b = Particles.ParticleUtils.initValue(b, this.a); + this.center = Particles.ParticleUtils.initValue(center, false); + } + } + Span.prototype.getValue = function (INT) { + if (typeof INT === "undefined") { INT = null; } + if (this.isArray) { + return this.a[Math.floor(this.a.length * Math.random())]; + } else { + if (!this.center) { + return Particles.ParticleUtils.randomAToB(this.a, this.b, INT); + } else { + return Particles.ParticleUtils.randomFloating(this.a, this.b, INT); + } + } + }; + + Span.getSpan = function (a, b, center) { + return new Span(a, b, center); + }; + return Span; + })(); + Particles.Span = Span; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/Span.ts b/Phaser/particles/Span.ts new file mode 100644 index 000000000..666280ec6 --- /dev/null +++ b/Phaser/particles/Span.ts @@ -0,0 +1,57 @@ +/// + +module Phaser.Particles { + + export class Span { + + constructor(a, b = null, center = null) { + + this.isArray = false; + + if (ParticleUtils.isArray(a)) + { + this.isArray = true; + this.a = a; + } + else + { + this.a = ParticleUtils.initValue(a, 1); + this.b = ParticleUtils.initValue(b, this.a); + this.center = ParticleUtils.initValue(center, false); + } + + } + + a; + b; + c; + center; + isArray; + + getValue(INT = null) { + + if (this.isArray) + { + return this.a[Math.floor(this.a.length * Math.random())]; + } + else + { + if (!this.center) + { + return ParticleUtils.randomAToB(this.a, this.b, INT); + } + else + { + return ParticleUtils.randomFloating(this.a, this.b, INT); + } + } + + } + + static getSpan(a, b, center) { + return new Span(a, b, center); + } + + } + +} diff --git a/Phaser/particles/behaviours/Behaviour.js b/Phaser/particles/behaviours/Behaviour.js new file mode 100644 index 000000000..ceaf4fc0c --- /dev/null +++ b/Phaser/particles/behaviours/Behaviour.js @@ -0,0 +1,129 @@ +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var Behaviour = (function () { + function Behaviour(life, easing) { + /** + * The behaviour's id; + * @property id + * @type {String} id + */ + this.id = 'Behaviour_' + Behaviour.ID++; + this.life = Particles.ParticleUtils.initValue(life, Infinity); + + /** + * The behaviour's decaying trend, for example Proton.easeOutQuart; + * @property easing + * @type {String} + * @default Proton.easeLinear + */ + this.easing = Particles.ParticleUtils.setEasingByName(easing); + this.age = 0; + this.energy = 1; + + /** + * The behaviour is Dead; + * @property dead + * @type {Boolean} + */ + this.dead = false; + + /** + * The behaviour's parents array; + * @property parents + * @type {Array} + */ + this.parents = []; + + /** + * The behaviour name; + * @property name + * @type {string} + */ + this.name = 'Behaviour'; + } + /** + * Reset this behaviour's parameters + * + * @method reset + * @param {Number} this behaviour's life + * @param {String} this behaviour's easing + */ + //reset (life, easing) { + // this.life = ParticleUtils.initValue(life, Infinity); + // //this.easing = ParticleUtils.initValue(easing, Proton.ease.setEasingByName(Proton.easeLinear)); + //} + /** + * Normalize a force by 1:100; + * + * @method normalizeForce + * @param {Proton.Vector2D} force + */ + Behaviour.prototype.normalizeForce = function (force) { + return force.multiplyScalar(Particles.ParticleManager.MEASURE); + }; + + /** + * Normalize a value by 1:100; + * + * @method normalizeValue + * @param {Number} value + */ + Behaviour.prototype.normalizeValue = function (value) { + return value * Particles.ParticleManager.MEASURE; + }; + + /** + * Initialize the behaviour's parameters for all particles + * + * @method initialize + * @param {Proton.Particle} particle + */ + Behaviour.prototype.initialize = function (particle) { + }; + + /** + * Apply this behaviour for all particles every time + * + * @method applyBehaviour + * @param {Proton.Particle} particle + * @param {Number} the integrate time 1/ms + * @param {Int} the particle index + */ + Behaviour.prototype.applyBehaviour = function (particle, time, index) { + this.age += time; + + if (this.age >= this.life || this.dead) { + this.energy = 0; + this.dead = true; + this.destroy(); + } else { + var scale = this.easing(particle.age / particle.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + /** + * Destory this behaviour + * @method destory + */ + Behaviour.prototype.destroy = function () { + var index; + var length = this.parents.length, i; + + for (i = 0; i < length; i++) { + this.parents[i].removeBehaviour(this); + } + + this.parents = []; + }; + return Behaviour; + })(); + Behaviours.Behaviour = Behaviour; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/behaviours/Behaviour.ts b/Phaser/particles/behaviours/Behaviour.ts new file mode 100644 index 000000000..d25479f20 --- /dev/null +++ b/Phaser/particles/behaviours/Behaviour.ts @@ -0,0 +1,140 @@ +/// + +module Phaser.Particles.Behaviours { + + export class Behaviour { + + constructor(life, easing) { + /** + * The behaviour's id; + * @property id + * @type {String} id + */ + this.id = 'Behaviour_' + Behaviour.ID++; + this.life = ParticleUtils.initValue(life, Infinity); + /** + * The behaviour's decaying trend, for example Proton.easeOutQuart; + * @property easing + * @type {String} + * @default Proton.easeLinear + */ + this.easing = ParticleUtils.setEasingByName(easing); + this.age = 0; + this.energy = 1; + /** + * The behaviour is Dead; + * @property dead + * @type {Boolean} + */ + this.dead = false; + /** + * The behaviour's parents array; + * @property parents + * @type {Array} + */ + this.parents = []; + /** + * The behaviour name; + * @property name + * @type {string} + */ + this.name = 'Behaviour'; + } + + static ID; + id; + life; + easing; + age; + energy; + dead; + parents; + name; + + /** + * Reset this behaviour's parameters + * + * @method reset + * @param {Number} this behaviour's life + * @param {String} this behaviour's easing + */ + //reset (life, easing) { + // this.life = ParticleUtils.initValue(life, Infinity); + // //this.easing = ParticleUtils.initValue(easing, Proton.ease.setEasingByName(Proton.easeLinear)); + //} + + /** + * Normalize a force by 1:100; + * + * @method normalizeForce + * @param {Proton.Vector2D} force + */ + normalizeForce (force) { + return force.multiplyScalar(ParticleManager.MEASURE); + } + + /** + * Normalize a value by 1:100; + * + * @method normalizeValue + * @param {Number} value + */ + normalizeValue (value) { + return value * ParticleManager.MEASURE; + } + + /** + * Initialize the behaviour's parameters for all particles + * + * @method initialize + * @param {Proton.Particle} particle + */ + initialize (particle) { + } + + /** + * Apply this behaviour for all particles every time + * + * @method applyBehaviour + * @param {Proton.Particle} particle + * @param {Number} the integrate time 1/ms + * @param {Int} the particle index + */ + applyBehaviour (particle, time, index) { + + this.age += time; + + if (this.age >= this.life || this.dead) + { + this.energy = 0; + this.dead = true; + this.destroy(); + } + else + { + var scale = this.easing(particle.age / particle.life); + this.energy = Math.max(1 - scale, 0); + } + + } + + /** + * Destory this behaviour + * @method destory + */ + destroy () { + + var index; + var length = this.parents.length, i; + + for (i = 0; i < length; i++) + { + this.parents[i].removeBehaviour(this); + } + + this.parents = []; + } + + } + +} \ No newline at end of file diff --git a/Phaser/particles/behaviours/RandomDrift.js b/Phaser/particles/behaviours/RandomDrift.js new file mode 100644 index 000000000..cb6669619 --- /dev/null +++ b/Phaser/particles/behaviours/RandomDrift.js @@ -0,0 +1,50 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var RandomDrift = (function (_super) { + __extends(RandomDrift, _super); + function RandomDrift(driftX, driftY, delay, life, easing) { + _super.call(this, life, easing); + this.reset(driftX, driftY, delay); + this.time = 0; + this.name = "RandomDrift"; + } + RandomDrift.prototype.reset = function (driftX, driftY, delay, life, easing) { + if (typeof life === "undefined") { life = null; } + if (typeof easing === "undefined") { easing = null; } + this.panFoce = new Phaser.Vec2(driftX, driftY); + this.panFoce = this.normalizeForce(this.panFoce); + this.delay = delay; + + if (life) { + this.life = Particles.ParticleUtils.initValue(life, Infinity); + this.easing = Particles.ParticleUtils.initValue(easing, Phaser.Easing.Linear.None); + } + }; + + RandomDrift.prototype.applyBehaviour = function (particle, time, index) { + _super.prototype.applyBehaviour.call(this, particle, time, index); + + this.time += time; + + if (this.time >= this.delay) { + particle.a.addXY(Particles.ParticleUtils.randomAToB(-this.panFoce.x, this.panFoce.x), Particles.ParticleUtils.randomAToB(-this.panFoce.y, this.panFoce.y)); + this.time = 0; + } + }; + return RandomDrift; + })(Behaviours.Behaviour); + Behaviours.RandomDrift = RandomDrift; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/behaviours/RandomDrift.ts b/Phaser/particles/behaviours/RandomDrift.ts new file mode 100644 index 000000000..030983eb1 --- /dev/null +++ b/Phaser/particles/behaviours/RandomDrift.ts @@ -0,0 +1,49 @@ +/// + +module Phaser.Particles.Behaviours { + + export class RandomDrift extends Behaviour { + + constructor(driftX, driftY, delay, life, easing) { + super(life, easing); + this.reset(driftX, driftY, delay); + this.time = 0; + this.name = "RandomDrift"; + } + + panFoce; + delay; + time; + + reset(driftX, driftY, delay, life= null, easing= null) { + + this.panFoce = new Phaser.Vec2(driftX, driftY); + this.panFoce = this.normalizeForce(this.panFoce); + this.delay = delay; + + if (life) + { + this.life = ParticleUtils.initValue(life, Infinity); + this.easing = ParticleUtils.initValue(easing, Phaser.Easing.Linear.None); + } + + } + + applyBehaviour(particle, time, index) { + + super.applyBehaviour(particle, time, index); + + this.time += time; + + if (this.time >= this.delay) + { + particle.a.addXY(ParticleUtils.randomAToB(-this.panFoce.x, this.panFoce.x), ParticleUtils.randomAToB(-this.panFoce.y, this.panFoce.y)); + this.time = 0; + } + + } + + } + +} + diff --git a/Phaser/particles/initialize/Initialize.js b/Phaser/particles/initialize/Initialize.js new file mode 100644 index 000000000..7cbfc68c7 --- /dev/null +++ b/Phaser/particles/initialize/Initialize.js @@ -0,0 +1,30 @@ +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Initialize = (function () { + function Initialize() { + } + Initialize.prototype.initialize = function (target) { + }; + + Initialize.prototype.reset = function (a, b, c) { + }; + + Initialize.prototype.init = function (emitter, particle) { + if (typeof particle === "undefined") { particle = null; } + if (particle) { + this.initialize(particle); + } else { + this.initialize(emitter); + } + }; + return Initialize; + })(); + Initializers.Initialize = Initialize; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Initialize.ts b/Phaser/particles/initialize/Initialize.ts new file mode 100644 index 000000000..3759a3668 --- /dev/null +++ b/Phaser/particles/initialize/Initialize.ts @@ -0,0 +1,28 @@ +/// + +module Phaser.Particles.Initializers { + + export class Initialize { + + initialize(target) { + } + + reset(a,b,c) { } + + init(emitter, particle= null) { + + if (particle) + { + this.initialize(particle); + } + else + { + this.initialize(emitter); + } + + } + + } + +} + diff --git a/Phaser/particles/initialize/Life.js b/Phaser/particles/initialize/Life.js new file mode 100644 index 000000000..7334f179c --- /dev/null +++ b/Phaser/particles/initialize/Life.js @@ -0,0 +1,33 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Life = (function (_super) { + __extends(Life, _super); + function Life(a, b, c) { + _super.call(this); + + this.lifePan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Life.prototype.initialize = function (target) { + if (this.lifePan.a == Infinity) { + target.life = Infinity; + } else { + target.life = this.lifePan.getValue(); + } + }; + return Life; + })(Initializers.Initialize); + Initializers.Life = Life; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Life.ts b/Phaser/particles/initialize/Life.ts new file mode 100644 index 000000000..66e556334 --- /dev/null +++ b/Phaser/particles/initialize/Life.ts @@ -0,0 +1,32 @@ +/// + +module Phaser.Particles.Initializers { + + export class Life extends Initialize { + + constructor(a,b,c) { + + super(); + + this.lifePan = ParticleUtils.setSpanValue(a, b, c); + + } + + lifePan: Phaser.Particles.Span; + + initialize(target) { + + if (this.lifePan.a == Infinity) + { + target.life = Infinity; + } + else + { + target.life = this.lifePan.getValue(); + } + } + + } + +} + diff --git a/Phaser/particles/initialize/Mass.js b/Phaser/particles/initialize/Mass.js new file mode 100644 index 000000000..710cf841b --- /dev/null +++ b/Phaser/particles/initialize/Mass.js @@ -0,0 +1,28 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Mass = (function (_super) { + __extends(Mass, _super); + function Mass(a, b, c) { + _super.call(this); + this.massPan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Mass.prototype.initialize = function (target) { + target.mass = this.massPan.getValue(); + }; + return Mass; + })(Initializers.Initialize); + Initializers.Mass = Mass; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Mass.ts b/Phaser/particles/initialize/Mass.ts new file mode 100644 index 000000000..16da15218 --- /dev/null +++ b/Phaser/particles/initialize/Mass.ts @@ -0,0 +1,21 @@ +/// + +module Phaser.Particles.Initializers { + + export class Mass extends Initialize { + + constructor(a,b,c) { + super(); + this.massPan = ParticleUtils.setSpanValue(a, b, c); + } + + massPan: Phaser.Particles.Span; + + initialize(target) { + target.mass = this.massPan.getValue(); + } + + } + +} + diff --git a/Phaser/particles/initialize/Position.js b/Phaser/particles/initialize/Position.js new file mode 100644 index 000000000..2855d71f7 --- /dev/null +++ b/Phaser/particles/initialize/Position.js @@ -0,0 +1,44 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Position = (function (_super) { + __extends(Position, _super); + function Position(zone) { + _super.call(this); + + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + } + Position.prototype.reset = function (zone) { + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + }; + + Position.prototype.initialize = function (target) { + this.zone.getPosition(); + + target.p.x = this.zone.vector.x; + target.p.y = this.zone.vector.y; + }; + return Position; + })(Initializers.Initialize); + Initializers.Position = Position; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Position.ts b/Phaser/particles/initialize/Position.ts new file mode 100644 index 000000000..f92c64bc6 --- /dev/null +++ b/Phaser/particles/initialize/Position.ts @@ -0,0 +1,45 @@ +/// + +module Phaser.Particles.Initializers { + + export class Position extends Initialize { + + constructor(zone) { + + super(); + + if (zone != null && zone != undefined) + { + this.zone = zone; + } + else + { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + + } + + zone; + + reset(zone) { + if (zone != null && zone != undefined) + { + this.zone = zone; + } + else + { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + } + + initialize(target) { + + this.zone.getPosition(); + + target.p.x = this.zone.vector.x; + target.p.y = this.zone.vector.y; + } + + } + +} diff --git a/Phaser/particles/initialize/Radius.js b/Phaser/particles/initialize/Radius.js new file mode 100644 index 000000000..2f9b56882 --- /dev/null +++ b/Phaser/particles/initialize/Radius.js @@ -0,0 +1,19 @@ +var Shapes; +(function (Shapes) { + + var Point = Shapes.Point = (function () { + function Point(x, y) { + this.x = x; + this.y = y; + } + Point.prototype.getDist = function () { + return Math.sqrt((this.x * this.x) + (this.y * this.y)); + }; + Point.origin = new Point(0, 0); + return Point; + })(); + +})(Shapes || (Shapes = {})); + +var p = new Shapes.Point(3, 4); +var dist = p.getDist(); diff --git a/Phaser/particles/initialize/Radius.ts b/Phaser/particles/initialize/Radius.ts new file mode 100644 index 000000000..f80ff261e --- /dev/null +++ b/Phaser/particles/initialize/Radius.ts @@ -0,0 +1,33 @@ +/// + +module Phaser.Particles.Initializers { + + export class Radius extends Initialize { + + constructor(a,b,c) { + + super(); + + this.radius = ParticleUtils.setSpanValue(a, b, c); + + } + + radius: Phaser.Particles.Span; + + reset(a, b, c) { + + this.radius = ParticleUtils.setSpanValue(a, b, c); + + } + + initialize(particle) { + + particle.radius = this.radius.getValue(); + particle.transform.oldRadius = particle.radius; + + } + + } + +} + diff --git a/Phaser/particles/initialize/Rate.js b/Phaser/particles/initialize/Rate.js new file mode 100644 index 000000000..74546f8d3 --- /dev/null +++ b/Phaser/particles/initialize/Rate.js @@ -0,0 +1,57 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Rate = (function (_super) { + __extends(Rate, _super); + function Rate(numpan, timepan) { + _super.call(this); + + numpan = Particles.ParticleUtils.initValue(numpan, 1); + timepan = Particles.ParticleUtils.initValue(timepan, 1); + this.numPan = new Phaser.Particles.Span(numpan); + this.timePan = new Phaser.Particles.Span(timepan); + this.startTime = 0; + this.nextTime = 0; + this.init(); + } + Rate.prototype.init = function () { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + }; + + Rate.prototype.getValue = function (time) { + this.startTime += time; + + if (this.startTime >= this.nextTime) { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + + if (this.numPan.b == 1) { + if (this.numPan.getValue(false) > 0.5) { + return 1; + } else { + return 0; + } + } else { + return this.numPan.getValue(true); + } + } + + return 0; + }; + return Rate; + })(Initializers.Initialize); + Initializers.Rate = Rate; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Rate.ts b/Phaser/particles/initialize/Rate.ts new file mode 100644 index 000000000..71510c30f --- /dev/null +++ b/Phaser/particles/initialize/Rate.ts @@ -0,0 +1,60 @@ +/// + +module Phaser.Particles.Initializers { + + export class Rate extends Initialize { + + constructor(numpan, timepan) { + super(); + + numpan = ParticleUtils.initValue(numpan, 1); + timepan = ParticleUtils.initValue(timepan, 1); + this.numPan = new Phaser.Particles.Span(numpan); + this.timePan = new Phaser.Particles.Span(timepan); + this.startTime = 0; + this.nextTime = 0; + this.init(); + } + + numPan: Phaser.Particles.Span; + timePan: Phaser.Particles.Span; + startTime; + nextTime; + + init() { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + } + + getValue (time) { + + this.startTime += time; + + if (this.startTime >= this.nextTime) + { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + + if (this.numPan.b == 1) + { + if (this.numPan.getValue(false) > 0.5) + { + return 1; + } + else + { + return 0; + } + } + else + { + return this.numPan.getValue(true); + } + } + + return 0; + } + + } + +} diff --git a/Phaser/particles/initialize/Velocity.js b/Phaser/particles/initialize/Velocity.js new file mode 100644 index 000000000..e41f471cb --- /dev/null +++ b/Phaser/particles/initialize/Velocity.js @@ -0,0 +1,48 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Velocity = (function (_super) { + __extends(Velocity, _super); + function Velocity(rpan, thapan, type) { + _super.call(this); + + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + } + Velocity.prototype.reset = function (rpan, thapan, type) { + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + }; + + Velocity.prototype.normalizeVelocity = function (vr) { + return vr * Particles.ParticleManager.MEASURE; + }; + + Velocity.prototype.initialize = function (target) { + if (this.type == 'p' || this.type == 'P' || this.type == 'polar') { + var polar2d = new Particles.Polar2D(this.normalizeVelocity(this.rPan.getValue()), this.thaPan.getValue() * Math.PI / 180); + target.v.x = polar2d.getX(); + target.v.y = polar2d.getY(); + } else { + target.v.x = this.normalizeVelocity(this.rPan.getValue()); + target.v.y = this.normalizeVelocity(this.thaPan.getValue()); + } + }; + return Velocity; + })(Initializers.Initialize); + Initializers.Velocity = Velocity; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/initialize/Velocity.ts b/Phaser/particles/initialize/Velocity.ts new file mode 100644 index 000000000..61d93fdfc --- /dev/null +++ b/Phaser/particles/initialize/Velocity.ts @@ -0,0 +1,46 @@ +/// + +module Phaser.Particles.Initializers { + + export class Velocity extends Initialize { + + constructor(rpan, thapan, type) { + super(); + + this.rPan = ParticleUtils.setSpanValue(rpan); + this.thaPan = ParticleUtils.setSpanValue(thapan); + this.type = ParticleUtils.initValue(type, 'vector'); + } + + rPan: Phaser.Particles.Span; + thaPan: Phaser.Particles.Span; + type; + + reset(rpan, thapan, type) { + this.rPan = ParticleUtils.setSpanValue(rpan); + this.thaPan = ParticleUtils.setSpanValue(thapan); + this.type = ParticleUtils.initValue(type, 'vector'); + } + + normalizeVelocity(vr) { + return vr * ParticleManager.MEASURE; + } + + initialize(target) { + + if (this.type == 'p' || this.type == 'P' || this.type == 'polar') + { + var polar2d = new Polar2D(this.normalizeVelocity(this.rPan.getValue()), this.thaPan.getValue() * Math.PI / 180); + target.v.x = polar2d.getX(); + target.v.y = polar2d.getY(); + } + else + { + target.v.x = this.normalizeVelocity(this.rPan.getValue()); + target.v.y = this.normalizeVelocity(this.thaPan.getValue()); + } + } + + } + +} diff --git a/Phaser/particles/zone/PointZone.js b/Phaser/particles/zone/PointZone.js new file mode 100644 index 000000000..a861a3cfd --- /dev/null +++ b/Phaser/particles/zone/PointZone.js @@ -0,0 +1,38 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var PointZone = (function (_super) { + __extends(PointZone, _super); + function PointZone(x, y) { + if (typeof x === "undefined") { x = 0; } + if (typeof y === "undefined") { y = 0; } + _super.call(this); + this.x = x; + this.y = y; + } + PointZone.prototype.getPosition = function () { + return this.vector.setTo(this.x, this.y); + }; + + PointZone.prototype.crossing = function (particle) { + if (this.alert) { + alert('Sorry PointZone does not support crossing method'); + this.alert = false; + } + }; + return PointZone; + })(Zones.Zone); + Zones.PointZone = PointZone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/zone/PointZone.ts b/Phaser/particles/zone/PointZone.ts new file mode 100644 index 000000000..37387411e --- /dev/null +++ b/Phaser/particles/zone/PointZone.ts @@ -0,0 +1,31 @@ +/// + +module Phaser.Particles.Zones { + + export class PointZone extends Zone { + + constructor(x=0,y=0) { + super(); + this.x = x; + this.y = y; + } + + x; + y; + + getPosition() { + return this.vector.setTo(this.x, this.y); + } + + crossing(particle) { + + if (this.alert) + { + alert('Sorry PointZone does not support crossing method'); + this.alert = false; + } + + } + + } +} diff --git a/Phaser/particles/zone/Zone.js b/Phaser/particles/zone/Zone.js new file mode 100644 index 000000000..d34531692 --- /dev/null +++ b/Phaser/particles/zone/Zone.js @@ -0,0 +1,20 @@ +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var Zone = (function () { + function Zone() { + this.vector = new Phaser.Vec2(); + this.random = 0; + this.crossType = "dead"; + this.alert = true; + } + return Zone; + })(); + Zones.Zone = Zone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Phaser/particles/zone/Zone.ts b/Phaser/particles/zone/Zone.ts new file mode 100644 index 000000000..47e421fce --- /dev/null +++ b/Phaser/particles/zone/Zone.ts @@ -0,0 +1,20 @@ +/// + +module Phaser.Particles.Zones { + + export class Zone { + + constructor() { + this.vector = new Phaser.Vec2; + this.random = 0; + this.crossType = "dead"; + this.alert = true; + } + + vector: Phaser.Vec2; + random: number; + crossType: string; + alert: boolean; + + } +} diff --git a/Phaser/physics/AABB.js b/Phaser/physics/AABB.js new file mode 100644 index 000000000..943268fc2 --- /dev/null +++ b/Phaser/physics/AABB.js @@ -0,0 +1,196 @@ +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - AABB + */ + (function (Physics) { + var AABB = (function () { + function AABB(game, x, y, xw, yw) { + this.type = 0; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + + this.aabbTileProjections = {}; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.AABBFull.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.AABBConcave.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.AABBConvex.Collide; + } + AABB.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + //o = oldposition + var ox = o.x; + var oy = o.y; + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + AABB.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + AABB.prototype.CollideAABBVsTile = function (tile) { + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + this.xw) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + this.yw) - Math.abs(dy); + + if (0 < py) { + if (px < py) { + if (dx < 0) { + //project to the left + px *= -1; + py = 0; + } else { + //proj to right + py = 0; + } + } else { + if (dy < 0) { + //project up + px = 0; + py *= -1; + } else { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + } + } + }; + + AABB.prototype.CollideAABBVsWorldBounds = function () { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + AABB.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + }; + + AABB.prototype.ResolveBoxTile = function (x, y, box, t) { + if (0 < t.ID) { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } else { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + }; + AABB.COL_NONE = 0; + AABB.COL_AXIS = 1; + AABB.COL_OTHER = 2; + return AABB; + })(); + Physics.AABB = AABB; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/AABB.ts b/Phaser/physics/AABB.ts new file mode 100644 index 000000000..529f1c4d2 --- /dev/null +++ b/Phaser/physics/AABB.ts @@ -0,0 +1,253 @@ +/// + +/** +* Phaser - Physics - AABB +*/ + +module Phaser.Physics { + + export class AABB { + + constructor(game: Phaser.Game, x: number, y: number, xw: number, yw: number) { + + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + + this.aabbTileProjections = {}; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.AABBFull.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.AABBConcave.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.AABBConvex.Collide; + + } + + public game: Phaser.Game; + + public static COL_NONE = 0; + public static COL_AXIS = 1; + public static COL_OTHER = 2; + + public type: number = 0; + public pos: Phaser.Vec2; + public oldpos: Phaser.Vec2; + public xw: number; + public yw: number; + public oH: number; + public oV: number; + + private aabbTileProjections; + + public IntegrateVerlet() { + + var d = 1; // global drag + var g = 0.2; // global gravity + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + //o = oldposition + var ox = o.x; + var oy = o.y; + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px: number, py: number, dx: number, dy: number, obj: TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + + public CollideAABBVsTile(tile:Phaser.Physics.TileMapCell) { + + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + this.xw) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + this.yw) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile; call tile-specific collision function + + //calculate projection vectors + if (px < py) + { + //project in x + if (dx < 0) + { + //project to the left + px *= -1; + py = 0; + } + else + { + //proj to right + py = 0; + } + } + else + { + //project in y + if (dy < 0) + { + //project up + px = 0; + py *= -1; + } + else + { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + + } + } + } + + public CollideAABBVsWorldBounds() { + + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context: CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + + } + + public ResolveBoxTile(x, y, box, t) { + + if (0 < t.ID) + { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } + else + { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/arcade/Body.js b/Phaser/physics/Body.js similarity index 97% rename from Phaser/physics/arcade/Body.js rename to Phaser/physics/Body.js index dc4d8c362..91dc95651 100644 --- a/Phaser/physics/arcade/Body.js +++ b/Phaser/physics/Body.js @@ -1,8 +1,9 @@ var Phaser; (function (Phaser) { - /// + /// /** - * Phaser - ArcadePhysics - Body + * Phaser - Physics - Body + * A binding between a Sprite and a physics object (AABB or Circle) */ (function (Physics) { var Body = (function () { diff --git a/Phaser/physics/arcade/Body.ts b/Phaser/physics/Body.ts similarity index 97% rename from Phaser/physics/arcade/Body.ts rename to Phaser/physics/Body.ts index 1c056bb3d..3af9bdbb9 100644 --- a/Phaser/physics/arcade/Body.ts +++ b/Phaser/physics/Body.ts @@ -1,7 +1,8 @@ -/// +/// /** -* Phaser - ArcadePhysics - Body +* Phaser - Physics - Body +* A binding between a Sprite and a physics object (AABB or Circle) */ module Phaser.Physics { diff --git a/Phaser/physics/Circle.js b/Phaser/physics/Circle.js new file mode 100644 index 000000000..2f1206cc8 --- /dev/null +++ b/Phaser/physics/Circle.js @@ -0,0 +1,227 @@ +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - Circle + */ + (function (Physics) { + var Circle = (function () { + function Circle(game, x, y, radius) { + this.type = 1; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + this.radius = radius; + + this.circleTileProjections = {}; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.CircleFull.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_45DEG] = Phaser.Physics.Projection.Circle45Deg.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.CircleConcave.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.CircleConvex.Collide; + } + Circle.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + var ox = o.x; + var oy = o.y; + + //o = oldposition + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + Circle.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + Circle.prototype.CollideCircleVsWorldBounds = function () { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } else if (this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } else if (this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + }; + + Circle.prototype.CollideCircleVsTile = function (tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + r) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + r) - Math.abs(dy); + + if (0 < py) { + //object may be colliding with tile + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) { + //circle is on left side of tile + this.oH = -1; + } else if (txw < dx) { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) { + //circle is on top side of tile + this.oV = -1; + } else if (tyw < dy) { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + } + } + }; + + Circle.prototype.ResolveCircleTile = function (x, y, oH, oV, obj, t) { + if (0 < t.ID) { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } else { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + }; + Circle.COL_NONE = 0; + Circle.COL_AXIS = 1; + Circle.COL_OTHER = 2; + return Circle; + })(); + Physics.Circle = Circle; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/Circle.ts b/Phaser/physics/Circle.ts new file mode 100644 index 000000000..371f68add --- /dev/null +++ b/Phaser/physics/Circle.ts @@ -0,0 +1,279 @@ +/// + +/** +* Phaser - Physics - Circle +*/ + +module Phaser.Physics { + + export class Circle { + + constructor(game: Phaser.Game, x: number, y: number, radius:number) { + + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + this.radius = radius; + + this.circleTileProjections = {}; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.CircleFull.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_45DEG] = Phaser.Physics.Projection.Circle45Deg.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.CircleConcave.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.CircleConvex.Collide; + + } + + public game: Phaser.Game; + + public static COL_NONE = 0; + public static COL_AXIS = 1; + public static COL_OTHER = 2; + + public type: number = 1; + public pos: Phaser.Vec2; + public oldpos: Phaser.Vec2; + public radius: number; + public oH: number; // horizontal collision + public oV: number; + + private circleTileProjections; + + public IntegrateVerlet() { + + var d = 1; // drag + var g = 0.2; // gravity + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + var ox = o.x; + var oy = o.y; + //o = oldposition + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px: number, py: number, dx: number, dy: number, obj: Phaser.Physics.TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.9;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + public CollideCircleVsWorldBounds() { + + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context: CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + } + + public CollideCircleVsTile(tile) { + + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + r) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + r) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile + + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) + { + //circle is on left side of tile + this.oH = -1; + } + else if (txw < dx) + { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) + { + //circle is on top side of tile + this.oV = -1; + } + else if (tyw < dy) + { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + + } + } + } + + public ResolveCircleTile(x, y, oH, oV, obj, t) { + + if (0 < t.ID) + { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } + else + { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/PhysicsManager.js b/Phaser/physics/PhysicsManager.js new file mode 100644 index 000000000..5e2e8087f --- /dev/null +++ b/Phaser/physics/PhysicsManager.js @@ -0,0 +1,38 @@ +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - PhysicsManager + */ + (function (Physics) { + var PhysicsManager = (function () { + function PhysicsManager(game) { + this.grav = 0.2; + this.drag = 1; + this.bounce = 0.3; + this.friction = 0.05; + this.min_f = 0; + this.max_f = 1; + this.min_b = 0; + this.max_b = 1; + this.min_g = 0; + this.max_g = 1; + this.xmin = 0; + this.xmax = 800; + this.ymin = 0; + this.ymax = 600; + this.objrad = 24; + this.tilerad = 24 * 2; + this.objspeed = 0.2; + this.maxspeed = 20; + this.game = game; + } + PhysicsManager.prototype.update = function () { + // Booyah! + }; + return PhysicsManager; + })(); + Physics.PhysicsManager = PhysicsManager; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/PhysicsManager.ts b/Phaser/physics/PhysicsManager.ts new file mode 100644 index 000000000..491561be0 --- /dev/null +++ b/Phaser/physics/PhysicsManager.ts @@ -0,0 +1,51 @@ +/// + +/** +* Phaser - Physics - PhysicsManager +*/ + +module Phaser.Physics { + + export class PhysicsManager { + + constructor(game: Phaser.Game) { + + this.game = game; + + } + + public game: Phaser.Game; + + grav: number = 0.2; + drag: number = 1; + bounce: number = 0.3; + friction: number = 0.05; + + min_f: number = 0; + max_f: number = 1; + + min_b: number = 0; + max_b: number = 1; + + min_g: number = 0; + max_g = 1; + + xmin: number = 0; + xmax: number = 800; + ymin: number = 0; + ymax: number = 600; + + objrad: number = 24; + tilerad: number = 24 * 2; + objspeed: number = 0.2; + maxspeed: number = 20; + + public update() { + + // Booyah! + + } + + } + +} diff --git a/Phaser/physics/TileMapCell.js b/Phaser/physics/TileMapCell.js new file mode 100644 index 000000000..472fc4954 --- /dev/null +++ b/Phaser/physics/TileMapCell.js @@ -0,0 +1,361 @@ +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - TileMapCell + */ + (function (Physics) { + var TileMapCell = (function () { + function TileMapCell(game, x, y, xw, yw) { + this.game = game; + this.ID = TileMapCell.TID_EMPTY; + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x, y); + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + TileMapCell.prototype.SetState = function (ID) { + if (ID == TileMapCell.TID_EMPTY) { + this.Clear(); + } else { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + }; + + TileMapCell.prototype.Clear = function () { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY; + this.UpdateType(); + //this.Draw(); + }; + + TileMapCell.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + }; + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + TileMapCell.prototype.UpdateType = function () { + if (0 < this.ID) { + if (this.ID < TileMapCell.CTYPE_45DEG) { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } else if (this.ID < TileMapCell.CTYPE_CONCAVE) { + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnn) { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnp) { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGpp) { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_CONVEX) { + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGs) { + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGb) { + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGs) { + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGb) { + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_HALF) { + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFu) { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFl) { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFr) { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + } else { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + }; + TileMapCell.TID_EMPTY = 0; + TileMapCell.TID_FULL = 1; + TileMapCell.TID_45DEGpn = 2; + TileMapCell.TID_45DEGnn = 3; + TileMapCell.TID_45DEGnp = 4; + TileMapCell.TID_45DEGpp = 5; + TileMapCell.TID_CONCAVEpn = 6; + TileMapCell.TID_CONCAVEnn = 7; + TileMapCell.TID_CONCAVEnp = 8; + TileMapCell.TID_CONCAVEpp = 9; + TileMapCell.TID_CONVEXpn = 10; + TileMapCell.TID_CONVEXnn = 11; + TileMapCell.TID_CONVEXnp = 12; + TileMapCell.TID_CONVEXpp = 13; + TileMapCell.TID_22DEGpnS = 14; + TileMapCell.TID_22DEGnnS = 15; + TileMapCell.TID_22DEGnpS = 16; + TileMapCell.TID_22DEGppS = 17; + TileMapCell.TID_22DEGpnB = 18; + TileMapCell.TID_22DEGnnB = 19; + TileMapCell.TID_22DEGnpB = 20; + TileMapCell.TID_22DEGppB = 21; + TileMapCell.TID_67DEGpnS = 22; + TileMapCell.TID_67DEGnnS = 23; + TileMapCell.TID_67DEGnpS = 24; + TileMapCell.TID_67DEGppS = 25; + TileMapCell.TID_67DEGpnB = 26; + TileMapCell.TID_67DEGnnB = 27; + TileMapCell.TID_67DEGnpB = 28; + TileMapCell.TID_67DEGppB = 29; + TileMapCell.TID_HALFd = 30; + TileMapCell.TID_HALFr = 31; + TileMapCell.TID_HALFu = 32; + TileMapCell.TID_HALFl = 33; + + TileMapCell.CTYPE_EMPTY = 0; + TileMapCell.CTYPE_FULL = 1; + TileMapCell.CTYPE_45DEG = 2; + TileMapCell.CTYPE_CONCAVE = 6; + TileMapCell.CTYPE_CONVEX = 10; + TileMapCell.CTYPE_22DEGs = 14; + TileMapCell.CTYPE_22DEGb = 18; + TileMapCell.CTYPE_67DEGs = 22; + TileMapCell.CTYPE_67DEGb = 26; + TileMapCell.CTYPE_HALF = 30; + return TileMapCell; + })(); + Physics.TileMapCell = TileMapCell; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/TileMapCell.ts b/Phaser/physics/TileMapCell.ts new file mode 100644 index 000000000..b3081b961 --- /dev/null +++ b/Phaser/physics/TileMapCell.ts @@ -0,0 +1,492 @@ +/// + +/** +* Phaser - Physics - TileMapCell +*/ + +module Phaser.Physics { + + export class TileMapCell { + + constructor(game: Phaser.Game, x: number, y: number, xw: number, yw: number) { + + this.game = game; + this.ID = TileMapCell.TID_EMPTY; //all tiles start empty + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x, y); //setup collision properties + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + } + + public game: Phaser.Game; + + static TID_EMPTY = 0; + static TID_FULL = 1;//fullAABB tile + static TID_45DEGpn = 2;//45-degree triangle, whose normal is (+ve,-ve) + static TID_45DEGnn = 3;//(+ve,+ve) + static TID_45DEGnp = 4;//(-ve,+ve) + static TID_45DEGpp = 5;//(-ve,-ve) + static TID_CONCAVEpn = 6;//1/4-circle cutout + static TID_CONCAVEnn = 7; + static TID_CONCAVEnp = 8; + static TID_CONCAVEpp = 9; + static TID_CONVEXpn = 10;//1/4/circle + static TID_CONVEXnn = 11; + static TID_CONVEXnp = 12; + static TID_CONVEXpp = 13; + static TID_22DEGpnS = 14;//22.5 degree slope + static TID_22DEGnnS = 15; + static TID_22DEGnpS = 16; + static TID_22DEGppS = 17; + static TID_22DEGpnB = 18; + static TID_22DEGnnB = 19; + static TID_22DEGnpB = 20; + static TID_22DEGppB = 21; + static TID_67DEGpnS = 22;//67.5 degree slope + static TID_67DEGnnS = 23; + static TID_67DEGnpS = 24; + static TID_67DEGppS = 25; + static TID_67DEGpnB = 26; + static TID_67DEGnnB = 27; + static TID_67DEGnpB = 28; + static TID_67DEGppB = 29; + static TID_HALFd = 30;//half-full tiles + static TID_HALFr = 31; + static TID_HALFu = 32; + static TID_HALFl = 33; + + //collision shape "types" + static CTYPE_EMPTY = 0; + static CTYPE_FULL = 1; + static CTYPE_45DEG = 2; + static CTYPE_CONCAVE = 6; + static CTYPE_CONVEX = 10; + static CTYPE_22DEGs = 14; + static CTYPE_22DEGb = 18; + static CTYPE_67DEGs = 22; + static CTYPE_67DEGb = 26; + static CTYPE_HALF = 30; + + ID; + CTYPE; + pos: Phaser.Vec2; + xw; + yw; + minx; + maxx; + miny; + maxy; + signx; + signy; + sx; + sy; + + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + SetState(ID) { + if (ID == TileMapCell.TID_EMPTY) + { + this.Clear(); + } + else + { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + } + + Clear() { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY + this.UpdateType(); + //this.Draw(); + } + + public render(context: CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + + } + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + UpdateType() { + if (0 < this.ID) + { + //tile is non-empty; collide + if (this.ID < TileMapCell.CTYPE_45DEG) + { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + } + else if (this.ID < TileMapCell.CTYPE_CONCAVE) + { + + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) + { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnn) + { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnp) + { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.ID == TileMapCell.TID_45DEGpp) + { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_CONVEX) + { + + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGs) + { + + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGb) + { + + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGs) + { + + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGb) + { + + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_HALF) + { + + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else + { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) + { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFu) + { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFl) + { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFr) + { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else + { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + + } + + } + else + { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/aabb/ProjAABBConcave.js b/Phaser/physics/aabb/ProjAABBConcave.js new file mode 100644 index 000000000..2e397ed89 --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBConcave.js @@ -0,0 +1,56 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConcave = (function () { + function AABBConcave() { + } + AABBConcave.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw)); + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw)); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else { + //project along corner->circle vector + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConcave; + })(); + Projection.AABBConcave = AABBConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/aabb/ProjAABBConcave.ts b/Phaser/physics/aabb/ProjAABBConcave.ts new file mode 100644 index 000000000..d56621eb7 --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBConcave.ts @@ -0,0 +1,60 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class AABBConcave { + + public static Collide(x: number, y: number, obj: Phaser.Physics.AABB, t: Phaser.Physics.TileMapCell) { + + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw));//(ox,oy) is the vector form the innermost AABB corner to the + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw));//circle's center + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) + { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) + { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } + else + { + //project along corner->circle vector + ox /= len;//len should never be 0, since if it IS 0, rad should be > than len + oy /= len;//and we should never reach here + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + + } + + return Phaser.Physics.AABB.COL_NONE; + + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/aabb/ProjAABBConvex.js b/Phaser/physics/aabb/ProjAABBConvex.js new file mode 100644 index 000000000..db07fe0af --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBConvex.js @@ -0,0 +1,51 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConvex = (function () { + function AABBConvex() { + } + AABBConvex.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw)); + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw)); + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else if (0 < pen) { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConvex; + })(); + Projection.AABBConvex = AABBConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/aabb/ProjAABBConvex.ts b/Phaser/physics/aabb/ProjAABBConvex.ts new file mode 100644 index 000000000..9d3a86dfd --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBConvex.ts @@ -0,0 +1,50 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class AABBConvex { + + public static Collide(x: number, y: number, obj: Phaser.Physics.AABB, t: Phaser.Physics.TileMapCell) { + + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the circle center to + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw));//the AABB + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) + { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS;//we need to report + } + else if (0 < pen) + { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + + return Phaser.Physics.AABB.COL_NONE; + } + + } +} \ No newline at end of file diff --git a/Phaser/physics/aabb/ProjAABBFull.js b/Phaser/physics/aabb/ProjAABBFull.js new file mode 100644 index 000000000..22006e459 --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBFull.js @@ -0,0 +1,26 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBFull = (function () { + function AABBFull() { + } + AABBFull.Collide = function (x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.AABB.COL_AXIS; + }; + return AABBFull; + })(); + Projection.AABBFull = AABBFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/aabb/ProjAABBFull.ts b/Phaser/physics/aabb/ProjAABBFull.ts new file mode 100644 index 000000000..7a237434f --- /dev/null +++ b/Phaser/physics/aabb/ProjAABBFull.ts @@ -0,0 +1,23 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class AABBFull { + + public static Collide(x: number, y: number, obj: Phaser.Physics.AABB, t: Phaser.Physics.TileMapCell) { + + var l = Math.sqrt(x * x + y * y); + + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.AABB.COL_AXIS; + + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/arcade/Motion.ts b/Phaser/physics/arcade/Motion.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/Phaser/physics/circle/ProjCircle45Deg.js b/Phaser/physics/circle/ProjCircle45Deg.js new file mode 100644 index 000000000..39ab26d48 --- /dev/null +++ b/Phaser/physics/circle/ProjCircle45Deg.js @@ -0,0 +1,218 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var Circle45Deg = (function () { + function Circle45Deg() { + } + Circle45Deg.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x; + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp; + sy *= -dp; + + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y + (oV * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return Circle45Deg; + })(); + Projection.Circle45Deg = Circle45Deg; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/circle/ProjCircle45Deg.ts b/Phaser/physics/circle/ProjCircle45Deg.ts new file mode 100644 index 000000000..0f03a13df --- /dev/null +++ b/Phaser/physics/circle/ProjCircle45Deg.ts @@ -0,0 +1,271 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class Circle45Deg { + + public static Collide(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y;//point on the circle, relative to the tile center + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) + { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp;//(sx,sy) is now the penetration vector + sy *= -dp; + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (oV * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Phaser.Physics.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Circle.COL_NONE; + } + + + } + +} diff --git a/Phaser/physics/circle/ProjCircleConcave.js b/Phaser/physics/circle/ProjCircleConcave.js new file mode 100644 index 000000000..9c52e2a29 --- /dev/null +++ b/Phaser/physics/circle/ProjCircleConcave.js @@ -0,0 +1,181 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConcave = (function () { + function CircleConcave() { + } + CircleConcave.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x; + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y; + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + return Phaser.Physics.Circle.COL_NONE; + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the vertical tip + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out vertically + dx = 0; + dy = oV; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the horizontal tip + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out horizontally + dx = oH; + dy = 0; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConcave; + })(); + Projection.CircleConcave = CircleConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/circle/ProjCircleConcave.ts b/Phaser/physics/circle/ProjCircleConcave.ts new file mode 100644 index 000000000..76a1c1335 --- /dev/null +++ b/Phaser/physics/circle/ProjCircleConcave.ts @@ -0,0 +1,235 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class CircleConcave { + + public static Collide(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x;//(ox,oy) is the vector from the circle to + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y;//tile-circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + else + { + return Phaser.Physics.Circle.COL_NONE; + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the vertical tip + + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out vertically + dx = 0; + dy = oV; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the horizontal tip + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out horizontally + dx = oH; + dy = 0; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Circle.COL_NONE; + + } + + } + +} \ No newline at end of file diff --git a/Phaser/physics/circle/ProjCircleConvex.js b/Phaser/physics/circle/ProjCircleConvex.js new file mode 100644 index 000000000..bd62e0543 --- /dev/null +++ b/Phaser/physics/circle/ProjCircleConvex.js @@ -0,0 +1,193 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConvex = (function () { + function CircleConvex() { + } + CircleConvex.Collide = function (x, y, oH, oV, obj, t) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConvex; + })(); + Projection.CircleConvex = CircleConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/circle/ProjCircleConvex.ts b/Phaser/physics/circle/ProjCircleConvex.ts new file mode 100644 index 000000000..605f88c36 --- /dev/null +++ b/Phaser/physics/circle/ProjCircleConvex.ts @@ -0,0 +1,242 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class CircleConvex { + + public static Collide(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) { + + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + + } + } + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Circle.COL_NONE; + + } + } + +} \ No newline at end of file diff --git a/Phaser/physics/circle/ProjCircleFull.js b/Phaser/physics/circle/ProjCircleFull.js new file mode 100644 index 000000000..e1b1f88eb --- /dev/null +++ b/Phaser/physics/circle/ProjCircleFull.js @@ -0,0 +1,84 @@ +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleFull = (function () { + function CircleFull() { + } + CircleFull.Collide = function (x, y, oH, oV, obj, t) { + if (oH == 0) { + if (oV == 0) { + if (x < y) { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x; + + if (dx < 0) { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } else { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y; + + if (dy < 0) { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } + } else { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + } else if (oV == 0) { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + //diagonal collision + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleFull; + })(); + Projection.CircleFull = CircleFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); diff --git a/Phaser/physics/circle/ProjCircleFull.ts b/Phaser/physics/circle/ProjCircleFull.ts new file mode 100644 index 000000000..ad6b822ac --- /dev/null +++ b/Phaser/physics/circle/ProjCircleFull.ts @@ -0,0 +1,112 @@ +/// + +/** +* Phaser - Physics - Projection +*/ + +module Phaser.Physics.Projection { + + export class CircleFull { + + public static Collide(x, y, oH, oV, obj: Phaser.Physics.Circle, t: Phaser.Physics.TileMapCell) { + + //if we're colliding vs. the current cell, we need to project along the + //smallest penetration vector. + //if we're colliding vs. horiz. or vert. neighb, we simply project horiz/vert + //if we're colliding diagonally, we need to collide vs. tile corner + + if (oH == 0) + { + if (oV == 0) + { + //collision with current cell + if (x < y) + { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x;//get sign for projection along x-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dx < 0) + { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } + else + { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y;//get sign for projection along y-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dy < 0) + { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } + } + else + { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + } + else if (oV == 0) + { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + else + { + //diagonal collision + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Circle.COL_NONE; + + } + + } + +} \ No newline at end of file diff --git a/Phaser/utils/ColorUtils.js b/Phaser/utils/ColorUtils.js index 15c0d9d12..a43863f0c 100644 --- a/Phaser/utils/ColorUtils.js +++ b/Phaser/utils/ColorUtils.js @@ -54,6 +54,19 @@ var Phaser; return colors; }; + ColorUtils.hexToRGB = function (h) { + var hex16 = (h.charAt(0) == "#") ? h.substring(1, 7) : h; + var r = parseInt(hex16.substring(0, 2), 16); + var g = parseInt(hex16.substring(2, 4), 16); + var b = parseInt(hex16.substring(4, 6), 16); + + return { + r: r, + g: g, + b: b + }; + }; + ColorUtils.getComplementHarmony = /** * Returns a Complementary Color Harmony for the given color. *

A complementary hue is one directly opposite the color given on the color wheel

diff --git a/Phaser/utils/ColorUtils.ts b/Phaser/utils/ColorUtils.ts index 92a8a8d0d..cb6c7d67f 100644 --- a/Phaser/utils/ColorUtils.ts +++ b/Phaser/utils/ColorUtils.ts @@ -59,6 +59,21 @@ module Phaser { } + public static hexToRGB(h: string) { + + var hex16 = (h.charAt(0) == "#") ? h.substring(1, 7) : h; + var r = parseInt(hex16.substring(0, 2), 16); + var g = parseInt(hex16.substring(2, 4), 16); + var b = parseInt(hex16.substring(4, 6), 16); + + return { + r: r, + g: g, + b: b + } + + } + /** * Returns a Complementary Color Harmony for the given color. *

A complementary hue is one directly opposite the color given on the color wheel

diff --git a/Phaser/utils/DebugUtils.js b/Phaser/utils/DebugUtils.js index 670de2541..4ea96805d 100644 --- a/Phaser/utils/DebugUtils.js +++ b/Phaser/utils/DebugUtils.js @@ -183,6 +183,11 @@ var Phaser; Phaser.DebugUtils.line('bottom: ' + sprite.worldView.bottom + ' right: ' + sprite.worldView.right.toFixed(1)); }; + DebugUtils.renderSpriteWorldViewBounds = function (sprite, color) { + if (typeof color === "undefined") { color = 'rgba(0,255,0,0.3)'; } + Phaser.DebugUtils.renderRectangle(sprite.worldView, color); + }; + DebugUtils.renderSpriteInfo = /** * Render debug infos. (including name, bounds info, position and some other properties) * @param x {number} X position of the debug info to be rendered. diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 13bc37d0d..5d8941e0c 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -231,7 +231,6 @@ world drag.ts - @@ -252,21 +251,23 @@ - - sprite test 1.ts - + + + + circle 1.ts + + + sprite bounds.ts + + ballscroller.ts - - - blasteroids.ts - parallax.ts @@ -359,10 +360,6 @@ - - - map draw.ts - tiled layers.ts @@ -446,8 +443,8 @@
+ -
\ No newline at end of file diff --git a/Tests/buttons/basic button.js b/Tests/buttons/basic button.js index 19733c7a4..9964da5ac 100644 --- a/Tests/buttons/basic button.js +++ b/Tests/buttons/basic button.js @@ -31,10 +31,8 @@ function clickedIt() { if (this.image.visible == true) { - game.stage.backgroundColor = ''; this.image.visible = false; } else { - game.stage.backgroundColor = 'rgb(0,0,0)'; this.image.visible = true; } } diff --git a/Tests/phaser-debug.js b/Tests/phaser-debug.js index 1b6c7f4c2..3528a49cf 100644 --- a/Tests/phaser-debug.js +++ b/Tests/phaser-debug.js @@ -4459,6 +4459,7 @@ var Phaser; this.modified = false; this.game = game; this.type = Phaser.Types.GROUP; + this.active = true; this.exists = true; this.visible = true; @@ -4519,7 +4520,9 @@ var Phaser; this._member = this.members[this._i++]; if (this._member != null && this._member.exists && this._member.active) { - this._member.preUpdate(); + if (this._member.type != Phaser.Types.GROUP) { + this._member.preUpdate(); + } this._member.update(); } } @@ -14660,200 +14663,6 @@ var Phaser; Phaser.TilemapLayer = TilemapLayer; })(Phaser || (Phaser = {})); var Phaser; -(function (Phaser) { - /// - /** - * Phaser - ArcadePhysics - Body - */ - (function (Physics) { - var Body = (function () { - function Body(sprite, type) { - this.angularVelocity = 0; - this.angularAcceleration = 0; - this.angularDrag = 0; - this.maxAngular = 10000; - this.mass = 1; - this._width = 0; - this._height = 0; - this.sprite = sprite; - this.game = sprite.game; - this.type = type; - - // Fixture properties - // Will extend into its own class at a later date - can move the fixture defs there and add shape support, but this will do for 1.0 release - this.bounds = new Phaser.Rectangle(); - - this._width = sprite.width; - this._height = sprite.height; - - // Body properties - //this.gravity = Vec2Utils.clone(ArcadePhysics.gravity); - //this.bounce = Vec2Utils.clone(ArcadePhysics.bounce); - this.velocity = new Phaser.Vec2(); - this.acceleration = new Phaser.Vec2(); - - //this.drag = Vec2Utils.clone(ArcadePhysics.drag); - this.maxVelocity = new Phaser.Vec2(10000, 10000); - - this.angularVelocity = 0; - this.angularAcceleration = 0; - this.angularDrag = 0; - - this.touching = Phaser.Types.NONE; - this.wasTouching = Phaser.Types.NONE; - this.allowCollisions = Phaser.Types.ANY; - - this.position = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); - this.oldPosition = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); - this.offset = new Phaser.Vec2(); - } - Object.defineProperty(Body.prototype, "x", { - get: function () { - return this.sprite.x + this.offset.x; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "y", { - get: function () { - return this.sprite.y + this.offset.y; - }, - enumerable: true, - configurable: true - }); - - - - Object.defineProperty(Body.prototype, "width", { - get: function () { - return this._width * this.sprite.transform.scale.x; - }, - set: function (value) { - this._width = value; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "height", { - get: function () { - return this._height * this.sprite.transform.scale.y; - }, - set: function (value) { - this._height = value; - }, - enumerable: true, - configurable: true - }); - - Body.prototype.preUpdate = function () { - this.oldPosition.copyFrom(this.position); - - this.bounds.x = this.x; - this.bounds.y = this.y; - this.bounds.width = this.width; - this.bounds.height = this.height; - }; - - // Shall we do this? Or just update the values directly in the separate functions? But then the bounds will be out of sync - as long as - // the bounds are updated and used in calculations then we can do one final sprite movement here I guess? - Body.prototype.postUpdate = function () { - if (this.type !== Phaser.Types.BODY_DISABLED) { - //this.game.world.physics.updateMotion(this); - this.wasTouching = this.touching; - this.touching = Phaser.Types.NONE; - } - - this.position.setTo(this.x, this.y); - }; - - Object.defineProperty(Body.prototype, "hullWidth", { - get: function () { - if (this.deltaX > 0) { - return this.bounds.width + this.deltaX; - } else { - return this.bounds.width - this.deltaX; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullHeight", { - get: function () { - if (this.deltaY > 0) { - return this.bounds.height + this.deltaY; - } else { - return this.bounds.height - this.deltaY; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullX", { - get: function () { - if (this.position.x < this.oldPosition.x) { - return this.position.x; - } else { - return this.oldPosition.x; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullY", { - get: function () { - if (this.position.y < this.oldPosition.y) { - return this.position.y; - } else { - return this.oldPosition.y; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaXAbs", { - get: function () { - return (this.deltaX > 0 ? this.deltaX : -this.deltaX); - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaYAbs", { - get: function () { - return (this.deltaY > 0 ? this.deltaY : -this.deltaY); - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaX", { - get: function () { - return this.position.x - this.oldPosition.x; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaY", { - get: function () { - return this.position.y - this.oldPosition.y; - }, - enumerable: true, - configurable: true - }); - return Body; - })(); - Physics.Body = Body; - })(Phaser.Physics || (Phaser.Physics = {})); - var Physics = Phaser.Physics; -})(Phaser || (Phaser = {})); -var Phaser; (function (Phaser) { /// /** @@ -15085,10 +14894,6 @@ var Phaser; * Pre-update is called right before update() on each object in the game loop. */ Sprite.prototype.preUpdate = function () { - if (this.name == 'piece1') { - console.log('wv', this.worldView); - } - this.transform.update(); if (this.transform.scrollFactor.x != 1 && this.transform.scrollFactor.x != 0) { @@ -15334,8 +15139,7 @@ var Phaser; TransformManager.prototype.centerOn = function (x, y) { this.parent.x = x + (this.parent.x - this.center.x); this.parent.y = y + (this.parent.y - this.center.y); - - this.setCache(); + //this.setCache(); }; /** @@ -15354,10 +15158,8 @@ var Phaser; this._size.y = this.parent.height; this._origin.x = this.origin.x; this._origin.y = this.origin.y; - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); + this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._prevRotation = this.rotation; if (this.parent.texture && this.parent.texture.renderRotation) { @@ -15378,6 +15180,9 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + this._flippedX = this.parent.texture.flippedX; + this._flippedY = this.parent.texture.flippedY; }; /** @@ -15403,9 +15208,7 @@ var Phaser; this._dirty = true; } - if (this.rotation != this._prevRotation) { - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + if (this.rotation != this._prevRotation || this._dirty) { this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); @@ -15433,27 +15236,35 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + // Translate + this.local.data[2] = this.parent.x; + this.local.data[5] = this.parent.y; } - if (this.parent.texture.flippedX) { - this.local.data[0] = this._sc.y * -this.scale.x; - this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; - } else { - this.local.data[0] = this._sc.y * this.scale.x; - this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + if (this._dirty || this._flippedX != this.parent.texture.flippedX) { + this._flippedX = this.parent.texture.flippedX; + + if (this._flippedX) { + this.local.data[0] = this._sc.y * -this.scale.x; + this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; + } else { + this.local.data[0] = this._sc.y * this.scale.x; + this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + } } - if (this.parent.texture.flippedY) { - this.local.data[4] = this._sc.y * -this.scale.y; - this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; - } else { - this.local.data[4] = this._sc.y * this.scale.y; - this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; - } + if (this._dirty || this._flippedY != this.parent.texture.flippedY) { + this._flippedY = this.parent.texture.flippedY; - // Translate - this.local.data[2] = this.parent.x; - this.local.data[5] = this.parent.y; + if (this._flippedY) { + this.local.data[4] = this._sc.y * -this.scale.y; + this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; + } else { + this.local.data[4] = this._sc.y * this.scale.y; + this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; + } + } }; return TransformManager; })(); @@ -15835,6 +15646,19 @@ var Phaser; return this.game.sound.add(key, volume, loop); }; + GameObjectFactory.prototype.circle = function (x, y, radius) { + return new Phaser.Physics.Circle(this.game, x, y, radius); + }; + + GameObjectFactory.prototype.aabb = function (x, y, width, height) { + return new Phaser.Physics.AABB(this.game, x, y, Math.floor(width / 2), Math.floor(height / 2)); + }; + + GameObjectFactory.prototype.cell = function (x, y, width, height, state) { + if (typeof state === "undefined") { state = Phaser.Physics.TileMapCell.TID_FULL; } + return new Phaser.Physics.TileMapCell(this.game, x, y, width, height).SetState(state); + }; + /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -15876,10 +15700,9 @@ var Phaser; * * @return {Particle} The newly created particle object. */ - GameObjectFactory.prototype.particle = function () { - return new Phaser.ArcadeParticle(this.game); - }; - + //public particle(): Phaser.ArcadeParticle { + // return new Phaser.ArcadeParticle(this.game); + //} /** * Create a new Emitter. * @@ -15888,13 +15711,9 @@ var Phaser; * @param size {number} Optional, size of this emitter. * @return {Emitter} The newly created emitter object. */ - GameObjectFactory.prototype.emitter = function (x, y, size) { - if (typeof x === "undefined") { x = 0; } - if (typeof y === "undefined") { y = 0; } - if (typeof size === "undefined") { size = 0; } - return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); - }; - + //public emitter(x: number = 0, y: number = 0, size: number = 0): Phaser.ArcadeEmitter { + // return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); + //} /** * Create a new ScrollZone object with image key, position and size. * @@ -15993,10 +15812,9 @@ var Phaser; * @param emitter The Emitter to add to the Game World * @return {Phaser.Emitter} The Emitter object */ - GameObjectFactory.prototype.existingEmitter = function (emitter) { - return this._world.group.add(emitter); - }; - + //public existingEmitter(emitter: Phaser.ArcadeEmitter): Phaser.ArcadeEmitter { + // return this._world.group.add(emitter); + //} /** * Add an existing ScrollZone to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. @@ -16033,372 +15851,467 @@ var Phaser; })(); Phaser.GameObjectFactory = GameObjectFactory; })(Phaser || (Phaser = {})); -/// -/** -* Phaser - ArcadeEmitter -* -* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for -* continuous effects like rain and fire. All it really does is launch Particle objects out -* at set intervals, and fixes their positions and velocities accorindgly. -*/ var Phaser; (function (Phaser) { - var ArcadeEmitter = (function (_super) { - __extends(ArcadeEmitter, _super); - /** - * Creates a new Emitter object at a specific position. - * Does NOT automatically generate or attach particles! - * - * @param x {number} The X position of the emitter. - * @param y {number} The Y position of the emitter. - * @param [size] {number} Specifies a maximum capacity for this emitter. - */ - function ArcadeEmitter(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 Phaser.Vec2(-100, -100); - this.maxParticleSpeed = new Phaser.Vec2(100, 100); - this.minRotation = -360; - this.maxRotation = 360; - this.gravity = 0; - this.particleClass = null; - this.particleDrag = new Phaser.Vec2(); - this.frequency = 0.1; - this.lifespan = 3; - this.bounce = 0; - this._quantity = 0; - this._counter = 0; - this._explode = true; - this.on = false; - - this.exists = true; - this.active = true; - this.visible = true; - } - /** - * Clean up memory. - */ - ArcadeEmitter.prototype.destroy = function () { - this.minParticleSpeed = null; - this.maxParticleSpeed = null; - this.particleDrag = null; - this.particleClass = null; - this._point = null; - _super.prototype.destroy.call(this); - }; - - /** - * 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 Sprite objects, you can simply pass in a particle image or sprite sheet. - * @param quantity {number} The number of particles to generate when using the "create from image" option. - * @param multiple {boolean} 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 {number} 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 Emitter instance (nice for chaining stuff together, if you're into that). - */ - ArcadeEmitter.prototype.makeParticles = function (graphics, quantity, multiple, collide) { - if (typeof quantity === "undefined") { quantity = 50; } - if (typeof multiple === "undefined") { multiple = false; } - if (typeof collide === "undefined") { collide = 0; } - this.maxSize = quantity; - - var totalFrames = 1; - - /* - if(Multiple) - { - var sprite:Sprite = new Sprite(this.game); - sprite.loadGraphic(Graphics,true); - totalFrames = sprite.frames; - sprite.destroy(); - } + /// + (function (Particles) { + var Emitter = (function () { + /** + * You can use this emit particles. + * + * It will dispatch follow events: + * Proton.PARTICLE_CREATED + * Proton.PARTICLE_UPDATA + * Proton.PARTICLE_DEAD + * + * @class Proton.Emitter + * @constructor + * @param {Object} pObj the parameters object; + * for example {damping:0.01,bindEmitter:false} */ - var randomFrame; - var particle; - var i = 0; + function Emitter(pObj) { + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; - while (i < quantity) { - if (this.particleClass == null) { - particle = new Phaser.ArcadeParticle(this.game); - } else { - particle = new this.particleClass(this.game); - } + /** + * The friction coefficient for all particle emit by This; + * @property damping + * @type {Number} + * @default 0.006 + */ + this.damping = .006; - if (multiple) { - /* - randomFrame = this.game.math.random()*totalFrames; - */ - } else { - if (graphics) { - particle.texture.loadImage(graphics); - } - } + /** + * If bindEmitter the particles can bind this emitter's property; + * @property bindEmitter + * @type {Boolean} + * @default true + */ + this.bindEmitter = true; - if (collide > 0) { - //particle.body.allowCollisions = Types.ANY; - particle.body.type = Phaser.Types.BODY_DYNAMIC; - particle.width *= collide; - particle.height *= collide; - } else { - //particle.body.allowCollisions = Types.NONE; - } + /** + * The number of particles per second emit (a [particle]/b [s]); + * @property rate + * @type {Proton.Rate} + * @default Proton.Rate(1, .1) + */ + this.rate = new Phaser.Particles.Initializers.Rate(1, .1); - particle.exists = false; - - // Center the origin for rotation assistance - //particle.transform.origin.setTo(particle.body.bounds.halfWidth, particle.body.bounds.halfHeight); - this.add(particle); - - i++; + //Emitter._super_.call(this, pObj); + /** + * The emitter's id; + * @property id + * @type {String} id + */ + this.id = 'emitter_' + Emitter.ID++; } + /** + * start emit particle + * @method emit + * @param {Number} emitTime begin emit time; + * @param {String} life the life of this emitter + */ + Emitter.prototype.emit = function (emitTime, life) { + this.emitTime = 0; + this.emitTotalTimes = Particles.ParticleUtils.initValue(emitTime, Infinity); - return this; - }; + if (life == true || life == 'life' || life == 'destroy') { + if (emitTime == 'once') + this.life = 1; +else + this.life = this.emitTotalTimes; + } else if (!isNaN(life)) { + this.life = life; + } - ArcadeEmitter.prototype.preUpdate = function () { - }; - ArcadeEmitter.prototype.postUpdate = function () { - }; + this.rate.init(); + }; - /** - * Called automatically by the game loop, decides when to launch particles and when to "die". - */ - ArcadeEmitter.prototype.update = function () { - if (this.on) { - if (this._explode) { - this.on = false; + /** + * stop emiting + * @method stopEmit + */ + Emitter.prototype.stopEmit = function () { + this.emitTotalTimes = -1; + this.emitTime = 0; + }; - var i = 0; - var l = this._quantity; + /** + * remove current all particles + * @method removeAllParticles + */ + Emitter.prototype.removeAllParticles = function () { + for (var i = 0; i < this.particles.length; i++) + this.particles[i].dead = true; + }; - if ((l <= 0) || (l > this.length)) { - l = this.length; - } + /** + * create single particle; + * + * can use emit({x:10},new Gravity(10),{'particleUpdate',fun}) or emit([{x:10},new Initialize],new Gravity(10),{'particleUpdate',fun}) + * @method removeAllParticles + */ + Emitter.prototype.createParticle = function (initialize, behaviour) { + if (typeof initialize === "undefined") { initialize = null; } + if (typeof behaviour === "undefined") { behaviour = null; } + var particle = Particles.ParticleManager.pool.get(); + this.setupParticle(particle, initialize, behaviour); - while (i < l) { - this.emitParticle(); - i++; - } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_CREATED, + // particle: particle + //})); + return particle; + }; - this._quantity = 0; + /** + * add initialize to this emitter + * @method addSelfInitialize + */ + Emitter.prototype.addSelfInitialize = function (pObj) { + if (pObj['init']) { + pObj.init(this); } else { - this._timer += this.game.time.elapsed; + //this.initAll(); + } + }; - while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) { - this._timer -= this.frequency; - this.emitParticle(); + /** + * add the Initialize to particles; + * + * you can use initializes array:for example emitter.addInitialize(initialize1,initialize2,initialize3); + * @method addInitialize + * @param {Proton.Initialize} initialize like this new Proton.Radius(1, 12) + */ + Emitter.prototype.addInitialize = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.initializes.push(arguments[i]); + } + }; - if ((this._quantity > 0) && (++this._counter >= this._quantity)) { - this.on = false; - this._quantity = 0; + /** + * remove the Initialize + * @method removeInitialize + * @param {Proton.Initialize} initialize a initialize + */ + Emitter.prototype.removeInitialize = function (initializer) { + var index = this.initializes.indexOf(initializer); + if (index > -1) { + this.initializes.splice(index, 1); + } + }; + + /** + * remove all Initializes + * @method removeInitializers + */ + Emitter.prototype.removeInitializers = function () { + Particles.ParticleUtils.destroyArray(this.initializes); + }; + + /** + * add the Behaviour to particles; + * + * you can use Behaviours array:emitter.addBehaviour(Behaviour1,Behaviour2,Behaviour3); + * @method addBehaviour + * @param {Proton.Behaviour} behaviour like this new Proton.Color('random') + */ + Emitter.prototype.addBehaviour = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.behaviours.push(arguments[i]); + if (arguments[i].hasOwnProperty("parents")) + arguments[i].parents.push(this); + } + }; + + /** + * remove the Behaviour + * @method removeBehaviour + * @param {Proton.Behaviour} behaviour a behaviour + */ + Emitter.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) + this.behaviours.splice(index, 1); + }; + + /** + * remove all behaviours + * @method removeAllBehaviours + */ + Emitter.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + Emitter.prototype.integrate = function (time) { + var damping = 1 - this.damping; + Particles.ParticleManager.integrator.integrate(this, time, damping); + var length = this.particles.length, i; + for (i = 0; i < length; i++) { + var particle = this.particles[i]; + particle.update(time, i); + Particles.ParticleManager.integrator.integrate(particle, time, damping); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_UPDATE, + // particle: particle + //})); + } + }; + + Emitter.prototype.emitting = function (time) { + if (this.emitTotalTimes == 1) { + var length = this.rate.getValue(99999), i; + for (i = 0; i < length; i++) { + this.createParticle(); + } + + this.emitTotalTimes = 0; + } else if (!isNaN(this.emitTotalTimes)) { + this.emitTime += time; + if (this.emitTime < this.emitTotalTimes) { + var length = this.rate.getValue(time), i; + for (i = 0; i < length; i++) { + this.createParticle(); } } } - } + }; - _super.prototype.update.call(this); - }; + Emitter.prototype.update = function (time) { + this.age += time; + if (this.age >= this.life || this.dead) { + this.destroy(); + } - /** - * Call this function to turn off all the particles and the emitter. - */ - ArcadeEmitter.prototype.kill = function () { - this.on = false; - this.alive = false; - this.exists = false; - }; + this.emitting(time); + this.integrate(time); + var particle; + var length = this.particles.length, k; + for (k = length - 1; k >= 0; k--) { + particle = this.particles[k]; + if (particle.dead) { + Particles.ParticleManager.pool.set(particle); + this.particles.splice(k, 1); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_DEAD, + // particle: particle + //})); + } + } + }; - /** - * Handy for bringing game objects "back to life". Just sets alive and exists back to true. - * In practice, this is most often called by Object.reset(). - */ - ArcadeEmitter.prototype.revive = function () { - this.alive = true; - this.exists = true; - }; + Emitter.prototype.setupParticle = function (particle, initialize, behaviour) { + var initializes = this.initializes; + var behaviours = this.behaviours; - /** - * Call this function to start emitting particles. - * - * @param explode {boolean} Whether the particles should all burst out at once. - * @param lifespan {number} How long each particle lives once emitted. 0 = forever. - * @param frequency {number} 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 {number} How many particles to launch. 0 = "all of the particles". - */ - ArcadeEmitter.prototype.start = 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(); + if (initialize) { + if (initialize instanceof Array) + initializes = initialize; +else + initializes = [initialize]; + } - this.visible = true; - this.on = true; + if (behaviour) { + if (behaviour instanceof Array) + behaviours = behaviour; +else + behaviours = [behaviour]; + } - this._explode = explode; - this.lifespan = lifespan; - this.frequency = frequency; - this._quantity += quantity; + //Proton.InitializeUtil.initialize(this, particle, initializes); + particle.addBehaviours(behaviours); + particle.parent = this; + this.particles.push(particle); + }; - this._counter = 0; - this._timer = 0; - }; + /** + * Destory this Emitter + * @method destory + */ + Emitter.prototype.destroy = function () { + this.dead = true; + this.emitTotalTimes = -1; + if (this.particles.length == 0) { + this.removeInitializers(); + this.removeAllBehaviours(); - /** - * This function can be used both internally and externally to emit the next particle. - */ - ArcadeEmitter.prototype.emitParticle = function () { - var particle = this.recycle(Phaser.ArcadeParticle); - - particle.lifespan = this.lifespan; - - //particle.body.bounce.setTo(this.bounce, this.bounce); - Phaser.SpriteUtils.reset(particle, this.x - (particle.width >> 1) + this.game.rnd.integer * this.width, this.y - (particle.height >> 1) + this.game.rnd.integer * this.height); - particle.visible = true; - - if (this.minParticleSpeed.x != this.maxParticleSpeed.x) { - particle.body.velocity.x = this.minParticleSpeed.x + this.game.rnd.integer * (this.maxParticleSpeed.x - this.minParticleSpeed.x); - } else { - particle.body.velocity.x = this.minParticleSpeed.x; - } - - if (this.minParticleSpeed.y != this.maxParticleSpeed.y) { - particle.body.velocity.y = this.minParticleSpeed.y + this.game.rnd.integer * (this.maxParticleSpeed.y - this.minParticleSpeed.y); - } else { - particle.body.velocity.y = this.minParticleSpeed.y; - } - - if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) { - particle.body.angularVelocity = this.minRotation + this.game.rnd.integer * (this.maxRotation - this.minRotation); - } else { - particle.body.angularVelocity = this.minRotation; - } - - if (particle.body.angularVelocity != 0) { - particle.rotation = this.game.rnd.integer * 360 - 180; - } - - //particle.body.drag.x = this.particleDrag.x; - //particle.body.drag.y = this.particleDrag.y; - particle.onEmit(); - }; - - /** - * A more compact way of setting the width and height of the emitter. - * - * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). - * @param height {number} The desired height of the emitter. - */ - ArcadeEmitter.prototype.setSize = function (width, height) { - this.width = width; - this.height = height; - }; - - /** - * A more compact way of setting the X velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setXSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.x = min; - this.maxParticleSpeed.x = max; - }; - - /** - * A more compact way of setting the Y velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setYSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.y = min; - this.maxParticleSpeed.y = max; - }; - - /** - * A more compact way of setting the angular velocity constraints of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setRotation = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minRotation = min; - this.maxRotation = max; - }; - - /** - * Change the emitter's midpoint to match the midpoint of a Object. - * - * @param Object {object} The Object that you want to sync up with. - */ - ArcadeEmitter.prototype.at = function (object) { - //this.x = object.body.bounds.halfWidth - (this.width >> 1); - //this.y = object.body.bounds.halfHeight - (this.height >> 1); - }; - return ArcadeEmitter; - })(Phaser.Group); - Phaser.ArcadeEmitter = ArcadeEmitter; + if (this.parent) + this.parent.removeEmitter(this); + } + }; + Emitter.ID = 0; + return Emitter; + })(); + Particles.Emitter = Emitter; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); -/// -/** -* Phaser - ArcadeParticle -* -* This is a simple particle class that extends a Sprite to have a slightly more -* specialised behaviour. It is used exclusively by the Emitter class and can be extended as required. -*/ var Phaser; (function (Phaser) { - var ArcadeParticle = (function (_super) { - __extends(ArcadeParticle, _super); - /** - * Instantiate a new particle. Like Sprite, all meaningful creation - * happens during loadGraphic() or makeGraphic() or whatever. - */ - function ArcadeParticle(game) { - _super.call(this, game); - - this.body.type = Phaser.Types.BODY_DYNAMIC; - this.lifespan = 0; - } - /** - * The particle's main update logic. Basically it checks to see if it should be dead yet. - */ - ArcadeParticle.prototype.update = function () { - if (this.lifespan <= 0) { - return; + /// + (function (Particles) { + var Particle = (function () { + /** + * the Particle class + * + * @class Proton.Particle + * @constructor + * @param {Object} pObj the parameters object; + * for example {life:3,dead:false} + */ + function Particle() { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + /** + * The particle's id; + * @property id + * @type {String} id + */ + this.id = 'particle_' + Particle.ID++; + this.reset(true); } + Particle.prototype.getDirection = function () { + return Math.atan2(this.v.x, -this.v.y) * (180 / Math.PI); + }; - this.lifespan -= this.game.time.elapsed; + Particle.prototype.reset = function (init) { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + if (init) { + this.transform = {}; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + } else { + Particles.ParticleUtils.destroyObject(this.transform); + this.p.setTo(0, 0); + this.v.setTo(0, 0); + this.a.setTo(0, 0); + this.old.p.setTo(0, 0); + this.old.v.setTo(0, 0); + this.old.a.setTo(0, 0); + this.removeAllBehaviours(); + } - if (this.lifespan <= 0) { - this.kill(); - } - }; + this.transform.rgb = { + r: 255, + g: 255, + b: 255 + }; + return this; + }; - /** - * Triggered whenever this object is launched by a Emitter. - * You can override this to add custom behavior like a sound or AI or something. - */ - ArcadeParticle.prototype.onEmit = function () { - }; - return ArcadeParticle; - })(Phaser.Sprite); - Phaser.ArcadeParticle = ArcadeParticle; + Particle.prototype.update = function (time, index) { + if (!this.sleep) { + this.age += time; + var length = this.behaviours.length, i; + for (i = 0; i < length; i++) { + if (this.behaviours[i]) + this.behaviours[i].applyBehaviour(this, time, index); + } + } + + if (this.age >= this.life) { + this.destroy(); + } else { + var scale = this.easing(this.age / this.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + Particle.prototype.addBehaviour = function (behaviour) { + this.behaviours.push(behaviour); + if (behaviour.hasOwnProperty('parents')) + behaviour.parents.push(this); + behaviour.initialize(this); + }; + + Particle.prototype.addBehaviours = function (behaviours) { + var length = behaviours.length, i; + for (i = 0; i < length; i++) { + this.addBehaviour(behaviours[i]); + } + }; + + Particle.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) { + var behaviour = this.behaviours.splice(index, 1); + behaviour.parents = null; + } + }; + + Particle.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + /** + * Destory this particle + * @method destory + */ + Particle.prototype.destroy = function () { + this.removeAllBehaviours(); + this.energy = 0; + this.dead = true; + this.parent = null; + }; + Particle.ID = 0; + return Particle; + })(); + Particles.Particle = Particle; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); var Phaser; (function (Phaser) { @@ -16798,6 +16711,19 @@ var Phaser; return colors; }; + ColorUtils.hexToRGB = function (h) { + var hex16 = (h.charAt(0) == "#") ? h.substring(1, 7) : h; + var r = parseInt(hex16.substring(0, 2), 16); + var g = parseInt(hex16.substring(2, 4), 16); + var b = parseInt(hex16.substring(4, 6), 16); + + return { + r: r, + g: g, + b: b + }; + }; + ColorUtils.getComplementHarmony = /** * Returns a Complementary Color Harmony for the given color. *

A complementary hue is one directly opposite the color given on the color wheel

@@ -19002,6 +18928,2787 @@ var Phaser; })(Phaser.Renderer || (Phaser.Renderer = {})); var Renderer = Phaser.Renderer; })(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - PhysicsManager + */ + (function (Physics) { + var PhysicsManager = (function () { + function PhysicsManager(game) { + this.grav = 0.2; + this.drag = 1; + this.bounce = 0.3; + this.friction = 0.05; + this.min_f = 0; + this.max_f = 1; + this.min_b = 0; + this.max_b = 1; + this.min_g = 0; + this.max_g = 1; + this.xmin = 0; + this.xmax = 800; + this.ymin = 0; + this.ymax = 600; + this.objrad = 24; + this.tilerad = 24 * 2; + this.objspeed = 0.2; + this.maxspeed = 20; + this.game = game; + } + PhysicsManager.prototype.update = function () { + // Booyah! + }; + return PhysicsManager; + })(); + Physics.PhysicsManager = PhysicsManager; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - Body + * A binding between a Sprite and a physics object (AABB or Circle) + */ + (function (Physics) { + var Body = (function () { + function Body(sprite, type) { + this.angularVelocity = 0; + this.angularAcceleration = 0; + this.angularDrag = 0; + this.maxAngular = 10000; + this.mass = 1; + this._width = 0; + this._height = 0; + this.sprite = sprite; + this.game = sprite.game; + this.type = type; + + // Fixture properties + // Will extend into its own class at a later date - can move the fixture defs there and add shape support, but this will do for 1.0 release + this.bounds = new Phaser.Rectangle(); + + this._width = sprite.width; + this._height = sprite.height; + + // Body properties + //this.gravity = Vec2Utils.clone(ArcadePhysics.gravity); + //this.bounce = Vec2Utils.clone(ArcadePhysics.bounce); + this.velocity = new Phaser.Vec2(); + this.acceleration = new Phaser.Vec2(); + + //this.drag = Vec2Utils.clone(ArcadePhysics.drag); + this.maxVelocity = new Phaser.Vec2(10000, 10000); + + this.angularVelocity = 0; + this.angularAcceleration = 0; + this.angularDrag = 0; + + this.touching = Phaser.Types.NONE; + this.wasTouching = Phaser.Types.NONE; + this.allowCollisions = Phaser.Types.ANY; + + this.position = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); + this.oldPosition = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); + this.offset = new Phaser.Vec2(); + } + Object.defineProperty(Body.prototype, "x", { + get: function () { + return this.sprite.x + this.offset.x; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "y", { + get: function () { + return this.sprite.y + this.offset.y; + }, + enumerable: true, + configurable: true + }); + + + + Object.defineProperty(Body.prototype, "width", { + get: function () { + return this._width * this.sprite.transform.scale.x; + }, + set: function (value) { + this._width = value; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "height", { + get: function () { + return this._height * this.sprite.transform.scale.y; + }, + set: function (value) { + this._height = value; + }, + enumerable: true, + configurable: true + }); + + Body.prototype.preUpdate = function () { + this.oldPosition.copyFrom(this.position); + + this.bounds.x = this.x; + this.bounds.y = this.y; + this.bounds.width = this.width; + this.bounds.height = this.height; + }; + + // Shall we do this? Or just update the values directly in the separate functions? But then the bounds will be out of sync - as long as + // the bounds are updated and used in calculations then we can do one final sprite movement here I guess? + Body.prototype.postUpdate = function () { + if (this.type !== Phaser.Types.BODY_DISABLED) { + //this.game.world.physics.updateMotion(this); + this.wasTouching = this.touching; + this.touching = Phaser.Types.NONE; + } + + this.position.setTo(this.x, this.y); + }; + + Object.defineProperty(Body.prototype, "hullWidth", { + get: function () { + if (this.deltaX > 0) { + return this.bounds.width + this.deltaX; + } else { + return this.bounds.width - this.deltaX; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullHeight", { + get: function () { + if (this.deltaY > 0) { + return this.bounds.height + this.deltaY; + } else { + return this.bounds.height - this.deltaY; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullX", { + get: function () { + if (this.position.x < this.oldPosition.x) { + return this.position.x; + } else { + return this.oldPosition.x; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullY", { + get: function () { + if (this.position.y < this.oldPosition.y) { + return this.position.y; + } else { + return this.oldPosition.y; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaXAbs", { + get: function () { + return (this.deltaX > 0 ? this.deltaX : -this.deltaX); + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaYAbs", { + get: function () { + return (this.deltaY > 0 ? this.deltaY : -this.deltaY); + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaX", { + get: function () { + return this.position.x - this.oldPosition.x; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaY", { + get: function () { + return this.position.y - this.oldPosition.y; + }, + enumerable: true, + configurable: true + }); + return Body; + })(); + Physics.Body = Body; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - AABB + */ + (function (Physics) { + var AABB = (function () { + function AABB(game, x, y, xw, yw) { + this.type = 0; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + + this.aabbTileProjections = {}; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.AABBFull.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.AABBConcave.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.AABBConvex.Collide; + } + AABB.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + //o = oldposition + var ox = o.x; + var oy = o.y; + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + AABB.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + AABB.prototype.CollideAABBVsTile = function (tile) { + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + this.xw) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + this.yw) - Math.abs(dy); + + if (0 < py) { + if (px < py) { + if (dx < 0) { + //project to the left + px *= -1; + py = 0; + } else { + //proj to right + py = 0; + } + } else { + if (dy < 0) { + //project up + px = 0; + py *= -1; + } else { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + } + } + }; + + AABB.prototype.CollideAABBVsWorldBounds = function () { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + AABB.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + }; + + AABB.prototype.ResolveBoxTile = function (x, y, box, t) { + if (0 < t.ID) { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } else { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + }; + AABB.COL_NONE = 0; + AABB.COL_AXIS = 1; + AABB.COL_OTHER = 2; + return AABB; + })(); + Physics.AABB = AABB; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - Circle + */ + (function (Physics) { + var Circle = (function () { + function Circle(game, x, y, radius) { + this.type = 1; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + this.radius = radius; + + this.circleTileProjections = {}; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.CircleFull.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_45DEG] = Phaser.Physics.Projection.Circle45Deg.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.CircleConcave.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.CircleConvex.Collide; + } + Circle.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + var ox = o.x; + var oy = o.y; + + //o = oldposition + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + Circle.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.9; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + Circle.prototype.CollideCircleVsWorldBounds = function () { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } else if (this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } else if (this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + }; + + Circle.prototype.CollideCircleVsTile = function (tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + r) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + r) - Math.abs(dy); + + if (0 < py) { + //object may be colliding with tile + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) { + //circle is on left side of tile + this.oH = -1; + } else if (txw < dx) { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) { + //circle is on top side of tile + this.oV = -1; + } else if (tyw < dy) { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + } + } + }; + + Circle.prototype.ResolveCircleTile = function (x, y, oH, oV, obj, t) { + if (0 < t.ID) { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } else { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + }; + Circle.COL_NONE = 0; + Circle.COL_AXIS = 1; + Circle.COL_OTHER = 2; + return Circle; + })(); + Physics.Circle = Circle; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - TileMapCell + */ + (function (Physics) { + var TileMapCell = (function () { + function TileMapCell(game, x, y, xw, yw) { + this.game = game; + this.ID = TileMapCell.TID_EMPTY; + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x, y); + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + TileMapCell.prototype.SetState = function (ID) { + if (ID == TileMapCell.TID_EMPTY) { + this.Clear(); + } else { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + }; + + TileMapCell.prototype.Clear = function () { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY; + this.UpdateType(); + //this.Draw(); + }; + + TileMapCell.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + }; + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + TileMapCell.prototype.UpdateType = function () { + if (0 < this.ID) { + if (this.ID < TileMapCell.CTYPE_45DEG) { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } else if (this.ID < TileMapCell.CTYPE_CONCAVE) { + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnn) { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnp) { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGpp) { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_CONVEX) { + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGs) { + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGb) { + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGs) { + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGb) { + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_HALF) { + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFu) { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFl) { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFr) { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + } else { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + }; + TileMapCell.TID_EMPTY = 0; + TileMapCell.TID_FULL = 1; + TileMapCell.TID_45DEGpn = 2; + TileMapCell.TID_45DEGnn = 3; + TileMapCell.TID_45DEGnp = 4; + TileMapCell.TID_45DEGpp = 5; + TileMapCell.TID_CONCAVEpn = 6; + TileMapCell.TID_CONCAVEnn = 7; + TileMapCell.TID_CONCAVEnp = 8; + TileMapCell.TID_CONCAVEpp = 9; + TileMapCell.TID_CONVEXpn = 10; + TileMapCell.TID_CONVEXnn = 11; + TileMapCell.TID_CONVEXnp = 12; + TileMapCell.TID_CONVEXpp = 13; + TileMapCell.TID_22DEGpnS = 14; + TileMapCell.TID_22DEGnnS = 15; + TileMapCell.TID_22DEGnpS = 16; + TileMapCell.TID_22DEGppS = 17; + TileMapCell.TID_22DEGpnB = 18; + TileMapCell.TID_22DEGnnB = 19; + TileMapCell.TID_22DEGnpB = 20; + TileMapCell.TID_22DEGppB = 21; + TileMapCell.TID_67DEGpnS = 22; + TileMapCell.TID_67DEGnnS = 23; + TileMapCell.TID_67DEGnpS = 24; + TileMapCell.TID_67DEGppS = 25; + TileMapCell.TID_67DEGpnB = 26; + TileMapCell.TID_67DEGnnB = 27; + TileMapCell.TID_67DEGnpB = 28; + TileMapCell.TID_67DEGppB = 29; + TileMapCell.TID_HALFd = 30; + TileMapCell.TID_HALFr = 31; + TileMapCell.TID_HALFu = 32; + TileMapCell.TID_HALFl = 33; + + TileMapCell.CTYPE_EMPTY = 0; + TileMapCell.CTYPE_FULL = 1; + TileMapCell.CTYPE_45DEG = 2; + TileMapCell.CTYPE_CONCAVE = 6; + TileMapCell.CTYPE_CONVEX = 10; + TileMapCell.CTYPE_22DEGs = 14; + TileMapCell.CTYPE_22DEGb = 18; + TileMapCell.CTYPE_67DEGs = 22; + TileMapCell.CTYPE_67DEGb = 26; + TileMapCell.CTYPE_HALF = 30; + return TileMapCell; + })(); + Physics.TileMapCell = TileMapCell; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBFull = (function () { + function AABBFull() { + } + AABBFull.Collide = function (x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.AABB.COL_AXIS; + }; + return AABBFull; + })(); + Projection.AABBFull = AABBFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConvex = (function () { + function AABBConvex() { + } + AABBConvex.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw)); + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw)); + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else if (0 < pen) { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConvex; + })(); + Projection.AABBConvex = AABBConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConcave = (function () { + function AABBConcave() { + } + AABBConcave.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw)); + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw)); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else { + //project along corner->circle vector + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConcave; + })(); + Projection.AABBConcave = AABBConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleFull = (function () { + function CircleFull() { + } + CircleFull.Collide = function (x, y, oH, oV, obj, t) { + if (oH == 0) { + if (oV == 0) { + if (x < y) { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x; + + if (dx < 0) { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } else { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y; + + if (dy < 0) { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } + } else { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + } else if (oV == 0) { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + //diagonal collision + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleFull; + })(); + Projection.CircleFull = CircleFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConvex = (function () { + function CircleConvex() { + } + CircleConvex.Collide = function (x, y, oH, oV, obj, t) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConvex; + })(); + Projection.CircleConvex = CircleConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConcave = (function () { + function CircleConcave() { + } + CircleConcave.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x; + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y; + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + return Phaser.Physics.Circle.COL_NONE; + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the vertical tip + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out vertically + dx = 0; + dy = oV; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the horizontal tip + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out horizontally + dx = oH; + dy = 0; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConcave; + })(); + Projection.CircleConcave = CircleConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var Circle45Deg = (function () { + function Circle45Deg() { + } + Circle45Deg.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x; + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp; + sy *= -dp; + + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y + (oV * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return Circle45Deg; + })(); + Projection.Circle45Deg = Circle45Deg; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleManager = (function () { + function ParticleManager(proParticleCount, integrationType) { + this.PARTICLE_CREATED = 'partilcleCreated'; + this.PARTICLE_UPDATE = 'partilcleUpdate'; + this.PARTICLE_SLEEP = 'particleSleep'; + this.PARTICLE_DEAD = 'partilcleDead'; + this.PROTON_UPDATE = 'protonUpdate'; + this.PROTON_UPDATE_AFTER = 'protonUpdateAfter'; + this.EMITTER_ADDED = 'emitterAdded'; + this.EMITTER_REMOVED = 'emitterRemoved'; + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + this.amendChangeTabsBug = true; + this.TextureBuffer = {}; + this.TextureCanvasBuffer = {}; + this.proParticleCount = Particles.ParticleUtils.initValue(proParticleCount, ParticleManager.POOL_MAX); + this.integrationType = Particles.ParticleUtils.initValue(integrationType, ParticleManager.EULER); + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + + ParticleManager.pool = new Phaser.Particles.ParticlePool(proParticleCount); + ParticleManager.integrator = new Phaser.Particles.NumericalIntegration(this.integrationType); + } + /** + * add a type of Renderer + * + * @method addRender + * @param {Renderer} render + */ + ParticleManager.prototype.addRender = function (render) { + render.proton = this; + this.renderers.push(render.proton); + }; + + /** + * add the Emitter + * + * @method addEmitter + * @param {Emitter} emitter + */ + ParticleManager.prototype.addEmitter = function (emitter) { + this.emitters.push(emitter); + emitter.parent = this; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_ADDED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.removeEmitter = function (emitter) { + var index = this.emitters.indexOf(emitter); + this.emitters.splice(index, 1); + emitter.parent = null; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_REMOVED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.update = function () { + if (!this.oldTime) + this.oldTime = new Date().getTime(); + + var time = new Date().getTime(); + this.elapsed = (time - this.oldTime) / 1000; + + //if (ParticleUtils.amendChangeTabsBug) + // this.amendChangeTabsBugHandler(); + this.oldTime = time; + if (this.elapsed > 0) { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].update(this.elapsed); + } + } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PROTON_UPDATE_AFTER + //})); + }; + + ParticleManager.prototype.amendChangeTabsBugHandler = function () { + if (this.elapsed > .5) { + this.oldTime = new Date().getTime(); + this.elapsed = 0; + } + }; + + ParticleManager.prototype.getParticleNumber = function () { + var total = 0; + for (var i = 0; i < this.emitters.length; i++) { + total += this.emitters[i].particles.length; + } + return total; + }; + + ParticleManager.prototype.destroy = function () { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].destory(); + delete this.emitters[i]; + } + + this.emitters = []; + this.time = 0; + this.oldTime = 0; + ParticleManager.pool.release(); + }; + ParticleManager.POOL_MAX = 1000; + ParticleManager.TIME_STEP = 60; + + ParticleManager.MEASURE = 100; + ParticleManager.EULER = 'euler'; + ParticleManager.RK2 = 'runge-kutta2'; + ParticleManager.RK4 = 'runge-kutta4'; + ParticleManager.VERLET = 'verlet'; + return ParticleManager; + })(); + Particles.ParticleManager = ParticleManager; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticlePool = (function () { + function ParticlePool(num, releaseTime) { + if (typeof releaseTime === "undefined") { releaseTime = 0; } + this.poolList = []; + this.timeoutID = 0; + this.proParticleCount = Particles.ParticleUtils.initValue(num, 0); + this.releaseTime = Particles.ParticleUtils.initValue(releaseTime, -1); + this.poolList = []; + this.timeoutID = 0; + + for (var i = 0; i < this.proParticleCount; i++) { + this.add(); + } + + if (this.releaseTime > 0) { + // TODO - Hook to game clock so Pause works + this.timeoutID = setTimeout(this.release, this.releaseTime / 1000); + } + } + ParticlePool.prototype.create = function (newTypeParticleClass) { + if (typeof newTypeParticleClass === "undefined") { newTypeParticleClass = null; } + if (newTypeParticleClass) { + return new newTypeParticleClass(); + } else { + return new Phaser.Particles.Particle(); + } + }; + + ParticlePool.prototype.getCount = function () { + return this.poolList.length; + }; + + ParticlePool.prototype.add = function () { + return this.poolList.push(this.create()); + }; + + ParticlePool.prototype.get = function () { + if (this.poolList.length === 0) { + return this.create(); + } else { + return this.poolList.pop().reset(); + } + }; + + ParticlePool.prototype.set = function (particle) { + if (this.poolList.length < Particles.ParticleManager.POOL_MAX) { + return this.poolList.push(particle); + } + }; + + ParticlePool.prototype.release = function () { + for (var i = 0; i < this.poolList.length; i++) { + if (this.poolList[i]['destroy']) { + this.poolList[i].destroy(); + } + + delete this.poolList[i]; + } + + this.poolList = []; + }; + return ParticlePool; + })(); + Particles.ParticlePool = ParticlePool; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleUtils = (function () { + function ParticleUtils() { + } + ParticleUtils.initValue = function (value, defaults) { + var value = (value != null && value != undefined) ? value : defaults; + return value; + }; + + ParticleUtils.isArray = function (value) { + return typeof value === 'object' && value.hasOwnProperty('length'); + }; + + ParticleUtils.destroyArray = function (array) { + array.length = 0; + }; + + ParticleUtils.destroyObject = function (obj) { + for (var o in obj) { + delete obj[o]; + } + }; + + ParticleUtils.setSpanValue = function (a, b, c) { + if (typeof b === "undefined") { b = null; } + if (typeof c === "undefined") { c = null; } + if (a instanceof Phaser.Particles.Span) { + return a; + } else { + if (!b) { + return new Phaser.Particles.Span(a); + } else { + if (!c) { + return new Phaser.Particles.Span(a, b); + } else { + return new Phaser.Particles.Span(a, b, c); + } + } + } + }; + + ParticleUtils.getSpanValue = function (pan) { + if (pan instanceof Phaser.Particles.Span) { + return pan.getValue(); + } else { + return pan; + } + }; + + ParticleUtils.randomAToB = function (a, b, INT) { + if (typeof INT === "undefined") { INT = null; } + if (!INT) { + return a + Math.random() * (b - a); + } else { + return Math.floor(Math.random() * (b - a)) + a; + } + }; + + ParticleUtils.randomFloating = function (center, f, INT) { + return ParticleUtils.randomAToB(center - f, center + f, INT); + }; + + ParticleUtils.randomZone = function (display) { + }; + + ParticleUtils.degreeTransform = function (a) { + return a * Math.PI / 180; + }; + + ParticleUtils.randomColor = //static toColor16 getRGB(num) { + // return "#" + num.toString(16); + //} + function () { + return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6); + }; + + ParticleUtils.setEasingByName = function (name) { + switch (name) { + case 'easeLinear': + return Phaser.Easing.Linear.None; + break; + + case 'easeInQuad': + return Phaser.Easing.Quadratic.In; + break; + + case 'easeOutQuad': + return Phaser.Easing.Quadratic.Out; + break; + + case 'easeInOutQuad': + return Phaser.Easing.Quadratic.InOut; + break; + + case 'easeInCubic': + return Phaser.Easing.Cubic.In; + break; + + case 'easeOutCubic': + return Phaser.Easing.Cubic.Out; + break; + + case 'easeInOutCubic': + return Phaser.Easing.Cubic.InOut; + break; + + case 'easeInQuart': + return Phaser.Easing.Quartic.In; + break; + + case 'easeOutQuart': + return Phaser.Easing.Quartic.Out; + break; + + case 'easeInOutQuart': + return Phaser.Easing.Quartic.InOut; + break; + + case 'easeInSine': + return Phaser.Easing.Sinusoidal.In; + break; + + case 'easeOutSine': + return Phaser.Easing.Sinusoidal.Out; + break; + + case 'easeInOutSine': + return Phaser.Easing.Sinusoidal.InOut; + break; + + case 'easeInExpo': + return Phaser.Easing.Exponential.In; + break; + + case 'easeOutExpo': + return Phaser.Easing.Exponential.Out; + break; + + case 'easeInOutExpo': + return Phaser.Easing.Exponential.InOut; + break; + + case 'easeInCirc': + return Phaser.Easing.Circular.In; + break; + + case 'easeOutCirc': + return Phaser.Easing.Circular.Out; + break; + + case 'easeInOutCirc': + return Phaser.Easing.Circular.InOut; + break; + + case 'easeInBack': + return Phaser.Easing.Back.In; + break; + + case 'easeOutBack': + return Phaser.Easing.Back.Out; + break; + + case 'easeInOutBack': + return Phaser.Easing.Back.InOut; + break; + + default: + return Phaser.Easing.Linear.None; + break; + } + }; + return ParticleUtils; + })(); + Particles.ParticleUtils = ParticleUtils; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Polar2D = (function () { + function Polar2D(r, tha) { + this.r = Math.abs(r) || 0; + this.tha = tha || 0; + } + Polar2D.prototype.set = function (r, tha) { + this.r = r; + this.tha = tha; + return this; + }; + + Polar2D.prototype.setR = function (r) { + this.r = r; + return this; + }; + + Polar2D.prototype.setTha = function (tha) { + this.tha = tha; + return this; + }; + + Polar2D.prototype.copy = function (p) { + this.r = p.r; + this.tha = p.tha; + return this; + }; + + Polar2D.prototype.toVector = function () { + return new Phaser.Vec2(this.getX(), this.getY()); + }; + + Polar2D.prototype.getX = function () { + return this.r * Math.sin(this.tha); + }; + + Polar2D.prototype.getY = function () { + return -this.r * Math.cos(this.tha); + }; + + Polar2D.prototype.normalize = function () { + this.r = 1; + return this; + }; + + Polar2D.prototype.equals = function (v) { + return ((v.r === this.r) && (v.tha === this.tha)); + }; + + Polar2D.prototype.toArray = function () { + return [this.r, this.tha]; + }; + + Polar2D.prototype.clear = function () { + this.r = 0.0; + this.tha = 0.0; + return this; + }; + + Polar2D.prototype.clone = function () { + return new Polar2D(this.r, this.tha); + }; + return Polar2D; + })(); + Particles.Polar2D = Polar2D; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Span = (function () { + function Span(a, b, center) { + if (typeof b === "undefined") { b = null; } + if (typeof center === "undefined") { center = null; } + this.isArray = false; + + if (Particles.ParticleUtils.isArray(a)) { + this.isArray = true; + this.a = a; + } else { + this.a = Particles.ParticleUtils.initValue(a, 1); + this.b = Particles.ParticleUtils.initValue(b, this.a); + this.center = Particles.ParticleUtils.initValue(center, false); + } + } + Span.prototype.getValue = function (INT) { + if (typeof INT === "undefined") { INT = null; } + if (this.isArray) { + return this.a[Math.floor(this.a.length * Math.random())]; + } else { + if (!this.center) { + return Particles.ParticleUtils.randomAToB(this.a, this.b, INT); + } else { + return Particles.ParticleUtils.randomFloating(this.a, this.b, INT); + } + } + }; + + Span.getSpan = function (a, b, center) { + return new Span(a, b, center); + }; + return Span; + })(); + Particles.Span = Span; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var NumericalIntegration = (function () { + function NumericalIntegration(type) { + this.type = Particles.ParticleUtils.initValue(type, Particles.ParticleManager.EULER); + } + NumericalIntegration.prototype.integrate = function (particles, time, damping) { + this.eulerIntegrate(particles, time, damping); + }; + + NumericalIntegration.prototype.eulerIntegrate = function (particle, time, damping) { + if (!particle.sleep) { + particle.old.p.copy(particle.p); + particle.old.v.copy(particle.v); + particle.a.multiplyScalar(1 / particle.mass); + particle.v.add(particle.a.multiplyScalar(time)); + particle.p.add(particle.old.v.multiplyScalar(time)); + if (damping) + particle.v.multiplyScalar(damping); + particle.a.clear(); + } + }; + return NumericalIntegration; + })(); + Particles.NumericalIntegration = NumericalIntegration; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var Behaviour = (function () { + function Behaviour(life, easing) { + /** + * The behaviour's id; + * @property id + * @type {String} id + */ + this.id = 'Behaviour_' + Behaviour.ID++; + this.life = Particles.ParticleUtils.initValue(life, Infinity); + + /** + * The behaviour's decaying trend, for example Proton.easeOutQuart; + * @property easing + * @type {String} + * @default Proton.easeLinear + */ + this.easing = Particles.ParticleUtils.setEasingByName(easing); + this.age = 0; + this.energy = 1; + + /** + * The behaviour is Dead; + * @property dead + * @type {Boolean} + */ + this.dead = false; + + /** + * The behaviour's parents array; + * @property parents + * @type {Array} + */ + this.parents = []; + + /** + * The behaviour name; + * @property name + * @type {string} + */ + this.name = 'Behaviour'; + } + /** + * Reset this behaviour's parameters + * + * @method reset + * @param {Number} this behaviour's life + * @param {String} this behaviour's easing + */ + //reset (life, easing) { + // this.life = ParticleUtils.initValue(life, Infinity); + // //this.easing = ParticleUtils.initValue(easing, Proton.ease.setEasingByName(Proton.easeLinear)); + //} + /** + * Normalize a force by 1:100; + * + * @method normalizeForce + * @param {Proton.Vector2D} force + */ + Behaviour.prototype.normalizeForce = function (force) { + return force.multiplyScalar(Particles.ParticleManager.MEASURE); + }; + + /** + * Normalize a value by 1:100; + * + * @method normalizeValue + * @param {Number} value + */ + Behaviour.prototype.normalizeValue = function (value) { + return value * Particles.ParticleManager.MEASURE; + }; + + /** + * Initialize the behaviour's parameters for all particles + * + * @method initialize + * @param {Proton.Particle} particle + */ + Behaviour.prototype.initialize = function (particle) { + }; + + /** + * Apply this behaviour for all particles every time + * + * @method applyBehaviour + * @param {Proton.Particle} particle + * @param {Number} the integrate time 1/ms + * @param {Int} the particle index + */ + Behaviour.prototype.applyBehaviour = function (particle, time, index) { + this.age += time; + + if (this.age >= this.life || this.dead) { + this.energy = 0; + this.dead = true; + this.destroy(); + } else { + var scale = this.easing(particle.age / particle.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + /** + * Destory this behaviour + * @method destory + */ + Behaviour.prototype.destroy = function () { + var index; + var length = this.parents.length, i; + + for (i = 0; i < length; i++) { + this.parents[i].removeBehaviour(this); + } + + this.parents = []; + }; + return Behaviour; + })(); + Behaviours.Behaviour = Behaviour; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var RandomDrift = (function (_super) { + __extends(RandomDrift, _super); + function RandomDrift(driftX, driftY, delay, life, easing) { + _super.call(this, life, easing); + this.reset(driftX, driftY, delay); + this.time = 0; + this.name = "RandomDrift"; + } + RandomDrift.prototype.reset = function (driftX, driftY, delay, life, easing) { + if (typeof life === "undefined") { life = null; } + if (typeof easing === "undefined") { easing = null; } + this.panFoce = new Phaser.Vec2(driftX, driftY); + this.panFoce = this.normalizeForce(this.panFoce); + this.delay = delay; + + if (life) { + this.life = Particles.ParticleUtils.initValue(life, Infinity); + this.easing = Particles.ParticleUtils.initValue(easing, Phaser.Easing.Linear.None); + } + }; + + RandomDrift.prototype.applyBehaviour = function (particle, time, index) { + _super.prototype.applyBehaviour.call(this, particle, time, index); + + this.time += time; + + if (this.time >= this.delay) { + particle.a.addXY(Particles.ParticleUtils.randomAToB(-this.panFoce.x, this.panFoce.x), Particles.ParticleUtils.randomAToB(-this.panFoce.y, this.panFoce.y)); + this.time = 0; + } + }; + return RandomDrift; + })(Behaviours.Behaviour); + Behaviours.RandomDrift = RandomDrift; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Initialize = (function () { + function Initialize() { + } + Initialize.prototype.initialize = function (target) { + }; + + Initialize.prototype.reset = function (a, b, c) { + }; + + Initialize.prototype.init = function (emitter, particle) { + if (typeof particle === "undefined") { particle = null; } + if (particle) { + this.initialize(particle); + } else { + this.initialize(emitter); + } + }; + return Initialize; + })(); + Initializers.Initialize = Initialize; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Life = (function (_super) { + __extends(Life, _super); + function Life(a, b, c) { + _super.call(this); + + this.lifePan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Life.prototype.initialize = function (target) { + if (this.lifePan.a == Infinity) { + target.life = Infinity; + } else { + target.life = this.lifePan.getValue(); + } + }; + return Life; + })(Initializers.Initialize); + Initializers.Life = Life; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Mass = (function (_super) { + __extends(Mass, _super); + function Mass(a, b, c) { + _super.call(this); + this.massPan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Mass.prototype.initialize = function (target) { + target.mass = this.massPan.getValue(); + }; + return Mass; + })(Initializers.Initialize); + Initializers.Mass = Mass; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Position = (function (_super) { + __extends(Position, _super); + function Position(zone) { + _super.call(this); + + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + } + Position.prototype.reset = function (zone) { + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + }; + + Position.prototype.initialize = function (target) { + this.zone.getPosition(); + + target.p.x = this.zone.vector.x; + target.p.y = this.zone.vector.y; + }; + return Position; + })(Initializers.Initialize); + Initializers.Position = Position; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Rate = (function (_super) { + __extends(Rate, _super); + function Rate(numpan, timepan) { + _super.call(this); + + numpan = Particles.ParticleUtils.initValue(numpan, 1); + timepan = Particles.ParticleUtils.initValue(timepan, 1); + this.numPan = new Phaser.Particles.Span(numpan); + this.timePan = new Phaser.Particles.Span(timepan); + this.startTime = 0; + this.nextTime = 0; + this.init(); + } + Rate.prototype.init = function () { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + }; + + Rate.prototype.getValue = function (time) { + this.startTime += time; + + if (this.startTime >= this.nextTime) { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + + if (this.numPan.b == 1) { + if (this.numPan.getValue(false) > 0.5) { + return 1; + } else { + return 0; + } + } else { + return this.numPan.getValue(true); + } + } + + return 0; + }; + return Rate; + })(Initializers.Initialize); + Initializers.Rate = Rate; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Velocity = (function (_super) { + __extends(Velocity, _super); + function Velocity(rpan, thapan, type) { + _super.call(this); + + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + } + Velocity.prototype.reset = function (rpan, thapan, type) { + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + }; + + Velocity.prototype.normalizeVelocity = function (vr) { + return vr * Particles.ParticleManager.MEASURE; + }; + + Velocity.prototype.initialize = function (target) { + if (this.type == 'p' || this.type == 'P' || this.type == 'polar') { + var polar2d = new Particles.Polar2D(this.normalizeVelocity(this.rPan.getValue()), this.thaPan.getValue() * Math.PI / 180); + target.v.x = polar2d.getX(); + target.v.y = polar2d.getY(); + } else { + target.v.x = this.normalizeVelocity(this.rPan.getValue()); + target.v.y = this.normalizeVelocity(this.thaPan.getValue()); + } + }; + return Velocity; + })(Initializers.Initialize); + Initializers.Velocity = Velocity; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var Zone = (function () { + function Zone() { + this.vector = new Phaser.Vec2(); + this.random = 0; + this.crossType = "dead"; + this.alert = true; + } + return Zone; + })(); + Zones.Zone = Zone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var PointZone = (function (_super) { + __extends(PointZone, _super); + function PointZone(x, y) { + if (typeof x === "undefined") { x = 0; } + if (typeof y === "undefined") { y = 0; } + _super.call(this); + this.x = x; + this.y = y; + } + PointZone.prototype.getPosition = function () { + return this.vector.setTo(this.x, this.y); + }; + + PointZone.prototype.crossing = function (particle) { + if (this.alert) { + alert('Sorry PointZone does not support crossing method'); + this.alert = false; + } + }; + return PointZone; + })(Zones.Zone); + Zones.PointZone = PointZone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); /// /** * World @@ -20132,3 +22839,31 @@ var Phaser; })(); Phaser.CanvasUtils = CanvasUtils; })(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Radius = (function (_super) { + __extends(Radius, _super); + function Radius(a, b, c) { + _super.call(this); + + this.radius = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Radius.prototype.reset = function (a, b, c) { + this.radius = Particles.ParticleUtils.setSpanValue(a, b, c); + }; + + Radius.prototype.initialize = function (particle) { + particle.radius = this.radius.getValue(); + particle.transform.oldRadius = particle.radius; + }; + return Radius; + })(Initializers.Initialize); + Initializers.Radius = Radius; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Tests/physics/circle 1.js b/Tests/physics/circle 1.js new file mode 100644 index 000000000..ef5454789 --- /dev/null +++ b/Tests/physics/circle 1.js @@ -0,0 +1,90 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, preload, create, update, render); + + function preload() { + game.load.image('ball', 'assets/sprites/shinyball.png'); + game.load.image('card', 'assets/sprites/mana_card.png'); + } + + var cells; + var b; + var c; + var t; + var ball; + var card; + + function create() { + this.ball = game.add.sprite(0, 0, 'ball'); + + this.card = game.add.sprite(0, 0, 'card'); + this.card.rotation = 30; + + this.c = game.add.circle(200, 200, 16); + this.b = game.add.aabb(400, 200, 74, 128); + + // pos is center, not upper-left + this.cells = []; + + var tid; + + for (var i = 0; i < 10; i++) { + if (i % 2 == 0) { + tid = Phaser.Physics.TileMapCell.TID_CONCAVEpn; + } else { + tid = Phaser.Physics.TileMapCell.TID_CONCAVEnn; + } + + this.cells.push(game.add.cell(100 + (i * 100), 400, 50, 50, tid)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_CONCAVEpn)); + } + } + + function update() { + var fx = 0; + var fy = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + fx -= 0.2; + } else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + fx += 0.2; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) { + fy -= 0.2 + 0.2; + } else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + fy += 0.2; + } + + // update circle + this.c.pos.x = this.c.oldpos.x + Math.min(20, Math.max(-20, this.c.pos.x - this.c.oldpos.x + fx)); + this.c.pos.y = this.c.oldpos.y + Math.min(20, Math.max(-20, this.c.pos.y - this.c.oldpos.y + fy)); + this.c.IntegrateVerlet(); + + // update box + this.b.pos.x = this.b.oldpos.x + Math.min(40, Math.max(-40, this.b.pos.x - this.b.oldpos.x + fx)); + this.b.pos.y = this.b.oldpos.y + Math.min(40, Math.max(-40, this.b.pos.y - this.b.oldpos.y + fy)); + this.b.IntegrateVerlet(); + + for (var i = 0; i < this.cells.length; i++) { + this.c.CollideCircleVsTile(this.cells[i]); + this.b.CollideAABBVsTile(this.cells[i]); + } + + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + + this.ball.transform.centerOn(this.c.pos.x, this.c.pos.y); + this.card.transform.centerOn(this.b.pos.x, this.b.pos.y); + } + + function render() { + this.c.render(game.stage.context); + this.b.render(game.stage.context); + + for (var i = 0; i < this.cells.length; i++) { + this.cells[i].render(game.stage.context); + } + } +})(); diff --git a/Tests/physics/circle 1.ts b/Tests/physics/circle 1.ts new file mode 100644 index 000000000..af073ad2c --- /dev/null +++ b/Tests/physics/circle 1.ts @@ -0,0 +1,113 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, preload, create, update, render); + + function preload() { + + game.load.image('ball', 'assets/sprites/shinyball.png'); + game.load.image('card', 'assets/sprites/mana_card.png'); + + } + + var cells; + var b: Phaser.Physics.AABB; + var c: Phaser.Physics.Circle; + var t: Phaser.Physics.TileMapCell; + var ball: Phaser.Sprite; + var card: Phaser.Sprite; + + function create() { + + this.ball = game.add.sprite(0, 0, 'ball'); + + this.card = game.add.sprite(0, 0, 'card'); + this.card.rotation = 30; + + this.c = game.add.circle(200, 200, 16); + this.b = game.add.aabb(400, 200, 74, 128); + + // pos is center, not upper-left + this.cells = []; + + var tid; + + for (var i = 0; i < 10; i++) + { + if (i % 2 == 0) + { + tid = Phaser.Physics.TileMapCell.TID_CONCAVEpn; + } + else + { + tid = Phaser.Physics.TileMapCell.TID_CONCAVEnn; + } + + this.cells.push(game.add.cell(100 + (i * 100), 400, 50, 50, tid)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_CONCAVEpn)); + } + + } + + function update() { + + var fx = 0; + var fy = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + fx -= 0.2; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + fx += 0.2; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + fy -= 0.2 + 0.2; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + fy += 0.2; + } + + // update circle + this.c.pos.x = this.c.oldpos.x + Math.min(20, Math.max(-20, this.c.pos.x - this.c.oldpos.x + fx)); + this.c.pos.y = this.c.oldpos.y + Math.min(20, Math.max(-20, this.c.pos.y - this.c.oldpos.y + fy)); + this.c.IntegrateVerlet(); + + // update box + this.b.pos.x = this.b.oldpos.x + Math.min(40, Math.max(-40, this.b.pos.x - this.b.oldpos.x + fx)); + this.b.pos.y = this.b.oldpos.y + Math.min(40, Math.max(-40, this.b.pos.y - this.b.oldpos.y + fy)); + this.b.IntegrateVerlet(); + + for (var i = 0; i < this.cells.length; i++) + { + this.c.CollideCircleVsTile(this.cells[i]); + this.b.CollideAABBVsTile(this.cells[i]); + } + + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + + this.ball.transform.centerOn(this.c.pos.x, this.c.pos.y); + this.card.transform.centerOn(this.b.pos.x, this.b.pos.y); + + } + + function render() { + + this.c.render(game.stage.context); + this.b.render(game.stage.context); + + for (var i = 0; i < this.cells.length; i++) + { + this.cells[i].render(game.stage.context); + } + + } + +})(); diff --git a/Tests/physics/sprite bounds.js b/Tests/physics/sprite bounds.js new file mode 100644 index 000000000..3ba3db5c5 --- /dev/null +++ b/Tests/physics/sprite bounds.js @@ -0,0 +1,43 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, preload, create, update, render); + + function preload() { + game.load.image('fuji', 'assets/pics/atari_fujilogo.png'); + } + + var fuji; + var tween; + var b; + + function create() { + game.stage.backgroundColor = 'rgb(0,0,100)'; + + fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji'); + fuji.origin.setTo(0, 0.5); + fuji.rotation = 34; + + b = new Phaser.Rectangle(fuji.transform.center.x, fuji.transform.center.y, fuji.width, fuji.height); + //game.add.tween(fuji).to({ rotation: 360 }, 20000, Phaser.Easing.Linear.None, true, 0, true); + } + + function update() { + if (game.input.activePointer.justPressed()) { + //fuji.transform.centerOn(game.input.x, game.input.y); + fuji.x = game.input.x; + fuji.y = game.input.y; + } + + b.x = fuji.transform.center.x - fuji.transform.halfWidth; + b.y = fuji.transform.center.y - fuji.transform.halfHeight; + } + + function render() { + //Phaser.DebugUtils.renderSpriteWorldViewBounds(fuji); + //Phaser.DebugUtils.renderSpriteBounds(fuji); + Phaser.DebugUtils.renderSpriteCorners(fuji); + + //Phaser.DebugUtils.renderSpriteWorldView(fuji, 32, 32); + Phaser.DebugUtils.renderRectangle(b, 'rgba(237,20,91,0.3)'); + } +})(); diff --git a/Tests/physics/sprite bounds.ts b/Tests/physics/sprite bounds.ts new file mode 100644 index 000000000..f28ecb1fe --- /dev/null +++ b/Tests/physics/sprite bounds.ts @@ -0,0 +1,53 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, preload, create, update, render); + + function preload() { + game.load.image('fuji', 'assets/pics/atari_fujilogo.png'); + } + + var fuji: Phaser.Sprite; + var tween: Phaser.Tween; + var b: Phaser.Rectangle; + + function create() { + + game.stage.backgroundColor = 'rgb(0,0,100)'; + + fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji'); + fuji.origin.setTo(0, 0.5); + fuji.rotation = 34; + + b = new Phaser.Rectangle(fuji.transform.center.x, fuji.transform.center.y, fuji.width, fuji.height); + + //game.add.tween(fuji).to({ rotation: 360 }, 20000, Phaser.Easing.Linear.None, true, 0, true); + + } + + function update() { + + if (game.input.activePointer.justPressed()) + { + //fuji.transform.centerOn(game.input.x, game.input.y); + fuji.x = game.input.x; + fuji.y = game.input.y; + } + + b.x = fuji.transform.center.x - fuji.transform.halfWidth; + b.y = fuji.transform.center.y - fuji.transform.halfHeight; + + } + + function render() { + + //Phaser.DebugUtils.renderSpriteWorldViewBounds(fuji); + //Phaser.DebugUtils.renderSpriteBounds(fuji); + Phaser.DebugUtils.renderSpriteCorners(fuji); + //Phaser.DebugUtils.renderSpriteWorldView(fuji, 32, 32); + Phaser.DebugUtils.renderRectangle(b, 'rgba(237,20,91,0.3)'); + + } + +})(); diff --git a/build/phaser-debug.js b/build/phaser-debug.js index 1b6c7f4c2..3528a49cf 100644 --- a/build/phaser-debug.js +++ b/build/phaser-debug.js @@ -4459,6 +4459,7 @@ var Phaser; this.modified = false; this.game = game; this.type = Phaser.Types.GROUP; + this.active = true; this.exists = true; this.visible = true; @@ -4519,7 +4520,9 @@ var Phaser; this._member = this.members[this._i++]; if (this._member != null && this._member.exists && this._member.active) { - this._member.preUpdate(); + if (this._member.type != Phaser.Types.GROUP) { + this._member.preUpdate(); + } this._member.update(); } } @@ -14660,200 +14663,6 @@ var Phaser; Phaser.TilemapLayer = TilemapLayer; })(Phaser || (Phaser = {})); var Phaser; -(function (Phaser) { - /// - /** - * Phaser - ArcadePhysics - Body - */ - (function (Physics) { - var Body = (function () { - function Body(sprite, type) { - this.angularVelocity = 0; - this.angularAcceleration = 0; - this.angularDrag = 0; - this.maxAngular = 10000; - this.mass = 1; - this._width = 0; - this._height = 0; - this.sprite = sprite; - this.game = sprite.game; - this.type = type; - - // Fixture properties - // Will extend into its own class at a later date - can move the fixture defs there and add shape support, but this will do for 1.0 release - this.bounds = new Phaser.Rectangle(); - - this._width = sprite.width; - this._height = sprite.height; - - // Body properties - //this.gravity = Vec2Utils.clone(ArcadePhysics.gravity); - //this.bounce = Vec2Utils.clone(ArcadePhysics.bounce); - this.velocity = new Phaser.Vec2(); - this.acceleration = new Phaser.Vec2(); - - //this.drag = Vec2Utils.clone(ArcadePhysics.drag); - this.maxVelocity = new Phaser.Vec2(10000, 10000); - - this.angularVelocity = 0; - this.angularAcceleration = 0; - this.angularDrag = 0; - - this.touching = Phaser.Types.NONE; - this.wasTouching = Phaser.Types.NONE; - this.allowCollisions = Phaser.Types.ANY; - - this.position = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); - this.oldPosition = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); - this.offset = new Phaser.Vec2(); - } - Object.defineProperty(Body.prototype, "x", { - get: function () { - return this.sprite.x + this.offset.x; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "y", { - get: function () { - return this.sprite.y + this.offset.y; - }, - enumerable: true, - configurable: true - }); - - - - Object.defineProperty(Body.prototype, "width", { - get: function () { - return this._width * this.sprite.transform.scale.x; - }, - set: function (value) { - this._width = value; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "height", { - get: function () { - return this._height * this.sprite.transform.scale.y; - }, - set: function (value) { - this._height = value; - }, - enumerable: true, - configurable: true - }); - - Body.prototype.preUpdate = function () { - this.oldPosition.copyFrom(this.position); - - this.bounds.x = this.x; - this.bounds.y = this.y; - this.bounds.width = this.width; - this.bounds.height = this.height; - }; - - // Shall we do this? Or just update the values directly in the separate functions? But then the bounds will be out of sync - as long as - // the bounds are updated and used in calculations then we can do one final sprite movement here I guess? - Body.prototype.postUpdate = function () { - if (this.type !== Phaser.Types.BODY_DISABLED) { - //this.game.world.physics.updateMotion(this); - this.wasTouching = this.touching; - this.touching = Phaser.Types.NONE; - } - - this.position.setTo(this.x, this.y); - }; - - Object.defineProperty(Body.prototype, "hullWidth", { - get: function () { - if (this.deltaX > 0) { - return this.bounds.width + this.deltaX; - } else { - return this.bounds.width - this.deltaX; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullHeight", { - get: function () { - if (this.deltaY > 0) { - return this.bounds.height + this.deltaY; - } else { - return this.bounds.height - this.deltaY; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullX", { - get: function () { - if (this.position.x < this.oldPosition.x) { - return this.position.x; - } else { - return this.oldPosition.x; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "hullY", { - get: function () { - if (this.position.y < this.oldPosition.y) { - return this.position.y; - } else { - return this.oldPosition.y; - } - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaXAbs", { - get: function () { - return (this.deltaX > 0 ? this.deltaX : -this.deltaX); - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaYAbs", { - get: function () { - return (this.deltaY > 0 ? this.deltaY : -this.deltaY); - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaX", { - get: function () { - return this.position.x - this.oldPosition.x; - }, - enumerable: true, - configurable: true - }); - - Object.defineProperty(Body.prototype, "deltaY", { - get: function () { - return this.position.y - this.oldPosition.y; - }, - enumerable: true, - configurable: true - }); - return Body; - })(); - Physics.Body = Body; - })(Phaser.Physics || (Phaser.Physics = {})); - var Physics = Phaser.Physics; -})(Phaser || (Phaser = {})); -var Phaser; (function (Phaser) { /// /** @@ -15085,10 +14894,6 @@ var Phaser; * Pre-update is called right before update() on each object in the game loop. */ Sprite.prototype.preUpdate = function () { - if (this.name == 'piece1') { - console.log('wv', this.worldView); - } - this.transform.update(); if (this.transform.scrollFactor.x != 1 && this.transform.scrollFactor.x != 0) { @@ -15334,8 +15139,7 @@ var Phaser; TransformManager.prototype.centerOn = function (x, y) { this.parent.x = x + (this.parent.x - this.center.x); this.parent.y = y + (this.parent.y - this.center.y); - - this.setCache(); + //this.setCache(); }; /** @@ -15354,10 +15158,8 @@ var Phaser; this._size.y = this.parent.height; this._origin.x = this.origin.x; this._origin.y = this.origin.y; - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); + this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._prevRotation = this.rotation; if (this.parent.texture && this.parent.texture.renderRotation) { @@ -15378,6 +15180,9 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + this._flippedX = this.parent.texture.flippedX; + this._flippedY = this.parent.texture.flippedY; }; /** @@ -15403,9 +15208,7 @@ var Phaser; this._dirty = true; } - if (this.rotation != this._prevRotation) { - this._sc.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); - this._sc.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + if (this.rotation != this._prevRotation || this._dirty) { this._scA.y = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); this._scA.x = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._angle); @@ -15433,27 +15236,35 @@ var Phaser; this._pos.x = this.parent.x; this._pos.y = this.parent.y; + + // Translate + this.local.data[2] = this.parent.x; + this.local.data[5] = this.parent.y; } - if (this.parent.texture.flippedX) { - this.local.data[0] = this._sc.y * -this.scale.x; - this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; - } else { - this.local.data[0] = this._sc.y * this.scale.x; - this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + if (this._dirty || this._flippedX != this.parent.texture.flippedX) { + this._flippedX = this.parent.texture.flippedX; + + if (this._flippedX) { + this.local.data[0] = this._sc.y * -this.scale.x; + this.local.data[3] = (this._sc.x * -this.scale.x) + this.skew.x; + } else { + this.local.data[0] = this._sc.y * this.scale.x; + this.local.data[3] = (this._sc.x * this.scale.x) + this.skew.x; + } } - if (this.parent.texture.flippedY) { - this.local.data[4] = this._sc.y * -this.scale.y; - this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; - } else { - this.local.data[4] = this._sc.y * this.scale.y; - this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; - } + if (this._dirty || this._flippedY != this.parent.texture.flippedY) { + this._flippedY = this.parent.texture.flippedY; - // Translate - this.local.data[2] = this.parent.x; - this.local.data[5] = this.parent.y; + if (this._flippedY) { + this.local.data[4] = this._sc.y * -this.scale.y; + this.local.data[1] = -(this._sc.x * -this.scale.y) + this.skew.y; + } else { + this.local.data[4] = this._sc.y * this.scale.y; + this.local.data[1] = -(this._sc.x * this.scale.y) + this.skew.y; + } + } }; return TransformManager; })(); @@ -15835,6 +15646,19 @@ var Phaser; return this.game.sound.add(key, volume, loop); }; + GameObjectFactory.prototype.circle = function (x, y, radius) { + return new Phaser.Physics.Circle(this.game, x, y, radius); + }; + + GameObjectFactory.prototype.aabb = function (x, y, width, height) { + return new Phaser.Physics.AABB(this.game, x, y, Math.floor(width / 2), Math.floor(height / 2)); + }; + + GameObjectFactory.prototype.cell = function (x, y, width, height, state) { + if (typeof state === "undefined") { state = Phaser.Physics.TileMapCell.TID_FULL; } + return new Phaser.Physics.TileMapCell(this.game, x, y, width, height).SetState(state); + }; + /** * Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center. * @@ -15876,10 +15700,9 @@ var Phaser; * * @return {Particle} The newly created particle object. */ - GameObjectFactory.prototype.particle = function () { - return new Phaser.ArcadeParticle(this.game); - }; - + //public particle(): Phaser.ArcadeParticle { + // return new Phaser.ArcadeParticle(this.game); + //} /** * Create a new Emitter. * @@ -15888,13 +15711,9 @@ var Phaser; * @param size {number} Optional, size of this emitter. * @return {Emitter} The newly created emitter object. */ - GameObjectFactory.prototype.emitter = function (x, y, size) { - if (typeof x === "undefined") { x = 0; } - if (typeof y === "undefined") { y = 0; } - if (typeof size === "undefined") { size = 0; } - return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); - }; - + //public emitter(x: number = 0, y: number = 0, size: number = 0): Phaser.ArcadeEmitter { + // return this._world.group.add(new Phaser.ArcadeEmitter(this.game, x, y, size)); + //} /** * Create a new ScrollZone object with image key, position and size. * @@ -15993,10 +15812,9 @@ var Phaser; * @param emitter The Emitter to add to the Game World * @return {Phaser.Emitter} The Emitter object */ - GameObjectFactory.prototype.existingEmitter = function (emitter) { - return this._world.group.add(emitter); - }; - + //public existingEmitter(emitter: Phaser.ArcadeEmitter): Phaser.ArcadeEmitter { + // return this._world.group.add(emitter); + //} /** * Add an existing ScrollZone to the current world. * Note: This doesn't check or update the objects reference to Game. If that is wrong, all kinds of things will break. @@ -16033,372 +15851,467 @@ var Phaser; })(); Phaser.GameObjectFactory = GameObjectFactory; })(Phaser || (Phaser = {})); -/// -/** -* Phaser - ArcadeEmitter -* -* Emitter is a lightweight particle emitter. It can be used for one-time explosions or for -* continuous effects like rain and fire. All it really does is launch Particle objects out -* at set intervals, and fixes their positions and velocities accorindgly. -*/ var Phaser; (function (Phaser) { - var ArcadeEmitter = (function (_super) { - __extends(ArcadeEmitter, _super); - /** - * Creates a new Emitter object at a specific position. - * Does NOT automatically generate or attach particles! - * - * @param x {number} The X position of the emitter. - * @param y {number} The Y position of the emitter. - * @param [size] {number} Specifies a maximum capacity for this emitter. - */ - function ArcadeEmitter(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 Phaser.Vec2(-100, -100); - this.maxParticleSpeed = new Phaser.Vec2(100, 100); - this.minRotation = -360; - this.maxRotation = 360; - this.gravity = 0; - this.particleClass = null; - this.particleDrag = new Phaser.Vec2(); - this.frequency = 0.1; - this.lifespan = 3; - this.bounce = 0; - this._quantity = 0; - this._counter = 0; - this._explode = true; - this.on = false; - - this.exists = true; - this.active = true; - this.visible = true; - } - /** - * Clean up memory. - */ - ArcadeEmitter.prototype.destroy = function () { - this.minParticleSpeed = null; - this.maxParticleSpeed = null; - this.particleDrag = null; - this.particleClass = null; - this._point = null; - _super.prototype.destroy.call(this); - }; - - /** - * 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 Sprite objects, you can simply pass in a particle image or sprite sheet. - * @param quantity {number} The number of particles to generate when using the "create from image" option. - * @param multiple {boolean} 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 {number} 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 Emitter instance (nice for chaining stuff together, if you're into that). - */ - ArcadeEmitter.prototype.makeParticles = function (graphics, quantity, multiple, collide) { - if (typeof quantity === "undefined") { quantity = 50; } - if (typeof multiple === "undefined") { multiple = false; } - if (typeof collide === "undefined") { collide = 0; } - this.maxSize = quantity; - - var totalFrames = 1; - - /* - if(Multiple) - { - var sprite:Sprite = new Sprite(this.game); - sprite.loadGraphic(Graphics,true); - totalFrames = sprite.frames; - sprite.destroy(); - } + /// + (function (Particles) { + var Emitter = (function () { + /** + * You can use this emit particles. + * + * It will dispatch follow events: + * Proton.PARTICLE_CREATED + * Proton.PARTICLE_UPDATA + * Proton.PARTICLE_DEAD + * + * @class Proton.Emitter + * @constructor + * @param {Object} pObj the parameters object; + * for example {damping:0.01,bindEmitter:false} */ - var randomFrame; - var particle; - var i = 0; + function Emitter(pObj) { + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; + this.initializes = []; + this.particles = []; + this.behaviours = []; + this.emitTime = 0; + this.emitTotalTimes = -1; - while (i < quantity) { - if (this.particleClass == null) { - particle = new Phaser.ArcadeParticle(this.game); - } else { - particle = new this.particleClass(this.game); - } + /** + * The friction coefficient for all particle emit by This; + * @property damping + * @type {Number} + * @default 0.006 + */ + this.damping = .006; - if (multiple) { - /* - randomFrame = this.game.math.random()*totalFrames; - */ - } else { - if (graphics) { - particle.texture.loadImage(graphics); - } - } + /** + * If bindEmitter the particles can bind this emitter's property; + * @property bindEmitter + * @type {Boolean} + * @default true + */ + this.bindEmitter = true; - if (collide > 0) { - //particle.body.allowCollisions = Types.ANY; - particle.body.type = Phaser.Types.BODY_DYNAMIC; - particle.width *= collide; - particle.height *= collide; - } else { - //particle.body.allowCollisions = Types.NONE; - } + /** + * The number of particles per second emit (a [particle]/b [s]); + * @property rate + * @type {Proton.Rate} + * @default Proton.Rate(1, .1) + */ + this.rate = new Phaser.Particles.Initializers.Rate(1, .1); - particle.exists = false; - - // Center the origin for rotation assistance - //particle.transform.origin.setTo(particle.body.bounds.halfWidth, particle.body.bounds.halfHeight); - this.add(particle); - - i++; + //Emitter._super_.call(this, pObj); + /** + * The emitter's id; + * @property id + * @type {String} id + */ + this.id = 'emitter_' + Emitter.ID++; } + /** + * start emit particle + * @method emit + * @param {Number} emitTime begin emit time; + * @param {String} life the life of this emitter + */ + Emitter.prototype.emit = function (emitTime, life) { + this.emitTime = 0; + this.emitTotalTimes = Particles.ParticleUtils.initValue(emitTime, Infinity); - return this; - }; + if (life == true || life == 'life' || life == 'destroy') { + if (emitTime == 'once') + this.life = 1; +else + this.life = this.emitTotalTimes; + } else if (!isNaN(life)) { + this.life = life; + } - ArcadeEmitter.prototype.preUpdate = function () { - }; - ArcadeEmitter.prototype.postUpdate = function () { - }; + this.rate.init(); + }; - /** - * Called automatically by the game loop, decides when to launch particles and when to "die". - */ - ArcadeEmitter.prototype.update = function () { - if (this.on) { - if (this._explode) { - this.on = false; + /** + * stop emiting + * @method stopEmit + */ + Emitter.prototype.stopEmit = function () { + this.emitTotalTimes = -1; + this.emitTime = 0; + }; - var i = 0; - var l = this._quantity; + /** + * remove current all particles + * @method removeAllParticles + */ + Emitter.prototype.removeAllParticles = function () { + for (var i = 0; i < this.particles.length; i++) + this.particles[i].dead = true; + }; - if ((l <= 0) || (l > this.length)) { - l = this.length; - } + /** + * create single particle; + * + * can use emit({x:10},new Gravity(10),{'particleUpdate',fun}) or emit([{x:10},new Initialize],new Gravity(10),{'particleUpdate',fun}) + * @method removeAllParticles + */ + Emitter.prototype.createParticle = function (initialize, behaviour) { + if (typeof initialize === "undefined") { initialize = null; } + if (typeof behaviour === "undefined") { behaviour = null; } + var particle = Particles.ParticleManager.pool.get(); + this.setupParticle(particle, initialize, behaviour); - while (i < l) { - this.emitParticle(); - i++; - } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_CREATED, + // particle: particle + //})); + return particle; + }; - this._quantity = 0; + /** + * add initialize to this emitter + * @method addSelfInitialize + */ + Emitter.prototype.addSelfInitialize = function (pObj) { + if (pObj['init']) { + pObj.init(this); } else { - this._timer += this.game.time.elapsed; + //this.initAll(); + } + }; - while ((this.frequency > 0) && (this._timer > this.frequency) && this.on) { - this._timer -= this.frequency; - this.emitParticle(); + /** + * add the Initialize to particles; + * + * you can use initializes array:for example emitter.addInitialize(initialize1,initialize2,initialize3); + * @method addInitialize + * @param {Proton.Initialize} initialize like this new Proton.Radius(1, 12) + */ + Emitter.prototype.addInitialize = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.initializes.push(arguments[i]); + } + }; - if ((this._quantity > 0) && (++this._counter >= this._quantity)) { - this.on = false; - this._quantity = 0; + /** + * remove the Initialize + * @method removeInitialize + * @param {Proton.Initialize} initialize a initialize + */ + Emitter.prototype.removeInitialize = function (initializer) { + var index = this.initializes.indexOf(initializer); + if (index > -1) { + this.initializes.splice(index, 1); + } + }; + + /** + * remove all Initializes + * @method removeInitializers + */ + Emitter.prototype.removeInitializers = function () { + Particles.ParticleUtils.destroyArray(this.initializes); + }; + + /** + * add the Behaviour to particles; + * + * you can use Behaviours array:emitter.addBehaviour(Behaviour1,Behaviour2,Behaviour3); + * @method addBehaviour + * @param {Proton.Behaviour} behaviour like this new Proton.Color('random') + */ + Emitter.prototype.addBehaviour = function () { + var length = arguments.length, i; + for (i = 0; i < length; i++) { + this.behaviours.push(arguments[i]); + if (arguments[i].hasOwnProperty("parents")) + arguments[i].parents.push(this); + } + }; + + /** + * remove the Behaviour + * @method removeBehaviour + * @param {Proton.Behaviour} behaviour a behaviour + */ + Emitter.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) + this.behaviours.splice(index, 1); + }; + + /** + * remove all behaviours + * @method removeAllBehaviours + */ + Emitter.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + Emitter.prototype.integrate = function (time) { + var damping = 1 - this.damping; + Particles.ParticleManager.integrator.integrate(this, time, damping); + var length = this.particles.length, i; + for (i = 0; i < length; i++) { + var particle = this.particles[i]; + particle.update(time, i); + Particles.ParticleManager.integrator.integrate(particle, time, damping); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_UPDATE, + // particle: particle + //})); + } + }; + + Emitter.prototype.emitting = function (time) { + if (this.emitTotalTimes == 1) { + var length = this.rate.getValue(99999), i; + for (i = 0; i < length; i++) { + this.createParticle(); + } + + this.emitTotalTimes = 0; + } else if (!isNaN(this.emitTotalTimes)) { + this.emitTime += time; + if (this.emitTime < this.emitTotalTimes) { + var length = this.rate.getValue(time), i; + for (i = 0; i < length; i++) { + this.createParticle(); } } } - } + }; - _super.prototype.update.call(this); - }; + Emitter.prototype.update = function (time) { + this.age += time; + if (this.age >= this.life || this.dead) { + this.destroy(); + } - /** - * Call this function to turn off all the particles and the emitter. - */ - ArcadeEmitter.prototype.kill = function () { - this.on = false; - this.alive = false; - this.exists = false; - }; + this.emitting(time); + this.integrate(time); + var particle; + var length = this.particles.length, k; + for (k = length - 1; k >= 0; k--) { + particle = this.particles[k]; + if (particle.dead) { + Particles.ParticleManager.pool.set(particle); + this.particles.splice(k, 1); + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PARTICLE_DEAD, + // particle: particle + //})); + } + } + }; - /** - * Handy for bringing game objects "back to life". Just sets alive and exists back to true. - * In practice, this is most often called by Object.reset(). - */ - ArcadeEmitter.prototype.revive = function () { - this.alive = true; - this.exists = true; - }; + Emitter.prototype.setupParticle = function (particle, initialize, behaviour) { + var initializes = this.initializes; + var behaviours = this.behaviours; - /** - * Call this function to start emitting particles. - * - * @param explode {boolean} Whether the particles should all burst out at once. - * @param lifespan {number} How long each particle lives once emitted. 0 = forever. - * @param frequency {number} 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 {number} How many particles to launch. 0 = "all of the particles". - */ - ArcadeEmitter.prototype.start = 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(); + if (initialize) { + if (initialize instanceof Array) + initializes = initialize; +else + initializes = [initialize]; + } - this.visible = true; - this.on = true; + if (behaviour) { + if (behaviour instanceof Array) + behaviours = behaviour; +else + behaviours = [behaviour]; + } - this._explode = explode; - this.lifespan = lifespan; - this.frequency = frequency; - this._quantity += quantity; + //Proton.InitializeUtil.initialize(this, particle, initializes); + particle.addBehaviours(behaviours); + particle.parent = this; + this.particles.push(particle); + }; - this._counter = 0; - this._timer = 0; - }; + /** + * Destory this Emitter + * @method destory + */ + Emitter.prototype.destroy = function () { + this.dead = true; + this.emitTotalTimes = -1; + if (this.particles.length == 0) { + this.removeInitializers(); + this.removeAllBehaviours(); - /** - * This function can be used both internally and externally to emit the next particle. - */ - ArcadeEmitter.prototype.emitParticle = function () { - var particle = this.recycle(Phaser.ArcadeParticle); - - particle.lifespan = this.lifespan; - - //particle.body.bounce.setTo(this.bounce, this.bounce); - Phaser.SpriteUtils.reset(particle, this.x - (particle.width >> 1) + this.game.rnd.integer * this.width, this.y - (particle.height >> 1) + this.game.rnd.integer * this.height); - particle.visible = true; - - if (this.minParticleSpeed.x != this.maxParticleSpeed.x) { - particle.body.velocity.x = this.minParticleSpeed.x + this.game.rnd.integer * (this.maxParticleSpeed.x - this.minParticleSpeed.x); - } else { - particle.body.velocity.x = this.minParticleSpeed.x; - } - - if (this.minParticleSpeed.y != this.maxParticleSpeed.y) { - particle.body.velocity.y = this.minParticleSpeed.y + this.game.rnd.integer * (this.maxParticleSpeed.y - this.minParticleSpeed.y); - } else { - particle.body.velocity.y = this.minParticleSpeed.y; - } - - if (this.minRotation != this.maxRotation && this.minRotation !== 0 && this.maxRotation !== 0) { - particle.body.angularVelocity = this.minRotation + this.game.rnd.integer * (this.maxRotation - this.minRotation); - } else { - particle.body.angularVelocity = this.minRotation; - } - - if (particle.body.angularVelocity != 0) { - particle.rotation = this.game.rnd.integer * 360 - 180; - } - - //particle.body.drag.x = this.particleDrag.x; - //particle.body.drag.y = this.particleDrag.y; - particle.onEmit(); - }; - - /** - * A more compact way of setting the width and height of the emitter. - * - * @param width {number} The desired width of the emitter (particles are spawned randomly within these dimensions). - * @param height {number} The desired height of the emitter. - */ - ArcadeEmitter.prototype.setSize = function (width, height) { - this.width = width; - this.height = height; - }; - - /** - * A more compact way of setting the X velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setXSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.x = min; - this.maxParticleSpeed.x = max; - }; - - /** - * A more compact way of setting the Y velocity range of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setYSpeed = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minParticleSpeed.y = min; - this.maxParticleSpeed.y = max; - }; - - /** - * A more compact way of setting the angular velocity constraints of the emitter. - * - * @param Min {number} The minimum value for this range. - * @param Max {number} The maximum value for this range. - */ - ArcadeEmitter.prototype.setRotation = function (min, max) { - if (typeof min === "undefined") { min = 0; } - if (typeof max === "undefined") { max = 0; } - this.minRotation = min; - this.maxRotation = max; - }; - - /** - * Change the emitter's midpoint to match the midpoint of a Object. - * - * @param Object {object} The Object that you want to sync up with. - */ - ArcadeEmitter.prototype.at = function (object) { - //this.x = object.body.bounds.halfWidth - (this.width >> 1); - //this.y = object.body.bounds.halfHeight - (this.height >> 1); - }; - return ArcadeEmitter; - })(Phaser.Group); - Phaser.ArcadeEmitter = ArcadeEmitter; + if (this.parent) + this.parent.removeEmitter(this); + } + }; + Emitter.ID = 0; + return Emitter; + })(); + Particles.Emitter = Emitter; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); -/// -/** -* Phaser - ArcadeParticle -* -* This is a simple particle class that extends a Sprite to have a slightly more -* specialised behaviour. It is used exclusively by the Emitter class and can be extended as required. -*/ var Phaser; (function (Phaser) { - var ArcadeParticle = (function (_super) { - __extends(ArcadeParticle, _super); - /** - * Instantiate a new particle. Like Sprite, all meaningful creation - * happens during loadGraphic() or makeGraphic() or whatever. - */ - function ArcadeParticle(game) { - _super.call(this, game); - - this.body.type = Phaser.Types.BODY_DYNAMIC; - this.lifespan = 0; - } - /** - * The particle's main update logic. Basically it checks to see if it should be dead yet. - */ - ArcadeParticle.prototype.update = function () { - if (this.lifespan <= 0) { - return; + /// + (function (Particles) { + var Particle = (function () { + /** + * the Particle class + * + * @class Proton.Particle + * @constructor + * @param {Object} pObj the parameters object; + * for example {life:3,dead:false} + */ + function Particle() { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + /** + * The particle's id; + * @property id + * @type {String} id + */ + this.id = 'particle_' + Particle.ID++; + this.reset(true); } + Particle.prototype.getDirection = function () { + return Math.atan2(this.v.x, -this.v.y) * (180 / Math.PI); + }; - this.lifespan -= this.game.time.elapsed; + Particle.prototype.reset = function (init) { + this.life = Infinity; + this.age = 0; + this.energy = 1; + this.dead = false; + this.sleep = false; + this.target = null; + this.sprite = null; + this.parent = null; + this.mass = 1; + this.radius = 10; + this.alpha = 1; + this.scale = 1; + this.rotation = 0; + this.color = null; + this.easing = Phaser.Easing.Linear.None; + if (init) { + this.transform = {}; + this.p = new Phaser.Vec2(); + this.v = new Phaser.Vec2(); + this.a = new Phaser.Vec2(); + this.old = { + p: new Phaser.Vec2(), + v: new Phaser.Vec2(), + a: new Phaser.Vec2() + }; + this.behaviours = []; + } else { + Particles.ParticleUtils.destroyObject(this.transform); + this.p.setTo(0, 0); + this.v.setTo(0, 0); + this.a.setTo(0, 0); + this.old.p.setTo(0, 0); + this.old.v.setTo(0, 0); + this.old.a.setTo(0, 0); + this.removeAllBehaviours(); + } - if (this.lifespan <= 0) { - this.kill(); - } - }; + this.transform.rgb = { + r: 255, + g: 255, + b: 255 + }; + return this; + }; - /** - * Triggered whenever this object is launched by a Emitter. - * You can override this to add custom behavior like a sound or AI or something. - */ - ArcadeParticle.prototype.onEmit = function () { - }; - return ArcadeParticle; - })(Phaser.Sprite); - Phaser.ArcadeParticle = ArcadeParticle; + Particle.prototype.update = function (time, index) { + if (!this.sleep) { + this.age += time; + var length = this.behaviours.length, i; + for (i = 0; i < length; i++) { + if (this.behaviours[i]) + this.behaviours[i].applyBehaviour(this, time, index); + } + } + + if (this.age >= this.life) { + this.destroy(); + } else { + var scale = this.easing(this.age / this.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + Particle.prototype.addBehaviour = function (behaviour) { + this.behaviours.push(behaviour); + if (behaviour.hasOwnProperty('parents')) + behaviour.parents.push(this); + behaviour.initialize(this); + }; + + Particle.prototype.addBehaviours = function (behaviours) { + var length = behaviours.length, i; + for (i = 0; i < length; i++) { + this.addBehaviour(behaviours[i]); + } + }; + + Particle.prototype.removeBehaviour = function (behaviour) { + var index = this.behaviours.indexOf(behaviour); + if (index > -1) { + var behaviour = this.behaviours.splice(index, 1); + behaviour.parents = null; + } + }; + + Particle.prototype.removeAllBehaviours = function () { + Particles.ParticleUtils.destroyArray(this.behaviours); + }; + + /** + * Destory this particle + * @method destory + */ + Particle.prototype.destroy = function () { + this.removeAllBehaviours(); + this.energy = 0; + this.dead = true; + this.parent = null; + }; + Particle.ID = 0; + return Particle; + })(); + Particles.Particle = Particle; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; })(Phaser || (Phaser = {})); var Phaser; (function (Phaser) { @@ -16798,6 +16711,19 @@ var Phaser; return colors; }; + ColorUtils.hexToRGB = function (h) { + var hex16 = (h.charAt(0) == "#") ? h.substring(1, 7) : h; + var r = parseInt(hex16.substring(0, 2), 16); + var g = parseInt(hex16.substring(2, 4), 16); + var b = parseInt(hex16.substring(4, 6), 16); + + return { + r: r, + g: g, + b: b + }; + }; + ColorUtils.getComplementHarmony = /** * Returns a Complementary Color Harmony for the given color. *

A complementary hue is one directly opposite the color given on the color wheel

@@ -19002,6 +18928,2787 @@ var Phaser; })(Phaser.Renderer || (Phaser.Renderer = {})); var Renderer = Phaser.Renderer; })(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - PhysicsManager + */ + (function (Physics) { + var PhysicsManager = (function () { + function PhysicsManager(game) { + this.grav = 0.2; + this.drag = 1; + this.bounce = 0.3; + this.friction = 0.05; + this.min_f = 0; + this.max_f = 1; + this.min_b = 0; + this.max_b = 1; + this.min_g = 0; + this.max_g = 1; + this.xmin = 0; + this.xmax = 800; + this.ymin = 0; + this.ymax = 600; + this.objrad = 24; + this.tilerad = 24 * 2; + this.objspeed = 0.2; + this.maxspeed = 20; + this.game = game; + } + PhysicsManager.prototype.update = function () { + // Booyah! + }; + return PhysicsManager; + })(); + Physics.PhysicsManager = PhysicsManager; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - Body + * A binding between a Sprite and a physics object (AABB or Circle) + */ + (function (Physics) { + var Body = (function () { + function Body(sprite, type) { + this.angularVelocity = 0; + this.angularAcceleration = 0; + this.angularDrag = 0; + this.maxAngular = 10000; + this.mass = 1; + this._width = 0; + this._height = 0; + this.sprite = sprite; + this.game = sprite.game; + this.type = type; + + // Fixture properties + // Will extend into its own class at a later date - can move the fixture defs there and add shape support, but this will do for 1.0 release + this.bounds = new Phaser.Rectangle(); + + this._width = sprite.width; + this._height = sprite.height; + + // Body properties + //this.gravity = Vec2Utils.clone(ArcadePhysics.gravity); + //this.bounce = Vec2Utils.clone(ArcadePhysics.bounce); + this.velocity = new Phaser.Vec2(); + this.acceleration = new Phaser.Vec2(); + + //this.drag = Vec2Utils.clone(ArcadePhysics.drag); + this.maxVelocity = new Phaser.Vec2(10000, 10000); + + this.angularVelocity = 0; + this.angularAcceleration = 0; + this.angularDrag = 0; + + this.touching = Phaser.Types.NONE; + this.wasTouching = Phaser.Types.NONE; + this.allowCollisions = Phaser.Types.ANY; + + this.position = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); + this.oldPosition = new Phaser.Vec2(sprite.x + this.bounds.halfWidth, sprite.y + this.bounds.halfHeight); + this.offset = new Phaser.Vec2(); + } + Object.defineProperty(Body.prototype, "x", { + get: function () { + return this.sprite.x + this.offset.x; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "y", { + get: function () { + return this.sprite.y + this.offset.y; + }, + enumerable: true, + configurable: true + }); + + + + Object.defineProperty(Body.prototype, "width", { + get: function () { + return this._width * this.sprite.transform.scale.x; + }, + set: function (value) { + this._width = value; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "height", { + get: function () { + return this._height * this.sprite.transform.scale.y; + }, + set: function (value) { + this._height = value; + }, + enumerable: true, + configurable: true + }); + + Body.prototype.preUpdate = function () { + this.oldPosition.copyFrom(this.position); + + this.bounds.x = this.x; + this.bounds.y = this.y; + this.bounds.width = this.width; + this.bounds.height = this.height; + }; + + // Shall we do this? Or just update the values directly in the separate functions? But then the bounds will be out of sync - as long as + // the bounds are updated and used in calculations then we can do one final sprite movement here I guess? + Body.prototype.postUpdate = function () { + if (this.type !== Phaser.Types.BODY_DISABLED) { + //this.game.world.physics.updateMotion(this); + this.wasTouching = this.touching; + this.touching = Phaser.Types.NONE; + } + + this.position.setTo(this.x, this.y); + }; + + Object.defineProperty(Body.prototype, "hullWidth", { + get: function () { + if (this.deltaX > 0) { + return this.bounds.width + this.deltaX; + } else { + return this.bounds.width - this.deltaX; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullHeight", { + get: function () { + if (this.deltaY > 0) { + return this.bounds.height + this.deltaY; + } else { + return this.bounds.height - this.deltaY; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullX", { + get: function () { + if (this.position.x < this.oldPosition.x) { + return this.position.x; + } else { + return this.oldPosition.x; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "hullY", { + get: function () { + if (this.position.y < this.oldPosition.y) { + return this.position.y; + } else { + return this.oldPosition.y; + } + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaXAbs", { + get: function () { + return (this.deltaX > 0 ? this.deltaX : -this.deltaX); + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaYAbs", { + get: function () { + return (this.deltaY > 0 ? this.deltaY : -this.deltaY); + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaX", { + get: function () { + return this.position.x - this.oldPosition.x; + }, + enumerable: true, + configurable: true + }); + + Object.defineProperty(Body.prototype, "deltaY", { + get: function () { + return this.position.y - this.oldPosition.y; + }, + enumerable: true, + configurable: true + }); + return Body; + })(); + Physics.Body = Body; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - AABB + */ + (function (Physics) { + var AABB = (function () { + function AABB(game, x, y, xw, yw) { + this.type = 0; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + + this.aabbTileProjections = {}; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.AABBFull.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.AABBConcave.Collide; + this.aabbTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.AABBConvex.Collide; + } + AABB.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + //o = oldposition + var ox = o.x; + var oy = o.y; + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + AABB.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + AABB.prototype.CollideAABBVsTile = function (tile) { + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + this.xw) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + this.yw) - Math.abs(dy); + + if (0 < py) { + if (px < py) { + if (dx < 0) { + //project to the left + px *= -1; + py = 0; + } else { + //proj to right + py = 0; + } + } else { + if (dy < 0) { + //project up + px = 0; + py *= -1; + } else { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + } + } + }; + + AABB.prototype.CollideAABBVsWorldBounds = function () { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + AABB.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + }; + + AABB.prototype.ResolveBoxTile = function (x, y, box, t) { + if (0 < t.ID) { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } else { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + }; + AABB.COL_NONE = 0; + AABB.COL_AXIS = 1; + AABB.COL_OTHER = 2; + return AABB; + })(); + Physics.AABB = AABB; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - Circle + */ + (function (Physics) { + var Circle = (function () { + function Circle(game, x, y, radius) { + this.type = 1; + this.game = game; + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = new Phaser.Vec2(x, y); + this.radius = radius; + + this.circleTileProjections = {}; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_FULL] = Phaser.Physics.Projection.CircleFull.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_45DEG] = Phaser.Physics.Projection.Circle45Deg.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONCAVE] = Phaser.Physics.Projection.CircleConcave.Collide; + this.circleTileProjections[Phaser.Physics.TileMapCell.CTYPE_CONVEX] = Phaser.Physics.Projection.CircleConvex.Collide; + } + Circle.prototype.IntegrateVerlet = function () { + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px; + var py; + + var ox = o.x; + var oy = o.y; + + //o = oldposition + o.x = px = p.x; + o.y = py = p.y; + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + Circle.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.9; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + Circle.prototype.CollideCircleVsWorldBounds = function () { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } else if (this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } else if (this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + }; + + Circle.prototype.CollideCircleVsTile = function (tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + r) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + r) - Math.abs(dy); + + if (0 < py) { + //object may be colliding with tile + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) { + //circle is on left side of tile + this.oH = -1; + } else if (txw < dx) { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) { + //circle is on top side of tile + this.oV = -1; + } else if (tyw < dy) { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + } + } + }; + + Circle.prototype.ResolveCircleTile = function (x, y, oH, oV, obj, t) { + if (0 < t.ID) { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } else { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + }; + Circle.COL_NONE = 0; + Circle.COL_AXIS = 1; + Circle.COL_OTHER = 2; + return Circle; + })(); + Physics.Circle = Circle; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + /** + * Phaser - Physics - TileMapCell + */ + (function (Physics) { + var TileMapCell = (function () { + function TileMapCell(game, x, y, xw, yw) { + this.game = game; + this.ID = TileMapCell.TID_EMPTY; + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x, y); + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + TileMapCell.prototype.SetState = function (ID) { + if (ID == TileMapCell.TID_EMPTY) { + this.Clear(); + } else { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + }; + + TileMapCell.prototype.Clear = function () { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY; + this.UpdateType(); + //this.Draw(); + }; + + TileMapCell.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + }; + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + TileMapCell.prototype.UpdateType = function () { + if (0 < this.ID) { + if (this.ID < TileMapCell.CTYPE_45DEG) { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } else if (this.ID < TileMapCell.CTYPE_CONCAVE) { + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnn) { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnp) { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGpp) { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_CONVEX) { + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGs) { + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGb) { + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGs) { + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGb) { + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_HALF) { + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFu) { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFl) { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFr) { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + } else { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + }; + TileMapCell.TID_EMPTY = 0; + TileMapCell.TID_FULL = 1; + TileMapCell.TID_45DEGpn = 2; + TileMapCell.TID_45DEGnn = 3; + TileMapCell.TID_45DEGnp = 4; + TileMapCell.TID_45DEGpp = 5; + TileMapCell.TID_CONCAVEpn = 6; + TileMapCell.TID_CONCAVEnn = 7; + TileMapCell.TID_CONCAVEnp = 8; + TileMapCell.TID_CONCAVEpp = 9; + TileMapCell.TID_CONVEXpn = 10; + TileMapCell.TID_CONVEXnn = 11; + TileMapCell.TID_CONVEXnp = 12; + TileMapCell.TID_CONVEXpp = 13; + TileMapCell.TID_22DEGpnS = 14; + TileMapCell.TID_22DEGnnS = 15; + TileMapCell.TID_22DEGnpS = 16; + TileMapCell.TID_22DEGppS = 17; + TileMapCell.TID_22DEGpnB = 18; + TileMapCell.TID_22DEGnnB = 19; + TileMapCell.TID_22DEGnpB = 20; + TileMapCell.TID_22DEGppB = 21; + TileMapCell.TID_67DEGpnS = 22; + TileMapCell.TID_67DEGnnS = 23; + TileMapCell.TID_67DEGnpS = 24; + TileMapCell.TID_67DEGppS = 25; + TileMapCell.TID_67DEGpnB = 26; + TileMapCell.TID_67DEGnnB = 27; + TileMapCell.TID_67DEGnpB = 28; + TileMapCell.TID_67DEGppB = 29; + TileMapCell.TID_HALFd = 30; + TileMapCell.TID_HALFr = 31; + TileMapCell.TID_HALFu = 32; + TileMapCell.TID_HALFl = 33; + + TileMapCell.CTYPE_EMPTY = 0; + TileMapCell.CTYPE_FULL = 1; + TileMapCell.CTYPE_45DEG = 2; + TileMapCell.CTYPE_CONCAVE = 6; + TileMapCell.CTYPE_CONVEX = 10; + TileMapCell.CTYPE_22DEGs = 14; + TileMapCell.CTYPE_22DEGb = 18; + TileMapCell.CTYPE_67DEGs = 22; + TileMapCell.CTYPE_67DEGb = 26; + TileMapCell.CTYPE_HALF = 30; + return TileMapCell; + })(); + Physics.TileMapCell = TileMapCell; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBFull = (function () { + function AABBFull() { + } + AABBFull.Collide = function (x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.AABB.COL_AXIS; + }; + return AABBFull; + })(); + Projection.AABBFull = AABBFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConvex = (function () { + function AABBConvex() { + } + AABBConvex.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw)); + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw)); + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else if (0 < pen) { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConvex; + })(); + Projection.AABBConvex = AABBConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var AABBConcave = (function () { + function AABBConcave() { + } + AABBConcave.Collide = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw)); + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw)); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.AABB.COL_AXIS; + } else { + //project along corner->circle vector + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.AABB.COL_OTHER; + } + } + + return Phaser.Physics.AABB.COL_NONE; + }; + return AABBConcave; + })(); + Projection.AABBConcave = AABBConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleFull = (function () { + function CircleFull() { + } + CircleFull.Collide = function (x, y, oH, oV, obj, t) { + if (oH == 0) { + if (oV == 0) { + if (x < y) { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x; + + if (dx < 0) { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } else { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y; + + if (dy < 0) { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Phaser.Physics.Circle.COL_AXIS; + } + } + } else { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } + } else if (oV == 0) { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Phaser.Physics.Circle.COL_AXIS; + } else { + //diagonal collision + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleFull; + })(); + Projection.CircleFull = CircleFull; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConvex = (function () { + function CircleConvex() { + } + CircleConvex.Collide = function (x, y, oH, oV, obj, t) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConvex; + })(); + Projection.CircleConvex = CircleConvex; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var CircleConcave = (function () { + function CircleConcave() { + } + CircleConcave.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x; + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y; + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + return Phaser.Physics.Circle.COL_NONE; + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the vertical tip + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out vertically + dx = 0; + dy = oV; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the horizontal tip + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out horizontally + dx = oH; + dy = 0; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return CircleConcave; + })(); + Projection.CircleConcave = CircleConcave; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Physics) { + /// + /** + * Phaser - Physics - Projection + */ + (function (Projection) { + var Circle45Deg = (function () { + function Circle45Deg() { + } + Circle45Deg.Collide = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x; + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp; + sy *= -dp; + + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y + (oV * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Phaser.Physics.Circle.COL_OTHER; + } + } + } + + return Phaser.Physics.Circle.COL_NONE; + }; + return Circle45Deg; + })(); + Projection.Circle45Deg = Circle45Deg; + })(Physics.Projection || (Physics.Projection = {})); + var Projection = Physics.Projection; + })(Phaser.Physics || (Phaser.Physics = {})); + var Physics = Phaser.Physics; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleManager = (function () { + function ParticleManager(proParticleCount, integrationType) { + this.PARTICLE_CREATED = 'partilcleCreated'; + this.PARTICLE_UPDATE = 'partilcleUpdate'; + this.PARTICLE_SLEEP = 'particleSleep'; + this.PARTICLE_DEAD = 'partilcleDead'; + this.PROTON_UPDATE = 'protonUpdate'; + this.PROTON_UPDATE_AFTER = 'protonUpdateAfter'; + this.EMITTER_ADDED = 'emitterAdded'; + this.EMITTER_REMOVED = 'emitterRemoved'; + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + this.amendChangeTabsBug = true; + this.TextureBuffer = {}; + this.TextureCanvasBuffer = {}; + this.proParticleCount = Particles.ParticleUtils.initValue(proParticleCount, ParticleManager.POOL_MAX); + this.integrationType = Particles.ParticleUtils.initValue(integrationType, ParticleManager.EULER); + this.emitters = []; + this.renderers = []; + this.time = 0; + this.oldTime = 0; + + ParticleManager.pool = new Phaser.Particles.ParticlePool(proParticleCount); + ParticleManager.integrator = new Phaser.Particles.NumericalIntegration(this.integrationType); + } + /** + * add a type of Renderer + * + * @method addRender + * @param {Renderer} render + */ + ParticleManager.prototype.addRender = function (render) { + render.proton = this; + this.renderers.push(render.proton); + }; + + /** + * add the Emitter + * + * @method addEmitter + * @param {Emitter} emitter + */ + ParticleManager.prototype.addEmitter = function (emitter) { + this.emitters.push(emitter); + emitter.parent = this; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_ADDED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.removeEmitter = function (emitter) { + var index = this.emitters.indexOf(emitter); + this.emitters.splice(index, 1); + emitter.parent = null; + //this.dispatchEvent(new Proton.Event({ + // type: Proton.EMITTER_REMOVED, + // emitter: emitter + //})); + }; + + ParticleManager.prototype.update = function () { + if (!this.oldTime) + this.oldTime = new Date().getTime(); + + var time = new Date().getTime(); + this.elapsed = (time - this.oldTime) / 1000; + + //if (ParticleUtils.amendChangeTabsBug) + // this.amendChangeTabsBugHandler(); + this.oldTime = time; + if (this.elapsed > 0) { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].update(this.elapsed); + } + } + //this.dispatchEvent(new Proton.Event({ + // type: Proton.PROTON_UPDATE_AFTER + //})); + }; + + ParticleManager.prototype.amendChangeTabsBugHandler = function () { + if (this.elapsed > .5) { + this.oldTime = new Date().getTime(); + this.elapsed = 0; + } + }; + + ParticleManager.prototype.getParticleNumber = function () { + var total = 0; + for (var i = 0; i < this.emitters.length; i++) { + total += this.emitters[i].particles.length; + } + return total; + }; + + ParticleManager.prototype.destroy = function () { + for (var i = 0; i < this.emitters.length; i++) { + this.emitters[i].destory(); + delete this.emitters[i]; + } + + this.emitters = []; + this.time = 0; + this.oldTime = 0; + ParticleManager.pool.release(); + }; + ParticleManager.POOL_MAX = 1000; + ParticleManager.TIME_STEP = 60; + + ParticleManager.MEASURE = 100; + ParticleManager.EULER = 'euler'; + ParticleManager.RK2 = 'runge-kutta2'; + ParticleManager.RK4 = 'runge-kutta4'; + ParticleManager.VERLET = 'verlet'; + return ParticleManager; + })(); + Particles.ParticleManager = ParticleManager; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticlePool = (function () { + function ParticlePool(num, releaseTime) { + if (typeof releaseTime === "undefined") { releaseTime = 0; } + this.poolList = []; + this.timeoutID = 0; + this.proParticleCount = Particles.ParticleUtils.initValue(num, 0); + this.releaseTime = Particles.ParticleUtils.initValue(releaseTime, -1); + this.poolList = []; + this.timeoutID = 0; + + for (var i = 0; i < this.proParticleCount; i++) { + this.add(); + } + + if (this.releaseTime > 0) { + // TODO - Hook to game clock so Pause works + this.timeoutID = setTimeout(this.release, this.releaseTime / 1000); + } + } + ParticlePool.prototype.create = function (newTypeParticleClass) { + if (typeof newTypeParticleClass === "undefined") { newTypeParticleClass = null; } + if (newTypeParticleClass) { + return new newTypeParticleClass(); + } else { + return new Phaser.Particles.Particle(); + } + }; + + ParticlePool.prototype.getCount = function () { + return this.poolList.length; + }; + + ParticlePool.prototype.add = function () { + return this.poolList.push(this.create()); + }; + + ParticlePool.prototype.get = function () { + if (this.poolList.length === 0) { + return this.create(); + } else { + return this.poolList.pop().reset(); + } + }; + + ParticlePool.prototype.set = function (particle) { + if (this.poolList.length < Particles.ParticleManager.POOL_MAX) { + return this.poolList.push(particle); + } + }; + + ParticlePool.prototype.release = function () { + for (var i = 0; i < this.poolList.length; i++) { + if (this.poolList[i]['destroy']) { + this.poolList[i].destroy(); + } + + delete this.poolList[i]; + } + + this.poolList = []; + }; + return ParticlePool; + })(); + Particles.ParticlePool = ParticlePool; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var ParticleUtils = (function () { + function ParticleUtils() { + } + ParticleUtils.initValue = function (value, defaults) { + var value = (value != null && value != undefined) ? value : defaults; + return value; + }; + + ParticleUtils.isArray = function (value) { + return typeof value === 'object' && value.hasOwnProperty('length'); + }; + + ParticleUtils.destroyArray = function (array) { + array.length = 0; + }; + + ParticleUtils.destroyObject = function (obj) { + for (var o in obj) { + delete obj[o]; + } + }; + + ParticleUtils.setSpanValue = function (a, b, c) { + if (typeof b === "undefined") { b = null; } + if (typeof c === "undefined") { c = null; } + if (a instanceof Phaser.Particles.Span) { + return a; + } else { + if (!b) { + return new Phaser.Particles.Span(a); + } else { + if (!c) { + return new Phaser.Particles.Span(a, b); + } else { + return new Phaser.Particles.Span(a, b, c); + } + } + } + }; + + ParticleUtils.getSpanValue = function (pan) { + if (pan instanceof Phaser.Particles.Span) { + return pan.getValue(); + } else { + return pan; + } + }; + + ParticleUtils.randomAToB = function (a, b, INT) { + if (typeof INT === "undefined") { INT = null; } + if (!INT) { + return a + Math.random() * (b - a); + } else { + return Math.floor(Math.random() * (b - a)) + a; + } + }; + + ParticleUtils.randomFloating = function (center, f, INT) { + return ParticleUtils.randomAToB(center - f, center + f, INT); + }; + + ParticleUtils.randomZone = function (display) { + }; + + ParticleUtils.degreeTransform = function (a) { + return a * Math.PI / 180; + }; + + ParticleUtils.randomColor = //static toColor16 getRGB(num) { + // return "#" + num.toString(16); + //} + function () { + return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6); + }; + + ParticleUtils.setEasingByName = function (name) { + switch (name) { + case 'easeLinear': + return Phaser.Easing.Linear.None; + break; + + case 'easeInQuad': + return Phaser.Easing.Quadratic.In; + break; + + case 'easeOutQuad': + return Phaser.Easing.Quadratic.Out; + break; + + case 'easeInOutQuad': + return Phaser.Easing.Quadratic.InOut; + break; + + case 'easeInCubic': + return Phaser.Easing.Cubic.In; + break; + + case 'easeOutCubic': + return Phaser.Easing.Cubic.Out; + break; + + case 'easeInOutCubic': + return Phaser.Easing.Cubic.InOut; + break; + + case 'easeInQuart': + return Phaser.Easing.Quartic.In; + break; + + case 'easeOutQuart': + return Phaser.Easing.Quartic.Out; + break; + + case 'easeInOutQuart': + return Phaser.Easing.Quartic.InOut; + break; + + case 'easeInSine': + return Phaser.Easing.Sinusoidal.In; + break; + + case 'easeOutSine': + return Phaser.Easing.Sinusoidal.Out; + break; + + case 'easeInOutSine': + return Phaser.Easing.Sinusoidal.InOut; + break; + + case 'easeInExpo': + return Phaser.Easing.Exponential.In; + break; + + case 'easeOutExpo': + return Phaser.Easing.Exponential.Out; + break; + + case 'easeInOutExpo': + return Phaser.Easing.Exponential.InOut; + break; + + case 'easeInCirc': + return Phaser.Easing.Circular.In; + break; + + case 'easeOutCirc': + return Phaser.Easing.Circular.Out; + break; + + case 'easeInOutCirc': + return Phaser.Easing.Circular.InOut; + break; + + case 'easeInBack': + return Phaser.Easing.Back.In; + break; + + case 'easeOutBack': + return Phaser.Easing.Back.Out; + break; + + case 'easeInOutBack': + return Phaser.Easing.Back.InOut; + break; + + default: + return Phaser.Easing.Linear.None; + break; + } + }; + return ParticleUtils; + })(); + Particles.ParticleUtils = ParticleUtils; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Polar2D = (function () { + function Polar2D(r, tha) { + this.r = Math.abs(r) || 0; + this.tha = tha || 0; + } + Polar2D.prototype.set = function (r, tha) { + this.r = r; + this.tha = tha; + return this; + }; + + Polar2D.prototype.setR = function (r) { + this.r = r; + return this; + }; + + Polar2D.prototype.setTha = function (tha) { + this.tha = tha; + return this; + }; + + Polar2D.prototype.copy = function (p) { + this.r = p.r; + this.tha = p.tha; + return this; + }; + + Polar2D.prototype.toVector = function () { + return new Phaser.Vec2(this.getX(), this.getY()); + }; + + Polar2D.prototype.getX = function () { + return this.r * Math.sin(this.tha); + }; + + Polar2D.prototype.getY = function () { + return -this.r * Math.cos(this.tha); + }; + + Polar2D.prototype.normalize = function () { + this.r = 1; + return this; + }; + + Polar2D.prototype.equals = function (v) { + return ((v.r === this.r) && (v.tha === this.tha)); + }; + + Polar2D.prototype.toArray = function () { + return [this.r, this.tha]; + }; + + Polar2D.prototype.clear = function () { + this.r = 0.0; + this.tha = 0.0; + return this; + }; + + Polar2D.prototype.clone = function () { + return new Polar2D(this.r, this.tha); + }; + return Polar2D; + })(); + Particles.Polar2D = Polar2D; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var Span = (function () { + function Span(a, b, center) { + if (typeof b === "undefined") { b = null; } + if (typeof center === "undefined") { center = null; } + this.isArray = false; + + if (Particles.ParticleUtils.isArray(a)) { + this.isArray = true; + this.a = a; + } else { + this.a = Particles.ParticleUtils.initValue(a, 1); + this.b = Particles.ParticleUtils.initValue(b, this.a); + this.center = Particles.ParticleUtils.initValue(center, false); + } + } + Span.prototype.getValue = function (INT) { + if (typeof INT === "undefined") { INT = null; } + if (this.isArray) { + return this.a[Math.floor(this.a.length * Math.random())]; + } else { + if (!this.center) { + return Particles.ParticleUtils.randomAToB(this.a, this.b, INT); + } else { + return Particles.ParticleUtils.randomFloating(this.a, this.b, INT); + } + } + }; + + Span.getSpan = function (a, b, center) { + return new Span(a, b, center); + }; + return Span; + })(); + Particles.Span = Span; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + /// + (function (Particles) { + var NumericalIntegration = (function () { + function NumericalIntegration(type) { + this.type = Particles.ParticleUtils.initValue(type, Particles.ParticleManager.EULER); + } + NumericalIntegration.prototype.integrate = function (particles, time, damping) { + this.eulerIntegrate(particles, time, damping); + }; + + NumericalIntegration.prototype.eulerIntegrate = function (particle, time, damping) { + if (!particle.sleep) { + particle.old.p.copy(particle.p); + particle.old.v.copy(particle.v); + particle.a.multiplyScalar(1 / particle.mass); + particle.v.add(particle.a.multiplyScalar(time)); + particle.p.add(particle.old.v.multiplyScalar(time)); + if (damping) + particle.v.multiplyScalar(damping); + particle.a.clear(); + } + }; + return NumericalIntegration; + })(); + Particles.NumericalIntegration = NumericalIntegration; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var Behaviour = (function () { + function Behaviour(life, easing) { + /** + * The behaviour's id; + * @property id + * @type {String} id + */ + this.id = 'Behaviour_' + Behaviour.ID++; + this.life = Particles.ParticleUtils.initValue(life, Infinity); + + /** + * The behaviour's decaying trend, for example Proton.easeOutQuart; + * @property easing + * @type {String} + * @default Proton.easeLinear + */ + this.easing = Particles.ParticleUtils.setEasingByName(easing); + this.age = 0; + this.energy = 1; + + /** + * The behaviour is Dead; + * @property dead + * @type {Boolean} + */ + this.dead = false; + + /** + * The behaviour's parents array; + * @property parents + * @type {Array} + */ + this.parents = []; + + /** + * The behaviour name; + * @property name + * @type {string} + */ + this.name = 'Behaviour'; + } + /** + * Reset this behaviour's parameters + * + * @method reset + * @param {Number} this behaviour's life + * @param {String} this behaviour's easing + */ + //reset (life, easing) { + // this.life = ParticleUtils.initValue(life, Infinity); + // //this.easing = ParticleUtils.initValue(easing, Proton.ease.setEasingByName(Proton.easeLinear)); + //} + /** + * Normalize a force by 1:100; + * + * @method normalizeForce + * @param {Proton.Vector2D} force + */ + Behaviour.prototype.normalizeForce = function (force) { + return force.multiplyScalar(Particles.ParticleManager.MEASURE); + }; + + /** + * Normalize a value by 1:100; + * + * @method normalizeValue + * @param {Number} value + */ + Behaviour.prototype.normalizeValue = function (value) { + return value * Particles.ParticleManager.MEASURE; + }; + + /** + * Initialize the behaviour's parameters for all particles + * + * @method initialize + * @param {Proton.Particle} particle + */ + Behaviour.prototype.initialize = function (particle) { + }; + + /** + * Apply this behaviour for all particles every time + * + * @method applyBehaviour + * @param {Proton.Particle} particle + * @param {Number} the integrate time 1/ms + * @param {Int} the particle index + */ + Behaviour.prototype.applyBehaviour = function (particle, time, index) { + this.age += time; + + if (this.age >= this.life || this.dead) { + this.energy = 0; + this.dead = true; + this.destroy(); + } else { + var scale = this.easing(particle.age / particle.life); + this.energy = Math.max(1 - scale, 0); + } + }; + + /** + * Destory this behaviour + * @method destory + */ + Behaviour.prototype.destroy = function () { + var index; + var length = this.parents.length, i; + + for (i = 0; i < length; i++) { + this.parents[i].removeBehaviour(this); + } + + this.parents = []; + }; + return Behaviour; + })(); + Behaviours.Behaviour = Behaviour; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Behaviours) { + var RandomDrift = (function (_super) { + __extends(RandomDrift, _super); + function RandomDrift(driftX, driftY, delay, life, easing) { + _super.call(this, life, easing); + this.reset(driftX, driftY, delay); + this.time = 0; + this.name = "RandomDrift"; + } + RandomDrift.prototype.reset = function (driftX, driftY, delay, life, easing) { + if (typeof life === "undefined") { life = null; } + if (typeof easing === "undefined") { easing = null; } + this.panFoce = new Phaser.Vec2(driftX, driftY); + this.panFoce = this.normalizeForce(this.panFoce); + this.delay = delay; + + if (life) { + this.life = Particles.ParticleUtils.initValue(life, Infinity); + this.easing = Particles.ParticleUtils.initValue(easing, Phaser.Easing.Linear.None); + } + }; + + RandomDrift.prototype.applyBehaviour = function (particle, time, index) { + _super.prototype.applyBehaviour.call(this, particle, time, index); + + this.time += time; + + if (this.time >= this.delay) { + particle.a.addXY(Particles.ParticleUtils.randomAToB(-this.panFoce.x, this.panFoce.x), Particles.ParticleUtils.randomAToB(-this.panFoce.y, this.panFoce.y)); + this.time = 0; + } + }; + return RandomDrift; + })(Behaviours.Behaviour); + Behaviours.RandomDrift = RandomDrift; + })(Particles.Behaviours || (Particles.Behaviours = {})); + var Behaviours = Particles.Behaviours; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Initialize = (function () { + function Initialize() { + } + Initialize.prototype.initialize = function (target) { + }; + + Initialize.prototype.reset = function (a, b, c) { + }; + + Initialize.prototype.init = function (emitter, particle) { + if (typeof particle === "undefined") { particle = null; } + if (particle) { + this.initialize(particle); + } else { + this.initialize(emitter); + } + }; + return Initialize; + })(); + Initializers.Initialize = Initialize; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Life = (function (_super) { + __extends(Life, _super); + function Life(a, b, c) { + _super.call(this); + + this.lifePan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Life.prototype.initialize = function (target) { + if (this.lifePan.a == Infinity) { + target.life = Infinity; + } else { + target.life = this.lifePan.getValue(); + } + }; + return Life; + })(Initializers.Initialize); + Initializers.Life = Life; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Mass = (function (_super) { + __extends(Mass, _super); + function Mass(a, b, c) { + _super.call(this); + this.massPan = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Mass.prototype.initialize = function (target) { + target.mass = this.massPan.getValue(); + }; + return Mass; + })(Initializers.Initialize); + Initializers.Mass = Mass; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Position = (function (_super) { + __extends(Position, _super); + function Position(zone) { + _super.call(this); + + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + } + Position.prototype.reset = function (zone) { + if (zone != null && zone != undefined) { + this.zone = zone; + } else { + this.zone = new Phaser.Particles.Zones.PointZone(); + } + }; + + Position.prototype.initialize = function (target) { + this.zone.getPosition(); + + target.p.x = this.zone.vector.x; + target.p.y = this.zone.vector.y; + }; + return Position; + })(Initializers.Initialize); + Initializers.Position = Position; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Rate = (function (_super) { + __extends(Rate, _super); + function Rate(numpan, timepan) { + _super.call(this); + + numpan = Particles.ParticleUtils.initValue(numpan, 1); + timepan = Particles.ParticleUtils.initValue(timepan, 1); + this.numPan = new Phaser.Particles.Span(numpan); + this.timePan = new Phaser.Particles.Span(timepan); + this.startTime = 0; + this.nextTime = 0; + this.init(); + } + Rate.prototype.init = function () { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + }; + + Rate.prototype.getValue = function (time) { + this.startTime += time; + + if (this.startTime >= this.nextTime) { + this.startTime = 0; + this.nextTime = this.timePan.getValue(); + + if (this.numPan.b == 1) { + if (this.numPan.getValue(false) > 0.5) { + return 1; + } else { + return 0; + } + } else { + return this.numPan.getValue(true); + } + } + + return 0; + }; + return Rate; + })(Initializers.Initialize); + Initializers.Rate = Rate; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Velocity = (function (_super) { + __extends(Velocity, _super); + function Velocity(rpan, thapan, type) { + _super.call(this); + + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + } + Velocity.prototype.reset = function (rpan, thapan, type) { + this.rPan = Particles.ParticleUtils.setSpanValue(rpan); + this.thaPan = Particles.ParticleUtils.setSpanValue(thapan); + this.type = Particles.ParticleUtils.initValue(type, 'vector'); + }; + + Velocity.prototype.normalizeVelocity = function (vr) { + return vr * Particles.ParticleManager.MEASURE; + }; + + Velocity.prototype.initialize = function (target) { + if (this.type == 'p' || this.type == 'P' || this.type == 'polar') { + var polar2d = new Particles.Polar2D(this.normalizeVelocity(this.rPan.getValue()), this.thaPan.getValue() * Math.PI / 180); + target.v.x = polar2d.getX(); + target.v.y = polar2d.getY(); + } else { + target.v.x = this.normalizeVelocity(this.rPan.getValue()); + target.v.y = this.normalizeVelocity(this.thaPan.getValue()); + } + }; + return Velocity; + })(Initializers.Initialize); + Initializers.Velocity = Velocity; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var Zone = (function () { + function Zone() { + this.vector = new Phaser.Vec2(); + this.random = 0; + this.crossType = "dead"; + this.alert = true; + } + return Zone; + })(); + Zones.Zone = Zone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Zones) { + var PointZone = (function (_super) { + __extends(PointZone, _super); + function PointZone(x, y) { + if (typeof x === "undefined") { x = 0; } + if (typeof y === "undefined") { y = 0; } + _super.call(this); + this.x = x; + this.y = y; + } + PointZone.prototype.getPosition = function () { + return this.vector.setTo(this.x, this.y); + }; + + PointZone.prototype.crossing = function (particle) { + if (this.alert) { + alert('Sorry PointZone does not support crossing method'); + this.alert = false; + } + }; + return PointZone; + })(Zones.Zone); + Zones.PointZone = PointZone; + })(Particles.Zones || (Particles.Zones = {})); + var Zones = Particles.Zones; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); /// /** * World @@ -20132,3 +22839,31 @@ var Phaser; })(); Phaser.CanvasUtils = CanvasUtils; })(Phaser || (Phaser = {})); +var Phaser; +(function (Phaser) { + (function (Particles) { + /// + (function (Initializers) { + var Radius = (function (_super) { + __extends(Radius, _super); + function Radius(a, b, c) { + _super.call(this); + + this.radius = Particles.ParticleUtils.setSpanValue(a, b, c); + } + Radius.prototype.reset = function (a, b, c) { + this.radius = Particles.ParticleUtils.setSpanValue(a, b, c); + }; + + Radius.prototype.initialize = function (particle) { + particle.radius = this.radius.getValue(); + particle.transform.oldRadius = particle.radius; + }; + return Radius; + })(Initializers.Initialize); + Initializers.Radius = Radius; + })(Particles.Initializers || (Particles.Initializers = {})); + var Initializers = Particles.Initializers; + })(Phaser.Particles || (Phaser.Particles = {})); + var Particles = Phaser.Particles; +})(Phaser || (Phaser = {})); diff --git a/Tests/physics/aabb 1.js b/todo/phaser clean up/aabb 1.js similarity index 100% rename from Tests/physics/aabb 1.js rename to todo/phaser clean up/aabb 1.js diff --git a/Tests/physics/aabb 1.ts b/todo/phaser clean up/aabb 1.ts similarity index 100% rename from Tests/physics/aabb 1.ts rename to todo/phaser clean up/aabb 1.ts diff --git a/Tests/physics/aabb vs aabb 1.js b/todo/phaser clean up/aabb vs aabb 1.js similarity index 100% rename from Tests/physics/aabb vs aabb 1.js rename to todo/phaser clean up/aabb vs aabb 1.js diff --git a/Tests/physics/aabb vs aabb 1.ts b/todo/phaser clean up/aabb vs aabb 1.ts similarity index 100% rename from Tests/physics/aabb vs aabb 1.ts rename to todo/phaser clean up/aabb vs aabb 1.ts diff --git a/Tests/physics/body1.js b/todo/phaser clean up/body1.js similarity index 100% rename from Tests/physics/body1.js rename to todo/phaser clean up/body1.js diff --git a/Tests/physics/body1.ts b/todo/phaser clean up/body1.ts similarity index 100% rename from Tests/physics/body1.ts rename to todo/phaser clean up/body1.ts diff --git a/Tests/physics/obb vs obb.js b/todo/phaser clean up/obb vs obb.js similarity index 100% rename from Tests/physics/obb vs obb.js rename to todo/phaser clean up/obb vs obb.js diff --git a/Tests/physics/obb vs obb.ts b/todo/phaser clean up/obb vs obb.ts similarity index 100% rename from Tests/physics/obb vs obb.ts rename to todo/phaser clean up/obb vs obb.ts diff --git a/todo/phaser clean up/temp1.js b/todo/phaser clean up/temp1.js new file mode 100644 index 000000000..9b6e962b9 --- /dev/null +++ b/todo/phaser clean up/temp1.js @@ -0,0 +1,113 @@ +/// +var Physics = (function () { + function Physics() { + this.max_bodies = 512; + this.max_vertices = 1024; + this.max_edges = 1024; + this.max_body_vertices = 64; + this.max_body_edges = 64; + this.vertices = []; + this.edges = []; + this.bodies = []; + } + Physics.prototype.updateForces = // Sets the force on each vertex to the gravity force. You could of course apply other forces like magnetism etc. + function () { + for(var i = 0; i < this.vertexCount; i++) { + this.vertices[i].acceleration = this.gravity; + } + }; + Physics.prototype.updateVerlet = // Updates the vertex position + function () { + for(var i = 0; i < this.vertexCount; i++) { + var v = this.vertices[i]; + var temp = v.position; + //v.position.mutableAdd( + //v.position += v.position - v.oldPosition + v.acceleration * this.timestep * this.timestep; + } + }; + Physics.prototype.updateEdges = function () { + }; + Physics.prototype.iterateCollisions = function () { + }; + Physics.prototype.detectCollision = function (body1, body2) { + }; + Physics.prototype.processCollision = function () { + }; + Physics.prototype.intervalDistance = function (minA, maxA, minB, maxB) { + }; + Physics.prototype.bodiesOverlap = function (body1, body2) { + }; + Physics.prototype.update = // CollisionInfo + // depth, normal, edge, vertex + function () { + }; + Physics.prototype.render = function () { + }; + Physics.prototype.addBody = function (body) { + this.bodies.push(body); + this.bodyCount = this.bodies.length; + }; + Physics.prototype.addEdge = function (edge) { + this.edges.push(edge); + this.edgeCount = this.edges.length; + }; + Physics.prototype.addVertex = function (vertex) { + this.vertices.push(vertex); + this.vertexCount = this.vertices.length; + }; + Physics.prototype.findVertex = function (x, y) { + }; + return Physics; +})(); +var PhysicsBody = (function () { + function PhysicsBody() { + this.vertices = []; + this.edges = []; + } + PhysicsBody.prototype.addEdge = function (edge) { + }; + PhysicsBody.prototype.addVertex = function (vertex) { + }; + PhysicsBody.prototype.projectToAxis = function (axis, min, max) { + }; + PhysicsBody.prototype.calculateCenter = function () { + }; + PhysicsBody.prototype.createBox = function (x, y, width, height) { + }; + return PhysicsBody; +})(); +var Vertex = (function () { + function Vertex(body, posX, posY) { + } + return Vertex; +})(); +var Edge = (function () { + function Edge(body, pV1, pV2, pBoundary) { + } + return Edge; +})(); +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + } + function create() { + var p = new Physics(); + //p.max_bodies + } + function update() { + } + function render() { + //myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + //myGame.stage.context.beginPath(); + //myGame.stage.context.moveTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + //for (var i = 1; i < poly1.points.length; i++) + //{ + // myGame.stage.context.lineTo(poly1.points[i].x + poly1.pos.x, poly1.points[i].y + poly1.pos.y); + //} + //myGame.stage.context.lineTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + //myGame.stage.context.stroke(); + //myGame.stage.context.closePath(); + } +})(); diff --git a/todo/phaser clean up/temp1.ts b/todo/phaser clean up/temp1.ts new file mode 100644 index 000000000..bd379200b --- /dev/null +++ b/todo/phaser clean up/temp1.ts @@ -0,0 +1,200 @@ +/// + +class Physics { + + constructor() { + + this.vertices = []; + this.edges = []; + this.bodies = []; + + } + + public gravity: Phaser.Vector2; + + public max_bodies: number = 512; + public max_vertices: number = 1024; + public max_edges: number = 1024; + public max_body_vertices: number = 64; + public max_body_edges: number = 64; + + public bodyCount: number; + public vertexCount: number; + public edgeCount: number; + public timestep: number; + public iterations; + + public vertices: Vertex[]; + public edges: Edge[]; + public bodies: PhysicsBody[]; + + // Sets the force on each vertex to the gravity force. You could of course apply other forces like magnetism etc. + public updateForces() { + + for (var i:number = 0; i < this.vertexCount; i++) + { + this.vertices[i].acceleration = this.gravity; + } + } + + // Updates the vertex position + public updateVerlet() { + + for (var i:number = 0; i < this.vertexCount; i++) + { + var v:Vertex = this.vertices[i]; + var temp: Phaser.Vector2 = v.position; + //v.position.mutableAdd( + //v.position += v.position - v.oldPosition + v.acceleration * this.timestep * this.timestep; + } + + } + + public updateEdges() { + } + + public iterateCollisions() { + } + + public detectCollision(body1, body2) { + } + + public processCollision() { + } + + public intervalDistance(minA, maxA, minB, maxB) { + } + + public bodiesOverlap(body1, body2) { + } + + // CollisionInfo + // depth, normal, edge, vertex + + public update() { + } + + public render() { + } + + public addBody(body:PhysicsBody) { + this.bodies.push(body); + this.bodyCount = this.bodies.length; + } + + public addEdge(edge:Edge) { + this.edges.push(edge); + this.edgeCount = this.edges.length; + } + + public addVertex(vertex:Vertex) { + this.vertices.push(vertex); + this.vertexCount = this.vertices.length; + } + + public findVertex(x, y) { + } + +} + +class PhysicsBody { + + constructor() { + } + + center: Phaser.Vector2; + minX; + minY; + maxX; + maxY; + vertextCount; + edgeCount; + + vertices = []; + edges = []; + + public addEdge(edge) { + } + + public addVertex(vertex) { + + } + + public projectToAxis(axis, min, max) { + } + + public calculateCenter() { + } + + public createBox(x, y, width, height) { + } + +} + +class Vertex { + + constructor(body, posX, posY) { + } + + position: Phaser.Vector2; + oldPosition: Phaser.Vector2; + acceleration: Phaser.Vector2; + parent: PhysicsBody; +} + +class Edge { + + constructor(body, pV1, pV2, pBoundary) { + } + + v1: Vertex; + v2: Vertex; + length; + boundary; + parent: PhysicsBody; + +} + +(function () { + + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + myGame.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + myGame.loader.load(); + + } + + function create() { + + var p = new Physics(); + //p.max_bodies + + } + + function update() { + + + } + + function render() { + + //myGame.stage.context.strokeStyle = 'rgb(0,255,0)'; + //myGame.stage.context.beginPath(); + //myGame.stage.context.moveTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + + //for (var i = 1; i < poly1.points.length; i++) + //{ + // myGame.stage.context.lineTo(poly1.points[i].x + poly1.pos.x, poly1.points[i].y + poly1.pos.y); + //} + + //myGame.stage.context.lineTo(poly1.points[0].x + poly1.pos.x, poly1.points[0].y + poly1.pos.y); + + //myGame.stage.context.stroke(); + //myGame.stage.context.closePath(); + + } + +})(); diff --git a/todo/phaser clean up/temp2.js b/todo/phaser clean up/temp2.js new file mode 100644 index 000000000..b7528e2d9 --- /dev/null +++ b/todo/phaser clean up/temp2.js @@ -0,0 +1,1604 @@ +/// +var NPhysics = (function () { + function NPhysics() { + this.grav = 0.2; + this.drag = 1; + this.bounce = 0.3; + this.friction = 0.05; + this.min_f = 0; + this.max_f = 1; + this.min_b = 0; + this.max_b = 1; + this.min_g = 0; + this.max_g = 1; + this.xmin = 0; + this.xmax = 800; + this.ymin = 0; + this.ymax = 600; + this.objrad = 24; + this.tilerad = 24 * 2; + this.objspeed = 0.2; + this.maxspeed = 20; + } + NPhysics.prototype.update = function () { + // demoObj.Verlet(); + // demoObj.CollideVsWorldBounds(); + }; + return NPhysics; +})(); + +var AABB = (function () { + function AABB(x, y, xw, yw) { + this.type = 0; + this.pos = new Phaser.Vec2(x, y); + this.oldpos = Phaser.Vec2Utils.clone(this.pos); + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + this.aabbTileProjections = {}; + this.aabbTileProjections[TileMapCell.CTYPE_FULL] = this.ProjAABB_Full; + this.aabbTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjAABB_Concave; + this.aabbTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjAABB_Convex; + } + AABB.prototype.IntegrateVerlet = function () { + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; + var oy = o.y; + o.x = px = p.x; + o.y = py = p.y; + + //o = oldposition + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + AABB.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + AABB.prototype.CollideAABBVsTile = function (tile) { + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + this.xw) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + this.yw) - Math.abs(dy); + + if (0 < py) { + if (px < py) { + if (dx < 0) { + //project to the left + px *= -1; + py = 0; + } else { + //proj to right + py = 0; + } + } else { + if (dy < 0) { + //project up + px = 0; + py *= -1; + } else { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + } + } + }; + + AABB.prototype.CollideAABBVsWorldBounds = function () { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + AABB.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + /* + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + */ + }; + + AABB.prototype.ResolveBoxTile = function (x, y, box, t) { + if (0 < t.ID) { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } else { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + }; + + AABB.prototype.ProjAABB_Full = function (x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return AABB.COL_AXIS; + }; + + AABB.prototype.ProjAABB_Convex = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw)); + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw)); + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return AABB.COL_AXIS; + } else if (0 < pen) { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return AABB.COL_OTHER; + } + + return AABB.COL_NONE; + }; + + AABB.prototype.ProjAABB_Concave = function (x, y, obj, t) { + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw)); + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw)); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + if (0 < pen) { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return AABB.COL_AXIS; + } else { + //project along corner->circle vector + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return AABB.COL_OTHER; + } + } + + return AABB.COL_NONE; + }; + AABB.COL_NONE = 0; + AABB.COL_AXIS = 1; + AABB.COL_OTHER = 2; + return AABB; +})(); + +var TileMapCell = (function () { + function TileMapCell(x, y, xw, yw) { + this.ID = TileMapCell.TID_EMPTY; + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x, y); + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + TileMapCell.prototype.SetState = function (ID) { + if (ID == TileMapCell.TID_EMPTY) { + this.Clear(); + } else { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + }; + + TileMapCell.prototype.Clear = function () { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY; + this.UpdateType(); + //this.Draw(); + }; + + TileMapCell.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + }; + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + TileMapCell.prototype.UpdateType = function () { + if (0 < this.ID) { + if (this.ID < TileMapCell.CTYPE_45DEG) { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } else if (this.ID < TileMapCell.CTYPE_CONCAVE) { + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnn) { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGnp) { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else if (this.ID == TileMapCell.TID_45DEGpp) { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2; + this.sy = this.signy / Math.SQRT2; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_CONVEX) { + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONCAVEpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGs) { + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnn) { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXnp) { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else if (this.ID == TileMapCell.TID_CONVEXpp) { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_22DEGb) { + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGs) { + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else if (this.ID == TileMapCell.TID_22DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_67DEGb) { + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnS) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpS) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppS) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else if (this.ID < TileMapCell.CTYPE_HALF) { + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnnB) { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGnpB) { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else if (this.ID == TileMapCell.TID_67DEGppB) { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } else { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } else { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFu) { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFl) { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else if (this.ID == TileMapCell.TID_HALFr) { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } else { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + } else { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + }; + TileMapCell.TID_EMPTY = 0; + TileMapCell.TID_FULL = 1; + TileMapCell.TID_45DEGpn = 2; + TileMapCell.TID_45DEGnn = 3; + TileMapCell.TID_45DEGnp = 4; + TileMapCell.TID_45DEGpp = 5; + TileMapCell.TID_CONCAVEpn = 6; + TileMapCell.TID_CONCAVEnn = 7; + TileMapCell.TID_CONCAVEnp = 8; + TileMapCell.TID_CONCAVEpp = 9; + TileMapCell.TID_CONVEXpn = 10; + TileMapCell.TID_CONVEXnn = 11; + TileMapCell.TID_CONVEXnp = 12; + TileMapCell.TID_CONVEXpp = 13; + TileMapCell.TID_22DEGpnS = 14; + TileMapCell.TID_22DEGnnS = 15; + TileMapCell.TID_22DEGnpS = 16; + TileMapCell.TID_22DEGppS = 17; + TileMapCell.TID_22DEGpnB = 18; + TileMapCell.TID_22DEGnnB = 19; + TileMapCell.TID_22DEGnpB = 20; + TileMapCell.TID_22DEGppB = 21; + TileMapCell.TID_67DEGpnS = 22; + TileMapCell.TID_67DEGnnS = 23; + TileMapCell.TID_67DEGnpS = 24; + TileMapCell.TID_67DEGppS = 25; + TileMapCell.TID_67DEGpnB = 26; + TileMapCell.TID_67DEGnnB = 27; + TileMapCell.TID_67DEGnpB = 28; + TileMapCell.TID_67DEGppB = 29; + TileMapCell.TID_HALFd = 30; + TileMapCell.TID_HALFr = 31; + TileMapCell.TID_HALFu = 32; + TileMapCell.TID_HALFl = 33; + + TileMapCell.CTYPE_EMPTY = 0; + TileMapCell.CTYPE_FULL = 1; + TileMapCell.CTYPE_45DEG = 2; + TileMapCell.CTYPE_CONCAVE = 6; + TileMapCell.CTYPE_CONVEX = 10; + TileMapCell.CTYPE_22DEGs = 14; + TileMapCell.CTYPE_22DEGb = 18; + TileMapCell.CTYPE_67DEGs = 22; + TileMapCell.CTYPE_67DEGb = 26; + TileMapCell.CTYPE_HALF = 30; + return TileMapCell; +})(); + +var Circle = (function () { + function Circle(x, y, radius) { + this.type = 1; + this.pos = new Phaser.Vec2(x, y); + this.oldpos = Phaser.Vec2Utils.clone(this.pos); + this.radius = radius; + this.circleTileProjections = {}; + this.circleTileProjections[TileMapCell.CTYPE_FULL] = this.ProjCircle_Full; + this.circleTileProjections[TileMapCell.CTYPE_45DEG] = this.ProjCircle_45Deg; + this.circleTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjCircle_Concave; + this.circleTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjCircle_Convex; + //Proj_CircleTile[CTYPE_22DEGs] = ProjCircle_22DegS; + //Proj_CircleTile[CTYPE_22DEGb] = ProjCircle_22DegB; + //Proj_CircleTile[CTYPE_67DEGs] = ProjCircle_67DegS; + //Proj_CircleTile[CTYPE_67DEGb] = ProjCircle_67DegB; + //Proj_CircleTile[CTYPE_HALF] = ProjCircle_Half; + } + Circle.prototype.IntegrateVerlet = function () { + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; + var oy = o.y; + o.x = px = p.x; + o.y = py = p.y; + + //o = oldposition + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + }; + + Circle.prototype.ReportCollisionVsWorld = function (px, py, dx, dy, obj) { + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; + + var ny = dp * dy; + + var tx = vx - nx; + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3; + + bx = (nx * b); + by = (ny * b); + } else { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px; + p.y += py; + + o.x += px + bx + fx; + o.y += py + by + fy; + }; + + Circle.prototype.CollideCircleVsWorldBounds = function () { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + if (0 < dx) { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } else { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + if (0 < dy) { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } else { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + }; + + Circle.prototype.render = function (context) { + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } else if (this.oH == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } else if (this.oV == -1) { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + }; + + Circle.prototype.CollideCircleVsTile = function (tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; + var px = (txw + r) - Math.abs(dx); + + if (0 < px) { + var dy = pos.y - ty; + var py = (tyw + r) - Math.abs(dy); + + if (0 < py) { + //object may be colliding with tile + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) { + //circle is on left side of tile + this.oH = -1; + } else if (txw < dx) { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) { + //circle is on top side of tile + this.oV = -1; + } else if (tyw < dy) { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + } + } + }; + + Circle.prototype.ResolveCircleTile = function (x, y, oH, oV, obj, t) { + if (0 < t.ID) { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } else { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + }; + + Circle.prototype.ProjCircle_Full = function (x, y, oH, oV, obj, t) { + if (oH == 0) { + if (oV == 0) { + if (x < y) { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x; + + if (dx < 0) { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Circle.COL_AXIS; + } + } else { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y; + + if (dy < 0) { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Circle.COL_AXIS; + } + } + } else { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + } else if (oV == 0) { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } else { + //diagonal collision + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + + return Circle.COL_NONE; + }; + + Circle.prototype.ProjCircle_45Deg = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x; + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp; + sy *= -dp; + + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } else { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y + (oV * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } else { + //collide vs. slope + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp); + if (0 < pen) { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + } + } + + return Circle.COL_NONE; + }; + + Circle.prototype.ProjCircle_Concave = function (x, y, oH, oV, obj, t) { + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x; + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y; + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } else { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } else { + return Circle.COL_NONE; + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the vertical tip + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out vertically + dx = 0; + dy = oV; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } else { + //we could only be colliding vs the horizontal tip + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out horizontally + dx = oH; + dy = 0; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + + return Circle.COL_NONE; + }; + + Circle.prototype.ProjCircle_Convex = function (x, y, oH, oV, obj, t) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) { + if (oV == 0) { + //colliding with current tile + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + if (x < y) { + //penetration in x is smaller + lenP = x; + y = 0; + + if ((obj.pos.x - t.pos.x) < 0) { + x *= -1; + } + } else { + //penetration in y is smaller + lenP = y; + x = 0; + + if ((obj.pos.y - t.pos.y) < 0) { + y *= -1; + } + } + + if (lenP < pen) { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } else { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } else { + if ((signy * oV) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } + } else if (oV == 0) { + if ((signx * oH) < 0) { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } else { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } else { + if (0 < ((signx * oH) + (signy * oV))) { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + var ox = obj.pos.x - (t.pos.x - (signx * t.xw)); + var oy = obj.pos.y - (t.pos.y - (signy * t.yw)); + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0); + + //note that this should be precomputed at compile-time since it's constant + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) { + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } else { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx; + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) { + if (len == 0) { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } else { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + + return Circle.COL_NONE; + }; + Circle.COL_NONE = 0; + Circle.COL_AXIS = 1; + Circle.COL_OTHER = 2; + return Circle; +})(); + +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + game.load.image('ball', 'assets/sprites/shinyball.png'); + game.load.image('card', 'assets/sprites/mana_card.png'); + } + + var cells; + var physics; + var b; + var c; + var t; + var ball; + var card; + + function create() { + this.ball = game.add.sprite(0, 0, 'ball'); + this.ball.origin.setTo(0.5, 0.5); + + this.card = game.add.sprite(0, 0, 'card'); + this.card.rotation = 30; + + //this.card.origin.setTo(0.5, 0.5); + this.physics = new NPhysics(); + this.c = new Circle(200, 100, 16); + this.b = new AABB(200, 200, 74 / 2, 128 / 2); + + // pos is center, not upper-left + this.cells = []; + + var tid; + + for (var i = 0; i < 10; i++) { + if (i % 2 == 0) { + console.log('pn'); + tid = TileMapCell.TID_CONCAVEpn; + } else { + console.log('nn'); + tid = TileMapCell.TID_CONCAVEnn; + } + + //this.cells.push(new TileMapCell(100 + (i * 100), 400, 50, 100).SetState(TileMapCell.TID_FULL)); + this.cells.push(new TileMapCell(100 + (i * 100), 400, 50, 50).SetState(tid)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_CONCAVEpn)); + } + //this.t = new TileMapCell(200, 500, 100, 100); + //this.t.SetState(TileMapCell.TID_FULL); + //this.t.SetState(TileMapCell.TID_45DEGpn); + //this.t.SetState(TileMapCell.TID_CONCAVEpn); + //this.t.SetState(TileMapCell.TID_CONVEXpn); + } + + function update() { + var fx = 0; + var fy = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { + fx -= 0.2; + } else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { + fx += 0.2; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) { + fy -= 0.2 + 0.2; + } else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { + fy += 0.2; + } + + // update circle + this.c.pos.x = this.c.oldpos.x + Math.min(20, Math.max(-20, this.c.pos.x - this.c.oldpos.x + fx)); + this.c.pos.y = this.c.oldpos.y + Math.min(20, Math.max(-20, this.c.pos.y - this.c.oldpos.y + fy)); + this.c.IntegrateVerlet(); + + // update box + this.b.pos.x = this.b.oldpos.x + Math.min(40, Math.max(-40, this.b.pos.x - this.b.oldpos.x + fx)); + this.b.pos.y = this.b.oldpos.y + Math.min(40, Math.max(-40, this.b.pos.y - this.b.oldpos.y + fy)); + this.b.IntegrateVerlet(); + + for (var i = 0; i < this.cells.length; i++) { + this.c.CollideCircleVsTile(this.cells[i]); + this.b.CollideAABBVsTile(this.cells[i]); + } + + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + + this.ball.x = this.c.pos.x; + this.ball.y = this.c.pos.y; + + this.card.transform.centerOn(this.b.pos.x, this.b.pos.y); + //this.card.x = this.b.pos.x; + //this.card.y = this.b.pos.y; + } + + function render() { + this.c.render(game.stage.context); + this.b.render(game.stage.context); + + for (var i = 0; i < this.cells.length; i++) { + this.cells[i].render(game.stage.context); + } + } +})(); diff --git a/todo/phaser clean up/temp2.ts b/todo/phaser clean up/temp2.ts new file mode 100644 index 000000000..df7ba1f46 --- /dev/null +++ b/todo/phaser clean up/temp2.ts @@ -0,0 +1,2091 @@ +/// + +class NPhysics { + + grav: number = 0.2; + drag: number = 1; + bounce: number = 0.3; + friction: number = 0.05; + + min_f: number = 0; + max_f: number = 1; + + min_b: number = 0; + max_b: number = 1; + + min_g: number = 0; + max_g = 1; + + xmin: number = 0; + xmax: number = 800; + ymin: number = 0; + ymax: number = 600; + + objrad: number = 24; + tilerad: number = 24*2; + objspeed: number = 0.2; + maxspeed: number = 20; + + public update() { + // demoObj.Verlet(); + // demoObj.CollideVsWorldBounds(); + } + +} + +class AABB { + + constructor(x: number, y: number, xw, yw) { + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = Phaser.Vec2Utils.clone(this.pos); + this.xw = Math.abs(xw); + this.yw = Math.abs(yw); + this.aabbTileProjections = {};//hash object to hold tile-specific collision functions + this.aabbTileProjections[TileMapCell.CTYPE_FULL] = this.ProjAABB_Full; + this.aabbTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjAABB_Concave; + this.aabbTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjAABB_Convex; + + } + + type:number = 0; + pos: Phaser.Vec2; + oldpos: Phaser.Vec2; + xw: number; + yw: number; + aabbTileProjections; + public oH: number; + public oV: number; + static COL_NONE = 0; + static COL_AXIS = 1; + static COL_OTHER = 2; + + public IntegrateVerlet() { + + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; //we can't swap buffers since mcs/sticks point directly to vector2s.. + var oy = o.y; + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + //o = oldposition + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px, py, dx, dy, obj: TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + + public CollideAABBVsTile(tile) + { + var pos = this.pos; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + this.xw) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + this.yw) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile; call tile-specific collision function + + //calculate projection vectors + if (px < py) + { + //project in x + if (dx < 0) + { + //project to the left + px *= -1; + py = 0; + } + else + { + //proj to right + py = 0; + } + } + else + { + //project in y + if (dy < 0) + { + //project up + px = 0; + py *= -1; + } + else + { + //project down + px = 0; + } + } + + this.ResolveBoxTile(px, py, this, c); + + } + } + } + + public CollideAABBVsWorldBounds() { + var p = this.pos; + var xw = this.xw; + var yw = this.yw; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - xw); + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + xw) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - yw); + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + yw) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context:CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.strokeRect(this.pos.x - this.xw, this.pos.y - this.yw, this.xw * 2, this.yw * 2); + context.stroke(); + context.closePath(); + + context.fillStyle = 'rgb(0,255,0)'; + context.fillRect(this.pos.x, this.pos.y, 2, 2); + + /* + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + */ + + } + + public ResolveBoxTile(x, y, box, t) { + if (0 < t.ID) + { + return this.aabbTileProjections[t.CTYPE](x, y, box, t); + } + else + { + //trace("ResolveBoxTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + + public ProjAABB_Full(x, y, obj, t) { + var l = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / l, y / l, t); + + return AABB.COL_AXIS; + } + + public ProjAABB_Convex(x, y, obj, t) { + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the circle center to + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw));//the AABB + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var pen = rad - len; + if (((signx * ox) < 0) || ((signy * oy) < 0)) + { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return AABB.COL_AXIS;//we need to report + } + else if (0 < pen) + { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return AABB.COL_OTHER; + } + + return AABB.COL_NONE; + } + + public ProjAABB_Concave(x, y, obj, t) + { + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw));//(ox,oy) is the vector form the innermost AABB corner to the + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw));//circle's center + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + if (0 < pen) + { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + + var lenP = Math.sqrt(x * x + y * y); + if (lenP < pen) + { + //it's shorter to move along axis directions + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return AABB.COL_AXIS; + } + else + { + //project along corner->circle vector + ox /= len;//len should never be 0, since if it IS 0, rad should be > than len + oy /= len;//and we should never reach here + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return AABB.COL_OTHER; + } + + } + + return AABB.COL_NONE; + + } + + + + +} + +class TileMapCell { + + //TILETYPE ENUMERATION + static TID_EMPTY = 0; + static TID_FULL = 1;//fullAABB tile + static TID_45DEGpn = 2;//45-degree triangle, whose normal is (+ve,-ve) + static TID_45DEGnn = 3;//(+ve,+ve) + static TID_45DEGnp = 4;//(-ve,+ve) + static TID_45DEGpp = 5;//(-ve,-ve) + static TID_CONCAVEpn = 6;//1/4-circle cutout + static TID_CONCAVEnn = 7; + static TID_CONCAVEnp = 8; + static TID_CONCAVEpp = 9; + static TID_CONVEXpn = 10;//1/4/circle + static TID_CONVEXnn = 11; + static TID_CONVEXnp = 12; + static TID_CONVEXpp = 13; + static TID_22DEGpnS = 14;//22.5 degree slope + static TID_22DEGnnS = 15; + static TID_22DEGnpS = 16; + static TID_22DEGppS = 17; + static TID_22DEGpnB = 18; + static TID_22DEGnnB = 19; + static TID_22DEGnpB = 20; + static TID_22DEGppB = 21; + static TID_67DEGpnS = 22;//67.5 degree slope + static TID_67DEGnnS = 23; + static TID_67DEGnpS = 24; + static TID_67DEGppS = 25; + static TID_67DEGpnB = 26; + static TID_67DEGnnB = 27; + static TID_67DEGnpB = 28; + static TID_67DEGppB = 29; + static TID_HALFd = 30;//half-full tiles + static TID_HALFr = 31; + static TID_HALFu = 32; + static TID_HALFl = 33; + + //collision shape "types" + static CTYPE_EMPTY = 0; + static CTYPE_FULL = 1; + static CTYPE_45DEG = 2; + static CTYPE_CONCAVE = 6; + static CTYPE_CONVEX = 10; + static CTYPE_22DEGs = 14; + static CTYPE_22DEGb = 18; + static CTYPE_67DEGs = 22; + static CTYPE_67DEGb = 26; + static CTYPE_HALF = 30; + + ID; + CTYPE; + pos: Phaser.Vec2; + xw; + yw; + minx; + maxx; + miny; + maxy; + signx; + signy; + sx; + sy; + + constructor(x,y,xw,yw) { + + this.ID = TileMapCell.TID_EMPTY; //all tiles start empty + this.CTYPE = TileMapCell.CTYPE_EMPTY; + + this.pos = new Phaser.Vec2(x,y); //setup collision properties + this.xw = xw; + this.yw = yw; + this.minx = this.pos.x - this.xw; + this.maxx = this.pos.x + this.xw; + this.miny = this.pos.y - this.yw; + this.maxy = this.pos.y + this.yw; + + //this stores tile-specific collision information + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + + //these functions are used to update the cell + //note: ID is assumed to NOT be "empty" state.. + //if it IS the empty state, the tile clears itself + SetState(ID) { + if (ID == TileMapCell.TID_EMPTY) + { + this.Clear(); + } + else + { + //set tile state to a non-emtpy value, and update it's edges and those of the neighbors + this.ID = ID; + this.UpdateType(); + //this.Draw(); + } + return this; + } + + Clear() { + //tile was on, turn it off + this.ID = TileMapCell.TID_EMPTY + this.UpdateType(); + //this.Draw(); + } + + public render(context: CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(255,255,0)'; + context.strokeRect(this.minx, this.miny, this.xw * 2, this.yw * 2); + context.strokeRect(this.pos.x, this.pos.y, 2, 2); + context.closePath(); + + } + + //this converts a tile from implicitly-defined (via ID), to explicit (via properties) + UpdateType() { + if (0 < this.ID) + { + //tile is non-empty; collide + if (this.ID < TileMapCell.CTYPE_45DEG) + { + //TID_FULL + this.CTYPE = TileMapCell.CTYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + } + else if (this.ID < TileMapCell.CTYPE_CONCAVE) + { + + //45deg + this.CTYPE = TileMapCell.CTYPE_45DEG; + if (this.ID == TileMapCell.TID_45DEGpn) + { + console.log('set tile as 45deg pn'); + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnn) + { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + + } + else if (this.ID == TileMapCell.TID_45DEGnp) + { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.ID == TileMapCell.TID_45DEGpp) + { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_CONVEX) + { + + //concave + this.CTYPE = TileMapCell.CTYPE_CONCAVE; + if (this.ID == TileMapCell.TID_CONCAVEpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONCAVEpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGs) + { + + //convex + this.CTYPE = TileMapCell.CTYPE_CONVEX; + if (this.ID == TileMapCell.TID_CONVEXpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.ID == TileMapCell.TID_CONVEXpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_22DEGb) + { + + //22deg small + this.CTYPE = TileMapCell.CTYPE_22DEGs; + if (this.ID == TileMapCell.TID_22DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGs) + { + + //22deg big + this.CTYPE = TileMapCell.CTYPE_22DEGb; + if (this.ID == TileMapCell.TID_22DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.ID == TileMapCell.TID_22DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_67DEGb) + { + + //67deg small + this.CTYPE = TileMapCell.CTYPE_67DEGs; + if (this.ID == TileMapCell.TID_67DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else if (this.ID < TileMapCell.CTYPE_HALF) + { + + //67deg big + this.CTYPE = TileMapCell.CTYPE_67DEGb; + if (this.ID == TileMapCell.TID_67DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.ID == TileMapCell.TID_67DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + //trace("BAAAD TILE!!!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + } + else + { + //half-full tile + this.CTYPE = TileMapCell.CTYPE_HALF; + if (this.ID == TileMapCell.TID_HALFd) + { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFu) + { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFl) + { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.ID == TileMapCell.TID_HALFr) + { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else + { + //trace("BAAD TILE!!!: ID=" + this.ID + " ("+ t.i + "," + t.j + ")"); + return false; + } + + } + + } + else + { + //TID_EMPTY + this.CTYPE = TileMapCell.CTYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + } + +} + +class Circle { + + constructor(x, y, radius) { + + this.pos = new Phaser.Vec2(x, y); + this.oldpos = Phaser.Vec2Utils.clone(this.pos); + this.radius = radius; + this.circleTileProjections = {};//hash object to hold tile-specific collision functions + this.circleTileProjections[TileMapCell.CTYPE_FULL] = this.ProjCircle_Full; + this.circleTileProjections[TileMapCell.CTYPE_45DEG] = this.ProjCircle_45Deg; + this.circleTileProjections[TileMapCell.CTYPE_CONCAVE] = this.ProjCircle_Concave; + this.circleTileProjections[TileMapCell.CTYPE_CONVEX] = this.ProjCircle_Convex; + + //Proj_CircleTile[CTYPE_22DEGs] = ProjCircle_22DegS; + //Proj_CircleTile[CTYPE_22DEGb] = ProjCircle_22DegB; + //Proj_CircleTile[CTYPE_67DEGs] = ProjCircle_67DegS; + //Proj_CircleTile[CTYPE_67DEGb] = ProjCircle_67DegB; + //Proj_CircleTile[CTYPE_HALF] = ProjCircle_Half; + + } + + type:number = 1; + pos: Phaser.Vec2; + oldpos: Phaser.Vec2; + radius: number; + circleTileProjections; + public oH: number; + public oV: number; + static COL_NONE = 0; + static COL_AXIS = 1; + static COL_OTHER = 2; + + public IntegrateVerlet() { + + //var d = DRAG; + //var g = GRAV; + var d = 1; + var g = 0.2; + + var p = this.pos; + var o = this.oldpos; + var px, py; + + var ox = o.x; //we can't swap buffers since mcs/sticks point directly to vector2s.. + var oy = o.y; + o.x = px = p.x; //get vector values + o.y = py = p.y; //p = position + //o = oldposition + + //integrate + p.x += (d * px) - (d * ox); + p.y += (d * py) - (d * oy) + g; + + } + + public ReportCollisionVsWorld(px, py, dx, dy, obj: TileMapCell) { + + var p = this.pos; + var o = this.oldpos; + + //calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + //find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx;//project velocity onto collision normal + + var ny = dp * dy;//nx,ny is normal velocity + + var tx = vx - nx;//px,py is tangent velocity + var ty = vy - ny; + + //we only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, f, fx, fy; + + if (dp < 0) + { + //f = FRICTION; + f = 0.05; + fx = tx * f; + fy = ty * f; + + //b = 1 + BOUNCE;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + b = 1 + 0.3;//this bounce constant should be elsewhere, i.e inside the object/tile/etc.. + + bx = (nx * b); + by = (ny * b); + + } + else + { + //moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + p.x += px;//project object out of collision + p.y += py; + + o.x += px + bx + fx;//apply bounce+friction impulses which alter velocity + o.y += py + by + fy; + + } + + public CollideCircleVsWorldBounds() { + var p = this.pos; + var r = this.radius; + var XMIN = 0; + var XMAX = 800; + var YMIN = 0; + var YMAX = 600; + + //collide vs. x-bounds + //test XMIN + var dx = XMIN - (p.x - r); + if (0 < dx) + { + //object is colliding with XMIN + this.ReportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + //test XMAX + dx = (p.x + r) - XMAX; + if (0 < dx) + { + //object is colliding with XMAX + this.ReportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + //collide vs. y-bounds + //test YMIN + var dy = YMIN - (p.y - r); + if (0 < dy) + { + //object is colliding with YMIN + this.ReportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + //test YMAX + dy = (p.y + r) - YMAX; + if (0 < dy) + { + //object is colliding with YMAX + this.ReportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + } + + public render(context:CanvasRenderingContext2D) { + + context.beginPath(); + context.strokeStyle = 'rgb(0,255,0)'; + context.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2); + context.stroke(); + context.closePath(); + + if (this.oH == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oH == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + if (this.oV == 1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y - this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y - this.radius); + context.stroke(); + context.closePath(); + } + else if (this.oV == -1) + { + context.beginPath(); + context.strokeStyle = 'rgb(255,0,0)'; + context.moveTo(this.pos.x - this.radius, this.pos.y + this.radius); + context.lineTo(this.pos.x + this.radius, this.pos.y + this.radius); + context.stroke(); + context.closePath(); + } + + } + + public CollideCircleVsTile(tile) { + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + r) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + r) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile + + //determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + if (dx < -txw) + { + //circle is on left side of tile + this.oH = -1; + } + else if (txw < dx) + { + //circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) + { + //circle is on top side of tile + this.oV = -1; + } + else if (tyw < dy) + { + //circle is on bottom side of tile + this.oV = 1; + } + + this.ResolveCircleTile(px, py, this.oH, this.oV, this, c); + + } + } + } + + public ResolveCircleTile(x, y, oH, oV, obj, t) { + + if (0 < t.ID) + { + return this.circleTileProjections[t.CTYPE](x, y, oH, oV, obj, t); + } + else + { + console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.ID + " (" + t.i + "," + t.j + ")"); + return false; + } + } + + public ProjCircle_Full(x, y, oH, oV, obj:Circle, t:TileMapCell) { + + //if we're colliding vs. the current cell, we need to project along the + //smallest penetration vector. + //if we're colliding vs. horiz. or vert. neighb, we simply project horiz/vert + //if we're colliding diagonally, we need to collide vs. tile corner + + if (oH == 0) + { + if (oV == 0) + { + //collision with current cell + if (x < y) + { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x;//get sign for projection along x-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dx < 0) + { + obj.ReportCollisionVsWorld(-x, 0, -1, 0, t); + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(x, 0, 1, 0, t); + return Circle.COL_AXIS; + } + } + else + { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y;//get sign for projection along y-axis + + //NOTE: should we handle the delta == 0 case?! and how? (project towards oldpos?) + if (dy < 0) + { + obj.ReportCollisionVsWorld(0, -y, 0, -1, t); + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(0, y, 0, 1, t); + return Circle.COL_AXIS; + } + } + } + else + { + //collision with vertical neighbor + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + } + else if (oV == 0) + { + //collision with horizontal neighbor + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Circle.COL_AXIS; + } + else + { + //diagonal collision + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + + return Circle.COL_NONE; + + } + + public ProjCircle_45Deg(x, y, oH, oV, obj: Circle, t: TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y;//point on the circle, relative to the tile center + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + if (dp < 0) + { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp;//(sx,sy) is now the penetration vector + sy *= -dp; + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + obj.ReportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Circle.COL_OTHER; + } + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (oV * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.ReportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Circle.COL_OTHER; + } + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + } + + public ProjCircle_Concave(x, y, oH, oV, obj: Circle, t: TileMapCell) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x;//(ox,oy) is the vector from the circle to + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y;//tile-circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + return Circle.COL_NONE; + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the vertical tip + + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out vertically + dx = 0; + dy = oV; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the horizontal tip + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out horizontally + dx = oH; + dy = 0; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + + } + + public ProjCircle_Convex(x, y, oH, oV, obj: Circle, t: TileMapCell) { + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH == 0) + { + if (oV == 0) + { + //colliding with current tile + + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.ReportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Circle.COL_AXIS; + } + else + { + //note: len should NEVER be == 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + + } + } + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } + } + else if (oV == 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.ReportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be == 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.ReportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Circle.COL_OTHER; + } + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len == 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.ReportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Circle.COL_OTHER; + } + + } + + } + + return Circle.COL_NONE; + + } + +} + + + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + game.load.image('ball', 'assets/sprites/shinyball.png'); + game.load.image('card', 'assets/sprites/mana_card.png'); + + } + + var cells; + var physics: NPhysics; + var b: AABB; + var c: Circle; + var t: TileMapCell; + var ball: Phaser.Sprite; + var card: Phaser.Sprite; + + function create() { + + this.ball = game.add.sprite(0, 0, 'ball'); + this.ball.origin.setTo(0.5, 0.5); + + this.card = game.add.sprite(0, 0, 'card'); + this.card.rotation = 30; + //this.card.origin.setTo(0.5, 0.5); + + this.physics = new NPhysics(); + this.c = new Circle(200, 100, 16); + this.b = new AABB(200, 200, 74/2, 128/2); + + // pos is center, not upper-left + this.cells = []; + + var tid; + + for (var i = 0; i < 10; i++) + { + if (i % 2 == 0) + { + console.log('pn'); + tid = TileMapCell.TID_CONCAVEpn; + } + else + { + console.log('nn'); + tid = TileMapCell.TID_CONCAVEnn; + } + + //this.cells.push(new TileMapCell(100 + (i * 100), 400, 50, 100).SetState(TileMapCell.TID_FULL)); + + this.cells.push(new TileMapCell(100 + (i * 100), 400, 50, 50).SetState(tid)); + + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_FULL)); + //this.cells.push(new TileMapCell(100 + (i * 100), 500, 50, 50).SetState(TileMapCell.TID_CONCAVEpn)); + } + + //this.t = new TileMapCell(200, 500, 100, 100); + //this.t.SetState(TileMapCell.TID_FULL); + //this.t.SetState(TileMapCell.TID_45DEGpn); + //this.t.SetState(TileMapCell.TID_CONCAVEpn); + //this.t.SetState(TileMapCell.TID_CONVEXpn); + + } + + function update() { + + var fx = 0; + var fy = 0; + + if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) + { + fx -= 0.2; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) + { + fx += 0.2; + } + + if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) + { + fy -= 0.2 + 0.2; + } + else if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) + { + fy += 0.2; + } + + // update circle + this.c.pos.x = this.c.oldpos.x + Math.min(20, Math.max(-20, this.c.pos.x - this.c.oldpos.x + fx)); + this.c.pos.y = this.c.oldpos.y + Math.min(20, Math.max(-20, this.c.pos.y - this.c.oldpos.y + fy)); + this.c.IntegrateVerlet(); + + // update box + this.b.pos.x = this.b.oldpos.x + Math.min(40, Math.max(-40, this.b.pos.x - this.b.oldpos.x + fx)); + this.b.pos.y = this.b.oldpos.y + Math.min(40, Math.max(-40, this.b.pos.y - this.b.oldpos.y + fy)); + this.b.IntegrateVerlet(); + + for (var i = 0; i < this.cells.length; i++) + { + this.c.CollideCircleVsTile(this.cells[i]); + this.b.CollideAABBVsTile(this.cells[i]); + } + + this.c.CollideCircleVsWorldBounds(); + this.b.CollideAABBVsWorldBounds(); + + + + this.ball.x = this.c.pos.x; + this.ball.y = this.c.pos.y; + + this.card.transform.centerOn(this.b.pos.x, this.b.pos.y); + //this.card.x = this.b.pos.x; + //this.card.y = this.b.pos.y; + + } + + function render() { + + this.c.render(game.stage.context); + this.b.render(game.stage.context); + + for (var i = 0; i < this.cells.length; i++) + { + this.cells[i].render(game.stage.context); + } + + } + +})(); diff --git a/todo/phaser space paint s.png b/todo/phaser space paint s.png deleted file mode 100644 index 1c0a7d49d..000000000 Binary files a/todo/phaser space paint s.png and /dev/null differ