├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── ROADMAP.md
├── assets
├── arcade-slopes-128.png
├── arcade-slopes-16.png
├── arcade-slopes-32.png
└── arcade-slopes-64.png
├── dist
├── phaser-arcade-slopes.js
└── phaser-arcade-slopes.min.js
├── gulpfile.js
├── package.json
├── screenshot.gif
├── src
├── ArcadeSlopes.js
└── ArcadeSlopes
│ ├── Facade.js
│ ├── Overrides.js
│ ├── SatSolver.js
│ ├── TileSlope.js
│ └── TileSlopeFactory.js
└── typescript
└── phaser-arcade-slopes.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | .project
4 | npm-debug.log*
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Arcade Slopes Change Log
2 |
3 | ## v0.3.2 - 17th August 2021
4 | - Fixed overzealous ignorance of collision normals for less common tile configurations (\#55, \#56)
5 |
6 | ## v0.3.1 - 1st March 2018
7 | - Implemented Facade.resetCollision() helper method. This can be used to reset
8 | all Arcade Physics body collision flags to their default values.
9 | - Fixed typescript definition for `game.slopes`.
10 |
11 | ## v0.3.0 - 5th August 2017
12 | - Removed the snap feature (#34)
13 | - Removed heuristics in favour of a custom SAT implementation that prevents
14 | internal edge collisions (#36, #38, #40)
15 | - Implemented debug rendering for tile normals and ignored tile normals
16 | - Implemented simple object pooling for less memory-hungry calculations (#42)
17 | - Improved the consistency of SAT responses and overlap values set on physics
18 | bodies
19 |
20 | ## v0.2.1 - 1st August 2017
21 | - Dropped bower in favour of npm to load in the SAT.js dependency.
22 | - Added a banner to the minified distributive as part of the build process.
23 | - No functional changes.
24 |
25 | ## v0.2.0 - 18th June 2017
26 | - Added heuristics for square tiles to improve skipped collisions (#38) at the
27 | expense of some inconsistencies when bodies exit tiles from inside.
28 | - Allowed physics bodies to disable and enable heuristics for themselves using
29 | `body.slopes.heuristics`. `null` uses default, `false` disables and `true`
30 | enables.
31 |
32 | ## v0.2.0-beta2 - 7th June 2017
33 | - Implemented unintrusive support for an experimental Phaser CE fork.
34 | - Added sprite scale support for AABB physics bodies.
35 | - Included a reference to the plugin with the facade. This makes it easier to
36 | remove the plugin at runtime
37 | (`this.game.plugins.remove(this.game.slopes.plugin)`).
38 | - Fixed multiple calls to `slopes.body.enable(sprite)` causing an error.
39 | - Ensured that tilemap debug rendering only wraps if the layer has its `wrap`
40 | property set to `true`.
41 | - Fixed wasTouching flags (#35).
42 | - Added the last collided tile to `body.slopes.tile`.
43 | - Fixed a heuristics mistake that's been around since May 2016 (4ee0d23c)! There
44 | are probably more...
45 | - Fixed a wrapping issue with tilemap layer debug rendering.
46 | - Added dynamic `heuristics` and `preferY` properties to the Facade, making it
47 | simpler to toggle these features globally (`game.slopes.heuristics`,
48 | `game.slopes.preferY`).
49 | - Fixed SatSolver.collide() setting collision response properties on physics
50 | bodies before knowing whether the separation was successful.
51 | - Fixed SatSolver.collideOnAxis() not setting collision response properties on
52 | physics bodies.
53 | - Improved memory consumption by reusing SAT response objects for each body.
54 |
55 | ## v0.2.0-beta - 10th February 2017
56 | - Supported tile collision flags when determining separation (#27, #28,
57 | thanks @kevinchau321).
58 | - Fixed collision snapping running when it shouldn't (#21, thanks
59 | @michaeljcalkins).
60 | - Implemented tile slope polygon debug rendering (#4).
61 | - Added premade Arcade Slopes tilesets (#29).
62 | - Implemented Arcade Slopes tileset mapping shortcut (#9).
63 | - Implemented support for tile property type mapping (#9).
64 |
65 | ## v0.2.0-alpha2 - 8th February 2017
66 | - Prevented separation if a body isn't moving into a tile (#14).
67 | - Refactored the plugin class to attach the Facade to the Phaser game instead of
68 | itself.
69 | - Extracted a separate roadmap file from the readme.
70 | - Implemented automatic slope mapping for the Ninja Physics tileset.
71 | - Added missing touching flags for physics bodies (#19, thanks @IkonOne).
72 | - Implemented internal edge flagging (#18).
73 | - Updated SAT.js dependency.
74 | - Implemented support for offset tilemaps (#26).
75 |
76 | ## v0.2.0-alpha - 19th August 2016
77 | - Initial circular physics body support.
78 | - Typescript definitions (#12, #15, thanks @IkonOne).
79 |
80 | ## v0.1.1 - 26th June 2016
81 | - Implemented corner collision pulling (#3).
82 | - Fixed incompatibility with Phaser 2.4.9 and 2.5.0 (#5).
83 | - Updated the readme regarding changes to physics body sizes before enabling
84 | slopes (#6).
85 |
86 | ## v0.1.0 - 22nd May 2016
87 | - Collision pulling; an alternative approach to sticky slopes.
88 |
89 | ## v0.1.0-beta - 15th May 2016
90 | - Friction for physics bodies and tiles
91 | - Collision callback support, including `physics.arcade.collide()` calls,
92 | tile-specific callbacks and layer-level tile callbacks
93 | - Initial sticky slopes functionality
94 | - Arcade body properties that configure interaction with tiles
95 | - Tile slope type name retrieval
96 |
97 | ## v0.1.0-alpha3 - 11th May 2016
98 | - Further improved heuristics
99 |
100 | ## v0.1.0-alpha2 - 10th May 2016
101 | - Fixed heuristics not working after disabling and re-enabling the plugin at
102 | runtime
103 | - Fixed some heuristics rules
104 |
105 | Lesson learned: Heuristics are unreliable!
106 |
107 | ## v0.1.0-alpha - 8th May 2016
108 | - Initial functionality (in development since 19th April 2016)
109 | - 24 new tile types
110 | - SAT-driven collision solver using SAT.js
111 | - SAT restraints based on heuristics that prevent AABBs catching on hidden
112 | edges
113 | - Works with sprites, groups and particle emitters!
114 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | **The MIT License (MIT)**
2 |
3 | Copyright (c) 2016-2021 Chris Andrew
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Phaser Arcade Slopes Plugin
2 |
3 | **Arcade Slopes** brings sloped tile collision handling to
4 | [Phaser](http://phaser.io)'s [Arcade
5 | Physics](http://phaser.io/examples/v2/category/arcade-physics) engine.
6 |
7 | ## [Demo](http://hexus.github.io/phaser-arcade-slopes)
8 |
9 | Check out the **[demo](http://hexus.github.io/phaser-arcade-slopes)**!
10 |
11 | 
12 |
13 | ## Features
14 |
15 | - 24 new tile types :tada:
16 | - [SAT](https://github.com/jriecken/sat-js) collision handling :ok_hand:
17 | - Unobtrusive and cooperative integration with Arcade Physics :v:
18 | - Supports sprites :rocket:, groups :busts_in_silhouette:, particle
19 | emitters :sparkles: and circular physics bodies :white_circle:
20 |
21 | ## Compatibility
22 |
23 | | Phaser Version | Arcade Slopes Version |
24 | | ---------------- | ------------------------------------------------------------------- |
25 | | v2.4.1 - v2.4.8 | [v0.1.0](https://github.com/hexus/phaser-arcade-slopes/tree/v0.1.0) |
26 | | v2.5.0 - v2.10.1 | [v0.1.1](https://github.com/hexus/phaser-arcade-slopes/tree/v0.1.1) - [v0.3.2](https://github.com/hexus/phaser-arcade-slopes/tree/v0.3.2) |
27 | | v3.x | [hexus/phaser-slopes](https://github.com/hexus/phaser-slopes) |
28 |
29 | ## Installation
30 |
31 | Grab a copy of the
32 | [latest release](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/v0.3.2/dist/phaser-arcade-slopes.min.js)
33 | from the [**dist**](dist) directory in this repository and include it after
34 | Phaser.
35 |
36 | ```html
37 |
38 |
39 | ```
40 |
41 | ## Usage
42 |
43 | - [Enabling the plugin](#enabling-the-plugin)
44 | - [Mapping tiles](#mapping-tiles)
45 | - [Enabling physics bodies](#enabling-physics-bodies)
46 | - [Collision](#collision)
47 | - [Debug rendering](#debug-rendering)
48 | - [Extras](#extras)
49 | - [Minimum Y Offset](#minimum-y-offset)
50 | - [Collision pulling](#collision-pulling)
51 |
52 | ### Enabling the plugin
53 |
54 | Enable the plugin in the `create()` method of your Phaser state.
55 |
56 | ```js
57 | game.plugins.add(Phaser.Plugin.ArcadeSlopes);
58 | ```
59 |
60 | ### Mapping tiles
61 |
62 | The plugin provides a couple of built in tile slope mappings:
63 |
64 | - [Arcade Slopes tileset](assets)
65 | (`arcadeslopes`)
66 | ([16px](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/master/assets/arcade-slopes-16.png),
67 | [32px](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/master/assets/arcade-slopes-32.png),
68 | [64px](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/master/assets/arcade-slopes-64.png),
69 | [128px](https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/master/assets/arcade-slopes-128.png))
70 | - [Ninja Physics debug tileset](https://github.com/photonstorm/phaser/tree/v2.4.7/resources/Ninja%20Physics%20Debug%20Tiles)
71 | (`ninja`)
72 | ([32px](https://raw.githubusercontent.com/photonstorm/phaser/v2.4.7/resources/Ninja%20Physics%20Debug%20Tiles/32px/ninja-tiles32.png), [64px](https://raw.githubusercontent.com/photonstorm/phaser/v2.4.7/resources/Ninja%20Physics%20Debug%20Tiles/64px/ninja-tiles64.png),
73 | [128px](https://raw.githubusercontent.com/photonstorm/phaser/v2.4.7/resources/Ninja%20Physics%20Debug%20Tiles/128px/ninja-tiles128.png))
74 |
75 | After you've created a tilemap with a collision layer, you'll need to convert
76 | that layer to work with Arcade Slopes.
77 |
78 | ```js
79 | // Create the tilemap and make it aware of the tileset it uses
80 | map = game.add.tilemap('tilemap');
81 | map.addTilesetImage('collision', 'arcade-slopes-32');
82 | map.setCollisionBetween(1, 38);
83 |
84 | // Create the collision layer from the tilemap
85 | ground = map.createLayer('collision');
86 |
87 | // Convert the collision layer to work with Arcade Slopes
88 | game.slopes.convertTilemapLayer(ground, 'arcadeslopes');
89 | ```
90 |
91 | In the case that the first tile ID of the collision tileset in your tilemap is
92 | not `1` (the default), you can provide a third argument to specify it. The
93 | arguments provided to the `setCollisionBetween()` method may need to be adjusted
94 | as well.
95 |
96 | ```js
97 | map.setCollisionBetween(16, 53);
98 | game.slopes.convertTilemapLayer(ground, 'ninja', 16);
99 | ```
100 |
101 | **Please note:** Tile GIDs in maps exported from Tiled are always one more than
102 | the ID shown in the GUI. Arcade Slopes expects the above parameter to match the
103 | first GID _as specified in the Tiled map data_.
104 |
105 | ### Enabling physics bodies
106 |
107 | Now you need to enable slopes for any game entities you want to collide against
108 | the tilemap.
109 |
110 | ```js
111 | game.physics.arcade.enable(player);
112 |
113 | game.slopes.enable(player);
114 | game.slopes.enable(emitter);
115 | ```
116 |
117 | You don't need to do anything special for circular physics bodies, just the
118 | usual `sprite.body.setCircle(radius)`.
119 |
120 | _Make sure you call `game.slopes.enable(object)` **after** making any changes to
121 | the **size** or **shape** of the physics body._
122 |
123 | ### Collision
124 |
125 | Now you can collide your sprite against the tilemap in the `update()` method of
126 | your Phaser state, as you normally would, using Arcade Physics. Voila!
127 |
128 | ```js
129 | // Collide the player with the collision layer
130 | game.physics.arcade.collide(player, ground);
131 |
132 | // Collide the particles with the collision layer
133 | game.physics.arcade.collide(emitter, ground);
134 | ```
135 |
136 | ### Debug rendering
137 |
138 | To debug your collision layer, set its debug property to `true`.
139 |
140 | This will overlay the collision shapes of each tile when the layer is rendered.
141 |
142 | ```js
143 | ground.debug = true;
144 | ```
145 |
146 | ### Extras
147 |
148 | #### Minimum Y offset
149 |
150 | This feature separates rectangular physics bodies on the Y axis only, in the
151 | right situations.
152 |
153 | ```js
154 | // Prefer the minimum Y offset for this physics body
155 | player.body.slopes.preferY = true;
156 |
157 | // Prefer the minimum Y offset globally
158 | game.slopes.preferY = true;
159 | ```
160 |
161 | If you're making a platformer, your player has drag on the X axis, and you don't
162 | want it to slide down slopes, this should solve your problem.
163 |
164 | #### Collision pulling
165 |
166 | To attempt to keep objects on a surface, you can use collision pulling.
167 |
168 | This will pull physics bodies into a collision by a set velocity, if it matches
169 | the set direction.
170 |
171 | ```js
172 | // Pull the player into downwards collisions with a velocity of 50
173 | player.body.slopes.pullDown = 50;
174 | ```
175 |
176 | Here are the available properties for collision pulling:
177 |
178 | ```js
179 | body.slopes.pullUp
180 | body.slopes.pullDown
181 | body.slopes.pullLeft
182 | body.slopes.pullRight
183 | body.slopes.pullTopLeft
184 | body.slopes.pullTopRight
185 | body.slopes.pullBottomLeft
186 | body.slopes.pullBottomRight
187 | ```
188 |
189 | ## Building
190 |
191 | If you want to build the plugin yourself from source, clone the repository and
192 | run NPM and Gulp like so.
193 |
194 | ```bash
195 | npm install
196 | gulp build
197 | ```
198 |
199 | There's also a watch task that builds the plugin whenever you make changes
200 | to the source.
201 |
202 | ```bash
203 | gulp watch
204 | ```
205 |
206 | ## Thanks
207 |
208 | My thanks go out to those who made this Plugin possible.
209 |
210 | - [Richard Davey](https://twitter.com/photonstorm) - for Phaser :rocket:
211 | - [Jim Riecken](https://github.com/jriecken) - [SAT.js](https://github.com/jriecken/sat-js)
212 | is awesome and saved me loads of time
213 | - [Metanet](http://www.metanetsoftware.com/) - for their incredibly helpful
214 | tutorials about [collision](http://www.metanetsoftware.com/technique/tutorialA.html)
215 | [detection](http://www.metanetsoftware.com/technique/tutorialB.html)
216 | - [Olivier Renault](http://elancev.name/oliver/2D%20polygon.htm#tut4) - for their
217 | tutorial on 2D polygon collision and response (from 2004!)
218 | - [Jan Geselle](https://github.com/geselle-jan) - for writing [a sloped tile
219 | implementation](https://github.com/geselle-jan/Metroid/commit/9c213e9f5779df1dcd6f7d2bed2a9b676a9e3c6b#diff-467b4e6069f6692511fc5e60f3c426ccR158)
220 | in Phaser that gave me the idea to write this plugin
221 | - Bethany - for listening to me blabber on about slopes for well over a month
222 | :full_moon_with_face:
223 |
224 | And to contributors who have been generous with their time, talents and support:
225 |
226 | - [@IkonOne](https://github.com/IkonOne)
227 | - [@michaeljcalkins](https://github.com/michaeljcalkins)
228 | - [@kevin-chau](https://github.com/kevin-chau)
229 | - [@dave-kennedy](https://github.com/dave-kennedy)
230 | - [@SHG42](https://github.com/SHG42)
231 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | - [x] v0.1.0
4 | - [x] Full support for collision callbacks
5 | - [x] `physics.arcade.collide` callbacks
6 | - [x] Tile callbacks
7 | - [x] Sticky slopes
8 | - [x] Friction
9 | - [x] `body.slope` properties for friction, sticky slopes, preferred
10 | separation axis and last overlap response
11 | - [x] v0.1.1
12 | - [x] Phaser 2.4.9/2.5.0 compatibility
13 | - [x] Corner collision pulling
14 | - [x] v0.2.0
15 | - [x] Premade Arcade Slopes tilesets
16 | - [x] Mapping shortcuts
17 | - [x] Ninja Physics debug tileset
18 | - [x] Arcade Slopes tileset
19 | - [x] Tile properties (`tile.properties.type`)
20 | - [x] Debug rendering
21 | - [x] ~~Collision vectors~~
22 | - [x] Tile face properties
23 | - [x] Tile polygons
24 | - [x] Flag internal polygon edges
25 | - [x] Offset tilemap layer support
26 | - [x] Circular physics body support
27 | - [x] Tile collision direction flags
28 | - [x] Clearer yet more in-depth readme
29 | - [ ] v0.3.0
30 | - [x] Custom SAT.js implementation that can prevent internal edge collisions
31 | ([like this](http://www.wildbunny.co.uk/blog/2012/10/31/2d-polygonal-collision-detection-and-internal-edges/comment-page-1/#comment-1978))
32 | - [x] Debug rendering
33 | - [x] Tile edge collision normals
34 | - [x] Memory consumption improvements
35 | - [ ] v0.4.0
36 | - [ ] Automatically set colliding tiles for Tilemaps
37 | - [ ] Automatically update sprite body polygons
38 | - [ ] Tunnelling solutions
39 | - [ ] Swept intersection tests
40 | - [ ] Raycasting
41 | - [ ] v0.5.0
42 | - [ ] Automatic sprite rotation
43 | - [ ] Omni-directional
44 | - [ ] Selective
45 | - [ ] Raycasting for sticky slopes
46 | - [ ] Dynamic tilemap support
47 | - [ ] v1.0.0
48 | - [ ] AMD/UMD
49 | - [ ] More consistent naming
50 | - [ ] Tile slope type constants
51 | - [ ] Direction/neighbour names
52 |
53 | ## Ideas
54 |
55 | - [ ] AABB collision margins
56 |
--------------------------------------------------------------------------------
/assets/arcade-slopes-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/7c587b0e11f2efc3f1a667bf741c03e59a5402d4/assets/arcade-slopes-128.png
--------------------------------------------------------------------------------
/assets/arcade-slopes-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/7c587b0e11f2efc3f1a667bf741c03e59a5402d4/assets/arcade-slopes-16.png
--------------------------------------------------------------------------------
/assets/arcade-slopes-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/7c587b0e11f2efc3f1a667bf741c03e59a5402d4/assets/arcade-slopes-32.png
--------------------------------------------------------------------------------
/assets/arcade-slopes-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/7c587b0e11f2efc3f1a667bf741c03e59a5402d4/assets/arcade-slopes-64.png
--------------------------------------------------------------------------------
/dist/phaser-arcade-slopes.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Phaser Arcade Slopes v0.3.2
3 | *
4 | * A Phaser CE plugin that brings sloped tile collision handling to Phaser's Arcade Physics engine
5 | *
6 | * @copyright 2016-2021 Chris Andrew
7 | * @license MIT
8 | * @see https://github.com/hexus/phaser-arcade-slopes
9 | */
10 | Phaser.Plugin.ArcadeSlopes=function(e,o,l){Phaser.Plugin.call(this,e,o);var t={};t[Phaser.Plugin.ArcadeSlopes.SAT]=new Phaser.Plugin.ArcadeSlopes.SatSolver,this.facade=new Phaser.Plugin.ArcadeSlopes.Facade(new Phaser.Plugin.ArcadeSlopes.TileSlopeFactory,t,l||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.2",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,l){this.factory=e,this.solvers=o,this.defaultSolver=l||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,l,t){return this.factory.convertTilemap(e,o,l,t)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer=function(e,o,l){return this.factory.convertTilemapLayer(e,o,l)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.collide=function(e,o,l,t,r){return this.solvers.sat.collide(e,o,l,t,r)},Phaser.Plugin.ArcadeSlopes.Facade.prototype.resetCollision=function(e){e.touching.none=!0,e.touching.up=!1,e.touching.down=!1,e.touching.left=!1,e.touching.right=!1,e.blocked.none=!0,e.blocked.up=!1,e.blocked.down=!1,e.blocked.left=!1,e.blocked.right=!1,e.overlapX=0,e.overlapY=0,e.slopes&&(e.slopes.sat.response=null)},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,l,t,r,s,i,p){if(!o.body||!l||!t)return!1;if(l.hasOwnProperty("slope")){if(this.game.slopes.collide(e,o.body,l,t,p))return this._total++,r&&r.call(i,o,l),!0}else if(this.separateTile(e,o.body,l,t,p))return this._total++,r&&r.call(i,o,l),!0;return!1},Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles=function(e,o,l,t,r,s,i){if(!(e.body&&o&&o.length&&l))return!1;for(var p=!1,n=0;n0&&l>0?this.layers[e].data[l-1][o-1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight=function(e,o,l){return o0?this.layers[e].data[l-1][o+1]:null},Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft=function(e,o,l){return o>0&&l=0;E++,L--,v+=h){E>=n&&(E-=n);var k=this.layer.data[E];for(_=U,O=f-A,R=B;O>=0;_++,O--,R+=a){_>=p&&(_-=p);var Y=k[_];if(Y&&!(Y.index<0)&&Y.collides&&(this.debugSettings.collidingTileOverfill&&(l.fillStyle=this.debugSettings.collidingTileOverfill,l.fillRect(R,v,P,u)),this.debugSettings.facingEdgeStroke&&(l.beginPath(),l.lineWidth=1,l.strokeStyle=this.debugSettings.facingEdgeStroke,Y.faceTop&&(l.moveTo(R,v),l.lineTo(R+P,v)),Y.faceBottom&&(l.moveTo(R,v+u),l.lineTo(R+P,v+u)),Y.faceLeft&&(l.moveTo(R,v),l.lineTo(R,v+u)),Y.faceRight&&(l.moveTo(R+P,v),l.lineTo(R+P,v+u)),l.closePath(),l.stroke(),Y.slope))){if(this.debugSettings.slopeEdgeStroke||this.debugSettings.slopeFill){for(l.beginPath(),l.lineWidth=1,I=Y.slope.polygon,l.moveTo(R+I.points[0].x*s,v+I.points[0].y*i),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,l,t){return!(!t&&!this.shouldSeparate(o.index,e,o,l))&&(!(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,l)?e.position.y+=Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY(l.overlapV):(e.position.x+=l.overlapV.x,e.position.y+=l.overlapV.y),!0)))},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity=function(e,o,l){var t=this.vectorPool.pop().copy(e.slopes.velocity).projectN(l.overlapN),r=this.vectorPool.pop().copy(e.slopes.velocity).sub(t);t.x=t.x*-e.bounce.x,t.y=t.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=t.x+r.x,e.velocity.y=t.y+r.y,this.pull(e,l),this.vectorPool.push(t,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 l=o.overlapN.clone().scale(-1);return e.slopes.pullUp&&l.y<0?(pullUp=l.clone().scale(e.slopes.pullUp),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullDown&&l.y>0?(pullDown=l.clone().scale(e.slopes.pullDown),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullLeft&&l.x<0?(pullLeft=l.clone().scale(e.slopes.pullLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):e.slopes.pullRight&&l.x>0?(pullRight=l.clone().scale(e.slopes.pullRight),e.velocity.x+=pullRight.x,e.velocity.y+=pullRight.y,!0):e.slopes.pullTopLeft&&l.x<0&&l.y<0?(pullUp=l.clone().scale(e.slopes.pullTopLeft),e.velocity.x+=pullUp.x,e.velocity.y+=pullUp.y,!0):e.slopes.pullTopRight&&l.x>0&&l.y<0?(pullDown=l.clone().scale(e.slopes.pullTopRight),e.velocity.x+=pullDown.x,e.velocity.y+=pullDown.y,!0):e.slopes.pullBottomLeft&&l.x<0&&l.y>0?(pullLeft=l.clone().scale(e.slopes.pullBottomLeft),e.velocity.x+=pullLeft.x,e.velocity.y+=pullLeft.y,!0):!!(e.slopes.pullBottomRight&&l.x>0&&l.y>0)&&(pullRight=l.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,l){for(var t=Number.MAX_VALUE,r=-Number.MAX_VALUE,s=e.length,i=0;ir&&(r=p)}l[0]=t,l[1]=r},Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.isSeparatingAxis=function(e,o,l,t){var r=e.pos,s=o.pos,i=e.calcPoints,p=o.calcPoints,n=this.arrayPool.pop(),a=this.arrayPool.pop(),h=this.vectorPool.pop().copy(s).sub(r),c=h.dot(l);if(Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(i,l,n),Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(p,l,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],t.aInB=!1):(T=n[1]-a[0],S=a[1]-n[0],T>=S&&(g=-S)));var P=Math.abs(g);return P0&&T[s].overlap0)){for(P=!1,i=0;i=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 l=e;return e=Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(l),this.definitions.hasOwnProperty(e)?"function"!=typeof this.definitions[e]?(console.warn("Slope type definition for type "+l+" is not a function"),null):this.definitions[e].call(this,e,o):(console.warn("Slope type "+l+" not defined"),null)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemap=function(e,o,l,t){return o=e.getLayer(o),this.convertTilemapLayer(o,l,t),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemapLayer=function(e,o,l){var t=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](l)}return e.layer.data.forEach(function(l){l.forEach(function(l){var r;l.properties.type&&(r=t.create(l.properties.type,l)),!r&&o.hasOwnProperty(l.index)&&(r=t.create(o[l.index],l)),r&&(l.slope=r);var s=l.x,i=l.y;l.neighbours=l.neighbours||{},l.neighbours.above=e.map.getTileAbove(e.index,s,i),l.neighbours.below=e.map.getTileBelow(e.index,s,i),l.neighbours.left=e.map.getTileLeft(e.index,s,i),l.neighbours.right=e.map.getTileRight(e.index,s,i),l.neighbours.topLeft=e.map.getTileTopLeft(e.index,s,i),l.neighbours.topRight=e.map.getTileTopRight(e.index,s,i),l.neighbours.bottomLeft=e.map.getTileBottomLeft(e.index,s,i),l.neighbours.bottomRight=e.map.getTileBottomRight(e.index,s,i)})}),this.calculateEdges(e),this.addDebugSettings(e),e},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges=function(e){var o,l,t,r,s,i,p,n,a;for(t=e.layer.height,r=e.layer.width,l=0;l=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 l=new SAT.Box(new SAT.Vector(o.worldX,o.worldY),o.width,o.height).toPolygon();return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom=function(e,o){var l=o.height/2,t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,l),new SAT.Vector(o.width,l),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),s={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,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop=function(e,o){var l=o.height/2,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,l),new SAT.Vector(0,l)]),r=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.top+o.height/2),s={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,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft=function(e,o){var l=o.width/2,t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(0,0),new SAT.Vector(l,0),new SAT.Vector(l,o.height),new SAT.Vector(0,o.height)]),r=new Phaser.Line(o.left+l,o.top,o.left+l,o.bottom),s={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,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight=function(e,o){var l=o.width/2,t=new SAT.Polygon(new SAT.Vector(o.worldX,o.worldY),[new SAT.Vector(l,0),new SAT.Vector(o.width,0),new SAT.Vector(o.width,o.height),new SAT.Vector(l,o.height)]),r=new Phaser.Line(o.left+l,o.top,o.left+l,o.bottom),s={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,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft=function(e,o){var l=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)]),t=new Phaser.Line(o.left,o.top,o.right,o.bottom),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector(.7071067811865475,(-.7071067811865475));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight=function(e,o){var l=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)]),t=new Phaser.Line(o.left,o.bottom,o.right,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector((-.707106781186548),(-.707106781186548));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft=function(e,o){var 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(0,o.height)]),t=new Phaser.Line(o.right,o.top,o.left,o.bottom),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector(.7071067811865475,.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight=function(e,o){var 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,o.height)]),t=new Phaser.Line(o.right,o.bottom,o.left,o.top),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector((-.7071067811865475),.7071067811865475);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow=function(e,o){var l=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)]),t=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},s=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh=function(e,o){var l=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)]),t=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},s=new SAT.Vector(.4472135954999579,(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow=function(e,o){var l=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)]),t=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},s=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh=function(e,o){var l=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)]),t=new Phaser.Line(o.left,o.top+o.height/2,o.right,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,left:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector((-.4472135954999579),(-.8944271909999159));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow=function(e,o){var l=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)]),t=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},s=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh=function(e,o){var l=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)]),t=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},s=new SAT.Vector(.8944271909999159,(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow=function(e,o){var l=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)]),t=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},s=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh=function(e,o){var l=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)]),t=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},s=new SAT.Vector((-.8944271909999159),(-.4472135954999579));return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow=function(e,o){var l=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)]),t=new Phaser.Line(o.left,o.bottom,o.left+o.width/2,o.top),r={top:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh=function(e,o){var 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/2,o.height),new SAT.Vector(0,o.height)]),t=new Phaser.Line(o.left+o.width/2,o.bottom,o.right,o.top),r={bottom:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,right:Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING},s=new SAT.Vector(.8944271909999159,.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow=function(e,o){var l=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)]),t=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},s=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh=function(e,o){var 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,o.height),new SAT.Vector(o.width/2,o.height)]),t=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},s=new SAT.Vector((-.8944271909999159),.4472135954999579);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow=function(e,o){var 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(0,o.height/2)]),t=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},s=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh=function(e,o){var 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,o.height/2),new SAT.Vector(0,o.height)]),t=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},s=new SAT.Vector(.4472135954999579,.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow=function(e,o){var 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,o.height/2)]),t=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},s=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh=function(e,o){var 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,o.height),new SAT.Vector(0,o.height/2)]),t=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},s=new SAT.Vector((-.4472135954999579),.8944271909999159);return new Phaser.Plugin.ArcadeSlopes.TileSlope(e,o,l,t,r,s)},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,l){this.pos=o||new e,this.r=l||0}function l(o,l){this.pos=o||new e,this.angle=0,this.offset=new e,this.setPoints(l||[])}function t(o,l,t){this.pos=o||new e,this.w=l||0,this.h=t||0}function r(){this.a=null,this.b=null,this.overlapN=new e,this.overlapV=new e,this.clear()}function s(e,o,l){for(var t=Number.MAX_VALUE,r=-Number.MAX_VALUE,s=e.length,i=0;ir&&(r=p)}l[0]=t,l[1]=r}function i(e,o,l,t,r,i){var p=d.pop(),n=d.pop(),a=P.pop().copy(o).sub(e),h=a.dot(r);if(s(l,r,p),s(t,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(i){var c=0;if(p[0]n[1])c=p[0]-n[1],i.aInB=!1;else{var T=p[1]-n[0],S=n[1]-p[0];c=Tl?R:w}function n(e,o){var l=P.pop().copy(e).sub(o.pos),t=o.r*o.r,r=l.len2();return P.push(l),r<=t}function a(e,o){f.pos.copy(e),A.clear();var l=S(f,o,A);return l&&(l=A.aInB),l}function h(e,o,l){var t=P.pop().copy(o.pos).sub(e.pos),r=e.r+o.r,s=r*r,i=t.len2();if(i>s)return P.push(t),!1;if(l){var p=Math.sqrt(i);l.a=e,l.b=o,l.overlap=r-p,l.overlapN.copy(t.normalize()),l.overlapV.copy(t).scale(l.overlap),l.aInB=e.r<=o.r&&p<=o.r-e.r,l.bInA=o.r<=e.r&&p<=e.r-o.r}return P.push(t),!0}function c(e,o,l){for(var t=P.pop().copy(o.pos).sub(e.pos),r=o.r,s=r*r,i=e.calcPoints,n=i.length,a=P.pop(),h=P.pop(),c=0;cs&&(l.aInB=!1);var d=p(a,h);if(d===y){a.copy(e.edges[S]);var A=P.pop().copy(t).sub(i[S]);if(d=p(a,A),d===R){var f=h.len();if(f>r)return P.push(t),P.push(a),P.push(h),P.push(A),!1;l&&(l.bInA=!1,u=h.normalize(),g=r-f)}P.push(A)}else if(d===R){if(a.copy(e.edges[T]),h.copy(t).sub(i[T]),d=p(a,h),d===y){var f=h.len();if(f>r)return P.push(t),P.push(a),P.push(h),!1;l&&(l.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(t),P.push(w),P.push(h),!1;l&&(u=w,g=r-f,(f>=0||g<2*r)&&(l.bInA=!1))}u&&l&&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,l=this.y;return this.project(e).scale(2),this.x-=o,this.y-=l,this},e.prototype.reflectN=e.prototype.reflectN=function(e){var o=this.x,l=this.y;return this.projectN(e).scale(2),this.x-=o,this.y-=l,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,l=this.pos.clone().sub(new e(o,o));return new t(l,2*o,2*o).toPolygon()},g.Polygon=l,l.prototype.setPoints=l.prototype.setPoints=function(o){var l=!this.points||this.points.length!==o.length;if(l){var t,r=this.calcPoints=[],s=this.edges=[],i=this.normals=[];for(t=0;ti&&(i=a.x),a.yp&&(p=a.y)}return new t(this.pos.clone().add(new e(r,s)),i-r,p-s).toPolygon()},g.Box=t,t.prototype.toPolygon=t.prototype.toPolygon=function(){var o=this.pos,t=this.w,r=this.h;return new l(new e(o.x,o.y),[new e,new e(t,0),new e(t,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 t(new e,1e-6,1e-6).toPolygon();g.isSeparatingAxis=i;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});
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp'),
2 | util = require('gulp-util'),
3 | banner = require('gulp-banner'),
4 | concat = require('gulp-concat'),
5 | rename = require('gulp-rename'),
6 | uglify = require('gulp-uglify');
7 | pack = require('./package.json');
8 |
9 | var comment = ['/**',
10 | ' * Phaser Arcade Slopes v<%= package.version %>',
11 | ' *',
12 | ' * <%= package.description %>',
13 | ' *',
14 | ' * @copyright 2016-2021 <%= package.author %>',
15 | ' * @license <%= package.license %>',
16 | ' * @see <%= package.homepage %>',
17 | ' */',
18 | ''
19 | ].join('\n');
20 |
21 | gulp.task('default', ['build']);
22 |
23 | gulp.task('build', function () {
24 | return gulp.src(['src/**/*.js', 'node_modules/sat/SAT.js'])
25 | .pipe(concat('phaser-arcade-slopes.js'))
26 | .pipe(gulp.dest('dist'))
27 | .pipe(rename({suffix: '.min'}))
28 | .pipe(uglify())
29 | .pipe(banner(comment, {
30 | package: pack
31 | }))
32 | .pipe(gulp.dest('dist'));
33 | });
34 |
35 | gulp.task('watch', function () {
36 | gulp.watch('src/**/*.js', ['build']);
37 | });
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser-arcade-slopes",
3 | "version": "0.3.2",
4 | "description": "A Phaser CE plugin that brings sloped tile collision handling to Phaser's Arcade Physics engine",
5 | "main": "dist/phaser-arcade-slopes.js",
6 | "scripts": {
7 | "build": "gulp build",
8 | "watch": "gulp watch"
9 | },
10 | "repository": "hexus/phaser-arcade-slopes",
11 | "keywords": [
12 | "phaser",
13 | "plugin",
14 | "arcade",
15 | "physics",
16 | "slopes",
17 | "tiles",
18 | "sloped",
19 | "tiles",
20 | "tile slopes",
21 | "slopes tiles",
22 | "collision",
23 | "collisions",
24 | "collision handling",
25 | "collision detection",
26 | "games"
27 | ],
28 | "author": "Chris Andrew ",
29 | "license": "MIT",
30 | "homepage": "https://github.com/hexus/phaser-arcade-slopes",
31 | "dependencies": {
32 | "sat": "0.6.0"
33 | },
34 | "devDependencies": {
35 | "gulp": "^3.9.1",
36 | "gulp-banner": "^0.1.3",
37 | "gulp-concat": "^2.6.0",
38 | "gulp-rename": "^1.2.2",
39 | "gulp-uglify": "^1.5.3",
40 | "gulp-util": "^3.0.7"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/screenshot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hexus/phaser-arcade-slopes/7c587b0e11f2efc3f1a667bf741c03e59a5402d4/screenshot.gif
--------------------------------------------------------------------------------
/src/ArcadeSlopes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * Arcade Slopes provides sloped tile functionality for tilemaps that use
9 | * Phaser's Arcade physics engine.
10 | *
11 | * @class Phaser.Plugin.ArcadeSlopes
12 | * @constructor
13 | * @extends Phaser.Plugin
14 | * @param {Phaser.Game} game - A reference to the game using this plugin.
15 | * @param {any} parent - The object that owns this plugin, usually a Phaser.PluginManager.
16 | * @param {integer} defaultSolver - The default collision solver type to use for sloped tiles.
17 | */
18 | Phaser.Plugin.ArcadeSlopes = function (game, parent, defaultSolver) {
19 | Phaser.Plugin.call(this, game, parent);
20 |
21 | /**
22 | * The collision solvers provided by the plugin.
23 | *
24 | * Maps solver constants to their respective instances.
25 | *
26 | * @property {object} solvers
27 | */
28 | var solvers = {};
29 |
30 | solvers[Phaser.Plugin.ArcadeSlopes.SAT] = new Phaser.Plugin.ArcadeSlopes.SatSolver();
31 |
32 | /**
33 | * The Arcade Slopes facade.
34 | *
35 | * @property {Phaser.Plugin.ArcadeSlopes.Facade} facade
36 | */
37 | this.facade = new Phaser.Plugin.ArcadeSlopes.Facade(
38 | new Phaser.Plugin.ArcadeSlopes.TileSlopeFactory(),
39 | solvers,
40 | defaultSolver || Phaser.Plugin.ArcadeSlopes.SAT
41 | );
42 |
43 | // Give the facade a reference to the plugin; this makes it easier to remove
44 | // it at runtime
45 | this.facade.plugin = this;
46 | };
47 |
48 | Phaser.Plugin.ArcadeSlopes.prototype = Object.create(Phaser.Plugin.prototype);
49 | Phaser.Plugin.ArcadeSlopes.prototype.constructor = Phaser.Plugin.ArcadeSlopes;
50 |
51 | /**
52 | * The Arcade Slopes plugin version number.
53 | *
54 | * @constant
55 | * @type {string}
56 | */
57 | Phaser.Plugin.ArcadeSlopes.VERSION = '0.3.2';
58 |
59 | /**
60 | * The Separating Axis Theorem collision solver type.
61 | *
62 | * Uses the excellent SAT.js library.
63 | *
64 | * @constant
65 | * @type {string}
66 | */
67 | Phaser.Plugin.ArcadeSlopes.SAT = 'sat';
68 |
69 | /**
70 | * Initializes the plugin.
71 | *
72 | * @method Phaser.Plugin.ArcadeSlopes#init
73 | */
74 | Phaser.Plugin.ArcadeSlopes.prototype.init = function () {
75 | // Give the game an Arcade Slopes facade
76 | this.game.slopes = this.game.slopes || this.facade;
77 |
78 | // Keep a reference to the original Arcade.collideSpriteVsTilemapLayer method
79 | this.originalCollideSpriteVsTilemapLayer = Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer;
80 |
81 | // Replace the original method with the Arcade Slopes override, along with
82 | // some extra methods that break down the functionality a little more
83 | Phaser.Physics.Arcade.prototype.collideSpriteVsTile = Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile;
84 | Phaser.Physics.Arcade.prototype.collideSpriteVsTiles = Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles;
85 | Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer = Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer;
86 |
87 | // Add some extra neighbour methods to the Tilemap class
88 | Phaser.Tilemap.prototype.getTileTopLeft = Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopLeft;
89 | Phaser.Tilemap.prototype.getTileTopRight = Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight;
90 | Phaser.Tilemap.prototype.getTileBottomLeft = Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft;
91 | Phaser.Tilemap.prototype.getTileBottomRight = Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomRight;
92 |
93 | // Keep a reference to the original TilemapLayer.renderDebug method
94 | this.originalRenderDebug = Phaser.TilemapLayer.prototype.renderDebug;
95 |
96 | // Add some overrides and helper methods to the TilemapLayer class
97 | Phaser.TilemapLayer.prototype.getCollisionOffsetX = Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetX;
98 | Phaser.TilemapLayer.prototype.getCollisionOffsetY = Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetY;
99 | Phaser.TilemapLayer.prototype.renderDebug = Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug;
100 | };
101 |
102 | /**
103 | * Destroys the plugin and nulls its references. Restores any overriden methods.
104 | *
105 | * @method Phaser.Plugin.ArcadeSlopes#destroy
106 | */
107 | Phaser.Plugin.ArcadeSlopes.prototype.destroy = function () {
108 | // Null the game's reference to the facade
109 | this.game.slopes = null;
110 |
111 | // Restore the original collideSpriteVsTilemapLayer method and null the rest
112 | Phaser.Physics.Arcade.prototype.collideSpriteVsTile = null;
113 | Phaser.Physics.Arcade.prototype.collideSpriteVsTiles = null;
114 | Phaser.Physics.Arcade.prototype.collideSpriteVsTilemapLayer = this.originalCollideSpriteVsTilemapLayer;
115 |
116 | // Remove the extra neighbour methods from the Tilemap class
117 | Phaser.Tilemap.prototype.getTileTopLeft = null;
118 | Phaser.Tilemap.prototype.getTileTopRight = null;
119 | Phaser.Tilemap.prototype.getTileBottomLeft = null;
120 | Phaser.Tilemap.prototype.getTileBottomRight = null;
121 |
122 | // Remove the overrides and helper methods from the TilemapLayer class
123 | Phaser.TilemapLayer.prototype.getCollisionOffsetX = null;
124 | Phaser.TilemapLayer.prototype.getCollisionOffsetY = null;
125 | Phaser.TilemapLayer.prototype.renderDebug = this.originalRenderDebug;
126 |
127 | // Call the parent destroy method
128 | Phaser.Plugin.prototype.destroy.call(this);
129 | };
130 |
--------------------------------------------------------------------------------
/src/ArcadeSlopes/Facade.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * A facade class to attach to a Phaser game.
9 | *
10 | * @class Phaser.Plugin.ArcadeSlopes.Facade
11 | * @constructor
12 | * @param {Phaser.Plugin.ArcadeSlopes.TileSlopeFactory} factory - A tile slope factory.
13 | * @param {object} solvers - A set of collision solvers.
14 | * @param {integer} defaultSolver - The default collision solver type to use for sloped tiles.
15 | */
16 | Phaser.Plugin.ArcadeSlopes.Facade = function (factory, solvers, defaultSolver) {
17 | /**
18 | * A tile slope factory.
19 | *
20 | * @property {Phaser.Plugin.ArcadeSlopes.TileSlopeFactory} factory
21 | */
22 | this.factory = factory;
23 |
24 | /**
25 | * A set of collision solvers.
26 | *
27 | * Maps solver constants to their respective instances.
28 | *
29 | * @property {object} solvers
30 | */
31 | this.solvers = solvers;
32 |
33 | /**
34 | * The default collision solver type to use for sloped tiles.
35 | *
36 | * @property {string} defaultSolver
37 | * @default
38 | */
39 | this.defaultSolver = defaultSolver || Phaser.Plugin.ArcadeSlopes.SAT;
40 |
41 | /**
42 | * The plugin this facade belongs to.
43 | *
44 | * @property {Phaser.Plugin.ArcadeSlopes} plugin
45 | */
46 | this.plugin = null;
47 | };
48 |
49 | /**
50 | * Enable the physics body of the given object for sloped tile interaction.
51 | *
52 | * @method Phaser.Plugin.ArcadeSlopes.Facade#enable
53 | * @param {Phaser.Sprite|Phaser.Group} object - The object to enable sloped tile physics for.
54 | */
55 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.enable = function (object) {
56 | if (Array.isArray(object)) {
57 | for (var i = 0; i < object.length; i++) {
58 | this.enable(object[i]);
59 | }
60 | } else {
61 | if (object instanceof Phaser.Group) {
62 | this.enable(object.children);
63 | } else {
64 | if (object.hasOwnProperty('body')) {
65 | this.enableBody(object.body);
66 | }
67 |
68 | if (object.hasOwnProperty('children') && object.children.length > 0) {
69 | this.enable(object.children);
70 | }
71 | }
72 | }
73 | };
74 |
75 | /**
76 | * Enable the given physics body for sloped tile collisions.
77 | *
78 | * @method Phaser.Plugin.ArcadeSlopes.Facade#enableBody
79 | * @param {Phaser.Physics.Arcade.Body} body - The physics body to enable.
80 | */
81 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.enableBody = function (body) {
82 | // Create an SAT shape for the body
83 | // TODO: Rename body.polygon to body.shape or body.slopes.shape
84 | if (body.isCircle) {
85 | body.polygon = new SAT.Circle(
86 | new SAT.Vector(
87 | body.x + body.halfWidth,
88 | body.y + body.halfHeight
89 | ),
90 | body.radius
91 | );
92 | } else {
93 | body.polygon = new SAT.Box(
94 | new SAT.Vector(body.x, body.y),
95 | body.width * body.sprite.scale.x,
96 | body.height * body.sprite.scale.y
97 | ).toPolygon();
98 | }
99 |
100 | // Attach a new set of properties that configure the body's interaction
101 | // with sloped tiles, if they don't exist (TODO: Formalize as a class)
102 | body.slopes = body.slopes || {
103 | debug: false,
104 | friction: new Phaser.Point(),
105 | preferY: false,
106 | pullUp: 0,
107 | pullDown: 0,
108 | pullLeft: 0,
109 | pullRight: 0,
110 | pullTopLeft: 0,
111 | pullTopRight: 0,
112 | pullBottomLeft: 0,
113 | pullBottomRight: 0,
114 | sat: {
115 | response: null,
116 | },
117 | skipFriction: false,
118 | tile: null,
119 | velocity: new SAT.Vector()
120 | };
121 | };
122 |
123 | /**
124 | * Converts a layer of the given tilemap.
125 | *
126 | * Attaches Phaser.Plugin.ArcadeSlopes.TileSlope objects that are used to define
127 | * how the tile should collide with a physics body.
128 | *
129 | * @method Phaser.Plugin.ArcadeSlopes.Facade#convertTilemap
130 | * @param {Phaser.Tilemap} map - The map containing the layer to convert.
131 | * @param {number|string|Phaser.TileMapLayer} layer - The layer of the map to convert.
132 | * @param {string|object} slopeMap - A mapping type string, or a map of tilemap indexes to ArcadeSlope.TileSlope constants.
133 | * @param {integer} index - An optional first tile index (firstgid).
134 | * @return {Phaser.Tilemap} - The converted tilemap.
135 | */
136 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemap = function (map, layer, slopeMap, index) {
137 | return this.factory.convertTilemap(map, layer, slopeMap, index);
138 | };
139 |
140 | /**
141 | * Converts a tilemap layer.
142 | *
143 | * @method Phaser.Plugin.ArcadeSlopes.Facade#convertTilemapLayer
144 | * @param {Phaser.TilemapLayer} layer - The tilemap layer to convert.
145 | * @param {string|object} slopeMap - A mapping type string, or a map of tilemap indexes to ArcadeSlope.TileSlope constants.
146 | * @param {integer} index - An optional first tile index (firstgid).
147 | * @return {Phaser.TilemapLayer} - The converted tilemap layer.
148 | */
149 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.convertTilemapLayer = function (layer, slopeMap, index) {
150 | return this.factory.convertTilemapLayer(layer, slopeMap, index);
151 | };
152 |
153 | /**
154 | * Collides a physics body against a tile.
155 | *
156 | * @method Phaser.Plugin.ArcadeSlopes.Facade#collide
157 | * @param {integer} i - The tile index.
158 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
159 | * @param {Phaser.Tile} tile - The tile.
160 | * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer.
161 | * @param {boolean} overlapOnly - Whether to only check for an overlap.
162 | * @return {boolean} - Whether the body was separated.
163 | */
164 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.collide = function (i, body, tile, tilemapLayer, overlapOnly) {
165 | return this.solvers.sat.collide(i, body, tile, tilemapLayer, overlapOnly);
166 | };
167 |
168 | /**
169 | * Reset all the collision properties on a physics body.
170 | *
171 | * Resets body.touching, body.blocked, body.overlap*, body.slopes.sat.response.
172 | *
173 | * Leaves wasTouching alone.
174 | *
175 | * @method Phaser.Plugin.ArcadeSlopes.Facade#resetBodyFlags
176 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
177 | */
178 | Phaser.Plugin.ArcadeSlopes.Facade.prototype.resetCollision = function (body) {
179 | body.touching.none = true;
180 | body.touching.up = false;
181 | body.touching.down = false;
182 | body.touching.left = false;
183 | body.touching.right = false;
184 |
185 | body.blocked.none = true;
186 | body.blocked.up = false;
187 | body.blocked.down = false;
188 | body.blocked.left = false;
189 | body.blocked.right = false;
190 |
191 | body.overlapX = 0;
192 | body.overlapY = 0;
193 |
194 | if (!body.slopes) {
195 | return;
196 | }
197 |
198 | body.slopes.sat.response = null;
199 | };
200 |
201 | /**
202 | * Whether to prefer Y axis separation in an attempt to prevent physics bodies
203 | * from sliding down slopes when they are separated.
204 | *
205 | * Disabled by default. Only relevant in a game that uses gravity.
206 | *
207 | * @name Phaser.Plugin.ArcadeSlopes.Facade#preferY
208 | * @property {boolean} preferY
209 | */
210 | Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype, 'preferY', {
211 | get: function () {
212 | return this.solvers.sat.options.preferY;
213 | },
214 | set: function (enabled) {
215 | this.solvers.sat.options.preferY = !!enabled;
216 | }
217 | });
218 |
219 | /**
220 | * Whether to use heuristics to avoid collisions with the internal edges between
221 | * connected tiles.
222 | *
223 | * Enabled by default. Relevant to platformers.
224 | *
225 | * @name Phaser.Plugin.ArcadeSlopes.Facade#heuristics
226 | * @property {boolean} heuristics
227 | */
228 | Object.defineProperty(Phaser.Plugin.ArcadeSlopes.Facade.prototype, 'heuristics', {
229 | get: function () {
230 | return this.solvers.sat.options.restrain;
231 | },
232 | set: function (enabled) {
233 | this.solvers.sat.options.restrain = !!enabled;
234 | }
235 | });
236 |
--------------------------------------------------------------------------------
/src/ArcadeSlopes/Overrides.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * A static class with override methods for Phaser's tilemap collisions and tile
9 | * neighbour checks.
10 | *
11 | * @static
12 | * @class Phaser.Plugin.ArcadeSlopes.Override
13 | */
14 | Phaser.Plugin.ArcadeSlopes.Overrides = {};
15 |
16 | /**
17 | * Collide a sprite against a single tile.
18 | *
19 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#collideSpriteVsTile
20 | * @param {integer} i - The tile index.
21 | * @param {Phaser.Sprite} sprite - The sprite to check.
22 | * @param {Phaser.Tile} tile - The tile to check.
23 | * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tile belongs to.
24 | * @param {function} [collideCallback] - An optional collision callback.
25 | * @param {function} [processCallback] - An optional overlap processing callback.
26 | * @param {object} [callbackContext] - The context in which to run the callbacks.
27 | * @param {boolean} [overlapOnly] - Whether to only check for an overlap.
28 | * @return {boolean} - Whether a collision occurred.
29 | */
30 | Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTile = function (i, sprite, tile, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) {
31 | if (!sprite.body || !tile || !tilemapLayer) {
32 | return false;
33 | }
34 |
35 | if (tile.hasOwnProperty('slope')) {
36 | if (this.game.slopes.collide(i, sprite.body, tile, tilemapLayer, overlapOnly)) {
37 | this._total++;
38 |
39 | if (collideCallback) {
40 | collideCallback.call(callbackContext, sprite, tile);
41 | }
42 |
43 | return true;
44 | }
45 | } else if (this.separateTile(i, sprite.body, tile, tilemapLayer, overlapOnly)) {
46 | this._total++;
47 |
48 | if (collideCallback) {
49 | collideCallback.call(callbackContext, sprite, tile);
50 | }
51 |
52 | return true;
53 | }
54 |
55 | return false;
56 | };
57 |
58 | /**
59 | * Collide a sprite against a set of tiles.
60 | *
61 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#collideSpriteVsTiles
62 | * @param {Phaser.Sprite} sprite - The sprite to check.
63 | * @param {Phaser.Tile[]} tiles - The tiles to check.
64 | * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer the tiles belong to.
65 | * @param {function} [collideCallback] - An optional collision callback.
66 | * @param {function} [processCallback] - An optional overlap processing callback.
67 | * @param {object} [callbackContext] - The context in which to run the callbacks.
68 | * @param {boolean} [overlapOnly] - Whether to only check for an overlap.
69 | * @return {boolean} - Whether a collision occurred.
70 | */
71 | Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTiles = function (sprite, tiles, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) {
72 | if (!sprite.body || !tiles || !tiles.length || !tilemapLayer) {
73 | return false;
74 | }
75 |
76 | var collided = false;
77 |
78 | for (var i = 0; i < tiles.length; i++) {
79 | if (processCallback) {
80 | if (processCallback.call(callbackContext, sprite, tiles[i])) {
81 | collided = this.collideSpriteVsTile(i, sprite, tiles[i], tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) || collided;
82 | }
83 | } else {
84 | collided = this.collideSpriteVsTile(i, sprite, tiles[i], tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) || collided;
85 | }
86 | }
87 |
88 | return collided;
89 | };
90 |
91 | /**
92 | * Collide a sprite against a tile map layer.
93 | *
94 | * This is used to override Phaser.Physics.Arcade.collideSpriteVsTilemapLayer().
95 | *
96 | * @override Phaser.Physics.Arcade#collideSpriteVsTilemapLayer
97 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#collideSpriteVsTilemapLayer
98 | * @param {Phaser.Sprite} sprite - The sprite to check.
99 | * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer to check.
100 | * @param {function} collideCallback - An optional collision callback.
101 | * @param {function} processCallback - An optional overlap processing callback.
102 | * @param {object} callbackContext - The context in which to run the callbacks.
103 | * @param {boolean} overlapOnly - Whether to only check for an overlap.
104 | * @return {boolean} - Whether a collision occurred.
105 | */
106 | Phaser.Plugin.ArcadeSlopes.Overrides.collideSpriteVsTilemapLayer = function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) {
107 | if (!sprite.body || !tilemapLayer) {
108 | return false;
109 | }
110 |
111 | var tiles = tilemapLayer.getTiles(
112 | sprite.body.position.x - sprite.body.tilePadding.x - tilemapLayer.getCollisionOffsetX(),
113 | sprite.body.position.y - sprite.body.tilePadding.y - tilemapLayer.getCollisionOffsetY(),
114 | sprite.body.width + sprite.body.tilePadding.x,
115 | sprite.body.height + sprite.body.tilePadding.y,
116 | true,
117 | false
118 | );
119 |
120 | if (tiles.length === 0) {
121 | return false;
122 | }
123 |
124 | // TODO: Sort by distance from body center to tile center?
125 |
126 | var collided = this.collideSpriteVsTiles(sprite, tiles, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly);
127 |
128 | return collided;
129 | };
130 |
131 | /**
132 | * Gets the tile to the top left of the coordinates given.
133 | *
134 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getTileTopLeft
135 | * @param {integer} layer - The index of the layer to read the tile from.
136 | * @param {integer} x - The X coordinate, in tiles, to get the tile from.
137 | * @param {integer} y - The Y coordinate, in tiles, to get the tile from.
138 | * @return {Phaser.Tile} - The tile found.
139 | */
140 | Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopLeft = function(layer, x, y) {
141 | if (x > 0 && y > 0) {
142 | return this.layers[layer].data[y - 1][x - 1];
143 | }
144 |
145 | return null;
146 | };
147 |
148 | /**
149 | * Gets the tile to the top right of the coordinates given.
150 | *
151 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getTileTopRight
152 | * @param {integer} layer - The index of the layer to read the tile from.
153 | * @param {integer} x - The X coordinate, in tiles, to get the tile from.
154 | * @param {integer} y - The Y coordinate, in tiles, to get the tile from.
155 | * @return {Phaser.Tile} - The tile found.
156 | */
157 | Phaser.Plugin.ArcadeSlopes.Overrides.getTileTopRight = function(layer, x, y) {
158 | if (x < this.layers[layer].width - 1 && y > 0) {
159 | return this.layers[layer].data[y - 1][x + 1];
160 | }
161 |
162 | return null;
163 | };
164 |
165 | /**
166 | * Gets the tile to the bottom left of the coordinates given.
167 | *
168 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getTileBottomLeft
169 | * @param {integer} layer - The index of the layer to read the tile from.
170 | * @param {integer} x - The X coordinate, in tiles, to get the tile from.
171 | * @param {integer} y - The Y coordinate, in tiles, to get the tile from.
172 | * @return {Phaser.Tile} - The tile found.
173 | */
174 | Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomLeft = function(layer, x, y) {
175 | if (x > 0 && y < this.layers[layer].height - 1) {
176 | return this.layers[layer].data[y + 1][x - 1];
177 | }
178 |
179 | return null;
180 | };
181 |
182 | /**
183 | * Gets the tile to the bottom right of the coordinates given.
184 | *
185 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getTileBottomRight
186 | * @param {integer} layer - The index of the layer to read the tile from.
187 | * @param {integer} x - The X coordinate, in tiles, to get the tile from.
188 | * @param {integer} y - The Y coordinate, in tiles, to get the tile from.
189 | * @return {Phaser.Tile} - The tile found.
190 | */
191 | Phaser.Plugin.ArcadeSlopes.Overrides.getTileBottomRight = function(layer, x, y) {
192 | if (x < this.layers[layer].width - 1 && y < this.layers[layer].height - 1) {
193 | return this.layers[layer].data[y + 1][x + 1];
194 | }
195 |
196 | return null;
197 | };
198 |
199 | /**
200 | * Get the X axis collision offset for the tilemap layer.
201 | *
202 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getCollisionOffsetY
203 | * @return {number}
204 | */
205 | Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetX = function () {
206 | if (this.getTileOffsetX) {
207 | return this.getTileOffsetX();
208 | }
209 |
210 | return !this.fixedToCamera ? this.position.x : 0;
211 | };
212 |
213 | /**
214 | * Get the Y axis collision offset for the tilemap layer.
215 | *
216 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#getCollisionOffsetY
217 | * @return {number}
218 | */
219 | Phaser.Plugin.ArcadeSlopes.Overrides.getCollisionOffsetY = function () {
220 | if (this.getTileOffsetY) {
221 | return this.getTileOffsetY();
222 | }
223 |
224 | return !this.fixedToCamera ? this.position.y : 0;
225 | };
226 |
227 | /**
228 | * Renders a tilemap debug overlay on-top of the canvas.
229 | *
230 | * Called automatically by render when `debug` is true.
231 | *
232 | * See `debugSettings` for assorted configuration options.
233 | *
234 | * This override renders extra information regarding Arcade Slopes collisions.
235 | *
236 | * @method Phaser.Plugin.ArcadeSlopes.Overrides#renderDebug
237 | * @private
238 | */
239 | Phaser.Plugin.ArcadeSlopes.Overrides.renderDebug = function () {
240 | var scrollX = this._mc.scrollX;
241 | var scrollY = this._mc.scrollY;
242 |
243 | var context = this.context;
244 | var renderW = this.canvas.width;
245 | var renderH = this.canvas.height;
246 |
247 | var scaleX = this.tileScale ? this.tileScale.x : 1.0 / this.scale.x;
248 | var scaleY = this.tileScale ? this.tileScale.y : 1.0 / this.scale.y;
249 |
250 | var width = this.layer.width;
251 | var height = this.layer.height;
252 | var tw = this._mc.tileWidth * scaleX; // Tile width
253 | var th = this._mc.tileHeight * scaleY; // Tile height
254 | var htw = tw / 2; // Half-tile width
255 | var hth = th / 2; // Half-tile height
256 | var qtw = tw / 4; // Quarter-tile width
257 | var qth = th / 4; // Quarter-tile height
258 | var cw = this._mc.cw * scaleX;
259 | var ch = this._mc.ch * scaleY;
260 | var m = this._mc.edgeMidpoint;
261 |
262 | var left = Math.floor(scrollX / tw);
263 | var right = Math.floor((renderW - 1 + scrollX) / tw);
264 | var top = Math.floor(scrollY / th);
265 | var bottom = Math.floor((renderH - 1 + scrollY) / th);
266 |
267 | if (!this._wrap)
268 | {
269 | if (left <= right) {
270 | left = Math.max(0, left);
271 | right = Math.min(width - 1, right);
272 | }
273 |
274 | if (top <= bottom) {
275 | top = Math.max(0, top);
276 | bottom = Math.min(height - 1, bottom);
277 | }
278 | }
279 |
280 | var baseX = (left * tw) - scrollX;
281 | var baseY = (top * th) - scrollY;
282 |
283 | var normStartX = (left + ((1 << 20) * width)) % width;
284 | var normStartY = (top + ((1 << 20) * height)) % height;
285 |
286 | var tx, ty, x, y, xmax, ymax, polygon, i, j, a, b, norm, gx, gy, line;
287 |
288 | for (y = normStartY, ymax = bottom - top, ty = baseY; ymax >= 0; y++, ymax--, ty += th) {
289 | if (y >= height) {
290 | y -= height;
291 | }
292 |
293 | var row = this.layer.data[y];
294 |
295 | for (x = normStartX, xmax = right - left, tx = baseX; xmax >= 0; x++, xmax--, tx += tw) {
296 | if (x >= width) {
297 | x -= width;
298 | }
299 |
300 | var tile = row[x];
301 |
302 | if (!tile || tile.index < 0 || !tile.collides) {
303 | continue;
304 | }
305 |
306 | if (this.debugSettings.collidingTileOverfill) {
307 | context.fillStyle = this.debugSettings.collidingTileOverfill;
308 | context.fillRect(tx, ty, cw, ch);
309 | }
310 |
311 | if (this.debugSettings.facingEdgeStroke) {
312 | context.beginPath();
313 |
314 | context.lineWidth = 1;
315 | context.strokeStyle = this.debugSettings.facingEdgeStroke;
316 |
317 | if (tile.faceTop) {
318 | context.moveTo(tx, ty);
319 | context.lineTo(tx + cw, ty);
320 | }
321 |
322 | if (tile.faceBottom) {
323 | context.moveTo(tx, ty + ch);
324 | context.lineTo(tx + cw, ty + ch);
325 | }
326 |
327 | if (tile.faceLeft) {
328 | context.moveTo(tx, ty);
329 | context.lineTo(tx, ty + ch);
330 | }
331 |
332 | if (tile.faceRight) {
333 | context.moveTo(tx + cw, ty);
334 | context.lineTo(tx + cw, ty + ch);
335 | }
336 |
337 | context.closePath();
338 |
339 | context.stroke();
340 |
341 | // Render the tile slope polygons
342 | if (tile.slope) {
343 | // Fill polygons and stroke their edges
344 | if (this.debugSettings.slopeEdgeStroke || this.debugSettings.slopeFill) {
345 | context.beginPath();
346 |
347 | context.lineWidth = 1;
348 |
349 | polygon = tile.slope.polygon;
350 |
351 | // Move to the first vertex
352 | context.moveTo(tx + polygon.points[0].x * scaleX, ty + polygon.points[0].y * scaleY);
353 |
354 | // Draw a path through all vertices
355 | for (i = 0; i < polygon.points.length; i++) {
356 | j = (i + 1) % polygon.points.length;
357 |
358 | context.lineTo(tx + polygon.points[j].x * scaleX, ty + polygon.points[j].y * scaleY);
359 | }
360 |
361 | context.closePath();
362 |
363 | if (this.debugSettings.slopeEdgeStroke) {
364 | context.strokeStyle = this.debugSettings.slopeEdgeStroke;
365 | context.stroke();
366 | }
367 |
368 | if (this.debugSettings.slopeFill) {
369 | context.fillStyle = this.debugSettings.slopeFill;
370 | context.fill();
371 | }
372 | }
373 |
374 | // Stroke the colliding edges and edge normals
375 | if (this.debugSettings.slopeCollidingEdgeStroke) {
376 | // Colliding edges
377 | context.beginPath();
378 |
379 | context.lineWidth = this.debugSettings.slopeCollidingEdgeStrokeWidth || 1;
380 | context.strokeStyle = this.debugSettings.slopeCollidingEdgeStroke;
381 |
382 | polygon = tile.slope.polygon;
383 |
384 | for (i = 0; i < polygon.points.length; i++) {
385 | // Skip the edges with ignored normals
386 | if (polygon.normals[i].ignore) {
387 | continue;
388 | }
389 |
390 | j = (i + 1) % polygon.points.length;
391 |
392 | context.moveTo(tx + polygon.points[i].x * scaleX, ty + polygon.points[i].y * scaleY);
393 | context.lineTo(tx + polygon.points[j].x * scaleX, ty + polygon.points[j].y * scaleY);
394 | }
395 |
396 | context.closePath();
397 |
398 | context.stroke();
399 |
400 | // Edge normals
401 | for (i = 0; i < polygon.points.length; i++) {
402 | context.beginPath();
403 |
404 | if (polygon.normals[i].ignore) {
405 | context.lineWidth = this.debugSettings.slopeNormalStrokeWidth;
406 | context.strokeStyle = this.debugSettings.slopeNormalStroke;
407 | } else {
408 | context.lineWidth = this.debugSettings.slopeCollidingNormalStrokeWidth;
409 | context.strokeStyle = this.debugSettings.slopeCollidingNormalStroke;
410 | }
411 |
412 | j = (i + 1) % polygon.points.length;
413 |
414 | a = polygon.points[i];
415 | b = polygon.points[j];
416 | norm = polygon.normals[i];
417 |
418 | // Midpoint of the edge
419 | m.x = (a.x + b.x) / 2;
420 | m.y = (a.y + b.y) / 2;
421 |
422 | // Draw from the midpoint outwards using the normal
423 | context.moveTo(tx + m.x * scaleX, ty + m.y * scaleY);
424 | context.lineTo(tx + m.x * scaleX + norm.x * qtw, ty + m.y * scaleY + norm.y * qth);
425 |
426 | context.closePath();
427 | context.stroke();
428 | }
429 |
430 | // Ignormals
431 | if (tile.slope.ignormals) {
432 | for (i = 0; i < tile.slope.ignormals.length; i++) {
433 | context.beginPath();
434 |
435 | context.lineWidth = 1;
436 | context.strokeStyle = 'rgba(255, 0, 0, 1)';
437 |
438 | gx = tile.slope.ignormals[i].x;
439 | gy = tile.slope.ignormals[i].y;
440 |
441 | context.moveTo(tx + htw, ty + hth);
442 | context.lineTo(tx + htw + gx * qtw, ty + hth + gy * qth);
443 |
444 | context.closePath();
445 | context.stroke();
446 | }
447 | }
448 | }
449 |
450 | // Slope line segments
451 | if (this.debugSettings.slopeLineStroke && tile.slope.line) {
452 | line = tile.slope.line;
453 |
454 | context.beginPath();
455 |
456 | context.lineWidth = this.debugSettings.slopeLineWidth || 2;
457 | context.strokeStyle = this.debugSettings.slopeLineStroke;
458 |
459 | context.moveTo(line.start.x - scrollX, line.start.y - scrollY);
460 | context.lineTo(line.end.x - scrollX, line.end.y - scrollY);
461 |
462 | context.closePath();
463 | context.stroke();
464 | }
465 | }
466 | }
467 | }
468 | }
469 | };
470 |
--------------------------------------------------------------------------------
/src/ArcadeSlopes/SatSolver.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * Solves tile collisions using the Separating Axis Theorem.
9 | *
10 | * @class Phaser.Plugin.ArcadeSlopes.SatSolver
11 | * @constructor
12 | * @param {object} options - Options for the SAT solver.
13 | */
14 | Phaser.Plugin.ArcadeSlopes.SatSolver = function (options) {
15 | /**
16 | * Options for the SAT solver.
17 | *
18 | * @property {object} options
19 | */
20 | this.options = Phaser.Utils.mixin(options || {}, {
21 | // Whether to store debug data with all encountered physics bodies
22 | debug: false,
23 |
24 | // Whether to prefer the minimum Y offset over the smallest separation
25 | preferY: false
26 | });
27 |
28 | /**
29 | * A pool of arrays to use for calculations.
30 | *
31 | * @property {Array[]} arrayPool
32 | */
33 | this.arrayPool = [];
34 |
35 | for (var i = 0; i < 10; i++) {
36 | this.arrayPool.push([]);
37 | }
38 |
39 | /**
40 | * A pool of vectors to use for calculations.
41 | *
42 | * @property {SAT.Vector[]} vectorPool
43 | */
44 | this.vectorPool = [];
45 |
46 | for (i = 0; i < 20; i++) {
47 | this.vectorPool.push(new SAT.Vector());
48 | }
49 |
50 | /**
51 | * A pool of responses to use for collision tests.
52 | *
53 | * @property {SAT.Response[]} responsePool
54 | */
55 | this.responsePool = [];
56 |
57 | for (i = 0; i < 20; i++) {
58 | this.responsePool.push(new SAT.Response());
59 | }
60 | };
61 |
62 | /**
63 | * Prepare the given SAT response by inverting the overlap vectors.
64 | *
65 | * @static
66 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#prepareResponse
67 | * @param {SAT.Response} response
68 | * @return {SAT.Response}
69 | */
70 | Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse = function (response) {
71 | // Invert our overlap vectors so that we have them facing outwards
72 | response.overlapV.scale(-1);
73 | response.overlapN.scale(-1);
74 |
75 | return response;
76 | };
77 |
78 | /**
79 | * Reset the given SAT response's properties to their default values.
80 | *
81 | * @static
82 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#resetResponse
83 | * @param {SAT.Response} response
84 | * @return {SAT.Response}
85 | */
86 | Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse = function (response) {
87 | response.overlapN.x = 0;
88 | response.overlapN.y = 0;
89 | response.overlapV.x = 0;
90 | response.overlapV.y = 0;
91 | response.clear();
92 |
93 | return response;
94 | };
95 |
96 | /**
97 | * Copy the values of one SAT response to another.
98 | *
99 | * @static
100 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#copyResponse
101 | * @param {SAT.Response} a - The source response.
102 | * @param {SAT.Response} b - The target response.
103 | * @return {SAT.Response}
104 | */
105 | Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse = function (a, b) {
106 | b.a = a.a;
107 | b.b = a.b;
108 | b.aInB = a.aInB;
109 | b.bInA = a.bInA;
110 | b.overlap = a.overlap;
111 | b.overlapN.copy(a.overlapN);
112 | b.overlapV.copy(a.overlapV);
113 |
114 | return b;
115 | };
116 |
117 | /**
118 | * Calculate the minimum X offset given an overlap vector.
119 | *
120 | * @static
121 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#minimumOffsetX
122 | * @param {SAT.Vector} vector - The overlap vector.
123 | * @return {integer}
124 | */
125 | Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetX = function (vector) {
126 | return ((vector.y * vector.y) / vector.x) + vector.x;
127 | };
128 |
129 | /**
130 | * Calculate the minimum Y offset given an overlap vector.
131 | *
132 | * @static
133 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#minimumOffsetY
134 | * @param {SAT.Vector} vector - The overlap vector.
135 | * @return {integer}
136 | */
137 | Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY = function (vector) {
138 | return ((vector.x * vector.x) / vector.y) + vector.y;
139 | };
140 |
141 | /**
142 | * Determine whether the given body is moving against the overlap vector of the
143 | * given response on the Y axis.
144 | *
145 | * @static
146 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#movingAgainstY
147 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
148 | * @param {SAT.Response} response - The SAT response.
149 | * @return {boolean} - Whether the body is moving against the overlap vector.
150 | */
151 | Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY = function (body, response) {
152 | return (response.overlapV.y < 0 && body.velocity.y > 0) || (response.overlapV.y > 0 && body.velocity.y < 0);
153 | };
154 |
155 | // TODO: shouldPreferX()
156 |
157 | /**
158 | * Determine whether a body should be separated on the Y axis only, given an SAT
159 | * response.
160 | *
161 | * Returns true if options.preferY is true, the overlap vector is non-zero
162 | * for each axis and the body is moving against the overlap vector.
163 | *
164 | * TODO: Adapt for circle bodies, somehow. Disable for now?
165 | * TODO: Would be amazing to check to ensure that there are no other surrounding collisions.
166 | *
167 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#shouldPreferY
168 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
169 | * @param {SAT.Response} response - The SAT response.
170 | * @return {boolean} - Whether to separate on the Y axis only.
171 | */
172 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldPreferY = function (body, response) {
173 | return (this.options.preferY || body.slopes.preferY) && // Enabled globally or on the body
174 | response.overlapV.y !== 0 && response.overlapV.x !== 0 && // There's an overlap on both axes
175 | Phaser.Plugin.ArcadeSlopes.SatSolver.movingAgainstY(body, response); // And we're moving into the shape
176 | };
177 |
178 | /**
179 | * Separate a body from a tile using the given SAT response.
180 | *
181 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#separate
182 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
183 | * @param {Phaser.Tile} tile - The tile.
184 | * @param {SAT.Response} response - The SAT response.
185 | * @param {boolean} force - Whether to force separation.
186 | * @return {boolean} - Whether the body was separated.
187 | */
188 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.separate = function (body, tile, response, force) {
189 | // Test whether we need to separate from the tile by checking its edge
190 | // properties and any separation constraints
191 | if (!force && !this.shouldSeparate(tile.index, body, tile, response)) {
192 | return false;
193 | }
194 |
195 | // Run any custom tile callbacks, with local callbacks taking priority over
196 | // layer level callbacks
197 | if (tile.collisionCallback && !tile.collisionCallback.call(tile.collisionCallbackContext, body.sprite, tile)) {
198 | return false;
199 | } else if (tile.layer.callbacks[tile.index] && !tile.layer.callbacks[tile.index].callback.call(tile.layer.callbacks[tile.index].callbackContext, body.sprite, tile)) {
200 | return false;
201 | }
202 |
203 | // Separate the body from the tile, using the minimum Y offset if preferred
204 | if (this.shouldPreferY(body, response)) {
205 | body.position.y += Phaser.Plugin.ArcadeSlopes.SatSolver.minimumOffsetY(response.overlapV);
206 | } else {
207 | body.position.x += response.overlapV.x;
208 | body.position.y += response.overlapV.y;
209 | }
210 |
211 | return true;
212 | };
213 |
214 | /**
215 | * Apply velocity changes (friction and bounce) to a body given a tile and
216 | * SAT collision response.
217 | *
218 | * TODO: Optimize by pooling bounce and friction vectors.
219 | *
220 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#applyVelocity
221 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
222 | * @param {Phaser.Tile} tile - The tile.
223 | * @param {SAT.Response} response - The SAT response.
224 | */
225 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.applyVelocity = function (body, tile, response) {
226 | // Project our velocity onto the overlap normal for the bounce vector (Vn)
227 | var bounce = this.vectorPool.pop().copy(body.slopes.velocity).projectN(response.overlapN);
228 |
229 | // Then work out the surface vector (Vt)
230 | var friction = this.vectorPool.pop().copy(body.slopes.velocity).sub(bounce);
231 |
232 | // Apply bounce coefficients
233 | bounce.x = bounce.x * (-body.bounce.x);
234 | bounce.y = bounce.y * (-body.bounce.y);
235 |
236 | // Apply friction coefficients
237 | friction.x = friction.x * (1 - body.slopes.friction.x - tile.slope.friction.x);
238 | friction.y = friction.y * (1 - body.slopes.friction.y - tile.slope.friction.y);
239 |
240 | // Now we can get our new velocity by adding the bounce and friction vectors
241 | body.velocity.x = bounce.x + friction.x;
242 | body.velocity.y = bounce.y + friction.y;
243 |
244 | // Process collision pulling
245 | this.pull(body, response);
246 |
247 | // Recycle the vectors we used for bounce and friction
248 | this.vectorPool.push(bounce, friction);
249 | };
250 |
251 | /**
252 | * Update the position and velocity values of the slopes body.
253 | *
254 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#updateValues
255 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
256 | */
257 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateValues = function (body) {
258 | // Update the body polygon position
259 | body.polygon.pos.x = body.x;
260 | body.polygon.pos.y = body.y;
261 |
262 | // Update the body's velocity vector
263 | body.slopes.velocity.x = body.velocity.x;
264 | body.slopes.velocity.y = body.velocity.y;
265 | };
266 |
267 | /**
268 | * Update the flags of a physics body using a given SAT response.
269 | *
270 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#updateFlags
271 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
272 | * @param {SAT.Response} response - The SAT response.
273 | */
274 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.updateFlags = function (body, response) {
275 | // Set the touching values
276 | body.touching.up = body.touching.up || response.overlapV.y > 0;
277 | body.touching.down = body.touching.down || response.overlapV.y < 0;
278 | body.touching.left = body.touching.left || response.overlapV.x > 0;
279 | body.touching.right = body.touching.right || response.overlapV.x < 0;
280 | body.touching.none = !body.touching.up && !body.touching.down && !body.touching.left && !body.touching.right;
281 |
282 | // Set the blocked values
283 | body.blocked.up = body.blocked.up || response.overlapV.x === 0 && response.overlapV.y > 0;
284 | body.blocked.down = body.blocked.down || response.overlapV.x === 0 && response.overlapV.y < 0;
285 | body.blocked.left = body.blocked.left || response.overlapV.y === 0 && response.overlapV.x > 0;
286 | body.blocked.right = body.blocked.right || response.overlapV.y === 0 && response.overlapV.x < 0;
287 | };
288 |
289 | /**
290 | * Pull the body into a collision response based on its slopes options.
291 | *
292 | * TODO: Don't return after any condition is met, accumulate values into a
293 | * single SAT.Vector and apply at the end.
294 | *
295 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#pull
296 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
297 | * @param {SAT.Response} response - The SAT response.
298 | * @return {boolean} - Whether the body was pulled.
299 | */
300 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.pull = function (body, response) {
301 | if (!body.slopes.pullUp && !body.slopes.pullDown && !body.slopes.pullLeft && !body.slopes.pullRight &&
302 | !body.slopes.pullTopLeft && !body.slopes.pullTopRight && !body.slopes.pullBottomLeft && !body.slopes.pullBottomRight) {
303 | return false;
304 | }
305 |
306 | // Clone and flip the overlap normal so that it faces into the collision
307 | var overlapN = response.overlapN.clone().scale(-1);
308 |
309 | if (body.slopes.pullUp && overlapN.y < 0) {
310 | // Scale it by the configured amount
311 | pullUp = overlapN.clone().scale(body.slopes.pullUp);
312 |
313 | // Apply it to the body velocity
314 | body.velocity.x += pullUp.x;
315 | body.velocity.y += pullUp.y;
316 |
317 | return true;
318 | }
319 |
320 | if (body.slopes.pullDown && overlapN.y > 0) {
321 | pullDown = overlapN.clone().scale(body.slopes.pullDown);
322 |
323 | body.velocity.x += pullDown.x;
324 | body.velocity.y += pullDown.y;
325 |
326 | return true;
327 | }
328 |
329 | if (body.slopes.pullLeft && overlapN.x < 0) {
330 | pullLeft = overlapN.clone().scale(body.slopes.pullLeft);
331 |
332 | body.velocity.x += pullLeft.x;
333 | body.velocity.y += pullLeft.y;
334 |
335 | return true;
336 | }
337 |
338 | if (body.slopes.pullRight && overlapN.x > 0) {
339 | pullRight = overlapN.clone().scale(body.slopes.pullRight);
340 |
341 | body.velocity.x += pullRight.x;
342 | body.velocity.y += pullRight.y;
343 |
344 | return true;
345 | }
346 |
347 | if (body.slopes.pullTopLeft && overlapN.x < 0 && overlapN.y < 0) {
348 | pullUp = overlapN.clone().scale(body.slopes.pullTopLeft);
349 |
350 | body.velocity.x += pullUp.x;
351 | body.velocity.y += pullUp.y;
352 |
353 | return true;
354 | }
355 |
356 | if (body.slopes.pullTopRight && overlapN.x > 0 && overlapN.y < 0) {
357 | pullDown = overlapN.clone().scale(body.slopes.pullTopRight);
358 |
359 | body.velocity.x += pullDown.x;
360 | body.velocity.y += pullDown.y;
361 |
362 | return true;
363 | }
364 |
365 | if (body.slopes.pullBottomLeft && overlapN.x < 0 && overlapN.y > 0) {
366 | pullLeft = overlapN.clone().scale(body.slopes.pullBottomLeft);
367 |
368 | body.velocity.x += pullLeft.x;
369 | body.velocity.y += pullLeft.y;
370 |
371 | return true;
372 | }
373 |
374 | if (body.slopes.pullBottomRight && overlapN.x > 0 && overlapN.y > 0) {
375 | pullRight = overlapN.clone().scale(body.slopes.pullBottomRight);
376 |
377 | body.velocity.x += pullRight.x;
378 | body.velocity.y += pullRight.y;
379 |
380 | return true;
381 | }
382 |
383 | return false;
384 | };
385 |
386 | /**
387 | * Determine whether everything required to process a collision is available.
388 | *
389 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#shouldCollide
390 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
391 | * @param {Phaser.Tile} tile - The tile.
392 | * @return {boolean}
393 | */
394 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldCollide = function (body, tile) {
395 | return body.enable && body.polygon && body.slopes && tile.collides && tile.slope && tile.slope.polygon;
396 | };
397 |
398 | /**
399 | * Flattens the specified array of points onto a unit vector axis,
400 | * resulting in a one dimensional range of the minimum and
401 | * maximum value on that axis.
402 | *
403 | * Copied verbatim from SAT.flattenPointsOn.
404 | *
405 | * @see SAT.flattenPointsOn
406 | * @static
407 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#flattenPointsOn
408 | * @param {SAT.Vector[]} points - The points to flatten.
409 | * @param {SAT.Vector} normal - The unit vector axis to flatten on.
410 | * @param {number[]} result - An array. After calling this,
411 | * result[0] will be the minimum value,
412 | * result[1] will be the maximum value.
413 | */
414 | Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn = function (points, normal, result) {
415 | var min = Number.MAX_VALUE;
416 | var max = -Number.MAX_VALUE;
417 | var len = points.length;
418 |
419 | for (var i = 0; i < len; i++ ) {
420 | // The magnitude of the projection of the point onto the normal
421 | var dot = points[i].dot(normal);
422 | if (dot < min) { min = dot; }
423 | if (dot > max) { max = dot; }
424 | }
425 |
426 | result[0] = min; result[1] = max;
427 | };
428 |
429 | /**
430 | * Determine whether two polygons are separated by a given axis.
431 | *
432 | * Tailored to only push out in the direction of the given axis.
433 | *
434 | * Adapted from SAT.isSeparatingAxis.
435 | *
436 | * @see {SAT.isSeparatingAxis}
437 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#isSeparatingAxis
438 | * @param {SAT.Polygon} a - The first polygon.
439 | * @param {SAT.Polygon} b - The second polygon.
440 | * @param {SAT.Vector} axis - The axis (unit sized) to test against.
441 | * The points of both polygons are projected
442 | * onto this axis.
443 | * @param {SAT.Response} response - The response to populate if the polygons are
444 | * not separated by the given axis.
445 | * @return {boolean} true if it is a separating axis, false otherwise. If false,
446 | * and a response is passed in, information about how much overlap and
447 | * the direction of the overlap will be populated.
448 | */
449 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.isSeparatingAxis = function (a, b, axis, response) {
450 | var aPos = a.pos;
451 | var bPos = b.pos;
452 | var aPoints = a.calcPoints;
453 | var bPoints = b.calcPoints;
454 |
455 | var rangeA = this.arrayPool.pop();
456 | var rangeB = this.arrayPool.pop();
457 |
458 | // The magnitude of the offset between the two polygons
459 | var offsetV = this.vectorPool.pop().copy(bPos).sub(aPos);
460 | var projectedOffset = offsetV.dot(axis);
461 |
462 | // Project the polygons onto the axis.
463 | Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(aPoints, axis, rangeA);
464 | Phaser.Plugin.ArcadeSlopes.SatSolver.flattenPointsOn(bPoints, axis, rangeB);
465 |
466 | // Move B's range to its position relative to A.
467 | rangeB[0] += projectedOffset;
468 | rangeB[1] += projectedOffset;
469 |
470 | // Check if there is a gap. If there is, this is a separating axis and we can stop
471 | if (rangeA[0] >= rangeB[1] || rangeB[0] >= rangeA[1]) {
472 | this.vectorPool.push(offsetV);
473 | this.arrayPool.push(rangeA);
474 | this.arrayPool.push(rangeB);
475 | return true;
476 | }
477 |
478 | var option1, option2;
479 |
480 | // This is not a separating axis. If we're calculating a response, calculate
481 | // the overlap
482 | var overlap = 0;
483 |
484 | if (rangeA[0] < rangeB[0]) {
485 | // A starts further left than B
486 | response.aInB = false;
487 |
488 | if (rangeA[1] < rangeB[1]) {
489 | // A ends before B does. We have to pull A out of B
490 | //overlap = rangeA[1] - rangeB[0];
491 | response.bInA = false;
492 | }// else {
493 | // B is fully inside A. Pick the shortest way out.
494 | //option1 = rangeA[1] - rangeB[0];
495 | //option2 = rangeB[1] - rangeA[0];
496 | //overlap = option1 < option2 ? option1 : -option2;
497 | //}
498 | } else {
499 | // B starts further left than A
500 | response.bInA = false;
501 |
502 | if (rangeA[1] > rangeB[1]) {
503 | // B ends before A ends. We have to push A out of B
504 | overlap = rangeA[0] - rangeB[1];
505 | response.aInB = false;
506 | } else {
507 | // A is fully inside B. Pick the shortest way out.
508 | option1 = rangeA[1] - rangeB[0];
509 | option2 = rangeB[1] - rangeA[0];
510 | //overlap = option1 < option2 ? option1 : -option2;
511 |
512 | if (option1 >= option2) {
513 | overlap = -option2;
514 | }
515 | }
516 | }
517 |
518 | // If this is the smallest amount of overlap we've seen so far, set it
519 | // as the minimum overlap.
520 | var absOverlap = Math.abs(overlap);
521 |
522 | if (absOverlap < response.overlap) {
523 | response.overlap = absOverlap;
524 | response.overlapN.copy(axis);
525 |
526 | if (overlap < 0) {
527 | response.overlapN.reverse();
528 | }
529 | }
530 |
531 | this.vectorPool.push(offsetV);
532 | this.arrayPool.push(rangeA);
533 | this.arrayPool.push(rangeB);
534 |
535 | return false;
536 | };
537 |
538 | /**
539 | * Test whether two polygons overlap.
540 | *
541 | * Takes a response object that will be populated with the shortest
542 | * viable separation vector. Ignores collision responses that don't oppose
543 | * velocity enough.
544 | *
545 | * Returns true if there is a collision and false otherwise.
546 | *
547 | * Tailored to work with an AABB as the first polygon.
548 | *
549 | * Adapted from SAT.testPolygonPolygon.
550 | *
551 | * @see {SAT.testPolygonPolygon}
552 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#testPolygonPolygon
553 | * @param {SAT.Polygon} a - The first polygon.
554 | * @param {SAT.Polygon} b - The second polygon.
555 | * @param {SAT.Response} response - The response object to populate with overlap information.
556 | * @param {SAT.Vector} velocity - The velocity vector to ignore.
557 | * @param {SAT.Vector[]} ignore - The axes to ignore.
558 | * @return {boolean} - Whether the the two polygons overlap.
559 | */
560 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.testPolygonPolygon = function (a, b, response, velocity, ignore) {
561 | var aPoints = a.calcPoints;
562 | var aLen = aPoints.length;
563 | var bPoints = b.calcPoints;
564 | var bLen = bPoints.length;
565 |
566 | var i, j, k;
567 | var responses = this.arrayPool.pop();
568 | var axes = this.arrayPool.pop();
569 |
570 | responses.length = 0;
571 | axes.length = 0;
572 |
573 | // If any of the edge normals of A is a separating axis, no intersection
574 | for (i = 0; i < aLen; i++) {
575 | responses[i] = this.responsePool.pop();
576 | responses[i].clear();
577 | axes[i] = a.normals[i];
578 |
579 | if (this.isSeparatingAxis(a, b, a.normals[i], responses[i])) {
580 | for (k = 0; k < responses.length; k++) {
581 | this.responsePool.push(responses[k]);
582 | }
583 |
584 | this.arrayPool.push(responses, axes);
585 |
586 | return false;
587 | }
588 | }
589 |
590 | // If any of the edge normals of B is a separating axis, no intersection
591 | for (i = 0, j = aLen; i < bLen; i++, j++) {
592 | responses[j] = this.responsePool.pop();
593 | responses[j].clear();
594 | axes[j] = b.normals[i];
595 |
596 | if (this.isSeparatingAxis(a, b, b.normals[i], responses[j])) {
597 | for (k = 0; k < responses.length; k++) {
598 | this.responsePool.push(responses[k]);
599 | }
600 |
601 | this.arrayPool.push(responses, axes);
602 |
603 | return false;
604 | }
605 | }
606 |
607 | // Since none of the edge normals of A or B are a separating axis, there is
608 | // an intersection
609 |
610 | var viable = false;
611 | var ignored = false;
612 | var velocityTestVector = this.vectorPool.pop();
613 |
614 | // Determine the shortest desirable and viable separation from the responses
615 | for (i = 0; i < responses.length; i++) {
616 | // Is the overlap in the range we want?
617 | // TODO: Less than the max of tile width/height?
618 | if (!(responses[i].overlap > 0 && responses[i].overlap < Number.MAX_VALUE)) {
619 | continue;
620 | }
621 |
622 | // Is the overlap direction too close to that of the velocity direction?
623 | if (velocity && velocityTestVector.copy(responses[i].overlapN).scale(-1).dot(velocity) > 0) {
624 | continue;
625 | }
626 |
627 | ignored = false;
628 |
629 | // Is the axis of the overlap in the extra ignore list?
630 | for (j = 0; j < ignore.length; j++) {
631 | if (axes[i].x === ignore[j].x && axes[i].y === ignore[j].y) {
632 | ignored = true;
633 |
634 | break;
635 | }
636 | }
637 |
638 | // Skip this response if its normal is ignored
639 | if (ignored) {
640 | continue;
641 | }
642 |
643 | // Is this response's overlap shorter than that of the current?
644 | if (responses[i].overlap < response.overlap) {
645 | viable = true;
646 | response.aInB = responses[i].aInB;
647 | response.bInA = responses[i].bInA;
648 | response.overlap = responses[i].overlap;
649 | response.overlapN = responses[i].overlapN;
650 | }
651 | }
652 |
653 | // Set the polygons on the response and calculate the overlap vector
654 | if (viable) {
655 | response.a = a;
656 | response.b = b;
657 | response.overlapV.copy(response.overlapN).scale(response.overlap);
658 | }
659 |
660 | // Recycle the temporary responses, arrays and vectors used for calculations
661 | for (k = 0; k < responses.length; k++) {
662 | this.responsePool.push(responses[k]);
663 | }
664 |
665 | this.arrayPool.push(responses, axes);
666 | this.vectorPool.push(velocityTestVector);
667 |
668 | return viable;
669 | };
670 |
671 | /**
672 | * Separate the given body and tile from each other and apply any relevant
673 | * changes to the body's velocity.
674 | *
675 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#collide
676 | * @param {integer} i - The tile index.
677 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
678 | * @param {Phaser.Tile} tile - The tile.
679 | * @param {Phaser.TilemapLayer} tilemapLayer - The tilemap layer.
680 | * @param {boolean} overlapOnly - Whether to only check for an overlap.
681 | * @return {boolean} - Whether the body was separated.
682 | */
683 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.collide = function (i, body, tile, tilemapLayer, overlapOnly) {
684 | // Update the body's polygon position and velocity vector
685 | this.updateValues(body);
686 |
687 | // Bail out if we don't have everything we need
688 | if (!this.shouldCollide(body, tile)) {
689 | return false;
690 | }
691 |
692 | // Cater for SAT.js requiring center-origin circles
693 | if (body.isCircle) {
694 | body.polygon.pos.x += body.halfWidth;
695 | body.polygon.pos.y += body.halfHeight;
696 | }
697 |
698 | // Update the tile polygon position
699 | tile.slope.polygon.pos.x = tile.worldX + tilemapLayer.getCollisionOffsetX();
700 | tile.slope.polygon.pos.y = tile.worldY + tilemapLayer.getCollisionOffsetY();
701 |
702 | // Create the body's response if it doesn't have one
703 | body.slopes.sat.response = body.slopes.sat.response || new SAT.Response();
704 |
705 | // Acquire a temporary response from the pool
706 | var response = this.responsePool.pop();
707 | Phaser.Plugin.ArcadeSlopes.SatSolver.resetResponse(response);
708 |
709 | // Test for an overlap
710 | var circleOverlap = body.isCircle && SAT.testCirclePolygon(body.polygon, tile.slope.polygon, response);
711 | var polygonOverlap = !body.isCircle && this.testPolygonPolygon(body.polygon, tile.slope.polygon, response, body.slopes.velocity, tile.slope.ignormals);
712 |
713 | // Bail if there isn't one, leaving the body's response as is
714 | if (!circleOverlap && !polygonOverlap) {
715 | this.responsePool.push(response);
716 |
717 | return false;
718 | }
719 |
720 | // Invert our overlap vectors so that we have them facing outwards
721 | Phaser.Plugin.ArcadeSlopes.SatSolver.prepareResponse(response);
722 |
723 | // If we're only testing for the overlap, we can bail here
724 | if (overlapOnly) {
725 | Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse(response, body.slopes.sat.response);
726 | this.responsePool.push(response);
727 |
728 | return true;
729 | }
730 |
731 | // Bail out if no separation occurred
732 | if (!this.separate(body, tile, response)) {
733 | this.responsePool.push(response);
734 |
735 | return false;
736 | }
737 |
738 | // Copy the temporary response into the body's response, then recycle it
739 | Phaser.Plugin.ArcadeSlopes.SatSolver.copyResponse(response, body.slopes.sat.response);
740 | this.responsePool.push(response);
741 |
742 | response = body.slopes.sat.response;
743 |
744 | // Update the overlap properties of the body
745 | body.overlapX = response.overlapV.x;
746 | body.overlapY = response.overlapV.y;
747 |
748 | // Set the tile that the body separated from
749 | body.slopes.tile = tile;
750 |
751 | // Apply any velocity changes as a result of the collision
752 | this.applyVelocity(body, tile, response);
753 |
754 | // Update the touching and blocked flags of the physics body
755 | this.updateFlags(body, response);
756 |
757 | return true;
758 | };
759 |
760 | /**
761 | * Determine whether to separate a body from a tile, given an SAT response.
762 | *
763 | * Checks against the tile's collision flags and slope edge flags.
764 | *
765 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#shouldSeparate
766 | * @param {integer} i - The tile index.
767 | * @param {Phaser.Physics.Arcade.Body} body - The physics body.
768 | * @param {Phaser.Tile} tile - The tile.
769 | * @param {SAT.Response} response - The initial collision response.
770 | * @return {boolean} - Whether to pursue the narrow phase.
771 | */
772 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.shouldSeparate = function (i, body, tile, response) {
773 | // Bail if the body is disabled or there is no overlap
774 | if (!(body.enable && response.overlap)) {
775 | return false;
776 | }
777 |
778 | // Only separate if the body is moving into the collision
779 | // if (response.overlapV.clone().scale(-1).dot(body.slopes.velocity) < 0) {
780 | // return false;
781 | // }
782 |
783 | // Otherwise we should separate normally
784 | return true;
785 | };
786 |
787 | /**
788 | * Render the given SAT response as a set of lines from the given position.
789 | *
790 | * TODO: Actually maybe just collect the lines here for drawing later?
791 | * Or, make this static and just something you can call in the
792 | * context of a game, or game state.
793 | *
794 | * @method Phaser.Plugin.ArcadeSlopes.SatSolver#debug
795 | * @param {Phaser.Point} position
796 | * @param {SAT.Response} response
797 | */
798 | Phaser.Plugin.ArcadeSlopes.SatSolver.prototype.debug = function (position, response) {
799 | // TODO: Implement.
800 | };
801 |
--------------------------------------------------------------------------------
/src/ArcadeSlopes/TileSlope.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * Defines the slope of a tile.
9 | *
10 | * @class Phaser.Plugin.ArcadeSlopes.TileSlope
11 | * @constructor
12 | * @param {integer} type - The type of the tile slope.
13 | * @param {Phaser.Tile} tile - The tile this slope definition belongs to.
14 | * @param {SAT.Polygon} polygon - The polygon representing the shape of the tile.
15 | * @param {Phaser.Line} line - The line representing the slope of the tile.
16 | * @param {object} edges - The flags for each edge of the tile.
17 | * @param {SAT.Vector} axis - The preferred axis for separating physics bodies.
18 | * @param {SAT.Vector[]} [ignormals] - An optional set of collision normals to ignore.
19 | */
20 | Phaser.Plugin.ArcadeSlopes.TileSlope = function (type, tile, polygon, line, edges, axis, ignormals) {
21 | /**
22 | * The type of the tile slope.
23 | *
24 | * @property {integer} type
25 | */
26 | this.type = type;
27 |
28 | /**
29 | * The tile this slope definition is for.
30 | *
31 | * @property {Phaser.Tile} tile
32 | */
33 | this.tile = tile;
34 |
35 | /**
36 | * The polygon representing the shape of the tile.
37 | *
38 | * @property {SAT.Polygon} polygon
39 | */
40 | this.polygon = polygon;
41 |
42 | /**
43 | * The line representing the slope of the tile.
44 | *
45 | * @property {Phaser.Tile} line
46 | */
47 | this.line = line;
48 |
49 | /**
50 | * The flags for each edge of the tile: empty, solid or interesting?
51 | *
52 | * @property {object} edges
53 | */
54 | this.edges = Phaser.Utils.mixin(edges || {}, {
55 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,
56 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,
57 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID,
58 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID
59 | });
60 |
61 | /**
62 | * The preferred axis for separating physics bodies.
63 | *
64 | * @property {SAT.Vector} axis
65 | */
66 | this.axis = axis || null;
67 |
68 | /**
69 | * An optional set of collision normals to ignore.
70 | *
71 | * @property {SAT.Vector[]} ignormals
72 | */
73 | this.ignormals = ignormals || [];
74 |
75 | /**
76 | * The preferred solver to use for this slope.
77 | *
78 | * @property {string} solver
79 | */
80 | this.solver = null;
81 |
82 | /**
83 | * The friction of this slope.
84 | *
85 | * @property {Phaser.Point} friction
86 | */
87 | this.friction = new Phaser.Point();
88 | };
89 |
90 | /**
91 | * Determine whether the start or end of one slope line meets the start or end
92 | * of another's.
93 | *
94 | * @param {Phaser.Plugin.ArcadeSlopes.TileSlope} slope - The tile slope to check intersection with.
95 | */
96 | Phaser.Plugin.ArcadeSlopes.TileSlope.prototype.meets = function (slope) {
97 | return this.line.start.equals(slope.line.end)
98 | || this.line.end.equals(slope.line.start);
99 | };
100 |
101 | /**
102 | * Resolve a tile slope type constant from the given value.
103 | *
104 | * Returns any successfully parsed non-negative integers regardless of whether
105 | * they are valid slope tile types. This method is really for strings.
106 | *
107 | * @method Phaser.Plugin.ArcadeSlopes.TileSlope#resolveType
108 | * @param {string|integer} type - The value to resolve.
109 | * @return {integer} - The resolved tile slope type constant.
110 | */
111 | Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType = function (type) {
112 | if (parseInt(type) >= 0) {
113 | return type;
114 | }
115 |
116 | if (typeof type === 'string') {
117 | type = type.toUpperCase();
118 | }
119 |
120 | if (Phaser.Plugin.ArcadeSlopes.TileSlope.hasOwnProperty(type)) {
121 | return Phaser.Plugin.ArcadeSlopes.TileSlope[type];
122 | }
123 |
124 | console.warn('Unknown slope type \'' + type + '\'');
125 |
126 | return Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN;
127 | };
128 |
129 | /**
130 | * The slope of the tile.
131 | *
132 | * @name Phaser.Plugin.ArcadeSlopes.TileSlope#slope
133 | * @property {number} slope
134 | */
135 | Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype, 'slope', {
136 | get: function () {
137 | if (!this.line) {
138 | return 0;
139 | }
140 |
141 | return (this.line.start.y - this.line.end.y) / (this.line.start.x - this.line.end.x);
142 | }
143 | });
144 |
145 | /**
146 | * The name of the tile slope type.
147 | *
148 | * @name Phaser.Plugin.ArcadeSlopes.TileSlope#typeName
149 | * @property {string} typeName
150 | */
151 | Object.defineProperty(Phaser.Plugin.ArcadeSlopes.TileSlope.prototype, 'typeName', {
152 | get: function () {
153 | return Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName(this.type);
154 | },
155 | set: function (type) {
156 | this.type = Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(type);
157 | }
158 | });
159 |
160 | /**
161 | * Resolve a tile slope type name from the given type constant.
162 | *
163 | * @static
164 | * @method Phaser.Plugin.ArcadeSlopes.TileSlope#resolveTypeName
165 | * @param {integer} type - The type constant.
166 | * @return {integer} - The type name.
167 | */
168 | Phaser.Plugin.ArcadeSlopes.TileSlope.resolveTypeName = function (type) {
169 | if (Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames.hasOwnProperty(type)) {
170 | return Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[type];
171 | }
172 |
173 | return Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames[-1];
174 | };
175 |
176 | /**
177 | * The map of tile slope types to their corresponding type names.
178 | *
179 | * @static
180 | * @property {object} typeNames
181 | */
182 | Phaser.Plugin.ArcadeSlopes.TileSlope.typeNames = {
183 | '-1': 'UNKNOWN',
184 | 0: 'FULL',
185 | 21: 'HALF_BOTTOM',
186 | 22: 'HALF_TOP',
187 | 23: 'HALF_LEFT',
188 | 24: 'HALF_RIGHT',
189 | 1: 'HALF_BOTTOM_LEFT',
190 | 2: 'HALF_BOTTOM_RIGHT',
191 | 3: 'HALF_TOP_LEFT',
192 | 4: 'HALF_TOP_RIGHT',
193 | 5: 'QUARTER_BOTTOM_LEFT_LOW',
194 | 6: 'QUARTER_BOTTOM_LEFT_HIGH',
195 | 7: 'QUARTER_BOTTOM_RIGHT_LOW',
196 | 8: 'QUARTER_BOTTOM_RIGHT_HIGH',
197 | 9: 'QUARTER_LEFT_BOTTOM_LOW',
198 | 10: 'QUARTER_LEFT_BOTTOM_HIGH',
199 | 11: 'QUARTER_RIGHT_BOTTOM_LOW',
200 | 12: 'QUARTER_RIGHT_BOTTOM_HIGH',
201 | 13: 'QUARTER_LEFT_TOP_LOW',
202 | 14: 'QUARTER_LEFT_TOP_HIGH',
203 | 15: 'QUARTER_RIGHT_TOP_LOW',
204 | 16: 'QUARTER_RIGHT_TOP_HIGH',
205 | 17: 'QUARTER_TOP_LEFT_LOW',
206 | 18: 'QUARTER_TOP_LEFT_HIGH',
207 | 19: 'QUARTER_TOP_RIGHT_LOW',
208 | 20: 'QUARTER_TOP_RIGHT_HIGH',
209 | };
210 |
211 | // TODO: Misleading constants here - they aren't tile slope types, they're edges
212 |
213 | /**
214 | * An empty tile edge.
215 | *
216 | * @constant
217 | * @type {integer}
218 | */
219 | Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY = 0;
220 |
221 | /**
222 | * A solid tile edge.
223 | *
224 | * @constant
225 | * @type {integer}
226 | */
227 | Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID = 1;
228 |
229 | /**
230 | * An interesting tile edge.
231 | *
232 | * @constant
233 | * @type {integer}
234 | */
235 | Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING = 2;
236 |
237 | /**
238 | * An undefined tile slope type.
239 | *
240 | * @constant
241 | * @type {integer}
242 | */
243 | Phaser.Plugin.ArcadeSlopes.TileSlope.UNKNOWN = -1;
244 |
245 | /**
246 | * A full square tile.
247 | * .___
248 | * | |
249 | * |___|
250 | *
251 | * @constant
252 | * @type {integer}
253 | */
254 | Phaser.Plugin.ArcadeSlopes.TileSlope.FULL = 0;
255 |
256 | /**
257 | * A half bottom tile.
258 | * .
259 | * ___
260 | * |___|
261 | *
262 | * @constant
263 | * @type {integer}
264 | */
265 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM = 21;
266 |
267 | /**
268 | * A half top tile.
269 | * .___
270 | * |___|
271 | *
272 | * @constant
273 | * @type {integer}
274 | */
275 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP = 22;
276 |
277 | /**
278 | * A half left tile.
279 | * ._
280 | * | |
281 | * |_|
282 | *
283 | * @constant
284 | * @type {integer}
285 | */
286 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT = 23;
287 |
288 | /**
289 | * A half right tile.
290 | * . _
291 | * | |
292 | * |_|
293 | *
294 | * @constant
295 | * @type {integer}
296 | */
297 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT = 24;
298 |
299 | /**
300 | * A 45 degree bottom left slope.
301 | *
302 | * |\
303 | * | \
304 | * |__\
305 | *
306 | * @constant
307 | * @type {integer}
308 | */
309 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT = 1;
310 |
311 | /**
312 | * A 45 degree bottom right slope.
313 | *
314 | * /|
315 | * / |
316 | * /__|
317 | *
318 | * @constant
319 | * @type {integer}
320 | */
321 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT = 2;
322 |
323 | /**
324 | * A 45 degree top left slope.
325 | * __
326 | * | /
327 | * | /
328 | * |/
329 | *
330 | * @constant
331 | * @type {integer}
332 | */
333 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT = 3;
334 |
335 | /**
336 | * A 45 degree top right slope.
337 | * __
338 | * \ |
339 | * \ |
340 | * \|
341 | *
342 | * @constant
343 | * @type {integer}
344 | */
345 | Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT = 4;
346 |
347 | /**
348 | * |\
349 | * | | |\
350 | * |_| |_\ <--
351 | *
352 | * @constant
353 | * @type {integer}
354 | */
355 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW = 5;
356 |
357 | /**
358 | * |\
359 | * | | |\
360 | * -->|_| |_\
361 | *
362 | * @constant
363 | * @type {integer}
364 | */
365 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH = 6;
366 |
367 | /**
368 | * /|
369 | * /| | |
370 | * -->/_| |_|
371 | *
372 | * @constant
373 | * @type {integer}
374 | */
375 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW = 7;
376 |
377 | /**
378 | * /|
379 | * /| | |
380 | * /_| |_|<--
381 | *
382 | * @constant
383 | * @type {integer}
384 | */
385 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH = 8;
386 |
387 | /**
388 | * |\
389 | * |_\
390 | * __
391 | * | \ <--
392 | * |___\
393 | *
394 | * @constant
395 | * @type {integer}
396 | */
397 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW = 9;
398 |
399 | /**
400 | * |\
401 | * |_\ <--
402 | * __
403 | * | \
404 | * |___\
405 | *
406 | * @constant
407 | * @type {integer}
408 | */
409 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH = 10;
410 |
411 | /**
412 | * /|
413 | * /_|
414 | * __
415 | * / | <--
416 | * /___|
417 | *
418 | * @constant
419 | * @type {integer}
420 | */
421 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW = 11;
422 |
423 | /**
424 | * /|
425 | * /_| <--
426 | * __
427 | * / |
428 | * /___|
429 | *
430 | * @constant
431 | * @type {integer}
432 | */
433 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH = 12;
434 |
435 | /**
436 | * ____
437 | * | /
438 | * |___/
439 | * __
440 | * | / <--
441 | * |/
442 | *
443 | * @constant
444 | * @type {integer}
445 | */
446 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW = 13;
447 |
448 | /**
449 | * ____
450 | * | / <--
451 | * |___/
452 | * __
453 | * | /
454 | * |/
455 | *
456 | * @constant
457 | * @type {integer}
458 | */
459 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH = 14;
460 |
461 | /**
462 | * ____
463 | * \ |
464 | * \___|
465 | * __
466 | * \ | <--
467 | * \|
468 | *
469 | * @constant
470 | * @type {integer}
471 | */
472 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW = 15;
473 |
474 | /**
475 | * ____
476 | * \ | <--
477 | * \___|
478 | * __
479 | * \ |
480 | * \|
481 | *
482 | * @constant
483 | * @type {integer}
484 | */
485 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH = 16;
486 |
487 | /**
488 | * __ __
489 | * | | | / <--
490 | * | | |/
491 | * | /
492 | * |/
493 | * @constant
494 | * @type {integer}
495 | */
496 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW = 17;
497 |
498 | /**
499 | * __ __
500 | * | | | /
501 | * --> | | |/
502 | * | /
503 | * |/
504 | * @constant
505 | * @type {integer}
506 | */
507 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH = 18;
508 |
509 | /**
510 | * __ __
511 | * \ | | |
512 | * --> \| | |
513 | * \ |
514 | * \|
515 | *
516 | * @constant
517 | * @type {integer}
518 | */
519 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW = 19;
520 |
521 | /**
522 | * __ __
523 | * \ | | |
524 | * \| | | <--
525 | * \ |
526 | * \|
527 | *
528 | * @constant
529 | * @type {integer}
530 | */
531 | Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH = 20;
532 |
--------------------------------------------------------------------------------
/src/ArcadeSlopes/TileSlopeFactory.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Chris Andrew
3 | * @copyright 2016-2021 Chris Andrew
4 | * @license MIT
5 | */
6 |
7 | /**
8 | * Builds TileSlope objects from a set of definition functions.
9 | *
10 | * @class Phaser.Plugin.ArcadeSlopes.TileSlopeFactory
11 | * @constructor
12 | */
13 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory = function () {
14 | /**
15 | * A set of definition functions for the factory to use to build tile slopes
16 | * of a given type.
17 | *
18 | * Maps slope type constants to definition functions.
19 | *
20 | * @property {object} definitions
21 | */
22 | this.definitions = {};
23 |
24 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.FULL] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull;
25 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom;
26 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop;
27 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft;
28 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight;
29 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_LEFT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft;
30 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM_RIGHT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight;
31 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_LEFT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft;
32 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP_RIGHT] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight;
33 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow;
34 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_LEFT_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh;
35 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow;
36 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_BOTTOM_RIGHT_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh;
37 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow;
38 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_BOTTOM_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh;
39 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow;
40 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_BOTTOM_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh;
41 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow;
42 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_LEFT_TOP_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh;
43 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow;
44 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_RIGHT_TOP_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh;
45 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow;
46 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_LEFT_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh;
47 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_LOW] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow;
48 | this.definitions[Phaser.Plugin.ArcadeSlopes.TileSlope.QUARTER_TOP_RIGHT_HIGH] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh;
49 |
50 | /**
51 | * A set of common slope mapping functions that can be used instead of an
52 | * explicit map.
53 | *
54 | * Maps TileSlopeFactory constants to mapping functions.
55 | *
56 | * @property {object} mappings
57 | */
58 | this.mappings = {};
59 |
60 | this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes;
61 | this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA] = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics;
62 |
63 | /**
64 | * A pool of vectors.
65 | *
66 | * @property {SAT.Vector[]} vectorPool
67 | */
68 | this.vectorPool = [];
69 |
70 | for (var i = 0; i < 100; i++) {
71 | this.vectorPool.push(new SAT.Vector());
72 | }
73 | };
74 |
75 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.constructor = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory;
76 |
77 | /**
78 | * Define a new tile slope type.
79 | *
80 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#define
81 | * @param {integer} type - The slope type key.
82 | * @param {function} definition - The slope type definition function.
83 | */
84 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.define = function (type, definition) {
85 | if (typeof definition !== 'function') {
86 | return;
87 | }
88 |
89 | this.definitions[type] = definition;
90 | };
91 |
92 | /**
93 | * Create a TileSlope of the given type for the given tile.
94 | *
95 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#create
96 | * @param {integer} type - The slope type.
97 | * @param {Phaser.Tile} tile - The tile object.
98 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile.
99 | */
100 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.create = function (type, tile) {
101 | var original = type;
102 |
103 | type = Phaser.Plugin.ArcadeSlopes.TileSlope.resolveType(original);
104 |
105 | if (!this.definitions.hasOwnProperty(type)) {
106 | console.warn('Slope type ' + original + ' not defined');
107 |
108 | return null;
109 | }
110 |
111 | if (typeof this.definitions[type] !== 'function') {
112 | console.warn('Slope type definition for type ' + original + ' is not a function');
113 |
114 | return null;
115 | }
116 |
117 | return this.definitions[type].call(this, type, tile);
118 | };
119 |
120 | /**
121 | * Convert a layer of the given tilemap.
122 | *
123 | * Attaches Phaser.Plugin.ArcadeSlopes.TileSlope objects that are used to define
124 | * how the tile should collide with a physics body.
125 | *
126 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#convertTilemap
127 | * @param {Phaser.Tilemap} map - The map containing the layer to convert.
128 | * @param {number|string|Phaser.TileMapLayer} layer - The layer of the map to convert.
129 | * @param {string|object} slopeMap - A mapping type string, or a map of tilemap indexes to ArcadeSlope.TileSlope constants.
130 | * @param {integer} index - An optional first tile index (firstgid).
131 | * @return {Phaser.Tilemap} - The converted tilemap.
132 | */
133 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemap = function (map, layer, slopeMap, offset) {
134 | layer = map.getLayer(layer);
135 |
136 | this.convertTilemapLayer(layer, slopeMap, offset);
137 |
138 | return map;
139 | };
140 |
141 | /**
142 | * Convert a tilemap layer.
143 | *
144 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#convertTilemapLayer
145 | * @param {Phaser.TilemapLayer} layer - The tilemap layer to convert.
146 | * @param {string|object} slopeMap - A mapping type string, or a map of tilemap indexes to ArcadeSlope.TileSlope constants.
147 | * @param {integer} index - An optional first tile index (firstgid).
148 | * @return {Phaser.TilemapLayer} - The converted tilemap layer.
149 | */
150 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.convertTilemapLayer = function (layer, slopeMap, index) {
151 | var that = this;
152 |
153 | // Resolve a predefined slope map if a string is given
154 | if (typeof slopeMap === 'string') {
155 | var mappingType = this.resolveMappingType(slopeMap);
156 |
157 | if (!this.mappings[mappingType]) {
158 | console.warn('Tilemap could not be converted; mapping type \'' + slopeMap + '\' is unknown');
159 |
160 | return layer;
161 | }
162 |
163 | slopeMap = this.mappings[mappingType](index);
164 | }
165 |
166 | // Create the TileSlope objects for each relevant tile in the layer
167 | layer.layer.data.forEach(function (row) {
168 | row.forEach(function (tile) {
169 | var slope;
170 |
171 | // Try to resolve a slope from the tile's type property
172 | if (tile.properties.type) {
173 | slope = that.create(tile.properties.type, tile);
174 | }
175 |
176 | // Otherwise resolve a type from its index
177 | if (!slope && slopeMap.hasOwnProperty(tile.index)) {
178 | slope = that.create(slopeMap[tile.index], tile);
179 | }
180 |
181 | if (slope) {
182 | tile.slope = slope;
183 | }
184 |
185 | var x = tile.x;
186 | var y = tile.y;
187 |
188 | tile.neighbours = tile.neighbours || {};
189 |
190 | // Give each tile references to their eight neighbours
191 | tile.neighbours.above = layer.map.getTileAbove(layer.index, x, y);
192 | tile.neighbours.below = layer.map.getTileBelow(layer.index, x, y);
193 | tile.neighbours.left = layer.map.getTileLeft(layer.index, x, y);
194 | tile.neighbours.right = layer.map.getTileRight(layer.index, x, y);
195 | tile.neighbours.topLeft = layer.map.getTileTopLeft(layer.index, x, y);
196 | tile.neighbours.topRight = layer.map.getTileTopRight(layer.index, x, y);
197 | tile.neighbours.bottomLeft = layer.map.getTileBottomLeft(layer.index, x, y);
198 | tile.neighbours.bottomRight = layer.map.getTileBottomRight(layer.index, x, y);
199 | });
200 | });
201 |
202 | // Calculate the edge flags for each tile in the layer
203 | this.calculateEdges(layer);
204 |
205 | // Add some extra properties to the layer's debug settings
206 | this.addDebugSettings(layer);
207 |
208 | return layer;
209 | };
210 |
211 | /**
212 | * Calculate the edge flags for each tile in the given tilemap layer.
213 | *
214 | * TODO: Allow this to work with an optional range of tile coordinates.
215 | *
216 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#calculateEdges
217 | * @param {Phaser.TilemapLayer} layer - The tilemap layer to calculate edge flags for.
218 | */
219 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.calculateEdges = function (layer) {
220 | var x, y, h, w, tile, above, below, left, right;
221 |
222 | h = layer.layer.height;
223 | w = layer.layer.width;
224 |
225 | for (y = 0; y < h; y++) {
226 | for (x = 0; x < w; x++) {
227 | tile = layer.layer.data[y][x];
228 |
229 | if (tile && tile.hasOwnProperty('slope')) {
230 | // Compare edges and flag internal vertices
231 | above = layer.map.getTileAbove(layer.index, x, y);
232 | below = layer.map.getTileBelow(layer.index, x, y);
233 | left = layer.map.getTileLeft(layer.index, x, y);
234 | right = layer.map.getTileRight(layer.index, x, y);
235 |
236 | if (above && above.hasOwnProperty('slope')) {
237 | tile.slope.edges.top = this.compareEdges(tile.slope.edges.top, above.slope.edges.bottom);
238 | tile.collideUp = tile.slope.edges.top !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
239 | this.flagInternalVertices(tile, above);
240 | }
241 |
242 | if (below && below.hasOwnProperty('slope')) {
243 | tile.slope.edges.bottom = this.compareEdges(tile.slope.edges.bottom, below.slope.edges.top);
244 | tile.collideDown = tile.slope.edges.bottom !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
245 | this.flagInternalVertices(tile, below);
246 | }
247 |
248 | if (left && left.hasOwnProperty('slope')) {
249 | tile.slope.edges.left = this.compareEdges(tile.slope.edges.left, left.slope.edges.right);
250 | tile.collideLeft = tile.slope.edges.left !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
251 | this.flagInternalVertices(tile, left);
252 | }
253 |
254 | if (right && right.hasOwnProperty('slope')) {
255 | tile.slope.edges.right = this.compareEdges(tile.slope.edges.right, right.slope.edges.left);
256 | tile.collideRight = tile.slope.edges.right !== Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
257 | this.flagInternalVertices(tile, right);
258 | }
259 | }
260 | }
261 | }
262 |
263 | // Flag further normals that we want to ignore for this tile, now that all
264 | // of the edges have been set
265 | for (y = 0; y < h; y++) {
266 | for (x = 0; x < w; x++) {
267 | tile = layer.layer.data[y][x];
268 |
269 | this.flagIgnormals(tile);
270 | }
271 | }
272 | };
273 |
274 | /**
275 | * Resolve the given flags of two contiguous tile edges.
276 | *
277 | * Returns the new flag to use for the first edge after comparing it with the
278 | * second edge.
279 | *
280 | * This compares AABB edges of each tile, not polygon edges.
281 | *
282 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#compareEdges
283 | * @param {integer} firstEdge - The edge to resolve.
284 | * @param {integer} secondEdge - The edge to compare against.
285 | * @return {integer} - The resolved edge.
286 | */
287 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.compareEdges = function (firstEdge, secondEdge) {
288 | if (firstEdge === Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID && secondEdge === Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID) {
289 | return Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
290 | }
291 |
292 | if (firstEdge === Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID && secondEdge === Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY) {
293 | return Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
294 | }
295 |
296 | return firstEdge;
297 | };
298 |
299 | /**
300 | * Compares the polygon edges of two tiles and flags those that match.
301 | *
302 | * Because the polygons are represented by a set of points, instead of actual
303 | * edges, the first vector (assuming they are specified clockwise) of each
304 | * potential edge is flagged instead.
305 | *
306 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#flagInternalVertices
307 | * @param {Phaser.Tile} firstTile - The first tile to compare.
308 | * @param {Phaser.Tile} secondTile - The second tile to compare.
309 | */
310 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.flagInternalVertices = function (firstTile, secondTile) {
311 | // Bail if either tile lacks a polygon
312 | if (!firstTile.slope.polygon || !secondTile.slope.polygon) {
313 | return;
314 | }
315 |
316 | // Access the tile polygons and grab some vectors from the pool
317 | var firstPolygon = firstTile.slope.polygon;
318 | var secondPolygon = secondTile.slope.polygon;
319 | var firstPosition = this.vectorPool.pop();
320 | var secondPosition = this.vectorPool.pop();
321 | var firstTileVertexOne = this.vectorPool.pop();
322 | var firstTileVertexTwo = this.vectorPool.pop();
323 | var secondTileVertexOne = this.vectorPool.pop();
324 | var secondTileVertexTwo = this.vectorPool.pop();
325 | var exactMatch;
326 | var inverseMatch;
327 |
328 | // TODO: Take into account tilemap offset...
329 | firstPosition.x = firstTile.worldX;
330 | firstPosition.y = firstTile.worldY;
331 | secondPosition.x = secondTile.worldX;
332 | secondPosition.y = secondTile.worldY;
333 |
334 | for (var i = 0; i < firstPolygon.points.length; i++) {
335 | firstTileVertexOne.copy(firstPolygon.points[i]).add(firstPosition);
336 | firstTileVertexTwo.copy(firstPolygon.points[(i + 1) % firstPolygon.points.length]).add(firstPosition);
337 |
338 | for (var j = 0; j < secondPolygon.points.length; j++) {
339 | secondTileVertexOne.copy(secondPolygon.points[j]).add(secondPosition);
340 | secondTileVertexTwo.copy(secondPolygon.points[(j + 1) % secondPolygon.points.length]).add(secondPosition);
341 |
342 | // Now we can compare vertices for an exact or inverse match
343 | exactMatch = firstTileVertexOne.x === secondTileVertexOne.x &&
344 | firstTileVertexOne.y === secondTileVertexOne.y &&
345 | firstTileVertexTwo.x === secondTileVertexTwo.x &&
346 | firstTileVertexTwo.y === secondTileVertexTwo.y;
347 |
348 | inverseMatch = firstTileVertexOne.x === secondTileVertexTwo.x &&
349 | firstTileVertexOne.y === secondTileVertexTwo.y &&
350 | firstTileVertexTwo.x === secondTileVertexOne.x &&
351 | firstTileVertexTwo.y === secondTileVertexOne.y;
352 |
353 | // Flag the first vertex and the normal of the internal edge
354 | if (exactMatch || inverseMatch) {
355 | firstPolygon.normals[i].ignore = true;
356 | secondPolygon.normals[j].ignore = true;
357 |
358 | firstTile.slope.ignormals.push(firstPolygon.normals[i]);
359 | secondTile.slope.ignormals.push(secondPolygon.normals[j]);
360 | }
361 | }
362 | }
363 |
364 | // Recycle the vectors we used
365 | this.vectorPool.push(
366 | firstPosition, secondPosition, firstTileVertexOne, firstTileVertexTwo,
367 | secondTileVertexOne, secondTileVertexTwo
368 | );
369 | };
370 |
371 | /**
372 | * Flag further normals to ignore to prevent unwanted collision responses.
373 | *
374 | * Simply observes Phaser's edge flags of neighbouring tiles to decide whether
375 | * axis-aligned collisions are desirable.
376 | *
377 | * Heuristics 2.0. Generalised instead of tile-specific.
378 | *
379 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#flagIgnormals
380 | * @param {Phaser.Tile} tile - The tile to flag ignormals for.
381 | */
382 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.flagIgnormals = function (tile) {
383 | if (!tile.slope || !tile.slope.polygon) {
384 | return;
385 | }
386 |
387 | // Skip full and half blocks
388 | // TODO: Skip any tiles with purely axis-aligned edges
389 | if (tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.FULL ||
390 | tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_TOP ||
391 | tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_BOTTOM ||
392 | tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_LEFT ||
393 | tile.slope.type === Phaser.Plugin.ArcadeSlopes.TileSlope.HALF_RIGHT
394 | ) {
395 | return;
396 | }
397 |
398 | // Define some shorthand variables to use in the conditions
399 | var empty = Phaser.Plugin.ArcadeSlopes.TileSlope.EMPTY;
400 | var interesting = Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING;
401 | var solid = Phaser.Plugin.ArcadeSlopes.TileSlope.SOLID;
402 | var slope = tile.slope.slope;
403 | var above = tile.neighbours.above;
404 | var below = tile.neighbours.below;
405 | var left = tile.neighbours.left;
406 | var right = tile.neighbours.right;
407 | var topLeft = tile.neighbours.topLeft;
408 | var topRight = tile.neighbours.topRight;
409 | var bottomLeft = tile.neighbours.bottomLeft;
410 | var bottomRight = tile.neighbours.bottomRight;
411 |
412 | // Skip neighbours without a TileSlope
413 | if (above && !above.hasOwnProperty('slope')) {
414 | above = null;
415 | }
416 |
417 | if (below && !below.hasOwnProperty('slope')) {
418 | below = null;
419 | }
420 |
421 | if (left && !left.hasOwnProperty('slope')) {
422 | left = null;
423 | }
424 |
425 | if (right && !right.hasOwnProperty('slope')) {
426 | right = null;
427 | }
428 |
429 | if (topLeft && !topLeft.hasOwnProperty('slope')) {
430 | topLeft = null;
431 | }
432 |
433 | if (topRight && !topRight.hasOwnProperty('slope')) {
434 | topRight = null;
435 | }
436 |
437 | if (bottomLeft && !bottomLeft.hasOwnProperty('slope')) {
438 | bottomLeft = null;
439 | }
440 |
441 | if (bottomRight && !bottomRight.hasOwnProperty('slope')) {
442 | bottomRight = null;
443 | }
444 |
445 | // Determine the interesting edges of the current tile
446 | var topInteresting = tile.slope.edges.top === interesting;
447 | var bottomInteresting = tile.slope.edges.bottom === interesting;
448 | var leftInteresting = tile.slope.edges.left === interesting;
449 | var rightInteresting = tile.slope.edges.right === interesting;
450 |
451 | // Skip top collisions
452 | if (topInteresting && (
453 | (topLeft && topLeft.slope.edges.right === interesting && slope === topLeft.slope.slope && tile.slope.meets(topLeft.slope)) ||
454 | (topRight && topRight.slope.edges.left === interesting && slope === topRight.slope.slope && tile.slope.meets(topRight.slope)) ||
455 | (leftInteresting && rightInteresting && (
456 | (left && left.slope.edges.top !== solid && left.slope.edges.right === interesting && slope === left.slope.slope && tile.slope.meets(left.slope)) ||
457 | (right && right.slope.edges.top !== solid && right.slope.edges.left === interesting && slope === right.slope.slope && tile.slope.meets(right.slope))
458 | ))
459 | )) {
460 | tile.slope.ignormals.push(new SAT.Vector(0, -1));
461 | }
462 |
463 | // Skip bottom collisions
464 | if (bottomInteresting && (
465 | (bottomLeft && bottomLeft.slope.edges.right === interesting && slope === bottomLeft.slope.slope && tile.slope.meets(bottomLeft.slope)) ||
466 | (bottomRight && bottomRight.slope.edges.left === interesting && slope === bottomRight.slope.slope && tile.slope.meets(bottomRight.slope)) ||
467 | (leftInteresting && rightInteresting && (
468 | (left && left.slope.edges.bottom !== solid && left.slope.edges.right === interesting && slope === left.slope.slope && tile.slope.meets(left.slope)) ||
469 | (right && right.slope.edges.bottom !== solid && right.slope.edges.left === interesting && slope === right.slope.slope && tile.slope.meets(right.slope))
470 | ))
471 | )) {
472 | tile.slope.ignormals.push(new SAT.Vector(0, 1));
473 | }
474 |
475 | // Skip left collisions
476 | if (leftInteresting && (
477 | (topLeft && topLeft.slope.edges.bottom === interesting && slope === topLeft.slope.slope && tile.slope.meets(topLeft.slope)) ||
478 | (bottomLeft && bottomLeft.slope.edges.top === interesting && slope === bottomLeft.slope.slope && tile.slope.meets(bottomLeft.slope)) ||
479 | (topInteresting && bottomInteresting && (
480 | (above && above.slope.edges.left !== solid && above.slope.edges.bottom === interesting && slope === above.slope.slope && tile.slope.meets(above.slope)) ||
481 | (below && below.slope.edges.left !== solid && below.slope.edges.top === interesting && slope === below.slope.slope && tile.slope.meets(below.slope))
482 | ))
483 | )) {
484 | tile.slope.ignormals.push(new SAT.Vector(-1, 0));
485 | }
486 |
487 | // Skip right collisions
488 | if (rightInteresting && (
489 | (topRight && topRight.slope.edges.bottom === interesting && slope === topRight.slope.slope && tile.slope.meets(topRight.slope)) ||
490 | (bottomRight && bottomRight.slope.edges.top === interesting && slope === bottomRight.slope.slope && tile.slope.meets(bottomRight.slope)) ||
491 | (topInteresting && bottomInteresting && (
492 | (above && above.slope.edges.right !== solid && above.slope.edges.bottom === interesting && slope === above.slope.slope && tile.slope.meets(above.slope)) ||
493 | (below && below.slope.edges.right !== solid && below.slope.edges.top === interesting && slope === below.slope.slope && tile.slope.meets(below.slope))
494 | ))
495 | )) {
496 | tile.slope.ignormals.push(new SAT.Vector(1, 0));
497 | }
498 | };
499 |
500 | /**
501 | * Add some extra debug settings to a tilemap layer for debug rendering.
502 | *
503 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#addDebugSettings
504 | * @param {Phaser.TilemapLayer} layer - The tilemap layer.
505 | */
506 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.addDebugSettings = function (layer) {
507 | layer._mc.edgeMidpoint = new SAT.Vector();
508 | layer.debugSettings.slopeFill = 'rgba(255, 0, 255, 0.2)';
509 | layer.debugSettings.slopeEdgeStroke = 'rgba(255, 0, 255, 0.4)';
510 | layer.debugSettings.slopeLineStroke = 'rgba(255, 128, 128, 1)';
511 | layer.debugSettings.slopeCollidingEdgeStroke = 'rgba(255, 0, 255, 1)';
512 | layer.debugSettings.slopeCollidingEdgeStrokeWidth = 2;
513 | layer.debugSettings.slopeNormalStroke = 'rgba(0, 255, 255, 0.4)';
514 | layer.debugSettings.slopeNormalStrokeWidth = 1;
515 | layer.debugSettings.slopeCollidingNormalStroke = 'rgba(0, 255, 255, 1)';
516 | layer.debugSettings.slopeCollidingNormalStrokeWidth = 2;
517 | };
518 |
519 | /**
520 | * Resolve a tileset mapping constant from the given value.
521 | *
522 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#resolveMapping
523 | * @param {string} type - The value to resolve a mapping from.
524 | * @return {integer}
525 | */
526 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prototype.resolveMappingType = function (type) {
527 | if (parseInt(type) >= 0) {
528 | return type;
529 | }
530 |
531 | if (typeof type === 'string') {
532 | type = type.toUpperCase();
533 | }
534 |
535 | if (Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.hasOwnProperty(type) &&
536 | this.mappings[Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[type]]
537 | ) {
538 | return Phaser.Plugin.ArcadeSlopes.TileSlopeFactory[type];
539 | }
540 |
541 | console.warn('Unknown tileset mapping type \'' + type + '\'');
542 |
543 | return -1;
544 | };
545 |
546 | /**
547 | * Define a full square tile.
548 | *
549 | * @static
550 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createFull
551 | * @param {integer} type - The slope type.
552 | * @param {Phaser.Tile} tile - The tile object.
553 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
554 | */
555 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createFull = function (type, tile) {
556 | var polygon = new SAT.Box(
557 | new SAT.Vector(tile.worldX, tile.worldY),
558 | tile.width,
559 | tile.height
560 | ).toPolygon();
561 |
562 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon);
563 | };
564 |
565 | /**
566 | * Define a bottom half tile.
567 | *
568 | * @static
569 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfBottom
570 | * @param {integer} type - The slope type.
571 | * @param {Phaser.Tile} tile - The tile object.
572 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
573 | */
574 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottom = function (type, tile) {
575 | var halfHeight = tile.height / 2;
576 |
577 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
578 | new SAT.Vector(0, halfHeight),
579 | new SAT.Vector(tile.width, halfHeight),
580 | new SAT.Vector(tile.width, tile.height),
581 | new SAT.Vector(0, tile.height)
582 | ]);
583 |
584 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.top + tile.height / 2);
585 |
586 | var edges = {
587 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
588 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
589 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
590 | };
591 |
592 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges);
593 | };
594 |
595 | /**
596 | * Define a top half tile.
597 | *
598 | * @static
599 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfTop
600 | * @param {integer} type - The slope type.
601 | * @param {Phaser.Tile} tile - The tile object.
602 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
603 | */
604 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTop = function (type, tile) {
605 | var halfHeight = tile.height / 2;
606 |
607 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
608 | new SAT.Vector(0, 0),
609 | new SAT.Vector(tile.width, 0),
610 | new SAT.Vector(tile.width, halfHeight),
611 | new SAT.Vector(0, halfHeight)
612 | ]);
613 |
614 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.top + tile.height / 2);
615 |
616 | var edges = {
617 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
618 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
619 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
620 | };
621 |
622 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges);
623 | };
624 |
625 | /**
626 | * Define a left half tile.
627 | *
628 | * @static
629 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfLeft
630 | * @param {integer} type - The slope type.
631 | * @param {Phaser.Tile} tile - The tile object.
632 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
633 | */
634 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfLeft = function (type, tile) {
635 | var halfWidth = tile.width / 2;
636 |
637 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
638 | new SAT.Vector(0, 0),
639 | new SAT.Vector(halfWidth, 0),
640 | new SAT.Vector(halfWidth, tile.height),
641 | new SAT.Vector(0, tile.height)
642 | ]);
643 |
644 | var line = new Phaser.Line(tile.left + halfWidth, tile.top, tile.left + halfWidth, tile.bottom);
645 |
646 | var edges = {
647 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
648 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
649 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
650 | };
651 |
652 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges);
653 | };
654 |
655 | /**
656 | * Define a right half tile.
657 | *
658 | * @static
659 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfRight
660 | * @param {integer} type - The slope type.
661 | * @param {Phaser.Tile} tile - The tile object.
662 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
663 | */
664 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfRight = function (type, tile) {
665 | var halfWidth = tile.width / 2;
666 |
667 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
668 | new SAT.Vector(halfWidth, 0),
669 | new SAT.Vector(tile.width, 0),
670 | new SAT.Vector(tile.width, tile.height),
671 | new SAT.Vector(halfWidth, tile.height)
672 | ]);
673 |
674 | var line = new Phaser.Line(tile.left + halfWidth, tile.top, tile.left + halfWidth, tile.bottom);
675 |
676 | var edges = {
677 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
678 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
679 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
680 | };
681 |
682 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges);
683 | };
684 |
685 | /**
686 | * Define a 45 degree bottom left slope.
687 | *
688 | * @static
689 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfBottomLeft
690 | * @param {integer} type - The slope type.
691 | * @param {Phaser.Tile} tile - The tile object.
692 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
693 | */
694 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomLeft = function (type, tile) {
695 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
696 | new SAT.Vector(0, 0), // Top left
697 | new SAT.Vector(tile.width, tile.height), // Bottom right
698 | new SAT.Vector(0, tile.height) // Bottom left
699 | ]);
700 |
701 | var line = new Phaser.Line(tile.left, tile.top, tile.right, tile.bottom);
702 |
703 | var edges = {
704 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
705 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
706 | };
707 |
708 | var axis = new SAT.Vector(0.7071067811865475, -0.7071067811865475);
709 |
710 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
711 | };
712 |
713 | /**
714 | * Define a 45 degree bottom right slope.
715 | *
716 | * @static
717 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfBottomRight
718 | * @param {integer} type - The slope type.
719 | * @param {Phaser.Tile} tile - The tile object.
720 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
721 | */
722 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfBottomRight = function (type, tile) {
723 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
724 | new SAT.Vector(tile.width, 0), // Top right
725 | new SAT.Vector(tile.width, tile.height), // Bottom right
726 | new SAT.Vector(0, tile.height) // Bottom left
727 | ]);
728 |
729 | var line = new Phaser.Line(tile.left, tile.bottom, tile.right, tile.top);
730 |
731 | var edges = {
732 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
733 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
734 | };
735 |
736 | var axis = new SAT.Vector(-0.707106781186548, -0.707106781186548);
737 |
738 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
739 | };
740 |
741 | /**
742 | * Define a 45 degree top left slope.
743 | *
744 | * @static
745 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfTopLeft
746 | * @param {integer} type - The slope type.
747 | * @param {Phaser.Tile} tile - The tile object.
748 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
749 | */
750 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopLeft = function (type, tile) {
751 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
752 | new SAT.Vector(0, 0), // Top left
753 | new SAT.Vector(tile.width, 0), // Top right
754 | new SAT.Vector(0, tile.height) // Bottom right
755 | ]);
756 |
757 | var line = new Phaser.Line(tile.right, tile.top, tile.left, tile.bottom);
758 |
759 | var edges = {
760 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
761 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
762 | };
763 |
764 | var axis = new SAT.Vector(0.7071067811865475, 0.7071067811865475);
765 |
766 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
767 | };
768 |
769 | /**
770 | * Define a 45 degree top left slope.
771 | *
772 | * @static
773 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createHalfTopRight
774 | * @param {integer} type - The slope type.
775 | * @param {Phaser.Tile} tile - The tile object.
776 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
777 | */
778 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createHalfTopRight = function (type, tile) {
779 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
780 | new SAT.Vector(0, 0), // Top left
781 | new SAT.Vector(tile.width, 0), // Top right
782 | new SAT.Vector(tile.width, tile.height) // Bottom right
783 | ]);
784 |
785 | var line = new Phaser.Line(tile.right, tile.bottom, tile.left, tile.top);
786 |
787 | var edges = {
788 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
789 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
790 | };
791 |
792 | var axis = new SAT.Vector(-0.7071067811865475, 0.7071067811865475);
793 |
794 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
795 | };
796 |
797 | /**
798 | * Define a lower 22.5 degree bottom left slope.
799 | *
800 | * @static
801 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterBottomLeftLow
802 | * @param {integer} type - The slope type.
803 | * @param {Phaser.Tile} tile - The tile object.
804 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
805 | */
806 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftLow = function (type, tile) {
807 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
808 | new SAT.Vector(0, tile.height / 2), // Center left
809 | new SAT.Vector(tile.width, tile.height), // Bottom right
810 | new SAT.Vector(0, tile.height) // Bottom left
811 | ]);
812 |
813 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.bottom);
814 |
815 | var edges = {
816 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
817 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
818 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
819 | };
820 |
821 | var axis = new SAT.Vector(0.4472135954999579, -0.8944271909999159);
822 |
823 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
824 | };
825 |
826 | /**
827 | * Define an upper 22.5 degree bottom left slope.
828 | *
829 | * @static
830 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterBottomLeftHigh
831 | * @param {integer} type - The slope type.
832 | * @param {Phaser.Tile} tile - The tile object.
833 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
834 | */
835 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomLeftHigh = function (type, tile) {
836 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
837 | new SAT.Vector(0, 0), // Top left
838 | new SAT.Vector(tile.width, tile.height / 2), // Center right
839 | new SAT.Vector(tile.width, tile.height), // Bottom right
840 | new SAT.Vector(0, tile.height) // Bottom left
841 | ]);
842 |
843 | var line = new Phaser.Line(tile.left, tile.top, tile.right, tile.top + tile.height / 2);
844 |
845 | var edges = {
846 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
847 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
848 | };
849 |
850 | var axis = new SAT.Vector(0.4472135954999579, -0.8944271909999159);
851 |
852 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
853 | };
854 |
855 | /**
856 | * Define a lower 22.5 degree bottom right slope.
857 | *
858 | * @static
859 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterBottomRightLow
860 | * @param {integer} type - The slope type.
861 | * @param {Phaser.Tile} tile - The tile object.
862 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
863 | */
864 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightLow = function (type, tile) {
865 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
866 | new SAT.Vector(tile.width, tile.height / 2), // Center right
867 | new SAT.Vector(tile.width, tile.height), // Bottom right
868 | new SAT.Vector(0, tile.height) // Bottom left
869 | ]);
870 |
871 | var line = new Phaser.Line(tile.left, tile.bottom, tile.right, tile.top + tile.height / 2);
872 |
873 | var edges = {
874 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
875 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
876 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
877 | };
878 |
879 | var axis = new SAT.Vector(-0.4472135954999579, -0.8944271909999159);
880 |
881 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
882 | };
883 |
884 | /**
885 | * Define an upper 22.5 degree bottom right slope.
886 | *
887 | * @static
888 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterBottomRightHigh
889 | * @param {integer} type - The slope type.
890 | * @param {Phaser.Tile} tile - The tile object.
891 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
892 | */
893 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterBottomRightHigh = function (type, tile) {
894 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
895 | new SAT.Vector(0, tile.height / 2), // Center left
896 | new SAT.Vector(tile.width, 0), // Top right
897 | new SAT.Vector(tile.width, tile.height), // Bottom right
898 | new SAT.Vector(0, tile.height) // Bottom left
899 | ]);
900 |
901 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.top);
902 |
903 | var edges = {
904 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
905 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
906 | };
907 |
908 | var axis = new SAT.Vector(-0.4472135954999579, -0.8944271909999159);
909 |
910 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
911 | };
912 |
913 |
914 | /**
915 | * Define a lower 22.5 degree left bottom slope.
916 | *
917 | * @static
918 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterLeftBottomLow
919 | * @param {integer} type - The slope type.
920 | * @param {Phaser.Tile} tile - The tile object.
921 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
922 | */
923 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomLow = function (type, tile) {
924 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
925 | new SAT.Vector(0, 0), // Top left
926 | new SAT.Vector(tile.width / 2, 0), // Top center
927 | new SAT.Vector(tile.width, tile.height), // Bottom right
928 | new SAT.Vector(0, tile.height) // Bottom left
929 | ]);
930 |
931 | var line = new Phaser.Line(tile.left + tile.width / 2, tile.top, tile.right, tile.bottom);
932 |
933 | var edges = {
934 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
935 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
936 | };
937 |
938 | var axis = new SAT.Vector(0.8944271909999159, -0.4472135954999579);
939 |
940 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
941 | };
942 |
943 | /**
944 | * Define an upper 22.5 degree left bottom slope.
945 | *
946 | * @static
947 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterLeftBottomHigh
948 | * @param {integer} type - The slope type.
949 | * @param {Phaser.Tile} tile - The tile object.
950 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
951 | */
952 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftBottomHigh = function (type, tile) {
953 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
954 | new SAT.Vector(0, 0), // Top left
955 | new SAT.Vector(tile.width / 2, tile.height), // Bottom center
956 | new SAT.Vector(0, tile.height) // Bottom left
957 | ]);
958 |
959 | var line = new Phaser.Line(tile.left, tile.top, tile.left + tile.width / 2, tile.bottom);
960 |
961 | var edges = {
962 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
963 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
964 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
965 | };
966 |
967 | var axis = new SAT.Vector(0.8944271909999159, -0.4472135954999579);
968 |
969 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
970 | };
971 |
972 |
973 | /**
974 | * Define a lower 22.5 degree right bottom slope.
975 | *
976 | * @static
977 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterRightBottomLow
978 | * @param {integer} type - The slope type.
979 | * @param {Phaser.Tile} tile - The tile object.
980 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
981 | */
982 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomLow = function (type, tile) {
983 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
984 | new SAT.Vector(tile.width / 2, 0), // Top center
985 | new SAT.Vector(tile.width, 0), // Top right
986 | new SAT.Vector(tile.width, tile.height), // Bottom right
987 | new SAT.Vector(0, tile.height) // Bottom left
988 | ]);
989 |
990 | var line = new Phaser.Line(tile.left, tile.bottom, tile.left + tile.width / 2, tile.top);
991 |
992 | var edges = {
993 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
994 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
995 | };
996 |
997 | var axis = new SAT.Vector(-0.8944271909999159, -0.4472135954999579);
998 |
999 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1000 | };
1001 |
1002 |
1003 | /**
1004 | * Define an upper 22.5 degree right bottom slope.
1005 | *
1006 | * @static
1007 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterRightBottomHigh
1008 | * @param {integer} type - The slope type.
1009 | * @param {Phaser.Tile} tile - The tile object.
1010 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1011 | */
1012 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightBottomHigh = function (type, tile) {
1013 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1014 | new SAT.Vector(tile.width, 0), // Top right
1015 | new SAT.Vector(tile.width, tile.height), // Bottom right
1016 | new SAT.Vector(tile.width / 2, tile.height) // Bottom center
1017 | ]);
1018 |
1019 | var line = new Phaser.Line(tile.left + tile.width / 2, tile.bottom, tile.right, tile.top);
1020 |
1021 | var edges = {
1022 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1023 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1024 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1025 | };
1026 |
1027 | var axis = new SAT.Vector(-0.8944271909999159, -0.4472135954999579);
1028 |
1029 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1030 | };
1031 |
1032 | /**
1033 | * Define a lower 22.5 degree left top slope.
1034 | *
1035 | * @static
1036 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterLeftTopLow
1037 | * @param {integer} type - The slope type.
1038 | * @param {Phaser.Tile} tile - The tile object.
1039 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1040 | */
1041 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopLow = function (type, tile) {
1042 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1043 | new SAT.Vector(0, 0), // Top left
1044 | new SAT.Vector(tile.width / 2, 0), // Top center
1045 | new SAT.Vector(0, tile.height) // Bottom left
1046 | ]);
1047 |
1048 | var line = new Phaser.Line(tile.left, tile.bottom, tile.left + tile.width / 2, tile.top);
1049 |
1050 | var edges = {
1051 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1052 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1053 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1054 | };
1055 |
1056 | var axis = new SAT.Vector(0.8944271909999159, 0.4472135954999579);
1057 |
1058 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1059 | };
1060 |
1061 | /**
1062 | * Define an upper 22.5 degree left top slope.
1063 | *
1064 | * @static
1065 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterLeftTopHigh
1066 | * @param {integer} type - The slope type.
1067 | * @param {Phaser.Tile} tile - The tile object.
1068 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1069 | */
1070 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterLeftTopHigh = function (type, tile) {
1071 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1072 | new SAT.Vector(0, 0), // Top left
1073 | new SAT.Vector(tile.width, 0), // Top right
1074 | new SAT.Vector(tile.width / 2, tile.height), // Bottom center
1075 | new SAT.Vector(0, tile.height) // Bottom left
1076 | ]);
1077 |
1078 | var line = new Phaser.Line(tile.left + tile.width / 2, tile.bottom, tile.right, tile.top);
1079 |
1080 | var edges = {
1081 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1082 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1083 | };
1084 |
1085 | var axis = new SAT.Vector(0.8944271909999159, 0.4472135954999579);
1086 |
1087 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1088 | };
1089 |
1090 | /**
1091 | * Define a lower 22.5 degree right top slope.
1092 | *
1093 | * @static
1094 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterRightTopLow
1095 | * @param {integer} type - The slope type.
1096 | * @param {Phaser.Tile} tile - The tile object.
1097 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1098 | */
1099 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopLow = function (type, tile) {
1100 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1101 | new SAT.Vector(tile.width / 2, 0), // Top center
1102 | new SAT.Vector(tile.width, 0), // Top right
1103 | new SAT.Vector(tile.width, tile.height) // Bottom right
1104 | ]);
1105 |
1106 | var line = new Phaser.Line(tile.left + tile.width / 2, tile.top, tile.right, tile.bottom);
1107 |
1108 | var edges = {
1109 | top: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1110 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1111 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1112 | };
1113 |
1114 | var axis = new SAT.Vector(-0.8944271909999159, 0.4472135954999579);
1115 |
1116 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1117 | };
1118 |
1119 | /**
1120 | * Define an upper 22.5 degree right top slope.
1121 | *
1122 | * @static
1123 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterRightTopHigh
1124 | * @param {integer} type - The slope type.
1125 | * @param {Phaser.Tile} tile - The tile object.
1126 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1127 | */
1128 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterRightTopHigh = function (type, tile) {
1129 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1130 | new SAT.Vector(0, 0), // Top left
1131 | new SAT.Vector(tile.width, 0), // Top right
1132 | new SAT.Vector(tile.width, tile.height), // Bottom right
1133 | new SAT.Vector(tile.width / 2, tile.height) // Bottom center
1134 | ]);
1135 |
1136 | var line = new Phaser.Line(tile.left, tile.top, tile.left + tile.width / 2, tile.bottom);
1137 |
1138 | var edges = {
1139 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1140 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1141 | };
1142 |
1143 | var axis = new SAT.Vector(-0.8944271909999159, 0.4472135954999579);
1144 |
1145 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1146 | };
1147 |
1148 | /**
1149 | * Define a lower 22.5 degree top left slope.
1150 | *
1151 | * @static
1152 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterTopLeftLow
1153 | * @param {integer} type - The slope type.
1154 | * @param {Phaser.Tile} tile - The tile object.
1155 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1156 | */
1157 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftLow = function (type, tile) {
1158 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1159 | new SAT.Vector(0, 0), // Top left
1160 | new SAT.Vector(tile.width, 0), // Top right
1161 | new SAT.Vector(0, tile.height / 2) // Center left
1162 | ]);
1163 |
1164 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.top);
1165 |
1166 | var edges = {
1167 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1168 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1169 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1170 | };
1171 |
1172 | var axis = new SAT.Vector(0.4472135954999579, 0.8944271909999159);
1173 |
1174 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1175 | };
1176 |
1177 | /**
1178 | * Define an upper 22.5 degree top left slope.
1179 | *
1180 | * @static
1181 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterTopLeftHigh
1182 | * @param {integer} type - The slope type.
1183 | * @param {Phaser.Tile} tile - The tile object.
1184 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1185 | */
1186 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopLeftHigh = function (type, tile) {
1187 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1188 | new SAT.Vector(0, 0), // Top left
1189 | new SAT.Vector(tile.width, 0), // Top right
1190 | new SAT.Vector(tile.width, tile.height / 2), // Right center
1191 | new SAT.Vector(0, tile.height) // Bottom left
1192 | ]);
1193 |
1194 | var line = new Phaser.Line(tile.left, tile.bottom, tile.right, tile.top + tile.height / 2);
1195 |
1196 | var edges = {
1197 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1198 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1199 | };
1200 |
1201 | var axis = new SAT.Vector(0.4472135954999579, 0.8944271909999159);
1202 |
1203 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1204 | };
1205 |
1206 | /**
1207 | * Define a lower 22.5 degree top right slope.
1208 | *
1209 | * @static
1210 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterTopRightLow
1211 | * @param {integer} type - The slope type.
1212 | * @param {Phaser.Tile} tile - The tile object.
1213 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1214 | */
1215 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightLow = function (type, tile) {
1216 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1217 | new SAT.Vector(0, 0), // Top left
1218 | new SAT.Vector(tile.width, 0), // Top right
1219 | new SAT.Vector(tile.width, tile.height / 2) // Right center
1220 | ]);
1221 |
1222 | var line = new Phaser.Line(tile.left, tile.top, tile.right, tile.top + tile.height / 2);
1223 |
1224 | var edges = {
1225 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1226 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1227 | right: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1228 | };
1229 |
1230 | var axis = new SAT.Vector(-0.4472135954999579, 0.8944271909999159);
1231 |
1232 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1233 | };
1234 |
1235 | /**
1236 | * Define an upper 22.5 degree top right slope.
1237 | *
1238 | * @static
1239 | * @method Phaser.Plugin.ArcadeSlopes.TileSlopeFactory#createQuarterTopRightHigh
1240 | * @param {integer} type - The slope type.
1241 | * @param {Phaser.Tile} tile - The tile object.
1242 | * @return {Phaser.Plugin.ArcadeSlopes.TileSlope} - The defined tile slope.
1243 | */
1244 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.createQuarterTopRightHigh = function (type, tile) {
1245 | var polygon = new SAT.Polygon(new SAT.Vector(tile.worldX, tile.worldY), [
1246 | new SAT.Vector(0, 0), // Top left
1247 | new SAT.Vector(tile.width, 0), // Top right
1248 | new SAT.Vector(tile.width, tile.height), // Bottom right
1249 | new SAT.Vector(0, tile.height / 2) // Left center
1250 | ]);
1251 |
1252 | var line = new Phaser.Line(tile.left, tile.top + tile.height / 2, tile.right, tile.top + tile.height);
1253 |
1254 | var edges = {
1255 | bottom: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING,
1256 | left: Phaser.Plugin.ArcadeSlopes.TileSlope.INTERESTING
1257 | };
1258 |
1259 | var axis = new SAT.Vector(-0.4472135954999579, 0.8944271909999159);
1260 |
1261 | return new Phaser.Plugin.ArcadeSlopes.TileSlope(type, tile, polygon, line, edges, axis);
1262 | };
1263 |
1264 | /**
1265 | * Prepare a slope mapping offset from the given tile index.
1266 | *
1267 | * An offset is just the first tile index - 1. Returns 0 if an integer can't be
1268 | * parsed.
1269 | *
1270 | * @static
1271 | * @param {integer} index - A tile index.
1272 | * @return {integer}
1273 | */
1274 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset = function (index) {
1275 | var offset = parseInt(index);
1276 |
1277 | offset = !isNaN(offset) && typeof offset === 'number' ? offset - 1 : 0;
1278 |
1279 | return offset;
1280 | };
1281 |
1282 | /**
1283 | * Create a tile slope mapping for the Arcade Slopes tileset.
1284 | *
1285 | * @static
1286 | * @param {integer} index - An optional first tile index (firstgid).
1287 | * @return {object}
1288 | */
1289 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapArcadeSlopes = function (index) {
1290 | offset = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(index);
1291 |
1292 | var mapping = {};
1293 |
1294 | mapping[offset + 1] = 'FULL';
1295 | mapping[offset + 2] = 'HALF_TOP';
1296 | mapping[offset + 3] = 'HALF_BOTTOM';
1297 | mapping[offset + 4] = 'HALF_LEFT';
1298 | mapping[offset + 5] = 'HALF_RIGHT';
1299 | mapping[offset + 6] = 'HALF_BOTTOM_LEFT';
1300 | mapping[offset + 7] = 'HALF_BOTTOM_RIGHT';
1301 | mapping[offset + 8] = 'HALF_TOP_LEFT';
1302 | mapping[offset + 9] = 'HALF_TOP_RIGHT';
1303 | mapping[offset + 10] = 'QUARTER_TOP_LEFT_HIGH';
1304 | mapping[offset + 11] = 'QUARTER_TOP_LEFT_LOW';
1305 | mapping[offset + 12] = 'QUARTER_TOP_RIGHT_LOW';
1306 | mapping[offset + 13] = 'QUARTER_TOP_RIGHT_HIGH';
1307 | mapping[offset + 14] = 'QUARTER_BOTTOM_LEFT_HIGH';
1308 | mapping[offset + 15] = 'QUARTER_BOTTOM_LEFT_LOW';
1309 | mapping[offset + 16] = 'QUARTER_BOTTOM_RIGHT_LOW';
1310 | mapping[offset + 17] = 'QUARTER_BOTTOM_RIGHT_HIGH';
1311 | mapping[offset + 18] = 'QUARTER_LEFT_BOTTOM_HIGH';
1312 | mapping[offset + 19] = 'QUARTER_RIGHT_BOTTOM_HIGH';
1313 | mapping[offset + 20] = 'QUARTER_LEFT_TOP_HIGH';
1314 | mapping[offset + 21] = 'QUARTER_RIGHT_TOP_HIGH';
1315 | mapping[offset + 35] = 'QUARTER_LEFT_BOTTOM_LOW';
1316 | mapping[offset + 36] = 'QUARTER_RIGHT_BOTTOM_LOW';
1317 | mapping[offset + 37] = 'QUARTER_LEFT_TOP_LOW';
1318 | mapping[offset + 38] = 'QUARTER_RIGHT_TOP_LOW';
1319 |
1320 | return mapping;
1321 | };
1322 |
1323 | /**
1324 | * Create a tile slope mapping for the Ninja Physics tileset.
1325 | *
1326 | * @static
1327 | * @param {integer} index - An optional first tile index (firstgid).
1328 | * @return {object}
1329 | */
1330 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.mapNinjaPhysics = function (index) {
1331 | offset = Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.prepareOffset(index);
1332 |
1333 | var mapping = {};
1334 |
1335 | mapping[offset + 2] = 'FULL';
1336 | mapping[offset + 3] = 'HALF_BOTTOM_LEFT';
1337 | mapping[offset + 4] = 'HALF_BOTTOM_RIGHT';
1338 | mapping[offset + 6] = 'HALF_TOP_LEFT';
1339 | mapping[offset + 5] = 'HALF_TOP_RIGHT';
1340 | mapping[offset + 15] = 'QUARTER_BOTTOM_LEFT_LOW';
1341 | mapping[offset + 16] = 'QUARTER_BOTTOM_RIGHT_LOW';
1342 | mapping[offset + 17] = 'QUARTER_TOP_RIGHT_LOW';
1343 | mapping[offset + 18] = 'QUARTER_TOP_LEFT_LOW';
1344 | mapping[offset + 19] = 'QUARTER_BOTTOM_LEFT_HIGH';
1345 | mapping[offset + 20] = 'QUARTER_BOTTOM_RIGHT_HIGH';
1346 | mapping[offset + 21] = 'QUARTER_TOP_RIGHT_HIGH';
1347 | mapping[offset + 22] = 'QUARTER_TOP_LEFT_HIGH';
1348 | mapping[offset + 23] = 'QUARTER_LEFT_BOTTOM_HIGH';
1349 | mapping[offset + 24] = 'QUARTER_RIGHT_BOTTOM_HIGH';
1350 | mapping[offset + 25] = 'QUARTER_RIGHT_TOP_LOW';
1351 | mapping[offset + 26] = 'QUARTER_LEFT_TOP_LOW';
1352 | mapping[offset + 27] = 'QUARTER_LEFT_BOTTOM_LOW';
1353 | mapping[offset + 28] = 'QUARTER_RIGHT_BOTTOM_LOW';
1354 | mapping[offset + 29] = 'QUARTER_RIGHT_TOP_HIGH';
1355 | mapping[offset + 30] = 'QUARTER_LEFT_TOP_HIGH';
1356 | mapping[offset + 31] = 'HALF_BOTTOM';
1357 | mapping[offset + 32] = 'HALF_RIGHT';
1358 | mapping[offset + 33] = 'HALF_TOP';
1359 | mapping[offset + 34] = 'HALF_LEFT';
1360 |
1361 | return mapping;
1362 | };
1363 |
1364 | /**
1365 | * The Ninja Physics tileset mapping.
1366 | *
1367 | * @constant
1368 | * @type {integer}
1369 | */
1370 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.ARCADESLOPES = 1;
1371 |
1372 | /**
1373 | * The Ninja Physics tileset mapping.
1374 | *
1375 | * @constant
1376 | * @type {integer}
1377 | */
1378 | Phaser.Plugin.ArcadeSlopes.TileSlopeFactory.NINJA = 2;
1379 |
--------------------------------------------------------------------------------
/typescript/phaser-arcade-slopes.d.ts:
--------------------------------------------------------------------------------
1 | declare module Phaser {
2 | interface Game {
3 | slopes:Phaser.Plugin.ArcadeSlopes.Facade;
4 | }
5 |
6 | module Physics.Arcade {
7 | interface Body {
8 | slopes:Phaser.Plugin.ArcadeSlopes.BodySlopes;
9 | }
10 | }
11 |
12 | module Plugin {
13 | class ArcadeSlopes extends Phaser.Plugin {
14 | constructor(game:Phaser.Game, parent:any, defaultSolver:number);
15 |
16 | solvers:Object;
17 | facade:Phaser.Plugin.ArcadeSlopes.Facade;
18 |
19 | static VERSION:string;
20 | static SAT:string;
21 | }
22 |
23 | module ArcadeSlopes {
24 |
25 | class Facade {
26 | constructor(factory:Phaser.Plugin.ArcadeSlopes.TileSlopeFactory, solvers:Object, defaultSolver:number);
27 |
28 | factory:Phaser.Plugin.ArcadeSlopes.TileSlopeFactory;
29 | solvers:Object;
30 | defaultSover:string;
31 | plugin:Phaser.Plugin.ArcadeSlopes;
32 |
33 | enable(obj:Phaser.Sprite | Phaser.Group):void;
34 | enableBody(body:Phaser.Physics.Arcade.Body):void;
35 | convertTilemap(map:Phaser.Tilemap, layer:number | string | Phaser.TilemapLayer, slopeMap:string | Object, index:number):Phaser.Tilemap;
36 | convertTilemapLayer(layer:Phaser.TilemapLayer, slopeMap:string | Object, index:number):Phaser.TilemapLayer;
37 | collide(i:number, body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, tilemapLayer:Phaser.TilemapLayer, overlapOnly:boolean):boolean;
38 | }
39 |
40 | class Overrides {
41 | static collideSpriteVsTile(i:number, sprite:Phaser.Sprite, tile:Phaser.Tile, tilemapLayer:Phaser.TilemapLayer, collideCallback:any, processCallback:any, callbackContext:Object, overlapOnly:boolean):boolean;
42 | static collideSpriteVsTiles(sprite:Phaser.Sprite, tiles:Phaser.Tile[], tilemapLayer:Phaser.TilemapLayer, collideCallback:any, processCallback:any, callbackContext:Object, overlapOnly:boolean):boolean;
43 | static collideSpriteVsTilemaplayer(sprite:Phaser.Sprite, tilemapLayer:Phaser.TilemapLayer, collideCallback:any, processCallback:any, callbackContext:Object, overlapOnly:boolean):boolean;
44 | static getTileTopLeft(layer:number, x:number, y:number):Phaser.Tile;
45 | static getTileTopRight(layer:number, x:number, y:number):Phaser.Tile;
46 | static getTileBottomLeft(layer:number, x:number, y:number):Phaser.Tile;
47 | static getTileBottomRight(layer:number, x:number, y:number):Phaser.Tile;
48 | static getCollisionOffsetX():number;
49 | static getCollisionOffsetY():number;
50 | static renderDebug():void;
51 | }
52 |
53 | class SatRestainer {
54 | restraints:Object;
55 |
56 | restrain(solver:Phaser.Plugin.ArcadeSlopes.SatSolver, body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, response:SAT.Response):boolean;
57 | resolveOverlaps(direction:string):Object;
58 | prepareRestraints(restraints:Object):Object;
59 | setDefaultRestraints():void;
60 | intersectArrays(a:any[], b:any[]):any[];
61 | resolve();
62 |
63 | static topVerticies:string[];
64 | static bottomVerticies:string[];
65 | static leftVerticies:string[];
66 | static rightVerticies:string[];
67 | static topLeftVerticies:string[];
68 | static topRightVerticies:string[];
69 | static bottomLeftVerticies:string[];
70 | static bottomRightVerticies:string[];
71 | }
72 |
73 | class SatSolver {
74 | constructor(options:Phaser.Plugin.ArcadeSlopes.SatSolverOptions);
75 |
76 | options:Phaser.Plugin.ArcadeSlopes.SatSolverOptions;
77 | restrainers:Phaser.Plugin.ArcadeSlopes.SatRestainer;
78 |
79 | static prepareResponse(response:SAT.Response):SAT.Response;
80 | static copyResponse(a:SAT.Response, b:SAT.Response):SAT.Response;
81 | static minimumOffsetX(vector:SAT.Vector):number;
82 | static minimumOffsetY(vector:SAT.Vector):number;
83 | static movingAgainstY(body:Phaser.Physics.Arcade.Body, response:SAT.Response):boolean;
84 | static shouldPreferY(body:Phaser.Physics.Arcade.Body, response:SAT.Response):boolean;
85 | static isSeparatingAxis(a:SAT.Polygon, b:SAT.Polygon, axis:SAT.Vector, response:SAT.Response):boolean;
86 | separate(body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, response:SAT.Response, force:boolean):boolean;
87 | applyVelocity(body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, response:SAT.Response):void;
88 | updateValues(body:Phaser.Physics.Arcade.Body):void;
89 | updateFlags(body:Phaser.Physics.Arcade.Body, response:SAT.Response):void;
90 | pull(body:Phaser.Physics.Arcade.Body, response:SAT.Response):boolean;
91 | shouldCollide(body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile):boolean;
92 | static flattenPointsOn(points:SAT.Vector[], normal:SAT.Vector, result:number[]):void;
93 | isSeparatingAxis(a:SAT.Polygon, b:SAT.Polygon, axis:SAT.Vector, response:SAT.Response):boolean;
94 | testPolygonPolygon(a:SAT.Polygon, b:SAT.Polygon, response:SAT.Response, velocity:SAT.Vector, ignore:SAT.Vector[]):boolean;
95 | collide(i:number, body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, tilemapLayer:Phaser.TilemapLayer, overlapOnly:boolean):boolean;
96 | shouldSeparate(i:number, body:Phaser.Physics.Arcade.Body, tile:Phaser.Tile, response:SAT.Response):boolean;
97 | debug(position:Phaser.Point, response:SAT.Response):void;
98 | }
99 |
100 | interface SatSolverOptions {
101 | debug:boolean;
102 | preferY:boolean;
103 | restrain:boolean;
104 | }
105 |
106 | class TileSlope {
107 | constructor(type:number, tile:Phaser.Tile, polygon:SAT.Polygon, line:Phaser.Line, edges:Object, axis:SAT.Vector, ignormals?:SAT.Vector[]);
108 | meets(slope:TileSlope);
109 | type:number;
110 | tile:Phaser.Tile;
111 | polygon:SAT.Polygon;
112 | line:Phaser.Tile;
113 | edges:Object;
114 | axis:SAT.Vector;
115 | ignormals:SAT.Vector[];
116 | solver:string;
117 | friction:Phaser.Point;
118 | slope:number;
119 | typeName:string;
120 | typeNames:Object;
121 |
122 | resolveType(type:string | number):number;
123 | resolveTypeName(type:number):number;
124 |
125 | static EMPTY:number;
126 | static SOLID:number;
127 | static INTERESTING:number;
128 | static UNKNOWN:number;
129 | static FULL:number;
130 | static HALF_BOTTOM:number;
131 | static HALF_TOP:number;
132 | static HALF_LEFT:number;
133 | static HALF_RIGHT:number;
134 | static HALF_BOTTOM_LEFT:number;
135 | static HALF_BOTTOM_RIGHT:number;
136 | static HALF_TOP_LEFT:number;
137 | static HALF_TOP_RIGHT:number;
138 | static QUARTER_BOTTOM_LEFT_LOW:number;
139 | static QUARTER_BOTTOM_LEFT_HIGH:number;
140 | static QUARTER_BOTTOM_RIGHT_LOW:number;
141 | static QUARTER_BOTTOM_RIGHT_HIGH:number;
142 | static QUARTER_LEFT_BOTTOM_LOW:number;
143 | static QUARTER_LEFT_BOTTOM_HIGH:number;
144 | static QUARTER_RIGHT_BOTTOM_LOW:number;
145 | static QUARTER_RIGHT_BOTTOM_HIGH:number;
146 | static QUARTER_LEFT_TOP_LOW:number;
147 | static QUARTER_LEFT_TOP_HIGH:number;
148 | static QUARTER_RIGHT_TOP_LOW:number;
149 | static QUARTER_RIGHT_TOP_HIGH:number;
150 | static QUARTER_TOP_LEFT_LOW:number;
151 | static QUARTER_TOP_LEFT_HIGH:number;
152 | static QUARTER_TOP_RIGHT_LOW:number;
153 | static QUARTER_TOP_RIGHT_HIGH:number;
154 | }
155 |
156 | class TileSlopeFactory {
157 | definitions:object;
158 | mappings:object;
159 |
160 | define(type:number, definition:any):void;
161 | create(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
162 | convertTilemap(tilemap:Phaser.Tilemap, layer:number | string | Phaser.TilemapLayer, slopeMap:string | Object, index:number):Phaser.Tilemap;
163 | convertTilemapLayer(layer:Phaser.TilemapLayer, slopeMap:string | Object, index:number):Phaser.TilemapLayer;
164 | calculateEdges(layer:Phaser.TilemapLayer):void;
165 | compareEdges(firstEdge:number, secondEdge:number):number;
166 | flagInternalVerticies(firstTile:Phaser.Tile, secondTile:Phaser.Tile):void;
167 | addDebugSettings(layer:Phaser.TilemapLayer):void;
168 | resolveMappingType(type:string):number;
169 |
170 | static createFull(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
171 | static createHalfBottom(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
172 | static createHalfTop(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
173 | static createHalfLeft(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
174 | static createHalfRight(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
175 | static createHalfBottomLeft(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
176 | static createHalfBottomRight(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
177 | static createHalfTopLeft(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
178 | static createHalfTopRight(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
179 | static createQuarterBottomLeftLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
180 | static createQuarterBottomLeftHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
181 | static createQuarterBottomRightLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
182 | static createQuarterBottomRightHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
183 | static createQuarterLeftBottomLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
184 | static createQuarterLeftBottomHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
185 | static createQuarterRightBottomLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
186 | static createQuarterRightBottomHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
187 | static createQuarterLeftTopLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
188 | static createQuarterLeftTopHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
189 | static createQuarterRightTopLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
190 | static createQuarterRightTopHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
191 | static createQuarterTopLeftLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
192 | static createQuarterTopLeftHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
193 | static createQuarterTopRightLow(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
194 | static createQuarterTopRightHigh(type:number, tile:Phaser.Tile):Phaser.Plugin.ArcadeSlopes.TileSlope;
195 |
196 | static prepareOffset(index:number):number;
197 | static mapArcadeSlopes(index:number):Object;
198 | static mapNinjaPhysics(offset:number):Object;
199 |
200 | static ARCADESLOPES:number;
201 | static NINJA:number;
202 | }
203 |
204 | interface BodySlopes {
205 | debug:boolean;
206 | friction:Phaser.Point;
207 | preferY:boolean;
208 | pullUp:number;
209 | pullDown:number;
210 | pullLeft:number;
211 | pullRight:number;
212 | pullTopLeft:number;
213 | pullTopRight:number;
214 | pullBottomLeft:number;
215 | pullBottomRight:number;
216 | sat:Phaser.Plugin.ArcadeSlopes.BodySlopesSat;
217 | skipFriction:boolean;
218 | tile:Phaser.Tile;
219 | velocity:SAT.Vector;
220 | }
221 |
222 | interface BodySlopesSat {
223 | response:SAT.Response;
224 | }
225 | }
226 | }
227 | }
228 |
229 | declare module SAT {
230 | interface Response {
231 | a:SAT.Polygon;
232 | b:SAT.Polygon;
233 | aInB:boolean;
234 | bInA:boolean;
235 | overlap:number;
236 | overlapV:SAT.Vector;
237 | overlapN:SAT.Vector;
238 |
239 | clear();
240 |
241 | }
242 |
243 | interface Vector {
244 | x:number;
245 | y:number;
246 |
247 | copy(other:SAT.Vector):SAT.Vector;
248 | clone():SAT.Vector;
249 | perp():SAT.Vector;
250 | rotate(angle:number):SAT.Vector;
251 | reverse():SAT.Vector;
252 | normalize():SAT.Vector;
253 | add(othre:SAT.Vector):SAT.Vector;
254 | sub(other:SAT.Vector):SAT.Vector;
255 | scale(x:number, y:number):SAT.Vector;
256 | project(other:SAT.Vector):SAT.Vector;
257 | projectN(other:SAT.Vector):SAT.Vector;
258 | reflect(axis:SAT.Vector):SAT.Vector;
259 | reflectN(axis:SAT.Vector):SAT.Vector;
260 | dot(other:SAT.Vector):SAT.Vector;
261 | len2():SAT.Vector;
262 | len():SAT.Vector;
263 | }
264 |
265 | interface Circle {
266 | pos:SAT.Vector;
267 | r:number;
268 |
269 | getAABB():SAT.Box;
270 | }
271 |
272 | interface Polygon {
273 | setPoints(points:SAT.Vector[]):SAT.Polygon;
274 | setAngle(angle:number):SAT.Polygon;
275 | setOffset(offset:SAT.Vector):SAT.Polygon;
276 | rotate(angle:number):SAT.Vector;
277 | translate(x:number, y:number):SAT.Polygon;
278 | _recalc():SAT.Polygon;
279 | getAABB():SAT.Box;
280 | }
281 |
282 | interface Box {
283 | pos:SAT.Vector;
284 | w:number;
285 | h:number;
286 | toPolygon:SAT.Polygon;
287 | }
288 |
289 | }
290 |
291 | declare class SAT {
292 | static T_VECTORS:SAT.Vector[];
293 | static T_ARRAYS:number[];
294 | static T_RESPONSE:SAT.Response[];
295 | static T_POLYGONS:SAT.Polygon[];
296 | static UNIT_SQUARE:SAT.Polygon;
297 | static LEFT_VORONOI_REGION:number;
298 | static MIDDLE_VORONOI_REGION:number;
299 | static RIGHT_VORONOI_REGION:number;
300 |
301 | static flattenPointsOn(points:SAT.Vector[], normal:SAT.Vector, result:number[]):void;
302 | static isSeparatingAxis(aPos:SAT.Vector, bPos:SAT.Vector, aPoints:SAT.Vector[], bPoints:SAT.Vector[], axis:SAT.Vector, response:SAT.Response):boolean;
303 | static voronoiRegion(line:SAT.Vector, point:SAT.Vector):number;
304 | static pointInCircle(p:SAT.Vector, c:SAT.Circle):boolean;
305 | static pointInPolygon(p:SAT.Vector, poly:SAT.Polygon):boolean;
306 | static testCircleCircle(a:SAT.Circle, b:SAT.Circle, response:SAT.Response):boolean;
307 | static testPolygonCircle(polygon:SAT.Polygon, circle:SAT.Circle, response:SAT.Response):boolean;
308 | static testCirclePolygon(circle:SAT.Circle, polygon:SAT.Polygon, response:SAT.Response):boolean;
309 | static testPolygonPolygon(a:SAT.Polygon, b:SAT.Polygon, response:SAT.Response):boolean;
310 | }
311 |
--------------------------------------------------------------------------------