diff --git a/README.md b/README.md index b427c40..03a6fd8 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,8 @@ Check out the **[demo](http://hexus.github.io/phaser-arcade-slopes)**! ## Features - 24 new tile types :tada: -- SAT-driven collision handling :ok_hand: +- [SAT](https://github.com/jriecken/sat-js) collision handling :ok_hand: - Unobtrusive and cooperative integration with Arcade Physics :v: -- Heuristic SAT restraints that prevent AABBs catching on hidden edges :clap: - Supports sprites :rocket:, groups :busts_in_silhouette:, particle emitters :sparkles: and circular physics bodies :white_circle: @@ -24,12 +23,12 @@ Check out the **[demo](http://hexus.github.io/phaser-arcade-slopes)**! | Phaser Version | Arcade Slopes Version | | --------------- | ------------------------------------------------------------------- | | v2.4.1 - v2.4.8 | [v0.1.0](https://github.com/hexus/phaser-arcade-slopes/tree/v0.1.0) | -| v2.5.0 - v2.7.9 | [v0.1.1](https://github.com/hexus/phaser-arcade-slopes/tree/v0.1.1) - [v0.2.1](https://github.com/hexus/phaser-arcade-slopes/tree/v0.2.1) | +| v2.5.0 - v2.8.3 | [v0.1.1](https://github.com/hexus/phaser-arcade-slopes/tree/v0.1.1) - [v0.3.0](https://github.com/hexus/phaser-arcade-slopes/tree/v0.3.0) | ## Installation Grab a copy of the -[latest release](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/v0.2.1/dist/phaser-arcade-slopes.min.js) +[latest release](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/v0.3.0/dist/phaser-arcade-slopes.min.js) from the [**dist**](dist) directory in this repository and include it after Phaser. @@ -46,7 +45,6 @@ Phaser. - [Collision](#collision) - [Debug rendering](#debug-rendering) - [Extras](#extras) - - [Heuristics](#heuristics) - [Minimum Y Offset](#minimum-y-offset) - [Collision pulling](#collision-pulling) @@ -146,18 +144,6 @@ ground.debug = true; ### Extras -#### Heuristics - -The plugin uses heuristics to prevent physics bodies from catching on -undesirable tile edges; the touching edges of two adjacent tiles. - -**This is enabled by default.** If you don't need this feature, you can disable -it. - -```js -game.slopes.heuristics = false; -``` - #### Minimum Y offset This feature separates rectangular physics bodies on the Y axis only, in the diff --git a/dist/phaser-arcade-slopes.js b/dist/phaser-arcade-slopes.js index 15c7a71..4e880cf 100644 --- a/dist/phaser-arcade-slopes.js +++ b/dist/phaser-arcade-slopes.js @@ -54,7 +54,7 @@ Phaser.Plugin.ArcadeSlopes.prototype.constructor = Phaser.Plugin.ArcadeSlopes; * @constant * @type {string} */ -Phaser.Plugin.ArcadeSlopes.VERSION = '0.2.1'; +Phaser.Plugin.ArcadeSlopes.VERSION = '0.3.0'; /** * The Separating Axis Theorem collision solver type. @@ -232,7 +232,6 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody = function (body) { body.slopes = body.slopes || { debug: false, friction: new Phaser.Point(), - heuristics: null, preferY: false, pullUp: 0, pullDown: 0, @@ -246,10 +245,6 @@ Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody = function (body) { response: null, }, skipFriction: false, - snapUp: 0, - snapDown: 0, - snapLeft: 0, - snapRight: 0, tile: null, velocity: new SAT.Vector() }; @@ -355,18 +350,18 @@ Phaser.Plugin.ArcadeSlopes.Overrides = {}; * Collide a sprite against a single tile. * * @method Phaser.Plugin.ArcadeSlopes.Overrides#collideSpriteVsTile - * @param {integer} i - The tile index. - * @param {Phaser.Sprite} sprite - The sprite to check. - * @param {Phaser.Tile} tile - The tile to check. - * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tile belongs to. - * @param {function} collideCallback - An optional collision callback. - * @param {function} processCallback - An optional overlap processing callback. - * @param {object} callbackContext - The context in which to run the callbacks. - * @param {boolean} overlapOnly - Whether to only check for an overlap. - * @return {boolean} - Whether a collision occurred. + * @param {integer} i - The tile index. + * @param {Phaser.Sprite} sprite - The sprite to check. + * @param {Phaser.Tile} tile - The tile to check. + * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tile belongs to. + * @param {function} [collideCallback] - An optional collision callback. + * @param {function} [processCallback] - An optional overlap processing callback. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @param {boolean} [overlapOnly] - Whether to only check for an overlap. + * @return {boolean} - Whether a collision occurred. */ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile = function (i, sprite, tile, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) { - if (!sprite.body) { + if (!sprite.body || !tile || !tilemapLayer) { return false; } @@ -397,22 +392,22 @@ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile = function (i, sprite, * Collide a sprite against a set of tiles. * * @method Phaser.Plugin.ArcadeSlopes.Overrides#collideSpriteVsTiles - * @param {Phaser.Sprite} sprite - The sprite to check. - * @param {Phaser.Tile[]} tiles - The tiles to check. - * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tiles belong to. - * @param {function} collideCallback - An optional collision callback. - * @param {function} processCallback - An optional overlap processing callback. - * @param {object} callbackContext - The context in which to run the callbacks. - * @param {boolean} overlapOnly - Whether to only check for an overlap. - * @return {boolean} - Whether a collision occurred. + * @param {Phaser.Sprite} sprite - The sprite to check. + * @param {Phaser.Tile[]} tiles - The tiles to check. + * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tiles belong to. + * @param {function} [collideCallback] - An optional collision callback. + * @param {function} [processCallback] - An optional overlap processing callback. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @param {boolean} [overlapOnly] - Whether to only check for an overlap. + * @return {boolean} - Whether a collision occurred. */ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles = function (sprite, tiles, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) { - var collided = false; - - if (!sprite.body) { - return collided; + if (!sprite.body || !tiles || !tiles.length || !tilemapLayer) { + return false; } + var collided = false; + for (var i = 0; i < tiles.length; i++) { if (processCallback) { if (processCallback.call(callbackContext, sprite, tiles[i])) { @@ -442,7 +437,7 @@ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles = function (sprite, ti * @return {boolean} - Whether a collision occurred. */ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer = function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) { - if (!sprite.body) { + if (!sprite.body || !tilemapLayer) { return false; } @@ -451,7 +446,7 @@ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer = function (spr sprite.body.position.y - sprite.body.tilePadding.y - tilemapLayer.getCollisionOffsetY(), sprite.body.width + sprite.body.tilePadding.x, sprite.body.height + sprite.body.tilePadding.y, - false, + true, false ); @@ -459,12 +454,9 @@ Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer = function (spr return false; } - var collided = this.collideSpriteVsTiles(sprite, tiles, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly); + // TODO: Sort by distance from body center to tile center? - if (!collided && !overlapOnly) { - // TODO: This call is too hacky and solver-specific - this.game.slopes.solvers.sat.snap(sprite.body, tiles, tilemapLayer); - } + var collided = this.collideSpriteVsTiles(sprite, tiles, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly); return collided; }; @@ -592,8 +584,13 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { var height = this.layer.height; var tw = this._mc.tileWidth * scaleX; var th = this._mc.tileHeight * scaleY; + var htw = tw / 2; + var hth = th / 2; + var qtw = tw / 4; + var qth = th / 4; var cw = this._mc.cw * scaleX; var ch = this._mc.ch * scaleY; + var m = this._mc.edgeMidpoint; var left = Math.floor(scrollX / tw); var right = Math.floor((renderW - 1 + scrollX) / tw); @@ -619,7 +616,7 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { var normStartX = (left + ((1 << 20) * width)) % width; var normStartY = (top + ((1 << 20) * height)) % height; - var tx, ty, x, y, xmax, ymax, polygon, i, j; + var tx, ty, x, y, xmax, ymax, polygon, i, j, a, b, norm, gx, gy; for (y = normStartY, ymax = bottom - top, ty = baseY; ymax >= 0; y++, ymax--, ty += th) { if (y >= height) { @@ -707,8 +704,9 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { } } - // Stroke the colliding edges + // Stroke the colliding edges and edge normals if (this.debugSettings.slopeCollidingEdgeStroke) { + // Colliding edges context.beginPath(); context.lineWidth = this.debugSettings.slopeCollidingEdgeStrokeWidth || 1; @@ -717,11 +715,12 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { polygon = tile.slope.polygon; for (i = 0; i < polygon.points.length; i++) { - j = (i + 1) % polygon.points.length; - - // Skip internal edges - if (polygon.points[i].internal) + // Skip the edges with ignored normals + if (polygon.normals[i].ignore) { continue; + } + + j = (i + 1) % polygon.points.length; context.moveTo(tx + polygon.points[i].x * scaleX, ty + polygon.points[i].y * scaleY); context.lineTo(tx + polygon.points[j].x * scaleX, ty + polygon.points[j].y * scaleY); @@ -730,6 +729,56 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { context.closePath(); context.stroke(); + + // Edge normals + for (i = 0; i < polygon.points.length; i++) { + context.beginPath(); + + if (polygon.normals[i].ignore) { + context.lineWidth = this.debugSettings.slopeNormalStrokeWidth; + context.strokeStyle = this.debugSettings.slopeNormalStroke; + } else { + context.lineWidth = this.debugSettings.slopeCollidingNormalStrokeWidth; + context.strokeStyle = this.debugSettings.slopeCollidingNormalStroke; + } + + + j = (i + 1) % polygon.points.length; + + a = polygon.points[i]; + b = polygon.points[j]; + norm = polygon.normals[i]; + + // Midpoint of the edge + m.x = (a.x + b.x) / 2; + m.y = (a.y + b.y) / 2; + + // Draw from the midpoint outwards using the normal + context.moveTo(tx + m.x * scaleX, ty + m.y * scaleY); + context.lineTo(tx + m.x * scaleX + norm.x * qtw, ty + m.y * scaleY + norm.y * qth); + + context.closePath(); + context.stroke(); + } + + // Ignormals + if (tile.slope.ignormals) { + for (i = 0; i < tile.slope.ignormals.length; i++) { + context.beginPath(); + + context.lineWidth = 1; + context.strokeStyle = 'rgba(255, 0, 0, 1)'; + + gx = tile.slope.ignormals[i].x; + gy = tile.slope.ignormals[i].y; + + context.moveTo(tx + htw, ty + hth); + context.lineTo(tx + htw + gx * qtw, ty + hth + gy * qth); + + context.closePath(); + context.stroke(); + } + } } } } @@ -737,994 +786,6 @@ Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () { } }; -/** - * @author Chris Andrew - * @copyright 2016-2017 Chris Andrew - * @license MIT - */ - -/** - * Restrains SAT tile collision handling based on their neighbouring tiles. - * - * Can separate on a tile's preferred axis if it has one. - * - * This is what keeps the sloped tile collisions smooth for AABBs. - * - * Think of it as the equivalent of the Arcade Physics tile face checks for all - * of the sloped tiles and their possible neighbour combinations. - * - * Thanks to some painstaking heuristics, it allows a set of touching tiles to - * behave more like a single shape. - * - * TODO: Change all of these rules to work with Phaser's own edge restraints. - * Will require checking all of these rules during tilemap convert. - * TileSlope specific edge flags would need to be set for this. - * See SatSolver.shouldSeparate(). That should deal with it. - * This would work because we're only trying to prevent - * axis-aligned overlap vectors, not anything else. - * - * TODO: Move away from these heuristics and start flagging edge visibility - * automatically, if that could at all work out as well as this has. - * Imagine using the normals of each face to prevent separation on - * that axis, and instead using the next shortest axis to collide. - * TL;DR: Disable separation on the normals of internal faces - * by flagging them and further customising SAT.js. - * - * @class Phaser.Plugin.ArcadeSlopes.SatRestrainer - * @constructor - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer = function () { - /** - * Restraint definitions for SAT collision handling. - * - * Each restraint is an array of rules, keyed by a corresponding tile type. - * - * Each rule defines a neighbour to check, overlap ranges to match and - * optionally neighbouring tile slope types to match (the same type is used - * otherwise). The separate property determines whether to attempt to - * collide on the tile's preferred axis, if there is one. - * - * Schema: - * [ - * { - * neighbour: 'above'|'below'|'left'|'right'|'topLeft'|'topRight'|'bottomLeft'|'bottomRight' - * overlapX: {integer}|[{integer}, {integer}] - * overlapY: {integer}|[{integer}, {integer}] - * types: {array of neighbour TileSlope type constants} - * separate: {boolean|function(body, tile, response)} - * }, - * { - * ... - * } - * ] - * - * Shorthand schema: - * [ - * { - * neighbour: 'above'|'below'|'left'|'right'|'topLeft'|'topRight'|'bottomLeft'|'bottomRight' - * direction: 'up'|'down'|'left'|'right' - * types: {array of neighbour TileSlope type constants} - * separate: {boolean=true|function(body, tile, response)} - * }, - * { - * ... - * } - * ] - * - * @property {object} restraints - */ - this.restraints = {}; - - /** - * A reusable separation axis vector. - * - * @property {SAT.Vector} separationAxis - */ - this.separationAxis = new SAT.Vector(); - - // Define all of the default restraints - this.setDefaultRestraints(); -}; - -/** - * Restrain the given SAT body-tile collision context based on the set rules. - * - * Returns false if the collision is handled by a restraint condition, either - * triggering separation itself in the best case or skipping it entirely in the - * worst case. - * - * @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#restrain - * @param {Phaser.Plugin.ArcadeSlopes.SatSolver} solver - The SAT solver. - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile} tile - The tile. - * @param {SAT.Response} response - The initial collision response. - * @return {boolean} - Whether to continue collision handling. - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.restrain = function (solver, body, tile, response) { - // Bail out if there's no overlap, no neighbours, or no tile type restraint - if (!response.overlap || !tile.neighbours || !this.restraints.hasOwnProperty(tile.slope.type)) { - return true; - } - - for (var r in this.restraints[tile.slope.type]) { - var rule = this.restraints[tile.slope.type][r]; - - var neighbour = tile.neighbours[rule.neighbour]; - - if (!(neighbour && neighbour.slope)) { - continue; - } - - // Restrain based on the same tile type by default - var condition = false; - - if (rule.types) { - condition = rule.types.indexOf(neighbour.slope.type) > -1; - } else { - condition = neighbour.slope.type === tile.slope.type; - } - - // Restrain based on the overlapN.x value - if (rule.hasOwnProperty('overlapX')) { - if (typeof rule.overlapX === 'number') { - condition = condition && response.overlapN.x === rule.overlapX; - } else { - condition = condition && response.overlapN.x >= rule.overlapX[0] && response.overlapN.x <= rule.overlapX[1]; - } - } - - // Restrain based on the overlapN.y value - if (rule.hasOwnProperty('overlapY')) { - if (typeof rule.overlapY === 'number') { - condition = condition && response.overlapN.y === rule.overlapY; - } else { - condition = condition && response.overlapN.y >= rule.overlapY[0] && response.overlapN.y <= rule.overlapY[1]; - } - } - - // Return false if the restraint condition has been matched - if (condition) { - var separate = rule.separate; - - // Resolve the restraint separation decision if it's a function - if (typeof separate === 'function') { - separate = separate.call(this, body, tile, response); - } - - // Separate on the tile's preferred axis by default - var separationAxis = tile.slope.axis; - - // Use the restraint decision as the axis if it's a vector - if (separate instanceof SAT.Vector) { - separationAxis = separate; - } - - // Collide on the separation axis if desired and available - if (separate && separationAxis) { - solver.collideOnAxis(body, tile, separationAxis); - } - - return false; - } - } - - return true; -}; - -/** - * Resolve overlapX and overlapY restraints from the given direction string. - * - * @static - * @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#resolveOverlaps - * @param {string} direction - * @return {object} - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps = function (direction) { - switch (direction) { - case 'up': - return { - overlapX: 0, - overlapY: [-1, 0] - }; - case 'down': - return { - overlapX: 0, - overlapY: [0, 1] - }; - case 'left': - return { - overlapX: [-1, 0], - overlapY: 0 - }; - case 'right': - return { - overlapX: [0, 1], - overlapY: 0 - }; - case 'any': - return {}; - } - - console.warn('Unknown overlap direction \'' + direction + '\''); - - return {}; -}; - -/** - * Formalizes the given informally defined restraints. - * - * Converts direction properties into overlapX and overlapY properties and - * tile type strings into tile type constants. - * - * This simply allows for more convenient constraint definitions. - * - * @static - * @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#createRestraints - * @param {object} restraints - The restraints to prepare. - * @return {object} - The prepared restraints. - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints = function (restraints) { - var prepared = {}; - - for (var type in restraints) { - var restraint = restraints[type]; - - // Resolve each rule in the restraint - for (var r in restraint) { - var rule = restraint[r]; - - // Resolve overlapX and overlapY restraints from a direction - if (rule.direction) { - var resolved = Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps(rule.direction); - - if (resolved.hasOwnProperty('overlapX')) { - rule.overlapX = resolved.overlapX; - } - - if (resolved.hasOwnProperty('overlapY')) { - rule.overlapY = resolved.overlapY; - } - } - - // Resolve neighbour types from their string representations - for (var nt in rule.types) { - rule.types[nt] = Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(rule.types[nt]); - } - - // Conveniently set separate to true unless it's already false or - // it's a function that resolves a separation decision - if (rule.separate !== false && typeof rule.separate !== 'function') { - rule.separate = true; - } - } - - var restraintType = Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(type); - - prepared[restraintType] = restraint; - } - - return prepared; -}; - -/** - * Eagerly separate a body from a square tile. - * - * This is used for full tile separation constraints to avoid tiny bodies - * slipping between tile seams. - * - * Ignores any non-colliding or internal edges. - * - * Returns a desired axis to separate on, if it can. - * - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile} tile - The tile. - * @param {SAT.Response} response - The initial collision response. - * @return {SAT.Vector|boolean} - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.fullTileSeparation = function (body, tile, response) { - // If the body is above the tile center and top collisions are allowed, - // and we're moving down, and more vertically than horizontally - if (body.top < tile.worldY + tile.centerY && tile.collideUp && tile.slope.edges.top !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.velocity.y > 0 && Math.abs(body.velocity.y) > Math.abs(body.velocity.x)) { - this.separationAxis.x = 0; - this.separationAxis.y = -1; - - return this.separationAxis; - } - - // If the body is below the tile center and bottom collisions are allowed, - // and we're moving up, and more vertically than horizontally - if (body.bottom > tile.worldY + tile.centerY && tile.collideDown && tile.slope.edges.bottom !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.y < 0 && Math.abs(body.slopes.velocity.y) > Math.abs(body.slopes.velocity.x)) { - this.separationAxis.x = 0; - this.separationAxis.y = 1; - - return this.separationAxis; - } - - // If the body is left of the tile center and left collisions are allowed, - // and we're moving right, and more horizontally than vertically - if (body.left < tile.worldX + tile.centerX && tile.collideLeft && tile.slope.edges.left !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.x > 0 && Math.abs(body.slopes.velocity.x) > Math.abs(body.slopes.velocity.y)) { - this.separationAxis.x = -1; - this.separationAxis.y = 0; - - return this.separationAxis; - } - - // If the body is right of the tile center and right collisions are allowed, - // and we're moving left, and more horizontally than vertically - if (body.right > tile.worldX + tile.centerX && tile.collideRight && tile.slope.edges.right !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY && body.slopes.velocity.x < 0 && Math.abs(body.slopes.velocity.x) > Math.abs(body.slopes.velocity.y)) { - this.separationAxis.x = 1; - this.separationAxis.y = 0; - - return this.separationAxis; - } - - // Otherwise separate normally - return true; -}; - -/** - * Set all of the default SAT collision handling restraints. - * - * These are the informally defined hueristics that get refined and utilised - * above. - * - * They were cumbersome to write but they definitely pay off. - * - * @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#setDefaultRestraints - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.setDefaultRestraints = function () { - var restraints = {}; - - restraints.FULL = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottomLeft', 'bottomRight'), - separate: this.fullTileSeparation - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('topLeft', 'topRight'), - separate: this.fullTileSeparation - }, - { - direction: 'left', - neighbour: 'left', - types: this.resolve('topRight', 'bottomRight'), - separate: this.fullTileSeparation - }, - { - direction: 'right', - neighbour: 'right', - types: this.resolve('topLeft', 'bottomLeft'), - separate: this.fullTileSeparation - } - ]; - - restraints.HALF_TOP = [ - { - direction: 'left', - neighbour: 'left', - types: this.resolve('topRight', 'right'), - separate: false - }, - { - direction: 'right', - neighbour: 'right', - types: this.resolve('topLeft', 'left'), - separate: false - } - ]; - - restraints.HALF_BOTTOM = [ - { - direction: 'left', - neighbour: 'left', - types: this.resolve('right', 'bottomRight'), - separate: false - }, - { - direction: 'right', - neighbour: 'right', - types: this.resolve('left', 'bottomLeft'), - separate: false - } - ]; - - restraints.HALF_LEFT = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottomLeft', 'bottom'), - separate: false - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('topLeft', 'top'), - separate: false - } - ]; - - restraints.HALF_RIGHT = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottom', 'bottomRight'), - separate: false - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('top', 'topRight'), - separate: false - } - ]; - - restraints.HALF_BOTTOM_LEFT = [ - { - direction: 'right', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - }, - { - direction: 'up', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - } - ]; - - restraints.HALF_BOTTOM_RIGHT = [ - { - direction: 'left', - neighbour: 'bottomLeft', - types: this.resolve('topRight'), - }, - { - direction: 'up', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - } - ]; - - restraints.HALF_TOP_LEFT = [ - { - direction: 'right', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - }, - { - direction: 'down', - neighbour: 'bottomLeft', - types: this.resolve('topRight') - } - ]; - - restraints.HALF_TOP_RIGHT = [ - { - direction: 'left', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - }, - { - direction: 'down', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - } - ]; - - restraints.QUARTER_BOTTOM_LEFT_LOW = [ - { - direction: 'right', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - }, - { - direction: 'up', - neighbour: 'left', - types: this.resolve('topLeft', 'right', 'bottomRight') - }, - { - direction: 'left', - neighbour: 'left', - types: this.resolve('right', 'bottomRight'), - separate: false - } - ]; - - restraints.QUARTER_BOTTOM_LEFT_HIGH = [ - { - direction: 'right', - neighbour: 'right', - types: this.resolve('left', 'bottomLeft'), - separate: function (body, tile) { - return body.bottom < tile.bottom; - } - }, - { - direction: 'up', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - } - ]; - - restraints.QUARTER_BOTTOM_RIGHT_LOW = [ - { - direction: 'left', - neighbour: 'bottomLeft', - types: this.resolve('topRight') - }, - { - direction: 'up', - neighbour: 'right', - types: this.resolve('topRight', 'left', 'bottomLeft') - }, - { - direction: 'right', - neighbour: 'right', - types: this.resolve('left', 'bottomLeft'), - separate: false - } - ]; - - restraints.QUARTER_BOTTOM_RIGHT_HIGH = [ - { - direction: 'left', - neighbour: 'left', - types: this.resolve('right', 'bottomRight'), - separate: function (body, tile) { - return body.bottom < tile.bottom; - } - }, - { - direction: 'up', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - } - ]; - - restraints.QUARTER_LEFT_BOTTOM_LOW = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottomLeft', 'bottom'), - separate: function (body, tile) { - return body.left > tile.left; - } - }, - { - direction: 'right', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - } - ]; - - restraints.QUARTER_LEFT_BOTTOM_HIGH = [ - { - direction: 'up', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('topLeft', 'top'), - separate: false - }, - { - direction: 'right', - neighbour: 'below', - types: this.resolve('topLeft', 'top', 'bottomRight') - } - ]; - - restraints.QUARTER_RIGHT_BOTTOM_LOW = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottom', 'bottomRight'), - separate: function (body, tile) { - return body.right < tile.right; - } - }, - { - direction: 'left', - neighbour: 'bottomLeft', - types: this.resolve('topRight') - } - ]; - - restraints.QUARTER_RIGHT_BOTTOM_HIGH = [ - { - direction: 'up', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('top', 'topRight'), - separate: false - }, - { - direction: 'left', - neighbour: 'below', - types: this.resolve('top', 'topRight', 'bottomLeft') - } - ]; - - restraints.QUARTER_LEFT_TOP_LOW = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottomLeft', 'bottom') - }, - { - direction: 'right', - neighbour: 'above', - types: this.resolve('bottomLeft', 'bottom'), - separate: false - }, - { - direction: 'down', - neighbour: 'bottomLeft', - types: this.resolve('topRight') - } - ]; - - restraints.QUARTER_LEFT_TOP_HIGH = [ - { - direction: 'right', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('topLeft', 'top'), - separate: function (body, tile) { - return body.left > tile.left; - } - } - ]; - - restraints.QUARTER_RIGHT_TOP_LOW = [ - { - direction: 'up', - neighbour: 'above', - types: this.resolve('bottom', 'bottomRight') - }, - { - direction: 'left', - neighbour: 'above', - types: this.resolve('bottom', 'bottomRight'), - separate: false - }, - { - direction: 'down', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - } - ]; - - restraints.QUARTER_RIGHT_TOP_HIGH = [ - { - direction: 'left', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - }, - { - direction: 'down', - neighbour: 'below', - types: this.resolve('top', 'topRight'), - separate: function (body, tile) { - return body.right < tile.right; - } - } - ]; - - restraints.QUARTER_TOP_LEFT_LOW = [ - { - direction: 'right', - neighbour: 'topRight', - types: this.resolve('bottomLeft') - }, - { - direction: 'left', - neighbour: 'left', - types: this.resolve('topRight', 'right'), - separate: false - }, - { - direction: 'down', - neighbour: 'left', - types: this.resolve('bottomLeft', 'topRight', 'right') - } - ]; - - restraints.QUARTER_TOP_LEFT_HIGH = [ - { - direction: 'right', - neighbour: 'right', - types: this.resolve('topLeft', 'left'), - separate: function (body, tile) { - return body.top > tile.top; - } - }, - { - direction: 'down', - neighbour: 'bottomLeft', - types: this.resolve('topRight') - } - ]; - - restraints.QUARTER_TOP_RIGHT_LOW = [ - { - direction: 'left', - neighbour: 'topLeft', - types: this.resolve('bottomRight') - }, - { - direction: 'right', - neighbour: 'right', - types: this.resolve('topLeft', 'left'), - separate: false - }, - { - direction: 'down', - neighbour: 'right', - types: this.resolve('bottomRight', 'topLeft', 'left') - } - ]; - - restraints.QUARTER_TOP_RIGHT_HIGH = [ - { - direction: 'left', - neighbour: 'left', - types: this.resolve('topRight', 'right'), - separate: function (body, tile) { - return body.top > tile.top; - } - }, - { - direction: 'down', - neighbour: 'bottomRight', - types: this.resolve('topLeft') - } - ]; - - // Keep a copy of the informal restraints for inspection - this.informalRestraints = JSON.parse(JSON.stringify(restraints)); - - this.restraints = Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints(restraints); -}; - -/** - * Compute the intersection of two arrays. - * - * Returns a unique set of values that exist in both arrays. - * - * @method Phaser.Plugin.ArcadeSlopes.SatRestrainer#intersectArrays - * @param {array} a - The first array. - * @param {array} b - The second array. - * @return {array} - The unique set of values shared by both arrays. - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.intersectArrays = function (a, b) { - return a.filter(function (value) { - return b.indexOf(value) !== -1; - }).filter(function (value, index, array) { - return array.indexOf(value) === index; - }); -}; - -/** - * Resolve the types of all tiles with vertices in all of the given locations. - * - * Locations can be: - * 'topLeft', 'top', 'topRight', - * 'left', 'right', - * 'bottomLeft', 'bottom', 'bottomRight' - * - * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#resolve - * @param {...string} locations - A set of AABB vertex locations as strings. - * @return {array} - The tile slope types with matching vertices. - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.resolve = function () { - var types = []; - - if (!arguments.length) { - return types; - } - - // Check the vertex maps of the given locations - for (var l in arguments) { - var location = arguments[l]; - - if (!Phaser.Plugin.ArcadeSlopes.SatRestrainer.hasOwnProperty(location + 'Vertices')) { - console.warn('Tried to resolve types from undefined vertex map location \'' + location + '\''); - continue; - } - - var vertexMap = Array.prototype.slice.call(Phaser.Plugin.ArcadeSlopes.SatRestrainer[location + 'Vertices']); - - // If we only have one location to match, we can return its vertex map - if (arguments.length === 1) { - return vertexMap; - } - - // If we don't have any types yet, use this vertex map to start with, - // otherwise intersect this vertex map with the current types - if (!types.length) { - types = vertexMap; - } else { - types = Phaser.Plugin.ArcadeSlopes.SatRestrainer.intersectArrays(types, vertexMap); - } - } - - return types; -}; - -// TODO: Automate these definitions instead of relying on tedious heuristics. -// Store them in a single vertexMaps property object, too. - -/** - * The set of tile slope types with a top center vertex. - * - * @static - * @property {array} topVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.topVertices = [ - 'HALF_LEFT', - 'HALF_RIGHT', - 'QUARTER_LEFT_TOP_LOW', - 'QUARTER_RIGHT_TOP_LOW', - 'QUARTER_LEFT_BOTTOM_LOW', - 'QUARTER_RIGHT_BOTTOM_LOW' -]; - -/** - * The set of tile slope types with a bottom center vertex. - * - * @static - * @property {array} bottomVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomVertices = [ - 'HALF_LEFT', - 'HALF_RIGHT', - 'QUARTER_LEFT_TOP_HIGH', - 'QUARTER_LEFT_BOTTOM_HIGH', - 'QUARTER_RIGHT_TOP_HIGH', - 'QUARTER_RIGHT_BOTTOM_HIGH' -]; - -/** - * The set of tile slope types with a left center vertex. - * - * @static - * @property {array} leftVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.leftVertices = [ - 'HALF_TOP', - 'HALF_BOTTOM', - 'QUARTER_TOP_LEFT_LOW', - 'QUARTER_TOP_RIGHT_HIGH', - 'QUARTER_BOTTOM_LEFT_LOW', - 'QUARTER_BOTTOM_RIGHT_HIGH' -]; - -/** - * The set of tile slope types with a right center vertex. - * - * @static - * @property {array} rightVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.rightVertices = [ - 'HALF_TOP', - 'HALF_BOTTOM', - 'QUARTER_TOP_LEFT_HIGH', - 'QUARTER_TOP_RIGHT_LOW', - 'QUARTER_BOTTOM_LEFT_HIGH', - 'QUARTER_BOTTOM_RIGHT_LOW', -]; - -/** - * The set of tile slope types with a top left vertex. - * - * @static - * @property {array} topLeftVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.topLeftVertices = [ - 'FULL', - 'HALF_TOP', - 'HALF_LEFT', - 'HALF_TOP_LEFT', - 'HALF_TOP_RIGHT', - 'HALF_BOTTOM_LEFT', - 'QUARTER_TOP_LEFT_LOW', - 'QUARTER_TOP_LEFT_HIGH', - 'QUARTER_TOP_RIGHT_HIGH', - 'QUARTER_BOTTOM_LEFT_HIGH', - 'QUARTER_LEFT_TOP_LOW', - 'QUARTER_LEFT_TOP_HIGH', - 'QUARTER_LEFT_BOTTOM_LOW', - 'QUARTER_LEFT_BOTTOM_HIGH', - 'QUARTER_RIGHT_TOP_HIGH' -]; - -/** - * The set of tile slope types with a top right vertex. - * - * @static - * @property {array} topRightVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.topRightVertices = [ - 'FULL', - 'HALF_TOP', - 'HALF_RIGHT', - 'HALF_TOP_LEFT', - 'HALF_TOP_RIGHT', - 'HALF_BOTTOM_RIGHT', - 'QUARTER_TOP_LEFT_LOW', - 'QUARTER_TOP_LEFT_HIGH', - 'QUARTER_TOP_RIGHT_LOW', - 'QUARTER_TOP_RIGHT_HIGH', - 'QUARTER_BOTTOM_RIGHT_HIGH', - 'QUARTER_LEFT_TOP_HIGH', - 'QUARTER_RIGHT_TOP_LOW', - 'QUARTER_RIGHT_TOP_HIGH', - 'QUARTER_RIGHT_BOTTOM_LOW', - 'QUARTER_RIGHT_BOTTOM_HIGH' -]; - -/** - * The set of tile slope types with a bottom left vertex. - * - * @static - * @property {array} bottomLeftVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomLeftVertices = [ - 'FULL', - 'HALF_LEFT', - 'HALF_BOTTOM', - 'HALF_TOP_LEFT', - 'HALF_BOTTOM_LEFT', - 'HALF_BOTTOM_RIGHT', - 'QUARTER_TOP_LEFT_HIGH', - 'QUARTER_BOTTOM_LEFT_LOW', - 'QUARTER_BOTTOM_LEFT_HIGH', - 'QUARTER_BOTTOM_RIGHT_LOW', - 'QUARTER_BOTTOM_RIGHT_HIGH', - 'QUARTER_LEFT_TOP_HIGH', - 'QUARTER_LEFT_BOTTOM_LOW', - 'QUARTER_LEFT_BOTTOM_HIGH', - 'QUARTER_RIGHT_BOTTOM_LOW' -]; - -/** - * The set of tile slope types with a bottom right vertex. - * - * @static - * @property {array} bottomRightVertices - */ -Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomRightVertices = [ - 'FULL', - 'HALF_RIGHT', - 'HALF_BOTTOM', - 'HALF_TOP_RIGHT', - 'HALF_BOTTOM_LEFT', - 'HALF_BOTTOM_RIGHT', - 'QUARTER_TOP_RIGHT_HIGH', - 'QUARTER_BOTTOM_LEFT_LOW', - 'QUARTER_BOTTOM_LEFT_HIGH', - 'QUARTER_BOTTOM_RIGHT_LOW', - 'QUARTER_BOTTOM_RIGHT_HIGH', - 'QUARTER_LEFT_BOTTOM_LOW', - 'QUARTER_RIGHT_TOP_HIGH', - 'QUARTER_RIGHT_BOTTOM_LOW', - 'QUARTER_RIGHT_BOTTOM_HIGH' -]; - /** * @author Chris Andrew * @copyright 2016-2017 Chris Andrew @@ -1749,22 +810,41 @@ Phaser.Plugin.ArcadeSlopes.SatSolver = function (options) { debug: false, // Whether to prefer the minimum Y offset over the smallest separation - preferY: false, - - // Whether to restrain SAT collisions - restrain: true + preferY: false }); /** - * Objects that have the chance to process collisions themselves. - * - * They should expose a restrain() function. - * - * @property {object[]} restrainers + * A pool of arrays to use for calculations. + * + * @property {Array[]} arrayPool */ - this.restrainers = [ - new Phaser.Plugin.ArcadeSlopes.SatRestrainer() - ]; + this.arrayPool = []; + + for (var i = 0; i < 10; i++) { + this.arrayPool.push([]); + } + + /** + * A pool of vectors to use for calculations. + * + * @property {SAT.Vector[]} vectorPool + */ + this.vectorPool = []; + + for (i = 0; i < 20; i++) { + this.vectorPool.push(new SAT.Vector()); + } + + /** + * A pool of responses to use for collision tests. + * + * @property {SAT.Response[]} responsePool + */ + this.responsePool = []; + + for (i = 0; i < 20; i++) { + this.responsePool.push(new SAT.Response()); + } }; /** @@ -1801,6 +881,27 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse = function (response) { return response; }; +/** + * Copy the values of one SAT response to another. + * + * @static + * @method Phaser.Plugin.ArcadeSlopes.SatSolver#copyResponse + * @param {SAT.Response} a - The source response. + * @param {SAT.Response} b - The target response. + * @return {SAT.Response} + */ +Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse = function (a, b) { + b.a = a.a; + b.b = a.b; + b.aInB = a.aInB; + b.bInA = a.bInA; + b.overlap = a.overlap; + b.overlapN.copy(a.overlapN); + b.overlapV.copy(a.overlapV); + + return b; +}; + /** * Calculate the minimum X offset given an overlap vector. * @@ -1862,29 +963,6 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldPreferY = function (body, r Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY(body, response); // And we're moving into the shape }; -/** - * Determine whether two polygons intersect on a given axis. - * - * @static - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#isSeparatingAxis - * @param {SAT.Polygon} a - The first polygon. - * @param {SAT.Polygon} b - The second polygon. - * @param {SAT.Vector} axis - The axis to test. - * @param {SAT.Response} response - The response to populate. - * @return {boolean} - Whether a separating axis was found. - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.isSeparatingAxis = function (a, b, axis, response) { - var result = SAT.isSeparatingAxis(a.pos, b.pos, a.points, b.points, axis, response || null); - - if (response) { - response.a = a; - response.b = b; - response.overlapV = response.overlapN.clone().scale(response.overlap); - } - - return result; -}; - /** * Separate a body from a tile using the given SAT response. * @@ -1910,7 +988,7 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.separate = function (body, tile, return false; } - // Separate the body from the tile + // Separate the body from the tile, using the minimum Y offset if preferred if (this.shouldPreferY(body, response)) { body.position.y += Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY(response.overlapV); } else { @@ -1934,10 +1012,10 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.separate = function (body, tile, */ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity = function (body, tile, response) { // Project our velocity onto the overlap normal for the bounce vector (Vn) - var bounce = body.slopes.velocity.clone().projectN(response.overlapN); + var bounce = this.vectorPool.pop().copy(body.slopes.velocity).projectN(response.overlapN); // Then work out the surface vector (Vt) - var friction = body.slopes.velocity.clone().sub(bounce); + var friction = this.vectorPool.pop().copy(body.slopes.velocity).sub(bounce); // Apply bounce coefficients bounce.x = bounce.x * (-body.bounce.x); @@ -1953,6 +1031,9 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity = function (body, t // Process collision pulling this.pull(body, response); + + // Recycle the vectors we used for bounce and friction + this.vectorPool.push(bounce, friction); }; /** @@ -1977,93 +1058,20 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateValues = function (body) { * @method Phaser.Plugin.ArcadeSlopes.SatSolver#updateFlags * @param {Phaser.Physics.Arcade.Body} body - The physics body. * @param {SAT.Response} response - The SAT response. - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateFlags = function (body, response) { - // Set the touching values - body.touching.up = body.touching.up || response.overlapV.y > 0; - body.touching.down = body.touching.down || response.overlapV.y < 0; - body.touching.left = body.touching.left || response.overlapV.x > 0; - body.touching.right = body.touching.right || response.overlapV.x < 0; - body.touching.none = !body.touching.up && !body.touching.down && !body.touching.left && !body.touching.right; - - // Set the blocked values - body.blocked.up = body.blocked.up || response.overlapV.x === 0 && response.overlapV.y > 0; - body.blocked.down = body.blocked.down || response.overlapV.x === 0 && response.overlapV.y < 0; - body.blocked.left = body.blocked.left || response.overlapV.y === 0 && response.overlapV.x > 0; - body.blocked.right = body.blocked.right || response.overlapV.y === 0 && response.overlapV.x < 0; -}; - -/** - * Attempt to snap the body to a given set of tiles based on its slopes options. - * - * TODO: Maybe remove snapping altogether. - * - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#snap - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile[]} tiles - The tiles. - * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer. - * @return {boolean} - Whether the body was snapped to any tiles. - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.snap = function (body, tiles, tilemapLayer) { - if (!body.slopes || (!body.slopes.snapUp && !body.slopes.snapDown && !body.slopes.snapLeft && !body.slopes.snapRight)) { - return false; - } - - // Keep the current body position to snap from - // TODO: Get rid of the instantiation - var current = new Phaser.Point(body.position.x, body.position.y); - - // Keep track of whether the body has snapped to a tile - var snapped = false; - - // For each tile, move the body in each direction by the configured amount, - // and try to collide, returning the body to its original position if no - // collision occurs - for (var t in tiles) { - var tile = tiles[t]; - - if (!tile.slope) { - continue; - } - - if (body.slopes.snapUp) { - body.position.x = current.x; - body.position.y = current.y - body.slopes.snapUp; - - if (this.snapCollide(body, tile, tilemapLayer, current)) { - return true; - } - } - - if (body.slopes.snapDown) { - body.position.x = current.x; - body.position.y = current.y + body.slopes.snapDown; - - if (this.snapCollide(body, tile, tilemapLayer, current)) { - return true; - } - } - - if (body.slopes.snapLeft) { - body.position.x = current.x - body.slopes.snapLeft; - body.position.y = current.y; - - if (this.snapCollide(body, tile, tilemapLayer, current)) { - return true; - } - } - - if (body.slopes.snapRight) { - body.position.x = current.x + body.slopes.snapRight; - body.position.y = current.y; - - if (this.snapCollide(body, tile, tilemapLayer, current)) { - return true; - } - } - } + */ +Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateFlags = function (body, response) { + // Set the touching values + body.touching.up = body.touching.up || response.overlapV.y > 0; + body.touching.down = body.touching.down || response.overlapV.y < 0; + body.touching.left = body.touching.left || response.overlapV.x > 0; + body.touching.right = body.touching.right || response.overlapV.x < 0; + body.touching.none = !body.touching.up && !body.touching.down && !body.touching.left && !body.touching.right; - return false; + // Set the blocked values + body.blocked.up = body.blocked.up || response.overlapV.x === 0 && response.overlapV.y > 0; + body.blocked.down = body.blocked.down || response.overlapV.x === 0 && response.overlapV.y < 0; + body.blocked.left = body.blocked.left || response.overlapV.y === 0 && response.overlapV.x > 0; + body.blocked.right = body.blocked.right || response.overlapV.y === 0 && response.overlapV.x < 0; }; /** @@ -2164,38 +1172,288 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.pull = function (body, response) }; /** - * Perform a snap collision between the given body and tile, setting the body - * back to the given current position if it fails. + * Determine whether everything required to process a collision is available. * - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#snapCollide - * @param {Phaser.Physics.Arcade.Body} body - The translated physics body. - * @param {Phaser.Tile} tile - The tile. - * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer. - * @param {Phaser.Point} current - The original position of the body. - * @return {boolean} - Whether the body snapped to the tile. + * @method Phaser.Plugin.ArcadeSlopes.SatSolver#shouldCollide + * @param {Phaser.Physics.Arcade.Body} body - The physics body. + * @param {Phaser.Tile} tile - The tile. + * @return {boolean} */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.snapCollide = function (body, tile, tilemapLayer, current) { - if (this.collide(0, body, tile, tilemapLayer)) { +Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldCollide = function (body, tile) { + return body.enable && body.polygon && body.slopes && tile.collides && tile.slope && tile.slope.polygon; +}; + +/** + * Flattens the specified array of points onto a unit vector axis, + * resulting in a one dimensional range of the minimum and + * maximum value on that axis. + * + * Copied verbatim from SAT.flattenPointsOn. + * + * @see SAT.flattenPointsOn + * @static + * @method Phaser.Plugin.ArcadeSlopes.SatSolver#flattenPointsOn + * @param {SAT.Vector[]} points - The points to flatten. + * @param {SAT.Vector} normal - The unit vector axis to flatten on. + * @param {number[]} result - An array. After calling this, + * result[0] will be the minimum value, + * result[1] will be the maximum value. + */ +Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn = function (points, normal, result) { + var min = Number.MAX_VALUE; + var max = -Number.MAX_VALUE; + var len = points.length; + + for (var i = 0; i < len; i++ ) { + // The magnitude of the projection of the point onto the normal + var dot = points[i].dot(normal); + if (dot < min) { min = dot; } + if (dot > max) { max = dot; } + } + + result[0] = min; result[1] = max; +}; + +/** + * Determine whether two polygons are separated by a given axis. + * + * Tailored to only push out in the direction of the given axis. + * + * Adapted from SAT.isSeparatingAxis. + * + * @see {SAT.isSeparatingAxis} + * @method Phaser.Plugin.ArcadeSlopes.SatSolver#isSeparatingAxis + * @param {SAT.Polygon} a - The first polygon. + * @param {SAT.Polygon} b - The second polygon. + * @param {SAT.Vector} axis - The axis (unit sized) to test against. + * The points of both polygons are projected + * onto this axis. + * @param {SAT.Response} response - The response to populate if the polygons are + * not separated by the given axis. + * @return {boolean} true if it is a separating axis, false otherwise. If false, + * and a response is passed in, information about how much overlap and + * the direction of the overlap will be populated. + */ +Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.isSeparatingAxis = function (a, b, axis, response) { + var aPos = a.pos; + var bPos = b.pos; + var aPoints = a.calcPoints; + var bPoints = b.calcPoints; + + var rangeA = this.arrayPool.pop(); + var rangeB = this.arrayPool.pop(); + + // The magnitude of the offset between the two polygons + var offsetV = this.vectorPool.pop().copy(bPos).sub(aPos); + var projectedOffset = offsetV.dot(axis); + + // Project the polygons onto the axis. + Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(aPoints, axis, rangeA); + Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(bPoints, axis, rangeB); + + // Move B's range to its position relative to A. + rangeB[0] += projectedOffset; + rangeB[1] += projectedOffset; + + // Check if there is a gap. If there is, this is a separating axis and we can stop + if (rangeA[0] >= rangeB[1] || rangeB[0] >= rangeA[1]) { + this.vectorPool.push(offsetV); + this.arrayPool.push(rangeA); + this.arrayPool.push(rangeB); return true; } - // There was no collision, so reset the body position - body.position.x = current.x; - body.position.y = current.y; + var option1, option2; + + // This is not a separating axis. If we're calculating a response, calculate + // the overlap + var overlap = 0; + + if (rangeA[0] < rangeB[0]) { + // A starts further left than B + response.aInB = false; + + if (rangeA[1] < rangeB[1]) { + // A ends before B does. We have to pull A out of B + //overlap = rangeA[1] - rangeB[0]; + response.bInA = false; + }// else { + // B is fully inside A. Pick the shortest way out. + //option1 = rangeA[1] - rangeB[0]; + //option2 = rangeB[1] - rangeA[0]; + //overlap = option1 < option2 ? option1 : -option2; + //} + } else { + // B starts further left than A + response.bInA = false; + + if (rangeA[1] > rangeB[1]) { + // B ends before A ends. We have to push A out of B + overlap = rangeA[0] - rangeB[1]; + response.aInB = false; + } else { + // A is fully inside B. Pick the shortest way out. + option1 = rangeA[1] - rangeB[0]; + option2 = rangeB[1] - rangeA[0]; + //overlap = option1 < option2 ? option1 : -option2; + + if (option1 >= option2) { + overlap = -option2; + } + } + } + + // If this is the smallest amount of overlap we've seen so far, set it + // as the minimum overlap. + var absOverlap = Math.abs(overlap); + + if (absOverlap < response.overlap) { + response.overlap = absOverlap; + response.overlapN.copy(axis); + + if (overlap < 0) { + response.overlapN.reverse(); + } + } + + this.vectorPool.push(offsetV); + this.arrayPool.push(rangeA); + this.arrayPool.push(rangeB); return false; }; /** - * Determine whether everything required to process a collision is available. + * Test whether two polygons overlap. * - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#shouldCollide - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile} tile - The tile. - * @return {boolean} - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldCollide = function (body, tile) { - return body.enable && body.polygon && body.slopes && tile.collides && tile.slope && tile.slope.polygon; + * Takes a response object that will be populated with the shortest + * viable separation vector. Ignores collision responses that don't oppose + * velocity enough. + * + * Returns true if there is a collision and false otherwise. + * + * Tailored to work with an AABB as the first polygon. + * + * Adapted from SAT.testPolygonPolygon. + * + * @see {SAT.testPolygonPolygon} + * @method Phaser.Plugin.ArcadeSlopes.SatSolver#testPolygonPolygon + * @param {SAT.Polygon} a - The first polygon. + * @param {SAT.Polygon} b - The second polygon. + * @param {SAT.Response} response - The response object to populate with overlap information. + * @param {SAT.Vector} velocity - The velocity vector to ignore. + * @param {SAT.Vector[]} ignore - The axes to ignore. + * @return {boolean} - Whether the the two polygons overlap. + */ +Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.testPolygonPolygon = function (a, b, response, velocity, ignore) { + var aPoints = a.calcPoints; + var aLen = aPoints.length; + var bPoints = b.calcPoints; + var bLen = bPoints.length; + + var i, j, k; + var responses = this.arrayPool.pop(); + var axes = this.arrayPool.pop(); + + responses.length = 0; + axes.length = 0; + + // If any of the edge normals of A is a separating axis, no intersection + for (i = 0; i < aLen; i++) { + responses[i] = this.responsePool.pop(); + responses[i].clear(); + axes[i] = a.normals[i]; + + if (this.isSeparatingAxis(a, b, a.normals[i], responses[i])) { + for (k = 0; k < responses.length; k++) { + this.responsePool.push(responses[k]); + } + + this.arrayPool.push(responses, axes); + + return false; + } + } + + // If any of the edge normals of B is a separating axis, no intersection + for (i = 0, j = aLen; i < bLen; i++, j++) { + responses[j] = this.responsePool.pop(); + responses[j].clear(); + axes[j] = b.normals[i]; + + if (this.isSeparatingAxis(a, b, b.normals[i], responses[j])) { + for (k = 0; k < responses.length; k++) { + this.responsePool.push(responses[k]); + } + + this.arrayPool.push(responses, axes); + + return false; + } + } + + // Since none of the edge normals of A or B are a separating axis, there is + // an intersection + + var viable = false; + var ignored = false; + var velocityTestVector = this.vectorPool.pop(); + + // Determine the shortest desirable and viable separation from the responses + for (i = 0; i < responses.length; i++) { + // Is the overlap in the range we want? + // TODO: Less than the max of tile width/height? + if (!(responses[i].overlap > 0 && responses[i].overlap < Number.MAX_VALUE)) { + continue; + } + + // Is the overlap direction too close to that of the velocity direction? + if (velocity && velocityTestVector.copy(responses[i].overlapN).scale(-1).dot(velocity) > 0) { + continue; + } + + ignored = false; + + // Is the axis of the overlap in the extra ignore list? + for (j = 0; j < ignore.length; j++) { + if (axes[i].x === ignore[j].x && axes[i].y === ignore[j].y) { + ignored = true; + + break; + } + } + + // Skip this response if its normal is ignored + if (ignored) { + continue; + } + + // Is this response's overlap shorter than that of the current? + if (responses[i].overlap < response.overlap) { + viable = true; + response.aInB = responses[i].aInB; + response.bInA = responses[i].bInA; + response.overlap = responses[i].overlap; + response.overlapN = responses[i].overlapN; + } + } + + // Set the polygons on the response and calculate the overlap vector + if (viable) { + response.a = a; + response.b = b; + response.overlapV.copy(response.overlapN).scale(response.overlap); + } + + // Recycle the temporary responses, arrays and vectors used for calculations + for (k = 0; k < responses.length; k++) { + this.responsePool.push(responses[k]); + } + + this.arrayPool.push(responses, axes); + this.vectorPool.push(velocityTestVector); + + return viable; }; /** @@ -2229,34 +1487,51 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide = function (i, body, tile tile.slope.polygon.pos.x = tile.worldX + tilemapLayer.getCollisionOffsetX(); tile.slope.polygon.pos.y = tile.worldY + tilemapLayer.getCollisionOffsetY(); - // Reuse the body's response or create one for it - var response = body.slopes.sat.response || new SAT.Response(); + // Create the body's response if it doesn't have one + body.slopes.sat.response = body.slopes.sat.response || new SAT.Response(); - // Reset the response + // Acquire a temporary response from the pool + var response = this.responsePool.pop(); Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse(response); - // Test for an overlap and bail if there isn't one - if ((body.isCircle && !SAT.testCirclePolygon(body.polygon, tile.slope.polygon, response)) || (!body.isCircle && !SAT.testPolygonPolygon(body.polygon, tile.slope.polygon, response))) { + // Test for an overlap + var circleOverlap = body.isCircle && SAT.testCirclePolygon(body.polygon, tile.slope.polygon, response); + var polygonOverlap = !body.isCircle && this.testPolygonPolygon(body.polygon, tile.slope.polygon, response, body.slopes.velocity, tile.slope.ignormals); + + // Bail if there isn't one, leaving the body's response as is + if (!circleOverlap && !polygonOverlap) { + this.responsePool.push(response); + return false; } + // Invert our overlap vectors so that we have them facing outwards + Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(response); + // If we're only testing for the overlap, we can bail here if (overlapOnly) { + Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse(response, body.slopes.sat.response); + this.responsePool.push(response); + return true; } - // Invert our overlap vectors so that we have them facing outwards - Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(response); - - // Bail out if no separation occurred, resetting the response + // Bail out if no separation occurred if (!this.separate(body, tile, response)) { + this.responsePool.push(response); + return false; } + // Copy the temporary response into the body's response, then recycle it + Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse(response, body.slopes.sat.response); + this.responsePool.push(response); + + response = body.slopes.sat.response; + // Update the overlap properties of the body body.overlapX = response.overlapV.x; body.overlapY = response.overlapV.y; - body.slopes.sat.response = response; // Set the tile that the body separated from body.slopes.tile = tile; @@ -2270,79 +1545,6 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide = function (i, body, tile return true; }; -/** - * Collide a body with a tile on a specific axis. - * - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#collideOnAxis - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile} tile - The tile. - * @param {SAT.Vector} axis - The axis unit vector. - * @param {SAT.Response} response - The SAT response to use. - * @return {boolean} - Whether the body was separated. - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collideOnAxis = function (body, tile, axis, response) { - // Update the body's polygon position and velocity vector - this.updateValues(body); - - // Bail out if we don't have everything we need or the body is circular - if (!this.shouldCollide(body, tile) || body.isCircle) { - return false; - } - - response = response || new SAT.Response(); - - var separatingAxis = Phaser.Plugin.ArcadeSlopes.SatSolver.isSeparatingAxis(body.polygon, tile.slope.polygon, axis, response); - - if (separatingAxis) { - return false; - } - - // Invert our overlap vectors so that we have them facing outwards - Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(response); - - // Bail out if no separation occurred, resetting the response - if (!this.separate(body, tile, response, true)) { - return false; - } - - // Update the overlap properties of the body - body.overlapX = response.overlapV.x; - body.overlapY = response.overlapV.y; - body.slopes.sat.response = response; - - this.applyVelocity(body, tile, response); - this.updateFlags(body, response); - - return true; -}; - -/** - * Run a constraint check for the given physics body, tile and response. - * - * @method Phaser.Plugin.ArcadeSlopes.SatSolver#restrain - * @param {Phaser.Physics.Arcade.Body} body - The physics body. - * @param {Phaser.Tile} tile - The tile. - * @param {SAT.Response} response - The initial collision response. - * @return {boolean} - Whether the collision was restrained. - */ -Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.restrain = function (body, tile, response) { - for (var r in this.restrainers) { - var restrainer = this.restrainers[r]; - - // Skip anything without a restrain function - if (typeof restrainer.restrain !== 'function') { - continue; - } - - // Bail if the restrainer dealt with the collision by itself - if (!restrainer.restrain(this, body, tile, response)) { - return true; - } - } - - return false; -}; - /** * Determine whether to separate a body from a tile, given an SAT response. * @@ -2361,34 +1563,10 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldSeparate = function (i, bod return false; } - // Only separate if the body is moving into the tile - if (response.overlapV.clone().scale(-1).dot(body.slopes.velocity) < 0) { - return false; - } - - // Run any separation restrainers if appropriate - if ((this.options.restrain || body.slopes.heuristics) && body.slopes.heuristics !== false && !body.isCircle) { - if (this.restrain(body, tile, response)) { - return false; - } - } - - // Ignore any non-colliding or internal edges - if ((!tile.collideUp || tile.slope.edges.top === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y < 0 && response.overlapN.x === 0) { - return false; - } - - if ((!tile.collideDown || tile.slope.edges.bottom === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.y > 0 && response.overlapN.x === 0) { - return false; - } - - if ((!tile.collideLeft || tile.slope.edges.left === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x < 0 && response.overlapN.y === 0) { - return false; - } - - if ((!tile.collideRight || tile.slope.edges.right === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) && response.overlapN.x > 0 && response.overlapN.y === 0) { - return false; - } + // Only separate if the body is moving into the collision + // if (response.overlapV.clone().scale(-1).dot(body.slopes.velocity) < 0) { + // return false; + // } // Otherwise we should separate normally return true; @@ -2420,14 +1598,15 @@ Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.debug = function (position, respo * * @class Phaser.Plugin.ArcadeSlopes.TileSlope * @constructor - * @param {integer} type - The type of the tile slope. - * @param {Phaser.Tile} tile - The tile this slope definition belongs to. - * @param {SAT.Polygon} polygon - The polygon representing the shape of the tile. - * @param {Phaser.Line} line - The line representing the slope of the tile. - * @param {object} edges - The flags for each edge of the tile. - * @param {SAT.Vector} axis - The preferred axis for separating physics bodies. - */ -Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edges, axis) { + * @param {integer} type - The type of the tile slope. + * @param {Phaser.Tile} tile - The tile this slope definition belongs to. + * @param {SAT.Polygon} polygon - The polygon representing the shape of the tile. + * @param {Phaser.Line} line - The line representing the slope of the tile. + * @param {object} edges - The flags for each edge of the tile. + * @param {SAT.Vector} axis - The preferred axis for separating physics bodies. + * @param {SAT.Vector[]} [ignormals] - An optional set of collision normals to ignore. + */ +Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edges, axis, ignormals) { /** * The type of the tile slope. * @@ -2475,6 +1654,13 @@ Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edge */ this.axis = axis || null; + /** + * An optional set of collision normals to ignore. + * + * @property {SAT.Vector[]} ignormals + */ + this.ignormals = ignormals || []; + /** * The preferred solver to use for this slope. * @@ -2983,6 +2169,17 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory = function () { this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes; this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics; + + /** + * A pool of vectors. + * + * @property {SAT.Vector[]} vectorPool + */ + this.vectorPool = []; + + for (var i = 0; i < 100; i++) { + this.vectorPool.push(new SAT.Vector()); + } }; Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.constructor = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory; @@ -3124,20 +2321,23 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemapLayer = func /** * Calculate the edge flags for each tile in the given tilemap layer. * + * TODO: Allow this to work with an optional range of tile coordinates. + * * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#calculateEdges * @param {Phaser.TilemapLayer} layer - The tilemap layer to calculate edge flags for. */ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges = function (layer) { - var above = null; - var below = null; - var left = null; - var right = null; - - for (var y = 0, h = layer.layer.height; y < h; y++) { - for (var x = 0, w = layer.layer.width; x < w; x++) { - var tile = layer.layer.data[y][x]; + var x, y, h, w, tile, above, below, left, right; + + h = layer.layer.height; + w = layer.layer.width; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + tile = layer.layer.data[y][x]; if (tile && tile.hasOwnProperty('slope')) { + // Compare edges and flag internal vertices above = layer.map.getTileAbove(layer.index, x, y); below = layer.map.getTileBelow(layer.index, x, y); left = layer.map.getTileLeft(layer.index, x, y); @@ -3169,6 +2369,16 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges = function } } } + + // Flag further normals that we want to ignore for this tile, now that all + // of the edges have been set + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + tile = layer.layer.data[y][x]; + + this.flagIgnormals(tile); + } + } }; /** @@ -3202,9 +2412,6 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.compareEdges = function (f * Because the polygons are represented by a set of points, instead of actual * edges, the first vector (assuming they are specified clockwise) of each * potential edge is flagged instead. - * - * TODO: Optimise by bailing if both first vertices are already flagged and - * possibly by avoiding SAT.Vector instantiation. * * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#flagInternalVertices * @param {Phaser.Tile} firstTile - The first tile to compare. @@ -3216,37 +2423,190 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.flagInternalVertices = fun return; } + // Access the tile polygons and grab some vectors from the pool var firstPolygon = firstTile.slope.polygon; var secondPolygon = secondTile.slope.polygon; - var firstPosition = new SAT.Vector(firstTile.worldX, firstTile.worldY); - var secondPosition = new SAT.Vector(secondTile.worldX, secondTile.worldY); + var firstPosition = this.vectorPool.pop(); + var secondPosition = this.vectorPool.pop(); + var firstTileVertexOne = this.vectorPool.pop(); + var firstTileVertexTwo = this.vectorPool.pop(); + var secondTileVertexOne = this.vectorPool.pop(); + var secondTileVertexTwo = this.vectorPool.pop(); + var exactMatch; + var inverseMatch; + + // TODO: Take into account tilemap offset... + firstPosition.x = firstTile.worldX; + firstPosition.y = firstTile.worldY; + secondPosition.x = secondTile.worldX; + secondPosition.y = secondTile.worldY; for (var i = 0; i < firstPolygon.points.length; i++) { - var firstTileVertexOne = firstPolygon.points[i].clone().add(firstPosition); - var firstTileVertexTwo = firstPolygon.points[(i + 1) % firstPolygon.points.length].clone().add(firstPosition); + firstTileVertexOne.copy(firstPolygon.points[i]).add(firstPosition); + firstTileVertexTwo.copy(firstPolygon.points[(i + 1) % firstPolygon.points.length]).add(firstPosition); for (var j = 0; j < secondPolygon.points.length; j++) { - var secondTileVertexOne = secondPolygon.points[j].clone().add(secondPosition); - var secondTileVertexTwo = secondPolygon.points[(j + 1) % secondPolygon.points.length].clone().add(secondPosition); + secondTileVertexOne.copy(secondPolygon.points[j]).add(secondPosition); + secondTileVertexTwo.copy(secondPolygon.points[(j + 1) % secondPolygon.points.length]).add(secondPosition); // Now we can compare vertices for an exact or inverse match - var exactMatch = firstTileVertexOne.x === secondTileVertexOne.x && + exactMatch = firstTileVertexOne.x === secondTileVertexOne.x && firstTileVertexOne.y === secondTileVertexOne.y && firstTileVertexTwo.x === secondTileVertexTwo.x && firstTileVertexTwo.y === secondTileVertexTwo.y; - var inverseMatch = firstTileVertexOne.x === secondTileVertexTwo.x && + inverseMatch = firstTileVertexOne.x === secondTileVertexTwo.x && firstTileVertexOne.y === secondTileVertexTwo.y && firstTileVertexTwo.x === secondTileVertexOne.x && firstTileVertexTwo.y === secondTileVertexOne.y; - // Flag the vertices that begin an internal edge + // Flag the first vertex and the normal of the internal edge if (exactMatch || inverseMatch) { - firstPolygon.points[i].internal = true; - secondPolygon.points[j].internal = true; + firstPolygon.normals[i].ignore = true; + secondPolygon.normals[j].ignore = true; + + firstTile.slope.ignormals.push(firstPolygon.normals[i]); + secondTile.slope.ignormals.push(secondPolygon.normals[j]); } } } + + // Recycle the vectors we used + this.vectorPool.push( + firstPosition, secondPosition, firstTileVertexOne, firstTileVertexTwo, + secondTileVertexOne, secondTileVertexTwo + ); +}; + +/** + * Flag further normals to ignore to prevent unwanted collision responses. + * + * Simply observes Phaser's edge flags of neighbouring tiles to decide whether + * axis-aligned collisions are desirable. + * + * Heuristics 2.0. Generalised instead of tile-specific. + * + * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#flagIgnormals + * @param {Phaser.Tile} tile - The tile to flag ignormals for. + */ +Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.flagIgnormals = function (tile) { + if (!tile.slope || !tile.slope.polygon) { + return; + } + + // Clear the current ignormals list for this tile + //tile.slope.ignormals.length = 0; + + // Skip full and half blocks + // TODO: Skip any tiles with purely axis-aligned edges + if (tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.FULL || + tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP || + tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM || + tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT || + tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT + ) { + return; + } + + // Define some shorthand variables to use in the conditions + var empty = Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY; + var interesting = Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING; + var solid = Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID; + var above = tile.neighbours.above; + var below = tile.neighbours.below; + var left = tile.neighbours.left; + var right = tile.neighbours.right; + var topLeft = tile.neighbours.topLeft; + var topRight = tile.neighbours.topRight; + var bottomLeft = tile.neighbours.bottomLeft; + var bottomRight = tile.neighbours.bottomRight; + + // Skip neighbours without a TileSlope + if (above && !above.hasOwnProperty('slope')) { + above = null; + } + + if (below && !below.hasOwnProperty('slope')) { + below = null; + } + + if (left && !left.hasOwnProperty('slope')) { + left = null; + } + + if (right && !right.hasOwnProperty('slope')) { + right = null; + } + + if (topLeft && !topLeft.hasOwnProperty('slope')) { + topLeft = null; + } + + if (topRight && !topRight.hasOwnProperty('slope')) { + topRight = null; + } + + if (bottomLeft && !bottomLeft.hasOwnProperty('slope')) { + bottomLeft = null; + } + + if (bottomRight && !bottomRight.hasOwnProperty('slope')) { + bottomRight = null; + } + + // Determine the interesting edges of the current tile + var topInteresting = tile.slope.edges.top === interesting; + var bottomInteresting = tile.slope.edges.bottom === interesting; + var leftInteresting = tile.slope.edges.left === interesting; + var rightInteresting = tile.slope.edges.right === interesting; + + // Skip top collisions + if (topInteresting && ( + (topLeft && topLeft.slope.edges.right !== empty) || + (topRight && topRight.slope.edges.left !== empty) || + (leftInteresting && rightInteresting && ( + (left && left.slope.edges.right !== empty && left.slope.edges.top !== solid && left.slope.edges.left !== interesting) || + (right && right.slope.edges.left !== empty && right.slope.edges.top !== solid && right.slope.edges.right !== interesting) + )) + )) { + tile.slope.ignormals.push(new SAT.Vector(0, -1)); + } + + // Skip bottom collisions + if (bottomInteresting && ( + (bottomLeft && bottomLeft.slope.edges.right !== empty) || + (bottomRight && bottomRight.slope.edges.left !== empty) || + (leftInteresting && rightInteresting && ( + (left && left.slope.edges.right !== empty && left.slope.edges.bottom !== solid && left.slope.edges.left !== interesting) || + (right && right.slope.edges.left !== empty && right.slope.edges.bottom !== solid && right.slope.edges.right !== interesting) + )) + )) { + tile.slope.ignormals.push(new SAT.Vector(0, 1)); + } + + // Skip left collisions + if (leftInteresting && ( + (topLeft && topLeft.slope.edges.bottom !== empty) || + (bottomLeft && bottomLeft.slope.edges.top !== empty) || + (topInteresting && bottomInteresting && ( + (above && above.slope.edges.bottom !== empty && above.slope.edges.left !== solid && above.slope.edges.top !== interesting) || + (below && below.slope.edges.top !== empty && below.slope.edges.left !== solid && below.slope.edges.bottom !== interesting) + )) + )) { + tile.slope.ignormals.push(new SAT.Vector(-1, 0)); + } + + // Skip right collisions + if (rightInteresting && ( + (topRight && topRight.slope.edges.bottom !== empty) || + (bottomRight && bottomRight.slope.edges.top !== empty) || + (topInteresting && bottomInteresting && ( + (above && above.slope.edges.bottom !== empty && above.slope.edges.right !== solid && above.slope.edges.top !== interesting) || + (below && below.slope.edges.top !== empty && below.slope.edges.right !== solid && below.slope.edges.bottom !== interesting) + )) + )) { + tile.slope.ignormals.push(new SAT.Vector(1, 0)); + } }; /** @@ -3256,10 +2616,15 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.flagInternalVertices = fun * @param {Phaser.TilemapLayer} layer - The tilemap layer. */ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.addDebugSettings = function (layer) { + layer._mc.edgeMidpoint = new SAT.Vector(); layer.debugSettings.slopeFill = 'rgba(255, 0, 255, 0.2)'; layer.debugSettings.slopeEdgeStroke = 'rgba(255, 0, 255, 0.4)'; layer.debugSettings.slopeCollidingEdgeStroke = 'rgba(255, 0, 255, 1)'; layer.debugSettings.slopeCollidingEdgeStrokeWidth = 2; + layer.debugSettings.slopeNormalStroke = 'rgba(0, 255, 255, 0.4)'; + layer.debugSettings.slopeNormalStrokeWidth = 1; + layer.debugSettings.slopeCollidingNormalStroke = 'rgba(0, 255, 255, 1)'; + layer.debugSettings.slopeCollidingNormalStrokeWidth = 2; }; /** @@ -3854,7 +3219,7 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow = function var edges = { top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING, bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING, - right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING + left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING }; var axis = new SAT.Vector(-0.8944271909999159, 0.4472135954999579); @@ -3883,7 +3248,7 @@ Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh = function var edges = { bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING, - right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING + left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING }; var axis = new SAT.Vector(-0.8944271909999159, 0.4472135954999579); diff --git a/dist/phaser-arcade-slopes.min.js b/dist/phaser-arcade-slopes.min.js index 7e2d3c9..d9072e4 100644 --- a/dist/phaser-arcade-slopes.min.js +++ b/dist/phaser-arcade-slopes.min.js @@ -1,11 +1,11 @@ /** - * Phaser Arcade Slopes v0.2.1 + * Phaser Arcade Slopes v0.3.0 * * A Phaser plugin that brings sloped tile collision handling to Phaser's Arcade Physics engine * - * https://github.com/hexus/phaser-arcade-slopes - * Copyright 2016-2017 Chris Andrew - * Released under the MIT license + * @copyright 2016-2017 Chris Andrew + * @license MIT + * @see https://github.com/hexus/phaser-arcade-slopes */ -Phaser.Plugin.ArcadeSlopes=function(e,t,o){Phaser.Plugin.call(this,e,t);var r={};r[Phaser.Plugin.ArcadeSlopes.SAT]=new Phaser.Plugin.ArcadeSlopes.SatSolver,this.facade=new Phaser.Plugin.ArcadeSlopes.Facade(new Phaser.Plugin.ArcadeSlopes.TileSlopeFactory,r,o||Phaser.Plugin.ArcadeSlopes.SAT),this.facade.plugin=this},Phaser.Plugin.ArcadeSlopes.prototype=Object.create(Phaser.Plugin.prototype),Phaser.Plugin.ArcadeSlopes.prototype.constructor=Phaser.Plugin.ArcadeSlopes,Phaser.Plugin.ArcadeSlopes.VERSION="0.2.1",Phaser.Plugin.ArcadeSlopes.SAT="sat",Phaser.Plugin.ArcadeSlopes.prototype.init=function(){this.game.slopes=this.game.slopes||this.facade,this.originalCollideSpriteVsTilemapLayer=Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer,Phaser.Physics.Arcade.prototype.collideSpriteVsTile=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile,Phaser.Physics.Arcade.prototype.collideSpriteVsTiles=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles,Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer,Phaser.Tilemap.prototype.getTileTopLeft=Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopLeft,Phaser.Tilemap.prototype.getTileTopRight=Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight,Phaser.Tilemap.prototype.getTileBottomLeft=Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft,Phaser.Tilemap.prototype.getTileBottomRight=Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomRight,this.originalRenderDebug=Phaser.TilemapLayer.prototype.renderDebug,Phaser.TilemapLayer.prototype.getCollisionOffsetX=Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetX,Phaser.TilemapLayer.prototype.getCollisionOffsetY=Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetY,Phaser.TilemapLayer.prototype.renderDebug=Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug},Phaser.Plugin.ArcadeSlopes.prototype.destroy=function(){this.game.slopes=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTile=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTiles=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer=this.originalCollideSpriteVsTilemapLayer,Phaser.Tilemap.prototype.getTileTopLeft=null,Phaser.Tilemap.prototype.getTileTopRight=null,Phaser.Tilemap.prototype.getTileBottomLeft=null,Phaser.Tilemap.prototype.getTileBottomRight=null,Phaser.TilemapLayer.prototype.getCollisionOffsetX=null,Phaser.TilemapLayer.prototype.getCollisionOffsetY=null,Phaser.TilemapLayer.prototype.renderDebug=this.originalRenderDebug,Phaser.Plugin.prototype.destroy.call(this)},Phaser.Plugin.ArcadeSlopes.Facade=function(e,t,o){this.factory=e,this.solvers=t,this.defaultSolver=o||Phaser.Plugin.ArcadeSlopes.SAT,this.plugin=null},Phaser.Plugin.ArcadeSlopes.Facade.prototype.enable=function(e){if(Array.isArray(e))for(var t=0;t0&&this.enable(e.children))},Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody=function(e){e.isCircle?e.polygon=new SAT.Circle(new SAT.Vector(e.x+e.halfWidth,e.y+e.halfHeight),e.radius):e.polygon=new SAT.Box(new SAT.Vector(e.x,e.y),e.width*e.sprite.scale.x,e.height*e.sprite.scale.y).toPolygon(),e.slopes=e.slopes||{debug:!1,friction:new Phaser.Point,heuristics:null,preferY:!1,pullUp:0,pullDown:0,pullLeft:0,pullRight:0,pullTopLeft:0,pullTopRight:0,pullBottomLeft:0,pullBottomRight:0,sat:{response:null},skipFriction:!1,snapUp:0,snapDown:0,snapLeft:0,snapRight:0,tile:null,velocity:new SAT.Vector}},Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemap=function(e,t,o,r){return this.factory.convertTilemap(e,t,o,r)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer=function(e,t,o){return this.factory.convertTilemapLayer(e,t,o)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.collide=function(e,t,o,r,l){return this.solvers.sat.collide(e,t,o,r,l)},Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype,"preferY",{get:function(){return this.solvers.sat.options.preferY},set:function(e){this.solvers.sat.options.preferY=!!e}}),Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype,"heuristics",{get:function(){return this.solvers.sat.options.restrain},set:function(e){this.solvers.sat.options.restrain=!!e}}),Phaser.Plugin.ArcadeSlopes.Overrides={},Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile=function(e,t,o,r,l,i,s,n){if(!t.body)return!1;if(o.hasOwnProperty("slope")){if(this.game.slopes.collide(e,t.body,o,r,n))return this._total++,l&&l.call(s,t,o),!0}else if(this.separateTile(e,t.body,o,r,n))return this._total++,l&&l.call(s,t,o),!0;return!1},Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles=function(e,t,o,r,l,i,s){var n=!1;if(!e.body)return n;for(var p=0;p0&&o>0?this.layers[e].data[o-1][t-1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight=function(e,t,o){return t0?this.layers[e].data[o-1][t+1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft=function(e,t,o){return t>0&&o=0;y++,_--,A+=h){y>=p&&(y-=p);var b=this.layer.data[y];for(f=H,R=u-g,d=v;R>=0;f++,R--,d+=a){f>=n&&(f-=n);var F=b[f];if(F&&!(F.index<0)&&F.collides&&(this.debugSettings.collidingTileOverfill&&(o.fillStyle=this.debugSettings.collidingTileOverfill,o.fillRect(d,A,T,c)),this.debugSettings.facingEdgeStroke&&(o.beginPath(),o.lineWidth=1,o.strokeStyle=this.debugSettings.facingEdgeStroke,F.faceTop&&(o.moveTo(d,A),o.lineTo(d+T,A)),F.faceBottom&&(o.moveTo(d,A+c),o.lineTo(d+T,A+c)),F.faceLeft&&(o.moveTo(d,A),o.lineTo(d,A+c)),F.faceRight&&(o.moveTo(d+T,A),o.lineTo(d+T,A+c)),o.closePath(),o.stroke(),F.slope))){if(this.debugSettings.slopeEdgeStroke||this.debugSettings.slopeFill){for(o.beginPath(),o.lineWidth=1,O=F.slope.polygon,o.moveTo(d+O.points[0].x*i,A+O.points[0].y*s),w=0;w-1:s.slope.type===o.slope.type,i.hasOwnProperty("overlapX")&&(n="number"==typeof i.overlapX?n&&r.overlapN.x===i.overlapX:n&&r.overlapN.x>=i.overlapX[0]&&r.overlapN.x<=i.overlapX[1]),i.hasOwnProperty("overlapY")&&(n="number"==typeof i.overlapY?n&&r.overlapN.y===i.overlapY:n&&r.overlapN.y>=i.overlapY[0]&&r.overlapN.y<=i.overlapY[1]),n){var p=i.separate;"function"==typeof p&&(p=p.call(this,t,o,r));var a=o.slope.axis;return p instanceof SAT.Vector&&(a=p),p&&a&&e.collideOnAxis(t,o,a),!1}}}return!0},Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps=function(e){switch(e){case"up":return{overlapX:0,overlapY:[-1,0]};case"down":return{overlapX:0,overlapY:[0,1]};case"left":return{overlapX:[-1,0],overlapY:0};case"right":return{overlapX:[0,1],overlapY:0};case"any":return{}}return console.warn("Unknown overlap direction '"+e+"'"),{}},Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints=function(e){var t={};for(var o in e){var r=e[o];for(var l in r){var i=r[l];if(i.direction){var s=Phaser.Plugin.ArcadeSlopes.SatRestrainer.resolveOverlaps(i.direction);s.hasOwnProperty("overlapX")&&(i.overlapX=s.overlapX),s.hasOwnProperty("overlapY")&&(i.overlapY=s.overlapY)}for(var n in i.types)i.types[n]=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(i.types[n]);i.separate!==!1&&"function"!=typeof i.separate&&(i.separate=!0)}var p=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(o);t[p]=r}return t},Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.fullTileSeparation=function(e,t,o){return e.top0&&Math.abs(e.velocity.y)>Math.abs(e.velocity.x)?(this.separationAxis.x=0,this.separationAxis.y=-1,this.separationAxis):e.bottom>t.worldY+t.centerY&&t.collideDown&&t.slope.edges.bottom!==Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY&&e.slopes.velocity.y<0&&Math.abs(e.slopes.velocity.y)>Math.abs(e.slopes.velocity.x)?(this.separationAxis.x=0,this.separationAxis.y=1,this.separationAxis):e.left0&&Math.abs(e.slopes.velocity.x)>Math.abs(e.slopes.velocity.y)?(this.separationAxis.x=-1,this.separationAxis.y=0,this.separationAxis):!(e.right>t.worldX+t.centerX&&t.collideRight&&t.slope.edges.right!==Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY&&e.slopes.velocity.x<0&&Math.abs(e.slopes.velocity.x)>Math.abs(e.slopes.velocity.y))||(this.separationAxis.x=1,this.separationAxis.y=0,this.separationAxis)},Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.setDefaultRestraints=function(){var e={};e.FULL=[{direction:"up",neighbour:"above",types:this.resolve("bottomLeft","bottomRight"),separate:this.fullTileSeparation},{direction:"down",neighbour:"below",types:this.resolve("topLeft","topRight"),separate:this.fullTileSeparation},{direction:"left",neighbour:"left",types:this.resolve("topRight","bottomRight"),separate:this.fullTileSeparation},{direction:"right",neighbour:"right",types:this.resolve("topLeft","bottomLeft"),separate:this.fullTileSeparation}],e.HALF_TOP=[{direction:"left",neighbour:"left",types:this.resolve("topRight","right"),separate:!1},{direction:"right",neighbour:"right",types:this.resolve("topLeft","left"),separate:!1}],e.HALF_BOTTOM=[{direction:"left",neighbour:"left",types:this.resolve("right","bottomRight"),separate:!1},{direction:"right",neighbour:"right",types:this.resolve("left","bottomLeft"),separate:!1}],e.HALF_LEFT=[{direction:"up",neighbour:"above",types:this.resolve("bottomLeft","bottom"),separate:!1},{direction:"down",neighbour:"below",types:this.resolve("topLeft","top"),separate:!1}],e.HALF_RIGHT=[{direction:"up",neighbour:"above",types:this.resolve("bottom","bottomRight"),separate:!1},{direction:"down",neighbour:"below",types:this.resolve("top","topRight"),separate:!1}],e.HALF_BOTTOM_LEFT=[{direction:"right",neighbour:"bottomRight",types:this.resolve("topLeft")},{direction:"up",neighbour:"topLeft",types:this.resolve("bottomRight")}],e.HALF_BOTTOM_RIGHT=[{direction:"left",neighbour:"bottomLeft",types:this.resolve("topRight")},{direction:"up",neighbour:"topRight",types:this.resolve("bottomLeft")}],e.HALF_TOP_LEFT=[{direction:"right",neighbour:"topRight",types:this.resolve("bottomLeft")},{direction:"down",neighbour:"bottomLeft",types:this.resolve("topRight")}],e.HALF_TOP_RIGHT=[{direction:"left",neighbour:"topLeft",types:this.resolve("bottomRight")},{direction:"down",neighbour:"bottomRight",types:this.resolve("topLeft")}],e.QUARTER_BOTTOM_LEFT_LOW=[{direction:"right",neighbour:"bottomRight",types:this.resolve("topLeft")},{direction:"up",neighbour:"left",types:this.resolve("topLeft","right","bottomRight")},{direction:"left",neighbour:"left",types:this.resolve("right","bottomRight"),separate:!1}],e.QUARTER_BOTTOM_LEFT_HIGH=[{direction:"right",neighbour:"right",types:this.resolve("left","bottomLeft"),separate:function(e,t){return e.bottomt.left}},{direction:"right",neighbour:"bottomRight",types:this.resolve("topLeft")}],e.QUARTER_LEFT_BOTTOM_HIGH=[{direction:"up",neighbour:"topLeft",types:this.resolve("bottomRight")},{direction:"down",neighbour:"below",types:this.resolve("topLeft","top"),separate:!1},{direction:"right",neighbour:"below",types:this.resolve("topLeft","top","bottomRight")}],e.QUARTER_RIGHT_BOTTOM_LOW=[{direction:"up",neighbour:"above",types:this.resolve("bottom","bottomRight"),separate:function(e,t){return e.rightt.left}}],e.QUARTER_RIGHT_TOP_LOW=[{direction:"up",neighbour:"above",types:this.resolve("bottom","bottomRight")},{direction:"left",neighbour:"above",types:this.resolve("bottom","bottomRight"),separate:!1},{direction:"down",neighbour:"bottomRight",types:this.resolve("topLeft")}],e.QUARTER_RIGHT_TOP_HIGH=[{direction:"left",neighbour:"topLeft",types:this.resolve("bottomRight")},{direction:"down",neighbour:"below",types:this.resolve("top","topRight"),separate:function(e,t){return e.rightt.top}},{direction:"down",neighbour:"bottomLeft",types:this.resolve("topRight")}],e.QUARTER_TOP_RIGHT_LOW=[{direction:"left",neighbour:"topLeft",types:this.resolve("bottomRight")},{direction:"right",neighbour:"right",types:this.resolve("topLeft","left"),separate:!1},{direction:"down",neighbour:"right",types:this.resolve("bottomRight","topLeft","left")}],e.QUARTER_TOP_RIGHT_HIGH=[{direction:"left",neighbour:"left",types:this.resolve("topRight","right"),separate:function(e,t){return e.top>t.top}},{direction:"down",neighbour:"bottomRight",types:this.resolve("topLeft")}],this.informalRestraints=JSON.parse(JSON.stringify(e)),this.restraints=Phaser.Plugin.ArcadeSlopes.SatRestrainer.prepareRestraints(e)},Phaser.Plugin.ArcadeSlopes.SatRestrainer.intersectArrays=function(e,t){return e.filter(function(e){return t.indexOf(e)!==-1}).filter(function(e,t,o){return o.indexOf(e)===t})},Phaser.Plugin.ArcadeSlopes.SatRestrainer.prototype.resolve=function(){var e=[];if(!arguments.length)return e;for(var t in arguments){var o=arguments[t];if(Phaser.Plugin.ArcadeSlopes.SatRestrainer.hasOwnProperty(o+"Vertices")){var r=Array.prototype.slice.call(Phaser.Plugin.ArcadeSlopes.SatRestrainer[o+"Vertices"]);if(1===arguments.length)return r;e=e.length?Phaser.Plugin.ArcadeSlopes.SatRestrainer.intersectArrays(e,r):r}else console.warn("Tried to resolve types from undefined vertex map location '"+o+"'")}return e},Phaser.Plugin.ArcadeSlopes.SatRestrainer.topVertices=["HALF_LEFT","HALF_RIGHT","QUARTER_LEFT_TOP_LOW","QUARTER_RIGHT_TOP_LOW","QUARTER_LEFT_BOTTOM_LOW","QUARTER_RIGHT_BOTTOM_LOW"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomVertices=["HALF_LEFT","HALF_RIGHT","QUARTER_LEFT_TOP_HIGH","QUARTER_LEFT_BOTTOM_HIGH","QUARTER_RIGHT_TOP_HIGH","QUARTER_RIGHT_BOTTOM_HIGH"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.leftVertices=["HALF_TOP","HALF_BOTTOM","QUARTER_TOP_LEFT_LOW","QUARTER_TOP_RIGHT_HIGH","QUARTER_BOTTOM_LEFT_LOW","QUARTER_BOTTOM_RIGHT_HIGH"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.rightVertices=["HALF_TOP","HALF_BOTTOM","QUARTER_TOP_LEFT_HIGH","QUARTER_TOP_RIGHT_LOW","QUARTER_BOTTOM_LEFT_HIGH","QUARTER_BOTTOM_RIGHT_LOW"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.topLeftVertices=["FULL","HALF_TOP","HALF_LEFT","HALF_TOP_LEFT","HALF_TOP_RIGHT","HALF_BOTTOM_LEFT","QUARTER_TOP_LEFT_LOW","QUARTER_TOP_LEFT_HIGH","QUARTER_TOP_RIGHT_HIGH","QUARTER_BOTTOM_LEFT_HIGH","QUARTER_LEFT_TOP_LOW","QUARTER_LEFT_TOP_HIGH","QUARTER_LEFT_BOTTOM_LOW","QUARTER_LEFT_BOTTOM_HIGH","QUARTER_RIGHT_TOP_HIGH"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.topRightVertices=["FULL","HALF_TOP","HALF_RIGHT","HALF_TOP_LEFT","HALF_TOP_RIGHT","HALF_BOTTOM_RIGHT","QUARTER_TOP_LEFT_LOW","QUARTER_TOP_LEFT_HIGH","QUARTER_TOP_RIGHT_LOW","QUARTER_TOP_RIGHT_HIGH","QUARTER_BOTTOM_RIGHT_HIGH","QUARTER_LEFT_TOP_HIGH","QUARTER_RIGHT_TOP_LOW","QUARTER_RIGHT_TOP_HIGH","QUARTER_RIGHT_BOTTOM_LOW","QUARTER_RIGHT_BOTTOM_HIGH"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomLeftVertices=["FULL","HALF_LEFT","HALF_BOTTOM","HALF_TOP_LEFT","HALF_BOTTOM_LEFT","HALF_BOTTOM_RIGHT","QUARTER_TOP_LEFT_HIGH","QUARTER_BOTTOM_LEFT_LOW","QUARTER_BOTTOM_LEFT_HIGH","QUARTER_BOTTOM_RIGHT_LOW","QUARTER_BOTTOM_RIGHT_HIGH","QUARTER_LEFT_TOP_HIGH","QUARTER_LEFT_BOTTOM_LOW","QUARTER_LEFT_BOTTOM_HIGH","QUARTER_RIGHT_BOTTOM_LOW"],Phaser.Plugin.ArcadeSlopes.SatRestrainer.bottomRightVertices=["FULL","HALF_RIGHT","HALF_BOTTOM","HALF_TOP_RIGHT","HALF_BOTTOM_LEFT","HALF_BOTTOM_RIGHT","QUARTER_TOP_RIGHT_HIGH","QUARTER_BOTTOM_LEFT_LOW","QUARTER_BOTTOM_LEFT_HIGH","QUARTER_BOTTOM_RIGHT_LOW","QUARTER_BOTTOM_RIGHT_HIGH","QUARTER_LEFT_BOTTOM_LOW","QUARTER_RIGHT_TOP_HIGH","QUARTER_RIGHT_BOTTOM_LOW","QUARTER_RIGHT_BOTTOM_HIGH"],Phaser.Plugin.ArcadeSlopes.SatSolver=function(e){this.options=Phaser.Utils.mixin(e||{},{debug:!1,preferY:!1,restrain:!0}),this.restrainers=[new Phaser.Plugin.ArcadeSlopes.SatRestrainer]},Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse=function(e){return e.overlapV.scale(-1),e.overlapN.scale(-1),e},Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse=function(e){return e.overlapN.x=0,e.overlapN.y=0,e.overlapV.x=0,e.overlapV.y=0,e.clear(),e},Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetX=function(e){return e.y*e.y/e.x+e.x},Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY=function(e){return e.x*e.x/e.y+e.y},Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY=function(e,t){return t.overlapV.y<0&&e.velocity.y>0||t.overlapV.y>0&&e.velocity.y<0},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldPreferY=function(e,t){return(this.options.preferY||e.slopes.preferY)&&0!==t.overlapV.y&&0!==t.overlapV.x&&Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY(e,t)},Phaser.Plugin.ArcadeSlopes.SatSolver.isSeparatingAxis=function(e,t,o,r){var l=SAT.isSeparatingAxis(e.pos,t.pos,e.points,t.points,o,r||null);return r&&(r.a=e,r.b=t,r.overlapV=r.overlapN.clone().scale(r.overlap)),l},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.separate=function(e,t,o,r){return!(!r&&!this.shouldSeparate(t.index,e,t,o))&&(!(t.collisionCallback&&!t.collisionCallback.call(t.collisionCallbackContext,e.sprite,t))&&(!(t.layer.callbacks[t.index]&&!t.layer.callbacks[t.index].callback.call(t.layer.callbacks[t.index].callbackContext,e.sprite,t))&&(this.shouldPreferY(e,o)?e.position.y+=Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY(o.overlapV):(e.position.x+=o.overlapV.x,e.position.y+=o.overlapV.y),!0)))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity=function(e,t,o){var r=e.slopes.velocity.clone().projectN(o.overlapN),l=e.slopes.velocity.clone().sub(r);r.x=r.x*-e.bounce.x,r.y=r.y*-e.bounce.y,l.x=l.x*(1-e.slopes.friction.x-t.slope.friction.x),l.y=l.y*(1-e.slopes.friction.y-t.slope.friction.y),e.velocity.x=r.x+l.x,e.velocity.y=r.y+l.y,this.pull(e,o)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateValues=function(e){e.polygon.pos.x=e.x,e.polygon.pos.y=e.y,e.slopes.velocity.x=e.velocity.x,e.slopes.velocity.y=e.velocity.y},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateFlags=function(e,t){e.touching.up=e.touching.up||t.overlapV.y>0,e.touching.down=e.touching.down||t.overlapV.y<0,e.touching.left=e.touching.left||t.overlapV.x>0,e.touching.right=e.touching.right||t.overlapV.x<0,e.touching.none=!(e.touching.up||e.touching.down||e.touching.left||e.touching.right),e.blocked.up=e.blocked.up||0===t.overlapV.x&&t.overlapV.y>0,e.blocked.down=e.blocked.down||0===t.overlapV.x&&t.overlapV.y<0,e.blocked.left=e.blocked.left||0===t.overlapV.y&&t.overlapV.x>0,e.blocked.right=e.blocked.right||0===t.overlapV.y&&t.overlapV.x<0},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.snap=function(e,t,o){if(!e.slopes||!e.slopes.snapUp&&!e.slopes.snapDown&&!e.slopes.snapLeft&&!e.slopes.snapRight)return!1;var r=new Phaser.Point(e.position.x,e.position.y);for(var l in t){var i=t[l];if(i.slope){if(e.slopes.snapUp&&(e.position.x=r.x,e.position.y=r.y-e.slopes.snapUp,this.snapCollide(e,i,o,r)))return!0;if(e.slopes.snapDown&&(e.position.x=r.x,e.position.y=r.y+e.slopes.snapDown,this.snapCollide(e,i,o,r)))return!0;if(e.slopes.snapLeft&&(e.position.x=r.x-e.slopes.snapLeft,e.position.y=r.y,this.snapCollide(e,i,o,r)))return!0;if(e.slopes.snapRight&&(e.position.x=r.x+e.slopes.snapRight,e.position.y=r.y,this.snapCollide(e,i,o,r)))return!0}}return!1},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.pull=function(e,t){if(!(e.slopes.pullUp||e.slopes.pullDown||e.slopes.pullLeft||e.slopes.pullRight||e.slopes.pullTopLeft||e.slopes.pullTopRight||e.slopes.pullBottomLeft||e.slopes.pullBottomRight))return!1;var o=t.overlapN.clone().scale(-1);return e.slopes.pullUp&&o.y<0?(pullUp=o.clone().scale(e.slopes.pullUp),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullDown&&o.y>0?(pullDown=o.clone().scale(e.slopes.pullDown),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullLeft&&o.x<0?(pullLeft=o.clone().scale(e.slopes.pullLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):e.slopes.pullRight&&o.x>0?(pullRight=o.clone().scale(e.slopes.pullRight),e.velocity.x+=pullRight.x,e.velocity.y+=pullRight.y,!0):e.slopes.pullTopLeft&&o.x<0&&o.y<0?(pullUp=o.clone().scale(e.slopes.pullTopLeft),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullTopRight&&o.x>0&&o.y<0?(pullDown=o.clone().scale(e.slopes.pullTopRight),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullBottomLeft&&o.x<0&&o.y>0?(pullLeft=o.clone().scale(e.slopes.pullBottomLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):!!(e.slopes.pullBottomRight&&o.x>0&&o.y>0)&&(pullRight=o.clone().scale(e.slopes.pullBottomRight),e.velocity.x+=pullRight.x,e.velocity.y+=pullRight.y,!0)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.snapCollide=function(e,t,o,r){return!!this.collide(0,e,t,o)||(e.position.x=r.x,e.position.y=r.y,!1)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldCollide=function(e,t){return e.enable&&e.polygon&&e.slopes&&t.collides&&t.slope&&t.slope.polygon},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide=function(e,t,o,r,l){if(this.updateValues(t),!this.shouldCollide(t,o))return!1;t.isCircle&&(t.polygon.pos.x+=t.halfWidth,t.polygon.pos.y+=t.halfHeight),o.slope.polygon.pos.x=o.worldX+r.getCollisionOffsetX(),o.slope.polygon.pos.y=o.worldY+r.getCollisionOffsetY();var i=t.slopes.sat.response||new SAT.Response;return Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse(i),!(t.isCircle&&!SAT.testCirclePolygon(t.polygon,o.slope.polygon,i)||!t.isCircle&&!SAT.testPolygonPolygon(t.polygon,o.slope.polygon,i))&&(!!l||(Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(i),!!this.separate(t,o,i)&&(t.overlapX=i.overlapV.x,t.overlapY=i.overlapV.y,t.slopes.sat.response=i,t.slopes.tile=o,this.applyVelocity(t,o,i),this.updateFlags(t,i),!0)))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collideOnAxis=function(e,t,o,r){if(this.updateValues(e),!this.shouldCollide(e,t)||e.isCircle)return!1;r=r||new SAT.Response;var l=Phaser.Plugin.ArcadeSlopes.SatSolver.isSeparatingAxis(e.polygon,t.slope.polygon,o,r);return!l&&(Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(r),!!this.separate(e,t,r,!0)&&(e.overlapX=r.overlapV.x,e.overlapY=r.overlapV.y,e.slopes.sat.response=r,this.applyVelocity(e,t,r),this.updateFlags(e,r),!0))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.restrain=function(e,t,o){for(var r in this.restrainers){var l=this.restrainers[r];if("function"==typeof l.restrain&&!l.restrain(this,e,t,o))return!0}return!1},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldSeparate=function(e,t,o,r){return!(!t.enable||!r.overlap)&&(!(r.overlapV.clone().scale(-1).dot(t.slopes.velocity)<0)&&(!((this.options.restrain||t.slopes.heuristics)&&t.slopes.heuristics!==!1&&!t.isCircle&&this.restrain(t,o,r))&&(!((!o.collideUp||o.slope.edges.top===Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY)&&r.overlapN.y<0&&0===r.overlapN.x)&&(!((!o.collideDown||o.slope.edges.bottom===Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY)&&r.overlapN.y>0&&0===r.overlapN.x)&&(!((!o.collideLeft||o.slope.edges.left===Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY)&&r.overlapN.x<0&&0===r.overlapN.y)&&!((!o.collideRight||o.slope.edges.right===Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY)&&r.overlapN.x>0&&0===r.overlapN.y))))))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.debug=function(e,t){},Phaser.Plugin.ArcadeSlopes.TileSlope=function(e,t,o,r,l,i){this.type=e,this.tile=t,this.polygon=o,this.line=r,this.edges=Phaser.Utils.mixin(l||{},{top:Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,left:Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,right:Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID}),this.axis=i||null,this.solver=null,this.friction=new Phaser.Point},Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType=function(e){return parseInt(e)>=0?e:("string"==typeof e&&(e=e.toUpperCase()),Phaser.Plugin.ArcadeSlopes.TileSlope.hasOwnProperty(e)?Phaser.Plugin.ArcadeSlopes.TileSlope[e]:(console.warn("Unknown slope type '"+e+"'"),Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN))},Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype,"slope",{get:function(){return this.line?(this.line.start.y-this.line.end.y)/(this.line.start.x-this.line.end.x):0}}),Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype,"typeName",{get:function(){return Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName(this.type)},set:function(e){this.type=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(e)}}),Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName=function(e){return Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames.hasOwnProperty(e)?Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[e]:Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[-1]},Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames={"-1":"UNKNOWN",0:"FULL",21:"HALF_BOTTOM",22:"HALF_TOP",23:"HALF_LEFT",24:"HALF_RIGHT",1:"HALF_BOTTOM_LEFT",2:"HALF_BOTTOM_RIGHT",3:"HALF_TOP_LEFT",4:"HALF_TOP_RIGHT",5:"QUARTER_BOTTOM_LEFT_LOW",6:"QUARTER_BOTTOM_LEFT_HIGH",7:"QUARTER_BOTTOM_RIGHT_LOW",8:"QUARTER_BOTTOM_RIGHT_HIGH",9:"QUARTER_LEFT_BOTTOM_LOW",10:"QUARTER_LEFT_BOTTOM_HIGH",11:"QUARTER_RIGHT_BOTTOM_LOW",12:"QUARTER_RIGHT_BOTTOM_HIGH",13:"QUARTER_LEFT_TOP_LOW",14:"QUARTER_LEFT_TOP_HIGH",15:"QUARTER_RIGHT_TOP_LOW",16:"QUARTER_RIGHT_TOP_HIGH",17:"QUARTER_TOP_LEFT_LOW",18:"QUARTER_TOP_LEFT_HIGH",19:"QUARTER_TOP_RIGHT_LOW",20:"QUARTER_TOP_RIGHT_HIGH"},Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY=0,Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID=1,Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING=2,Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN=-1,Phaser.Plugin.ArcadeSlopes.TileSlope.FULL=0,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM=21,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP=22,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT=23,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT=24,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT=1,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT=2,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT=3,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT=4,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW=5,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH=6,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW=7,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH=8,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW=9,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH=10,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW=11,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH=12,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW=13,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH=14,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW=15,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH=16,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW=17,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH=18,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW=19,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH=20,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory=function(){this.definitions={},this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.FULL]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom, -this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh,this.mappings={},this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes,this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.constructor=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.define=function(e,t){"function"==typeof t&&(this.definitions[e]=t)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.create=function(e,t){var o=e;return e=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(o),this.definitions.hasOwnProperty(e)?"function"!=typeof this.definitions[e]?(console.warn("Slope type definition for type "+o+" is not a function"),null):this.definitions[e].call(this,e,t):(console.warn("Slope type "+o+" not defined"),null)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemap=function(e,t,o,r){return t=e.getLayer(t),this.convertTilemapLayer(t,o,r),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemapLayer=function(e,t,o){var r=this;if("string"==typeof t){var l=this.resolveMappingType(t);if(!this.mappings[l])return console.warn("Tilemap could not be converted; mapping type '"+t+"' is unknown"),e;t=this.mappings[l](o)}return e.layer.data.forEach(function(o){o.forEach(function(o){var l;o.properties.type&&(l=r.create(o.properties.type,o)),!l&&t.hasOwnProperty(o.index)&&(l=r.create(t[o.index],o)),l&&(o.slope=l);var i=o.x,s=o.y;o.neighbours=o.neighbours||{},o.neighbours.above=e.map.getTileAbove(e.index,i,s),o.neighbours.below=e.map.getTileBelow(e.index,i,s),o.neighbours.left=e.map.getTileLeft(e.index,i,s),o.neighbours.right=e.map.getTileRight(e.index,i,s),o.neighbours.topLeft=e.map.getTileTopLeft(e.index,i,s),o.neighbours.topRight=e.map.getTileTopRight(e.index,i,s),o.neighbours.bottomLeft=e.map.getTileBottomLeft(e.index,i,s),o.neighbours.bottomRight=e.map.getTileBottomRight(e.index,i,s)})}),this.calculateEdges(e),this.addDebugSettings(e),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges=function(e){for(var t=null,o=null,r=null,l=null,i=0,s=e.layer.height;i=0?e:("string"==typeof e&&(e=e.toUpperCase()),Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.hasOwnProperty(e)&&this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[e]]?Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[e]:(console.warn("Unknown tileset mapping type '"+e+"'"),-1))},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull=function(e,t){var o=new SAT.Box(new SAT.Vector(t.worldX,t.worldY),t.width,t.height).toPolygon();return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom=function(e,t){var o=t.height/2,r=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,o),new SAT.Vector(t.width,o),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),l=new Phaser.Line(t.left,t.top+t.height/2,t.right,t.top+t.height/2),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop=function(e,t){var o=t.height/2,r=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,o),new SAT.Vector(0,o)]),l=new Phaser.Line(t.left,t.top,t.right,t.top),i={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft=function(e,t){var o=t.width/2,r=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(o,0),new SAT.Vector(o,t.height),new SAT.Vector(0,t.height)]),l=new Phaser.Line(t.left+o,t.top,t.left+o,t.bottom),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight=function(e,t){var o=t.width/2,r=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(o,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(o,t.height)]),l=new Phaser.Line(t.left+o,t.top,t.left+o,t.bottom),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.top,t.right,t.bottom),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.7071067811865475,(-.7071067811865475));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.bottom,t.right,t.top),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.707106781186548),(-.707106781186548));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.right,t.top,t.left,t.bottom),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.7071067811865475,.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height)]),r=new Phaser.Line(t.right,t.bottom,t.left,t.top),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.7071067811865475),.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,t.height/2),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.top+t.height/2,t.right,t.bottom),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,t.height/2),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.top,t.right,t.top+t.height/2),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(t.width,t.height/2),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.bottom,t.right,t.top+t.height/2),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,t.height/2),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.bottom,t.right,t.top+t.height/2),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width/2,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left+t.width/2,t.top,t.right,t.bottom),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width/2,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.top,t.left+t.width/2,t.bottom),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(t.width/2,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.bottom,t.left+t.width/2,t.top),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(t.width/2,t.height)]),r=new Phaser.Line(t.left+t.width/2,t.bottom,t.right,t.top),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width/2,0),new SAT.Vector(0,t.height)]),r=new Phaser.Line(0,t.height,t.width/2,0),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width/2,t.height),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left+t.width/2,t.bottom,t.right,t.bottom),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(t.width/2,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height)]),r=new Phaser.Line(t.left+t.width/2,t.top,t.right,t.bottom),l={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(t.width/2,t.height)]),r=new Phaser.Line(t.left,t.top,t.left+t.width/2,t.bottom),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(0,t.height/2)]),r=new Phaser.Line(t.left,t.top+t.height/2,t.right,t.top),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height/2),new SAT.Vector(0,t.height)]),r=new Phaser.Line(t.left,t.bottom,t.right,t.top+t.height/2),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height/2)]),r=new Phaser.Line(t.left,t.top,t.right,t.top+t.height/2),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh=function(e,t){var o=new SAT.Polygon(new SAT.Vector(t.worldX,t.worldY),[new SAT.Vector(0,0),new SAT.Vector(t.width,0),new SAT.Vector(t.width,t.height),new SAT.Vector(0,t.height/2)]),r=new Phaser.Line(t.left,t.top+t.height/2,t.right,t.top+t.height),l={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,t,o,r,l,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset=function(e){var t=parseInt(e);return t=isNaN(t)||"number"!=typeof t?0:t-1},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes=function(e){offset=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(e);var t={};return t[offset+1]="FULL",t[offset+2]="HALF_TOP",t[offset+3]="HALF_BOTTOM",t[offset+4]="HALF_LEFT",t[offset+5]="HALF_RIGHT",t[offset+6]="HALF_BOTTOM_LEFT",t[offset+7]="HALF_BOTTOM_RIGHT",t[offset+8]="HALF_TOP_LEFT",t[offset+9]="HALF_TOP_RIGHT",t[offset+10]="QUARTER_TOP_LEFT_HIGH",t[offset+11]="QUARTER_TOP_LEFT_LOW",t[offset+12]="QUARTER_TOP_RIGHT_LOW",t[offset+13]="QUARTER_TOP_RIGHT_HIGH",t[offset+14]="QUARTER_BOTTOM_LEFT_HIGH",t[offset+15]="QUARTER_BOTTOM_LEFT_LOW",t[offset+16]="QUARTER_BOTTOM_RIGHT_LOW",t[offset+17]="QUARTER_BOTTOM_RIGHT_HIGH",t[offset+18]="QUARTER_LEFT_BOTTOM_HIGH",t[offset+19]="QUARTER_RIGHT_BOTTOM_HIGH",t[offset+20]="QUARTER_LEFT_TOP_HIGH",t[offset+21]="QUARTER_RIGHT_TOP_HIGH",t[offset+35]="QUARTER_LEFT_BOTTOM_LOW",t[offset+36]="QUARTER_RIGHT_BOTTOM_LOW",t[offset+37]="QUARTER_LEFT_TOP_LOW",t[offset+38]="QUARTER_RIGHT_TOP_LOW",t},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics=function(e){offset=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(e);var t={};return t[offset+2]="FULL",t[offset+3]="HALF_BOTTOM_LEFT",t[offset+4]="HALF_BOTTOM_RIGHT",t[offset+6]="HALF_TOP_LEFT",t[offset+5]="HALF_TOP_RIGHT",t[offset+15]="QUARTER_BOTTOM_LEFT_LOW",t[offset+16]="QUARTER_BOTTOM_RIGHT_LOW",t[offset+17]="QUARTER_TOP_RIGHT_LOW",t[offset+18]="QUARTER_TOP_LEFT_LOW",t[offset+19]="QUARTER_BOTTOM_LEFT_HIGH",t[offset+20]="QUARTER_BOTTOM_RIGHT_HIGH",t[offset+21]="QUARTER_TOP_RIGHT_HIGH",t[offset+22]="QUARTER_TOP_LEFT_HIGH",t[offset+23]="QUARTER_LEFT_BOTTOM_HIGH",t[offset+24]="QUARTER_RIGHT_BOTTOM_HIGH",t[offset+25]="QUARTER_RIGHT_TOP_LOW",t[offset+26]="QUARTER_LEFT_TOP_LOW",t[offset+27]="QUARTER_LEFT_BOTTOM_LOW",t[offset+28]="QUARTER_RIGHT_BOTTOM_LOW",t[offset+29]="QUARTER_RIGHT_TOP_HIGH",t[offset+30]="QUARTER_LEFT_TOP_HIGH",t[offset+31]="HALF_BOTTOM",t[offset+32]="HALF_RIGHT",t[offset+33]="HALF_TOP",t[offset+34]="HALF_LEFT",t},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES=1,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA=2,function(e,t){"use strict";"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.SAT=t()}(this,function(){"use strict";function e(e,t){this.x=e||0,this.y=t||0}function t(t,o){this.pos=t||new e,this.r=o||0}function o(t,o){this.pos=t||new e,this.angle=0,this.offset=new e,this.setPoints(o||[])}function r(t,o,r){this.pos=t||new e,this.w=o||0,this.h=r||0}function l(){this.a=null,this.b=null,this.overlapN=new e,this.overlapV=new e,this.clear()}function i(e,t,o){for(var r=Number.MAX_VALUE,l=-Number.MAX_VALUE,i=e.length,s=0;sl&&(l=n)}o[0]=r,o[1]=l}function s(e,t,o,r,l,s){var n=d.pop(),p=d.pop(),a=S.pop().copy(t).sub(e),h=a.dot(l);if(i(o,l,n),i(r,l,p),p[0]+=h,p[1]+=h,n[0]>p[1]||p[0]>n[1])return S.push(a),d.push(n),d.push(p),!0;if(s){var T=0;if(n[0]p[1])T=n[0]-p[1],s.aInB=!1;else{var c=n[1]-p[0],g=p[1]-n[0];T=co?_:R}function p(e,t){var o=S.pop().copy(e).sub(t.pos),r=t.r*t.r,l=o.len2();return S.push(o),l<=r}function a(e,t){f.pos.copy(e),A.clear();var o=g(f,t,A);return o&&(o=A.aInB),o}function h(e,t,o){var r=S.pop().copy(t.pos).sub(e.pos),l=e.r+t.r,i=l*l,s=r.len2();if(s>i)return S.push(r),!1;if(o){var n=Math.sqrt(s);o.a=e,o.b=t,o.overlap=l-n,o.overlapN.copy(r.normalize()),o.overlapV.copy(r).scale(o.overlap),o.aInB=e.r<=t.r&&n<=t.r-e.r,o.bInA=t.r<=e.r&&n<=e.r-t.r}return S.push(r),!0}function T(e,t,o){for(var r=S.pop().copy(t.pos).sub(e.pos),l=t.r,i=l*l,s=e.calcPoints,p=s.length,a=S.pop(),h=S.pop(),T=0;Ti&&(o.aInB=!1);var d=n(a,h);if(d===y){a.copy(e.edges[g]);var A=S.pop().copy(r).sub(s[g]);if(d=n(a,A),d===_){var f=h.len();if(f>l)return S.push(r),S.push(a),S.push(h),S.push(A),!1;o&&(o.bInA=!1,P=h.normalize(),u=l-f)}S.push(A)}else if(d===_){if(a.copy(e.edges[c]),h.copy(r).sub(s[c]),d=n(a,h),d===y){var f=h.len();if(f>l)return S.push(r),S.push(a),S.push(h),!1;o&&(o.bInA=!1,P=h.normalize(),u=l-f)}}else{var R=a.perp().normalize(),f=h.dot(R),O=Math.abs(f);if(f>0&&O>l)return S.push(r),S.push(R),S.push(h),!1;o&&(P=R,u=l-f,(f>=0||u<2*l)&&(o.bInA=!1))}P&&o&&Math.abs(u)0&&(this.x=this.x/e,this.y=this.y/e),this},e.prototype.add=e.prototype.add=function(e){return this.x+=e.x,this.y+=e.y,this},e.prototype.sub=e.prototype.sub=function(e){return this.x-=e.x,this.y-=e.y,this},e.prototype.scale=e.prototype.scale=function(e,t){return this.x*=e,this.y*=t||e,this},e.prototype.project=e.prototype.project=function(e){var t=this.dot(e)/e.len2();return this.x=t*e.x,this.y=t*e.y,this},e.prototype.projectN=e.prototype.projectN=function(e){var t=this.dot(e);return this.x=t*e.x,this.y=t*e.y,this},e.prototype.reflect=e.prototype.reflect=function(e){var t=this.x,o=this.y;return this.project(e).scale(2),this.x-=t,this.y-=o,this},e.prototype.reflectN=e.prototype.reflectN=function(e){var t=this.x,o=this.y;return this.projectN(e).scale(2),this.x-=t,this.y-=o,this},e.prototype.dot=e.prototype.dot=function(e){return this.x*e.x+this.y*e.y},e.prototype.len2=e.prototype.len2=function(){return this.dot(this)},e.prototype.len=e.prototype.len=function(){return Math.sqrt(this.len2())},u.Circle=t,t.prototype.getAABB=t.prototype.getAABB=function(){var t=this.r,o=this.pos.clone().sub(new e(t,t));return new r(o,2*t,2*t).toPolygon()},u.Polygon=o,o.prototype.setPoints=o.prototype.setPoints=function(t){var o=!this.points||this.points.length!==t.length;if(o){var r,l=this.calcPoints=[],i=this.edges=[],s=this.normals=[];for(r=0;rs&&(s=a.x),a.yn&&(n=a.y)}return new r(this.pos.clone().add(new e(l,i)),s-l,n-i).toPolygon()},u.Box=r,r.prototype.toPolygon=r.prototype.toPolygon=function(){var t=this.pos,r=this.w,l=this.h;return new o(new e(t.x,t.y),[new e,new e(r,0),new e(r,l),new e(0,l)])},u.Response=l,l.prototype.clear=l.prototype.clear=function(){return this.aInB=!0,this.bInA=!0,this.overlap=Number.MAX_VALUE,this};for(var S=[],P=0;P<10;P++)S.push(new e);for(var d=[],P=0;P<5;P++)d.push([]);var A=new l,f=new r(new e,1e-6,1e-6).toPolygon();u.isSeparatingAxis=s;var y=-1,R=0,_=1;return u.pointInCircle=p,u.pointInPolygon=a,u.testCircleCircle=h,u.testPolygonCircle=T,u.testCirclePolygon=c,u.testPolygonPolygon=g,u}); \ No newline at end of file +Phaser.Plugin.ArcadeSlopes=function(e,o,t){Phaser.Plugin.call(this,e,o);var l={};l[Phaser.Plugin.ArcadeSlopes.SAT]=new Phaser.Plugin.ArcadeSlopes.SatSolver,this.facade=new Phaser.Plugin.ArcadeSlopes.Facade(new Phaser.Plugin.ArcadeSlopes.TileSlopeFactory,l,t||Phaser.Plugin.ArcadeSlopes.SAT),this.facade.plugin=this},Phaser.Plugin.ArcadeSlopes.prototype=Object.create(Phaser.Plugin.prototype),Phaser.Plugin.ArcadeSlopes.prototype.constructor=Phaser.Plugin.ArcadeSlopes,Phaser.Plugin.ArcadeSlopes.VERSION="0.3.0",Phaser.Plugin.ArcadeSlopes.SAT="sat",Phaser.Plugin.ArcadeSlopes.prototype.init=function(){this.game.slopes=this.game.slopes||this.facade,this.originalCollideSpriteVsTilemapLayer=Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer,Phaser.Physics.Arcade.prototype.collideSpriteVsTile=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile,Phaser.Physics.Arcade.prototype.collideSpriteVsTiles=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles,Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer=Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer,Phaser.Tilemap.prototype.getTileTopLeft=Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopLeft,Phaser.Tilemap.prototype.getTileTopRight=Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight,Phaser.Tilemap.prototype.getTileBottomLeft=Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft,Phaser.Tilemap.prototype.getTileBottomRight=Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomRight,this.originalRenderDebug=Phaser.TilemapLayer.prototype.renderDebug,Phaser.TilemapLayer.prototype.getCollisionOffsetX=Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetX,Phaser.TilemapLayer.prototype.getCollisionOffsetY=Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetY,Phaser.TilemapLayer.prototype.renderDebug=Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug},Phaser.Plugin.ArcadeSlopes.prototype.destroy=function(){this.game.slopes=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTile=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTiles=null,Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer=this.originalCollideSpriteVsTilemapLayer,Phaser.Tilemap.prototype.getTileTopLeft=null,Phaser.Tilemap.prototype.getTileTopRight=null,Phaser.Tilemap.prototype.getTileBottomLeft=null,Phaser.Tilemap.prototype.getTileBottomRight=null,Phaser.TilemapLayer.prototype.getCollisionOffsetX=null,Phaser.TilemapLayer.prototype.getCollisionOffsetY=null,Phaser.TilemapLayer.prototype.renderDebug=this.originalRenderDebug,Phaser.Plugin.prototype.destroy.call(this)},Phaser.Plugin.ArcadeSlopes.Facade=function(e,o,t){this.factory=e,this.solvers=o,this.defaultSolver=t||Phaser.Plugin.ArcadeSlopes.SAT,this.plugin=null},Phaser.Plugin.ArcadeSlopes.Facade.prototype.enable=function(e){if(Array.isArray(e))for(var o=0;o0&&this.enable(e.children))},Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody=function(e){e.isCircle?e.polygon=new SAT.Circle(new SAT.Vector(e.x+e.halfWidth,e.y+e.halfHeight),e.radius):e.polygon=new SAT.Box(new SAT.Vector(e.x,e.y),e.width*e.sprite.scale.x,e.height*e.sprite.scale.y).toPolygon(),e.slopes=e.slopes||{debug:!1,friction:new Phaser.Point,preferY:!1,pullUp:0,pullDown:0,pullLeft:0,pullRight:0,pullTopLeft:0,pullTopRight:0,pullBottomLeft:0,pullBottomRight:0,sat:{response:null},skipFriction:!1,tile:null,velocity:new SAT.Vector}},Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemap=function(e,o,t,l){return this.factory.convertTilemap(e,o,t,l)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer=function(e,o,t){return this.factory.convertTilemapLayer(e,o,t)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.collide=function(e,o,t,l,r){return this.solvers.sat.collide(e,o,t,l,r)},Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype,"preferY",{get:function(){return this.solvers.sat.options.preferY},set:function(e){this.solvers.sat.options.preferY=!!e}}),Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype,"heuristics",{get:function(){return this.solvers.sat.options.restrain},set:function(e){this.solvers.sat.options.restrain=!!e}}),Phaser.Plugin.ArcadeSlopes.Overrides={},Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile=function(e,o,t,l,r,i,s,p){if(!o.body||!t||!l)return!1;if(t.hasOwnProperty("slope")){if(this.game.slopes.collide(e,o.body,t,l,p))return this._total++,r&&r.call(s,o,t),!0}else if(this.separateTile(e,o.body,t,l,p))return this._total++,r&&r.call(s,o,t),!0;return!1},Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles=function(e,o,t,l,r,i,s){if(!(e.body&&o&&o.length&&t))return!1;for(var p=!1,n=0;n0&&t>0?this.layers[e].data[t-1][o-1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight=function(e,o,t){return o0?this.layers[e].data[t-1][o+1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft=function(e,o,t){return o>0&&t=0;E++,L--,v+=h){E>=n&&(E-=n);var M=this.layer.data[E];for(_=Q,O=f-A,R=G;O>=0;_++,O--,R+=a){_>=p&&(_-=p);var Y=M[_];if(Y&&!(Y.index<0)&&Y.collides&&(this.debugSettings.collidingTileOverfill&&(t.fillStyle=this.debugSettings.collidingTileOverfill,t.fillRect(R,v,P,u)),this.debugSettings.facingEdgeStroke&&(t.beginPath(),t.lineWidth=1,t.strokeStyle=this.debugSettings.facingEdgeStroke,Y.faceTop&&(t.moveTo(R,v),t.lineTo(R+P,v)),Y.faceBottom&&(t.moveTo(R,v+u),t.lineTo(R+P,v+u)),Y.faceLeft&&(t.moveTo(R,v),t.lineTo(R,v+u)),Y.faceRight&&(t.moveTo(R+P,v),t.lineTo(R+P,v+u)),t.closePath(),t.stroke(),Y.slope))){if(this.debugSettings.slopeEdgeStroke||this.debugSettings.slopeFill){for(t.beginPath(),t.lineWidth=1,I=Y.slope.polygon,t.moveTo(R+I.points[0].x*i,v+I.points[0].y*s),m=0;m0||o.overlapV.y>0&&e.velocity.y<0},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldPreferY=function(e,o){return(this.options.preferY||e.slopes.preferY)&&0!==o.overlapV.y&&0!==o.overlapV.x&&Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY(e,o)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.separate=function(e,o,t,l){return!(!l&&!this.shouldSeparate(o.index,e,o,t))&&(!(o.collisionCallback&&!o.collisionCallback.call(o.collisionCallbackContext,e.sprite,o))&&(!(o.layer.callbacks[o.index]&&!o.layer.callbacks[o.index].callback.call(o.layer.callbacks[o.index].callbackContext,e.sprite,o))&&(this.shouldPreferY(e,t)?e.position.y+=Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY(t.overlapV):(e.position.x+=t.overlapV.x,e.position.y+=t.overlapV.y),!0)))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity=function(e,o,t){var l=this.vectorPool.pop().copy(e.slopes.velocity).projectN(t.overlapN),r=this.vectorPool.pop().copy(e.slopes.velocity).sub(l);l.x=l.x*-e.bounce.x,l.y=l.y*-e.bounce.y,r.x=r.x*(1-e.slopes.friction.x-o.slope.friction.x),r.y=r.y*(1-e.slopes.friction.y-o.slope.friction.y),e.velocity.x=l.x+r.x,e.velocity.y=l.y+r.y,this.pull(e,t),this.vectorPool.push(l,r)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateValues=function(e){e.polygon.pos.x=e.x,e.polygon.pos.y=e.y,e.slopes.velocity.x=e.velocity.x,e.slopes.velocity.y=e.velocity.y},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateFlags=function(e,o){e.touching.up=e.touching.up||o.overlapV.y>0,e.touching.down=e.touching.down||o.overlapV.y<0,e.touching.left=e.touching.left||o.overlapV.x>0,e.touching.right=e.touching.right||o.overlapV.x<0,e.touching.none=!(e.touching.up||e.touching.down||e.touching.left||e.touching.right),e.blocked.up=e.blocked.up||0===o.overlapV.x&&o.overlapV.y>0,e.blocked.down=e.blocked.down||0===o.overlapV.x&&o.overlapV.y<0,e.blocked.left=e.blocked.left||0===o.overlapV.y&&o.overlapV.x>0,e.blocked.right=e.blocked.right||0===o.overlapV.y&&o.overlapV.x<0},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.pull=function(e,o){if(!(e.slopes.pullUp||e.slopes.pullDown||e.slopes.pullLeft||e.slopes.pullRight||e.slopes.pullTopLeft||e.slopes.pullTopRight||e.slopes.pullBottomLeft||e.slopes.pullBottomRight))return!1;var t=o.overlapN.clone().scale(-1);return e.slopes.pullUp&&t.y<0?(pullUp=t.clone().scale(e.slopes.pullUp),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullDown&&t.y>0?(pullDown=t.clone().scale(e.slopes.pullDown),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullLeft&&t.x<0?(pullLeft=t.clone().scale(e.slopes.pullLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):e.slopes.pullRight&&t.x>0?(pullRight=t.clone().scale(e.slopes.pullRight),e.velocity.x+=pullRight.x,e.velocity.y+=pullRight.y,!0):e.slopes.pullTopLeft&&t.x<0&&t.y<0?(pullUp=t.clone().scale(e.slopes.pullTopLeft),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullTopRight&&t.x>0&&t.y<0?(pullDown=t.clone().scale(e.slopes.pullTopRight),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullBottomLeft&&t.x<0&&t.y>0?(pullLeft=t.clone().scale(e.slopes.pullBottomLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):!!(e.slopes.pullBottomRight&&t.x>0&&t.y>0)&&(pullRight=t.clone().scale(e.slopes.pullBottomRight),e.velocity.x+=pullRight.x,e.velocity.y+=pullRight.y,!0)},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldCollide=function(e,o){return e.enable&&e.polygon&&e.slopes&&o.collides&&o.slope&&o.slope.polygon},Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn=function(e,o,t){for(var l=Number.MAX_VALUE,r=-Number.MAX_VALUE,i=e.length,s=0;sr&&(r=p)}t[0]=l,t[1]=r},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.isSeparatingAxis=function(e,o,t,l){var r=e.pos,i=o.pos,s=e.calcPoints,p=o.calcPoints,n=this.arrayPool.pop(),a=this.arrayPool.pop(),h=this.vectorPool.pop().copy(i).sub(r),c=h.dot(t);if(Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(s,t,n),Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(p,t,a),a[0]+=c,a[1]+=c,n[0]>=a[1]||a[0]>=n[1])return this.vectorPool.push(h),this.arrayPool.push(n),this.arrayPool.push(a),!0;var T,S,g=0;n[0]a[1]?(g=n[0]-a[1],l.aInB=!1):(T=n[1]-a[0],S=a[1]-n[0],T>=S&&(g=-S)));var P=Math.abs(g);return P0&&T[i].overlap0)){for(P=!1,s=0;s=0?e:("string"==typeof e&&(e=e.toUpperCase()),Phaser.Plugin.ArcadeSlopes.TileSlope.hasOwnProperty(e)?Phaser.Plugin.ArcadeSlopes.TileSlope[e]:(console.warn("Unknown slope type '"+e+"'"),Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN))},Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype,"slope",{get:function(){return this.line?(this.line.start.y-this.line.end.y)/(this.line.start.x-this.line.end.x):0}}),Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype,"typeName",{get:function(){return Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName(this.type)},set:function(e){this.type=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(e)}}),Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName=function(e){return Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames.hasOwnProperty(e)?Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[e]:Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[-1]},Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames={"-1":"UNKNOWN",0:"FULL",21:"HALF_BOTTOM",22:"HALF_TOP",23:"HALF_LEFT",24:"HALF_RIGHT",1:"HALF_BOTTOM_LEFT",2:"HALF_BOTTOM_RIGHT",3:"HALF_TOP_LEFT",4:"HALF_TOP_RIGHT",5:"QUARTER_BOTTOM_LEFT_LOW",6:"QUARTER_BOTTOM_LEFT_HIGH",7:"QUARTER_BOTTOM_RIGHT_LOW",8:"QUARTER_BOTTOM_RIGHT_HIGH",9:"QUARTER_LEFT_BOTTOM_LOW",10:"QUARTER_LEFT_BOTTOM_HIGH",11:"QUARTER_RIGHT_BOTTOM_LOW",12:"QUARTER_RIGHT_BOTTOM_HIGH",13:"QUARTER_LEFT_TOP_LOW",14:"QUARTER_LEFT_TOP_HIGH",15:"QUARTER_RIGHT_TOP_LOW",16:"QUARTER_RIGHT_TOP_HIGH",17:"QUARTER_TOP_LEFT_LOW",18:"QUARTER_TOP_LEFT_HIGH",19:"QUARTER_TOP_RIGHT_LOW",20:"QUARTER_TOP_RIGHT_HIGH"},Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY=0,Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID=1,Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING=2,Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN=-1,Phaser.Plugin.ArcadeSlopes.TileSlope.FULL=0,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM=21,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP=22,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT=23,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT=24,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT=1,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT=2,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT=3,Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT=4,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW=5,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH=6,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW=7,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH=8,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW=9,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH=10,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW=11,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH=12,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW=13,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH=14,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW=15,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH=16,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW=17,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH=18,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW=19,Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH=20,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory=function(){this.definitions={},this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.FULL]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow,this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh,this.mappings={},this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes,this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA]=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics,this.vectorPool=[];for(var e=0;e<100;e++)this.vectorPool.push(new SAT.Vector)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.constructor=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.define=function(e,o){"function"==typeof o&&(this.definitions[e]=o)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.create=function(e,o){var t=e;return e=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(t),this.definitions.hasOwnProperty(e)?"function"!=typeof this.definitions[e]?(console.warn("Slope type definition for type "+t+" is not a function"),null):this.definitions[e].call(this,e,o):(console.warn("Slope type "+t+" not defined"),null)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemap=function(e,o,t,l){return o=e.getLayer(o),this.convertTilemapLayer(o,t,l),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemapLayer=function(e,o,t){var l=this;if("string"==typeof o){var r=this.resolveMappingType(o);if(!this.mappings[r])return console.warn("Tilemap could not be converted; mapping type '"+o+"' is unknown"),e;o=this.mappings[r](t)}return e.layer.data.forEach(function(t){t.forEach(function(t){var r;t.properties.type&&(r=l.create(t.properties.type,t)),!r&&o.hasOwnProperty(t.index)&&(r=l.create(o[t.index],t)),r&&(t.slope=r);var i=t.x,s=t.y;t.neighbours=t.neighbours||{},t.neighbours.above=e.map.getTileAbove(e.index,i,s),t.neighbours.below=e.map.getTileBelow(e.index,i,s),t.neighbours.left=e.map.getTileLeft(e.index,i,s),t.neighbours.right=e.map.getTileRight(e.index,i,s),t.neighbours.topLeft=e.map.getTileTopLeft(e.index,i,s),t.neighbours.topRight=e.map.getTileTopRight(e.index,i,s),t.neighbours.bottomLeft=e.map.getTileBottomLeft(e.index,i,s),t.neighbours.bottomRight=e.map.getTileBottomRight(e.index,i,s)})}),this.calculateEdges(e),this.addDebugSettings(e),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges=function(e){var o,t,l,r,i,s,p,n,a;for(l=e.layer.height,r=e.layer.width,t=0;t=0?e:("string"==typeof e&&(e=e.toUpperCase()),Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.hasOwnProperty(e)&&this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[e]]?Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[e]:(console.warn("Unknown tileset mapping type '"+e+"'"),-1))},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull=function(e,o){var t=new SAT.Box(new SAT.Vector(o.worldX,o.worldY),o.width,o.height).toPolygon();return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom=function(e,o){var t=o.height/2,l=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,t),new SAT.Vector(o.width,t),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),r=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.top+o.height/2),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop=function(e,o){var t=o.height/2,l=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,t),new SAT.Vector(0,t)]),r=new Phaser.Line(o.left,o.top,o.right,o.top),i={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft=function(e,o){var t=o.width/2,l=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(t,0),new SAT.Vector(t,o.height),new SAT.Vector(0,o.height)]),r=new Phaser.Line(o.left+t,o.top,o.left+t,o.bottom),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight=function(e,o){var t=o.width/2,l=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(t,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(t,o.height)]),r=new Phaser.Line(o.left+t,o.top,o.left+t,o.bottom),i={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING};return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.top,o.right,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.7071067811865475,(-.7071067811865475));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.bottom,o.right,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.707106781186548),(-.707106781186548));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.right,o.top,o.left,o.bottom),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.7071067811865475,.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height)]),l=new Phaser.Line(o.right,o.bottom,o.left,o.top),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.7071067811865475),.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,o.height/2),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,o.height/2),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.top,o.right,o.top+o.height/2),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(o.width,o.height/2),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.bottom,o.right,o.top+o.height/2),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,o.height/2),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.bottom,o.right,o.top+o.height/2),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width/2,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left+o.width/2,o.top,o.right,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width/2,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.top,o.left+o.width/2,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(o.width/2,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.bottom,o.left+o.width/2,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(o.width/2,o.height)]),l=new Phaser.Line(o.left+o.width/2,o.bottom,o.right,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width/2,0),new SAT.Vector(0,o.height)]),l=new Phaser.Line(0,o.height,o.width/2,0),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width/2,o.height),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left+o.width/2,o.bottom,o.right,o.bottom),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(o.width/2,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height)]),l=new Phaser.Line(o.left+o.width/2,o.top,o.right,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(o.width/2,o.height)]),l=new Phaser.Line(o.left,o.top,o.left+o.width/2,o.bottom),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(0,o.height/2)]),l=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.top),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height/2),new SAT.Vector(0,o.height)]),l=new Phaser.Line(o.left,o.bottom,o.right,o.top+o.height/2),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height/2)]),l=new Phaser.Line(o.left,o.top,o.right,o.top+o.height/2),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh=function(e,o){var t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(0,o.height/2)]),l=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.top+o.height),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},i=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,t,l,r,i)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset=function(e){var o=parseInt(e);return o=isNaN(o)||"number"!=typeof o?0:o-1},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes=function(e){offset=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(e);var o={};return o[offset+1]="FULL",o[offset+2]="HALF_TOP",o[offset+3]="HALF_BOTTOM",o[offset+4]="HALF_LEFT",o[offset+5]="HALF_RIGHT",o[offset+6]="HALF_BOTTOM_LEFT",o[offset+7]="HALF_BOTTOM_RIGHT",o[offset+8]="HALF_TOP_LEFT",o[offset+9]="HALF_TOP_RIGHT",o[offset+10]="QUARTER_TOP_LEFT_HIGH",o[offset+11]="QUARTER_TOP_LEFT_LOW",o[offset+12]="QUARTER_TOP_RIGHT_LOW",o[offset+13]="QUARTER_TOP_RIGHT_HIGH",o[offset+14]="QUARTER_BOTTOM_LEFT_HIGH",o[offset+15]="QUARTER_BOTTOM_LEFT_LOW",o[offset+16]="QUARTER_BOTTOM_RIGHT_LOW",o[offset+17]="QUARTER_BOTTOM_RIGHT_HIGH",o[offset+18]="QUARTER_LEFT_BOTTOM_HIGH",o[offset+19]="QUARTER_RIGHT_BOTTOM_HIGH",o[offset+20]="QUARTER_LEFT_TOP_HIGH",o[offset+21]="QUARTER_RIGHT_TOP_HIGH",o[offset+35]="QUARTER_LEFT_BOTTOM_LOW",o[offset+36]="QUARTER_RIGHT_BOTTOM_LOW",o[offset+37]="QUARTER_LEFT_TOP_LOW",o[offset+38]="QUARTER_RIGHT_TOP_LOW",o},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics=function(e){offset=Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(e);var o={};return o[offset+2]="FULL",o[offset+3]="HALF_BOTTOM_LEFT",o[offset+4]="HALF_BOTTOM_RIGHT",o[offset+6]="HALF_TOP_LEFT",o[offset+5]="HALF_TOP_RIGHT",o[offset+15]="QUARTER_BOTTOM_LEFT_LOW",o[offset+16]="QUARTER_BOTTOM_RIGHT_LOW",o[offset+17]="QUARTER_TOP_RIGHT_LOW",o[offset+18]="QUARTER_TOP_LEFT_LOW",o[offset+19]="QUARTER_BOTTOM_LEFT_HIGH",o[offset+20]="QUARTER_BOTTOM_RIGHT_HIGH",o[offset+21]="QUARTER_TOP_RIGHT_HIGH",o[offset+22]="QUARTER_TOP_LEFT_HIGH",o[offset+23]="QUARTER_LEFT_BOTTOM_HIGH",o[offset+24]="QUARTER_RIGHT_BOTTOM_HIGH",o[offset+25]="QUARTER_RIGHT_TOP_LOW",o[offset+26]="QUARTER_LEFT_TOP_LOW",o[offset+27]="QUARTER_LEFT_BOTTOM_LOW",o[offset+28]="QUARTER_RIGHT_BOTTOM_LOW",o[offset+29]="QUARTER_RIGHT_TOP_HIGH",o[offset+30]="QUARTER_LEFT_TOP_HIGH",o[offset+31]="HALF_BOTTOM",o[offset+32]="HALF_RIGHT",o[offset+33]="HALF_TOP",o[offset+34]="HALF_LEFT",o},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES=1,Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA=2,function(e,o){"use strict";"function"==typeof define&&define.amd?define(o):"object"==typeof exports?module.exports=o():e.SAT=o()}(this,function(){"use strict";function e(e,o){this.x=e||0,this.y=o||0}function o(o,t){this.pos=o||new e,this.r=t||0}function t(o,t){this.pos=o||new e,this.angle=0,this.offset=new e,this.setPoints(t||[])}function l(o,t,l){this.pos=o||new e,this.w=t||0,this.h=l||0}function r(){this.a=null,this.b=null,this.overlapN=new e,this.overlapV=new e,this.clear()}function i(e,o,t){for(var l=Number.MAX_VALUE,r=-Number.MAX_VALUE,i=e.length,s=0;sr&&(r=p)}t[0]=l,t[1]=r}function s(e,o,t,l,r,s){var p=d.pop(),n=d.pop(),a=P.pop().copy(o).sub(e),h=a.dot(r);if(i(t,r,p),i(l,r,n),n[0]+=h,n[1]+=h,p[0]>n[1]||n[0]>p[1])return P.push(a),d.push(p),d.push(n),!0;if(s){var c=0;if(p[0]n[1])c=p[0]-n[1],s.aInB=!1;else{var T=p[1]-n[0],S=n[1]-p[0];c=Tt?R:w}function n(e,o){var t=P.pop().copy(e).sub(o.pos),l=o.r*o.r,r=t.len2();return P.push(t),r<=l}function a(e,o){f.pos.copy(e),A.clear();var t=S(f,o,A);return t&&(t=A.aInB),t}function h(e,o,t){var l=P.pop().copy(o.pos).sub(e.pos),r=e.r+o.r,i=r*r,s=l.len2();if(s>i)return P.push(l),!1;if(t){var p=Math.sqrt(s);t.a=e,t.b=o,t.overlap=r-p,t.overlapN.copy(l.normalize()),t.overlapV.copy(l).scale(t.overlap),t.aInB=e.r<=o.r&&p<=o.r-e.r,t.bInA=o.r<=e.r&&p<=e.r-o.r}return P.push(l),!0}function c(e,o,t){for(var l=P.pop().copy(o.pos).sub(e.pos),r=o.r,i=r*r,s=e.calcPoints,n=s.length,a=P.pop(),h=P.pop(),c=0;ci&&(t.aInB=!1);var d=p(a,h);if(d===y){a.copy(e.edges[S]);var A=P.pop().copy(l).sub(s[S]);if(d=p(a,A),d===R){var f=h.len();if(f>r)return P.push(l),P.push(a),P.push(h),P.push(A),!1;t&&(t.bInA=!1,u=h.normalize(),g=r-f)}P.push(A)}else if(d===R){if(a.copy(e.edges[T]),h.copy(l).sub(s[T]),d=p(a,h),d===y){var f=h.len();if(f>r)return P.push(l),P.push(a),P.push(h),!1;t&&(t.bInA=!1,u=h.normalize(),g=r-f)}}else{var w=a.perp().normalize(),f=h.dot(w),v=Math.abs(f);if(f>0&&v>r)return P.push(l),P.push(w),P.push(h),!1;t&&(u=w,g=r-f,(f>=0||g<2*r)&&(t.bInA=!1))}u&&t&&Math.abs(g)0&&(this.x=this.x/e,this.y=this.y/e),this},e.prototype.add=e.prototype.add=function(e){return this.x+=e.x,this.y+=e.y,this},e.prototype.sub=e.prototype.sub=function(e){return this.x-=e.x,this.y-=e.y,this},e.prototype.scale=e.prototype.scale=function(e,o){return this.x*=e,this.y*=o||e,this},e.prototype.project=e.prototype.project=function(e){var o=this.dot(e)/e.len2();return this.x=o*e.x,this.y=o*e.y,this},e.prototype.projectN=e.prototype.projectN=function(e){var o=this.dot(e);return this.x=o*e.x,this.y=o*e.y,this},e.prototype.reflect=e.prototype.reflect=function(e){var o=this.x,t=this.y;return this.project(e).scale(2),this.x-=o,this.y-=t,this},e.prototype.reflectN=e.prototype.reflectN=function(e){var o=this.x,t=this.y;return this.projectN(e).scale(2),this.x-=o,this.y-=t,this},e.prototype.dot=e.prototype.dot=function(e){return this.x*e.x+this.y*e.y},e.prototype.len2=e.prototype.len2=function(){return this.dot(this)},e.prototype.len=e.prototype.len=function(){return Math.sqrt(this.len2())},g.Circle=o,o.prototype.getAABB=o.prototype.getAABB=function(){var o=this.r,t=this.pos.clone().sub(new e(o,o));return new l(t,2*o,2*o).toPolygon()},g.Polygon=t,t.prototype.setPoints=t.prototype.setPoints=function(o){var t=!this.points||this.points.length!==o.length;if(t){var l,r=this.calcPoints=[],i=this.edges=[],s=this.normals=[];for(l=0;ls&&(s=a.x),a.yp&&(p=a.y)}return new l(this.pos.clone().add(new e(r,i)),s-r,p-i).toPolygon()},g.Box=l,l.prototype.toPolygon=l.prototype.toPolygon=function(){var o=this.pos,l=this.w,r=this.h;return new t(new e(o.x,o.y),[new e,new e(l,0),new e(l,r),new e(0,r)])},g.Response=r,r.prototype.clear=r.prototype.clear=function(){return this.aInB=!0,this.bInA=!0,this.overlap=Number.MAX_VALUE,this};for(var P=[],u=0;u<10;u++)P.push(new e);for(var d=[],u=0;u<5;u++)d.push([]);var A=new r,f=new l(new e,1e-6,1e-6).toPolygon();g.isSeparatingAxis=s;var y=-1,w=0,R=1;return g.pointInCircle=n,g.pointInPolygon=a,g.testCircleCircle=h,g.testPolygonCircle=c,g.testCirclePolygon=T,g.testPolygonPolygon=S,g}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 927c2e1..04aaf6f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,9 +11,9 @@ var comment = ['/**', ' *', ' * <%= package.description %>', ' *', - ' * <%= package.homepage %>', - ' * Copyright 2016-2017 <%= package.author %>', - ' * Released under the <%= package.license %> license', + ' * @copyright 2016-2017 <%= package.author %>', + ' * @license <%= package.license %>', + ' * @see <%= package.homepage %>', ' */', '' ].join('\n'); diff --git a/package.json b/package.json index bda86e4..68a7d90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "phaser-arcade-slopes", - "version": "0.3.0-dev", + "version": "0.3.0", "description": "A Phaser plugin that brings sloped tile collision handling to Phaser's Arcade Physics engine", "main": "dist/phaser-arcade-slopes.js", "scripts": {}, @@ -14,6 +14,8 @@ "tiles", "sloped", "tiles", + "tile slopes", + "slopes tiles", "collision", "collisions", "collision handling", diff --git a/src/ArcadeSlopes.js b/src/ArcadeSlopes.js index f92e1e7..c58fde5 100644 --- a/src/ArcadeSlopes.js +++ b/src/ArcadeSlopes.js @@ -54,7 +54,7 @@ Phaser.Plugin.ArcadeSlopes.prototype.constructor = Phaser.Plugin.ArcadeSlopes; * @constant * @type {string} */ -Phaser.Plugin.ArcadeSlopes.VERSION = '0.3.0-dev'; +Phaser.Plugin.ArcadeSlopes.VERSION = '0.3.0'; /** * The Separating Axis Theorem collision solver type.