├── .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 | ![Phaser Arcade Slopes](screenshot.gif) 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 | --------------------------------------------------------------------------------