diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..baa91a3 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e717f5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..0cd291c --- /dev/null +++ b/.jshintrc @@ -0,0 +1,21 @@ +{ + "node": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "white": true +} diff --git a/GruntFile.js b/GruntFile.js index 064263b..31c3e54 100644 --- a/GruntFile.js +++ b/GruntFile.js @@ -1,65 +1,97 @@ -module.exports = function (grunt) { - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-connect'); - grunt.loadNpmTasks('grunt-open'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-copy'); +// Generated on 2014-03-28 using generator-phaser-official 0.0.8-rc-2 +'use strict'; +var config = require('./config.json'); +var _ = require('underscore'); +_.str = require('underscore.string'); - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - connect: { - server: { - options: { - port: 8080, - base: './deploy' - } - } - }, - concat: { - dist: { - src: [ "src/lib/phaser.js", - "src/game/*.js" - ], - //dest: 'src/bin/<%= pkg.name %>.js' - dest: 'deploy/js/LudumDare29.js' - } - }, - watch: { - files: 'src/**/*.js', - tasks: ['concat'] - }, - open: { - dev: { - path: 'http://localhost:8080/index.html' - } - }, - uglify: { - options: { - compress: { - drop_console: true - } - }, - my_target: { - files: { - 'deploy/js/<%= pkg.name %>.min.js' : ['src/bin/<%= pkg.name %>.js'] - } - } - }, - copy: { - main: { - files: [ - {expand: true, src: ['assets/*'], dest: 'deploy/'}, - // TODO THIS IS THE RELEASE VERSION - //{expand: true, flatten: true, src: ['src/index.html'], dest: 'deploy/'} - {expand: true, flatten: true, src: ['src/dev/index.html'], dest: 'deploy/'} - ] - } - } - }); +// Mix in non-conflict functions to Underscore namespace if you want +_.mixin(_.str.exports()); - grunt.registerTask('default', ['concat', 'uglify', 'copy']); - grunt.registerTask('embiggened', ['concat', 'copy']); - grunt.registerTask('open-watch', ['concat', 'connect', 'open', 'watch']); +var LIVERELOAD_PORT = 35729; +var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); +var mountFolder = function (connect, dir) { + return connect.static(require('path').resolve(dir)); +}; + +module.exports = function (grunt) { + // load all grunt tasks + require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); + + grunt.initConfig({ + watch: { + scripts: { + files: [ + 'game/**/*.js', + '!game/main.js' + ], + options: { + spawn: false, + livereload: LIVERELOAD_PORT + }, + tasks: ['build'] + } + }, + connect: { + options: { + port: 9000, + // change this to '0.0.0.0' to access the server from outside + hostname: 'localhost' + }, + livereload: { + options: { + middleware: function (connect) { + return [ + lrSnippet, + mountFolder(connect, 'dist') + ]; + } + } + } + }, + open: { + server: { + path: 'http://localhost:9000' + } + }, + copy: { + dist: { + files: [ + // includes files within path and its sub-directories + { expand: true, src: ['assets/**'], dest: 'dist/' }, + { expand: true, flatten: true, src: ['game/plugins/*.js'], dest: 'dist/js/plugins/' }, + { expand: true, flatten: true, src: ['bower_components/**/build/*.js'], dest: 'dist/js/' }, + { expand: true, src: ['css/**'], dest: 'dist/' }, + { expand: true, src: ['index.html'], dest: 'dist/' } + ] + } + }, + browserify: { + build: { + src: ['game/main.js'], + dest: 'dist/js/game.js' + } + } + }); + + grunt.registerTask('build', ['buildBootstrapper', 'browserify','copy']); + grunt.registerTask('serve', ['build', 'connect:livereload', 'open', 'watch']); + grunt.registerTask('default', ['serve']); + grunt.registerTask('prod', ['build', 'copy']); -} \ No newline at end of file + grunt.registerTask('buildBootstrapper', 'builds the bootstrapper file correctly', function() { + var stateFiles = grunt.file.expand('game/states/*.js'); + var gameStates = []; + var statePattern = new RegExp(/(\w+).js$/); + stateFiles.forEach(function(file) { + var state = file.match(statePattern)[1]; + if (!!state) { + gameStates.push({shortName: state, stateName: _.capitalize(state) + 'State'}); + } + }); + config.gameStates = gameStates; + console.log(config); + var bootstrapper = grunt.file.read('templates/_main.js.tpl'); + bootstrapper = grunt.template.process(bootstrapper,{data: config}); + grunt.file.write('game/main.js', bootstrapper); + }); +}; \ No newline at end of file diff --git a/assets/preloader.gif b/assets/preloader.gif new file mode 100644 index 0000000..d84f653 Binary files /dev/null and b/assets/preloader.gif differ diff --git a/assets/yeoman-logo.png b/assets/yeoman-logo.png new file mode 100644 index 0000000..a9396d9 Binary files /dev/null and b/assets/yeoman-logo.png differ diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..95a8b57 --- /dev/null +++ b/bower.json @@ -0,0 +1,7 @@ +{ + "name": "ludumdare29", + "version": "0.0.0", + "dependencies": { + "phaser-official": "2.0.2" + } +} \ No newline at end of file diff --git a/bower_components/phaser-official/.bower.json b/bower_components/phaser-official/.bower.json new file mode 100644 index 0000000..d70f2dd --- /dev/null +++ b/bower_components/phaser-official/.bower.json @@ -0,0 +1,46 @@ +{ + "name": "phaser", + "version": "2.0.2", + "homepage": "http://phaser.io", + "authors": [ + "photonstorm " + ], + "description": "A fun, free and fast 2D game framework for making HTML5 games for desktop and mobile, supporting Canvas and WebGL.", + "main": "build/phaser.js", + "keywords": [ + "html5", + "game", + "games", + "framework", + "canvas", + "WebGL", + "tilemaps", + "physics", + "sprites", + "fonts", + "images", + "audio", + "Web", + "Audio", + "touch", + "input", + "mobile" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "_release": "2.0.2", + "_resolution": { + "type": "version", + "tag": "v2.0.2", + "commit": "5b73bb21bb74c2b3d4b9dd7a4518cec40e019d58" + }, + "_source": "git://github.com/photonstorm/phaser.git", + "_target": "2.0.2", + "_originalSource": "phaser-official" +} \ No newline at end of file diff --git a/bower_components/phaser-official/CONTRIBUTING.md b/bower_components/phaser-official/CONTRIBUTING.md new file mode 100644 index 0000000..9f3ca2c --- /dev/null +++ b/bower_components/phaser-official/CONTRIBUTING.md @@ -0,0 +1,74 @@ +#How to contribute + +It's important to us that you feel you can contribute towards the evolution of Phaser. This can take many forms: from helping to fix bugs or improve the docs, to adding in new features to the source. This guide should help you in making that process as smooth as possible. + + +##Reporting issues + +[GitHub Issues][0] is the place to report bugs you may have found in either the core library or any of the examples that are part of the repository. When submitting a bug please do the following: + +1. **Search for existing issues.** Your bug may have already been fixed or addressed in a development branch version of Phaser, so be sure to search the issues first before putting in a duplicate issue. + +2. **Not sure if it's a bug?.** Then please ask on the forum. If something is blatantly wrong then post it to github. But if you feel it might just be because you're not sure of expected behaviour, then it might save us time, and get you a response faster, if you post it to the Phaser forum instead. + +3. **Create an isolated and reproducible test case.** If you are reporting a bug, make sure you also have a minimal, runnable, code example that reproduces the problem you have. + +4. **Include a live example.** After narrowing your code down to only the problem areas, make use of [jsFiddle][1], [jsBin][2], or a link to your live site so that we can view a live example of the problem. + +5. **Share as much information as possible.** Include browser version affected, your OS, version of the library, steps to reproduce, etc. "X isn't working!!!1!" will probably just be closed. + + +##Pixi and Phaser + +It's important to understand that internally Phaser uses [Pixi.js](https://github.com/GoodBoyDigital/pixi.js/) for all rendering. It's possible you may find a bug that is generated on the Pixi level rather than Phaser. You're welcome to still report the issue of course, but if you get a reply saying we think it might be a Pixi issue this is what we're talking about :) + + +##Support Forum + +We have a very active [Phaser Support Forum](http://www.html5gamedevs.com/forum/14-phaser/). If you need general support, or are struggling to understand how to do something or need your code checked over, then we would urge you to post it to our forum. There are a lot of friendly devs in there who can help, as well as the core Phaser and Pixi teams, so it's a great place to get support from. You're welcome to report bugs directly on GitHub, but for general support we'd always recommend using the forum first. + + +##Dev vs. Master + +The dev branch of Phaser is our 'current working' version. It is always ahead of the master branch in terms of features and fixes. However it's also bleeding-edge and experimental and we cannot and do not guarantee it will compile or work for you. Very often we have to break things for a few days while we rebuild and patch. So by all means please export the dev branch and contribute towards it, indeed that is where all Pull Requests should be sent, but do so understanding the API may change beneath you. + + +##Making Changes + +To take advantage of our grunt build script and jshint config it will be easiest for you if you have node.js and grunt installed locally. + +You can download node.js from [nodejs.org][3]. After it has been installed open a console and run `npm i -g grunt -cli` to install the global `grunt` executable. + +After that you can clone the repository and run `npm i` inside the cloned folder. This will install dependencies necessary for building the project. Once that is ready, +make your changes and submit a Pull Request: + +- **Send Pull Requests to the `dev` branch.** All Pull Requests must be sent to the `dev` branch, `master` is the latest release and PRs to that branch will be closed. + +- **Ensure changes are jshint validated.** Our JSHint configuration file is provided in the repository and you should check against it before submitting. + +- **Never commit new builds.** When making a code change you should always run `grunt` which will rebuild the project so you can test, *however* please do not commit these new builds or your PR will be closed. Builds by default are placed in the `dist` folder, to keep them separate from the `build` folder releases. + +- **Only commit relevant changes.** Don't include changes that are not directly relevant to the fix you are making. The more focused a PR is, the faster it will get attention and be merged. Extra files changing only whitespace or trash files will likely get your PR closed. + + +##I don't really like git / node.js, but I can fix this bug + +That is fine too. While Pull Requests are the best thing in the world for us, they are not the only way to help. You're welcome to post fixes to our forum or even just email them to us. All we ask is that you still adhere to the guidelines presented here re: JSHint, etc. + + +##Code Style Guide + +- Use 4 spaces for tabs, never tab characters. + +- No trailing whitespace, blank lines should have no whitespace. + +- Always favor strict equals `===` unless you *need* to use type coercion. + +- Follow conventions already in the code, and listen to jshint. Our config is set-up for a reason. + +Thanks to Chad for creating the original Pixi.js Contributing file which we adapted for Phaser. + +[0]: https://github.com/photonstorm/phaser/issues +[1]: http://jsfiddle.net +[2]: http://jsbin.com/ +[3]: http://nodejs.org diff --git a/bower_components/phaser-official/Gruntfile.js b/bower_components/phaser-official/Gruntfile.js new file mode 100644 index 0000000..8ab1841 --- /dev/null +++ b/bower_components/phaser-official/Gruntfile.js @@ -0,0 +1,423 @@ +module.exports = function (grunt) { + + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-text-replace'); + grunt.loadTasks('./tasks'); + + grunt.initConfig({ + + pkg: grunt.file.readJSON('package.json'), + + banner: '/**\n' + +'* @author Richard Davey \n' + +'* @copyright 2014 Photon Storm Ltd.\n' + +'* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}\n' + +'*\n' + +'* @overview\n' + +'*\n' + +'* Phaser - http://www.phaser.io\n' + +'*\n' + +'* v<%= pkg.version %> "<%= pkg.release %>" - Built: <%= grunt.template.today() %>\n' + +'*\n' + +'* By Richard Davey http://www.photonstorm.com @photonstorm\n' + +'*\n' + +'* Phaser is a fun, free and fast 2D game framework for making HTML5 games \n' + +'* for desktop and mobile web browsers, supporting Canvas and WebGL.\n' + +'*\n' + +'* Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23\n' + +'* Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe\n' + +'* Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com\n' + +'*\n' + +'* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser\n' + +'* and my love of framework development can be traced.\n' + +'*\n' + +'* Follow development at http://phaser.io and on our forum\n' + +'*\n' + +'* "If you want your children to be intelligent, read them fairy tales."\n' + +'* "If you want them to be more intelligent, read them more fairy tales."\n' + +'* -- Albert Einstein\n' + +'*/\n', + + release_dir: 'build', + compile_dir: 'dist', + + pixi: [ + 'src/pixi/Intro.js', + 'src/pixi/Pixi.js', + 'src/pixi/core/Point.js', + 'src/pixi/core/Rectangle.js', + 'src/pixi/core/Polygon.js', + 'src/pixi/core/Circle.js', + 'src/pixi/core/Ellipse.js', + 'src/pixi/core/Matrix.js', + 'src/pixi/display/DisplayObject.js', + 'src/pixi/display/DisplayObjectContainer.js', + 'src/pixi/display/Sprite.js', + 'src/pixi/display/SpriteBatch.js', + 'src/pixi/filters/FilterBlock.js', + 'src/pixi/text/Text.js', + 'src/pixi/text/BitmapText.js', + 'src/pixi/display/Stage.js', + 'src/pixi/utils/Utils.js', + 'src/pixi/utils/EventTarget.js', + 'src/pixi/utils/Polyk.js', + 'src/pixi/renderers/webgl/utils/WebGLShaderUtils.js', + 'src/pixi/renderers/webgl/shaders/PixiShader.js', + 'src/pixi/renderers/webgl/shaders/PixiFastShader.js', + 'src/pixi/renderers/webgl/shaders/StripShader.js', + 'src/pixi/renderers/webgl/shaders/PrimitiveShader.js', + 'src/pixi/renderers/webgl/utils/WebGLGraphics.js', + 'src/pixi/renderers/webgl/WebGLRenderer.js', + 'src/pixi/renderers/webgl/utils/WebGLMaskManager.js', + 'src/pixi/renderers/webgl/utils/WebGLShaderManager.js', + 'src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js', + 'src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js', + 'src/pixi/renderers/webgl/utils/WebGLFilterManager.js', + 'src/pixi/renderers/webgl/utils/FilterTexture.js', + 'src/pixi/renderers/canvas/utils/CanvasMaskManager.js', + 'src/pixi/renderers/canvas/utils/CanvasTinter.js', + 'src/pixi/renderers/canvas/CanvasRenderer.js', + 'src/pixi/renderers/canvas/CanvasGraphics.js', + 'src/pixi/primitives/Graphics.js', + 'src/pixi/extras/TilingSprite.js', + 'src/pixi/textures/BaseTexture.js', + 'src/pixi/textures/Texture.js', + 'src/pixi/textures/RenderTexture.js', + 'src/pixi/Outro.js', + ], + + phaser: [ + 'src/Intro.js', + 'src/Phaser.js', + 'src/utils/Utils.js', + + 'src/geom/Circle.js', + 'src/geom/Point.js', + 'src/geom/Rectangle.js', + 'src/geom/Line.js', + 'src/geom/Ellipse.js', + 'src/geom/Polygon.js', + + 'src/core/Camera.js', + 'src/core/State.js', + 'src/core/StateManager.js', + 'src/core/LinkedList.js', + 'src/core/Signal.js', + 'src/core/SignalBinding.js', + 'src/core/Filter.js', + 'src/core/Plugin.js', + 'src/core/PluginManager.js', + 'src/core/Stage.js', + 'src/core/Group.js', + 'src/core/World.js', + 'src/core/ScaleManager.js', + 'src/core/Game.js', + + 'src/input/Input.js', + 'src/input/Key.js', + 'src/input/Keyboard.js', + 'src/input/Mouse.js', + 'src/input/MSPointer.js', + 'src/input/Pointer.js', + 'src/input/Touch.js', + 'src/input/Gamepad.js', + 'src/input/SinglePad.js', + 'src/input/GamepadButton.js', + 'src/input/InputHandler.js', + + 'src/gameobjects/Events.js', + 'src/gameobjects/GameObjectFactory.js', + 'src/gameobjects/GameObjectCreator.js', + 'src/gameobjects/BitmapData.js', + 'src/gameobjects/Sprite.js', + 'src/gameobjects/Image.js', + 'src/gameobjects/TileSprite.js', + 'src/gameobjects/Text.js', + 'src/gameobjects/BitmapText.js', + 'src/gameobjects/Button.js', + 'src/gameobjects/Graphics.js', + 'src/gameobjects/RenderTexture.js', + 'src/gameobjects/SpriteBatch.js', + 'src/gameobjects/RetroFont.js', + + 'src/system/Canvas.js', + 'src/system/Device.js', + 'src/system/RequestAnimationFrame.js', + + 'src/math/Math.js', + 'src/math/RandomDataGenerator.js', + 'src/math/QuadTree.js', + + 'src/net/Net.js', + + 'src/tween/TweenManager.js', + 'src/tween/Tween.js', + 'src/tween/Easing.js', + + 'src/time/Time.js', + 'src/time/Timer.js', + 'src/time/TimerEvent.js', + + 'src/animation/AnimationManager.js', + 'src/animation/Animation.js', + 'src/animation/Frame.js', + 'src/animation/FrameData.js', + 'src/animation/AnimationParser.js', + + 'src/loader/Cache.js', + 'src/loader/Loader.js', + 'src/loader/LoaderParser.js', + + 'src/sound/Sound.js', + 'src/sound/SoundManager.js', + + 'src/utils/Debug.js', + 'src/utils/Color.js', + + 'src/physics/Physics.js', + + 'src/physics/arcade/World.js', + 'src/physics/arcade/Body.js', + + 'src/particles/Particles.js', + 'src/particles/arcade/ArcadeParticles.js', + 'src/particles/arcade/Emitter.js', + + 'src/tilemap/Tile.js', + 'src/tilemap/Tilemap.js', + 'src/tilemap/TilemapLayer.js', + 'src/tilemap/TilemapParser.js', + 'src/tilemap/Tileset.js', + + 'src/Outro.js' + ], + + p2: [ + 'src/physics/p2/p2.js', + 'src/physics/p2/World.js', + 'src/physics/p2/PointProxy.js', + 'src/physics/p2/InversePointProxy.js', + 'src/physics/p2/Body.js', + 'src/physics/p2/BodyDebug.js', + 'src/physics/p2/Spring.js', + 'src/physics/p2/Material.js', + 'src/physics/p2/ContactMaterial.js', + 'src/physics/p2/CollisionGroup.js', + 'src/physics/p2/DistanceConstraint.js', + 'src/physics/p2/GearConstraint.js', + 'src/physics/p2/LockConstraint.js', + 'src/physics/p2/PrismaticConstraint.js', + 'src/physics/p2/RevoluteConstraint.js' + ], + + ninja: [ + 'src/physics/ninja/World.js', + 'src/physics/ninja/Body.js', + 'src/physics/ninja/AABB.js', + 'src/physics/ninja/Tile.js', + 'src/physics/ninja/Circle.js' + ], + + // If we've updated pixi or p2 then their UMD wrappers will be wrong, this will fix it: + replace: { + + pixi: { + src: ['src/pixi/Outro.js'], + dest: 'src/pixi/Outro.js', + replacements: [{ + from: "define(PIXI);", + to: "define('PIXI', (function() { return root.PIXI = PIXI; })() );" + }] + }, + + p2: { + src: ['src/physics/p2/p2.js'], + dest: 'src/physics/p2/p2.js', + replacements: [{ + from: '!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.p2=e():"undefined"!=typeof global?self.p2=e():"undefined"!=typeof self&&(self.p2=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module \'"+o+"\'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o'], + + concat: { + + // Our custom version of p2 + p2: { + src: ['<%= p2 %>'], + dest: '<%= compile_dir %>/p2.js' + }, + + // Our custom version of Pixi + pixi: { + src: ['<%= pixi %>'], + dest: '<%= compile_dir %>/pixi.js' + }, + + // Our custom version of Ninja Physics + ninja: { + src: ['<%= ninja %>'], + dest: '<%= compile_dir %>/ninja.js' + }, + + // Phaser with no bundled libs + phaser: { + options: { + banner: '<%= banner %>' + }, + src: ['<%= phaser %>'], + dest: '<%= compile_dir %>/phaser-no-libs.js' + }, + + // Phaser with pixi but no physics libs + phaserNoPhysics: { + options: { + banner: '<%= banner %>' + }, + src: ['<%= compile_dir %>/pixi.js', '<%= compile_dir %>/phaser-no-libs.js'], + dest: '<%= compile_dir %>/phaser-no-physics.js' + }, + + // One ring to rule them all + standalone: { + options: { + banner: '<%= banner %>' + }, + src: ['<%= compile_dir %>/pixi.js', '<%= compile_dir %>/phaser-no-libs.js', '<%= compile_dir %>/ninja.js', '<%= compile_dir %>/p2.js'], + dest: '<%= compile_dir %>/phaser.js' + } + + }, + + uglify: { + + p2: { + options: { + banner: '/* p2.js custom build for Phaser v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.p2.dest %>'], + dest: '<%= compile_dir %>/p2.min.js' + }, + + pixi: { + options: { + banner: '/* Pixi.js custom build for Phaser v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.pixi.dest %>'], + dest: '<%= compile_dir %>/pixi.min.js' + }, + + ninja: { + options: { + banner: '/* Ninja Physics for Phaser v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.ninja.dest %>'], + dest: '<%= compile_dir %>/ninja.min.js' + }, + + phaser: { + options: { + banner: '/* Phaser (no libs) v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.phaser.dest %>'], + dest: '<%= compile_dir %>/phaser-no-libs.min.js' + }, + + phaserNoPhysics: { + options: { + banner: '/* Phaser (no physics) v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.phaserNoPhysics.dest %>'], + dest: '<%= compile_dir %>/phaser-no-physics.min.js' + }, + + standalone: { + options: { + sourceMap: true, + sourceMapName: '<%= compile_dir %>/phaser.map', + banner: '/* Phaser v<%= pkg.version %> - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */\n' + }, + src: ['<%= concat.standalone.dest %>'], + dest: '<%= compile_dir %>/phaser.min.js' + } + + }, + + copy: { + main: { + files: [ + { src: ['dist/phaser.js'], dest: 'build/phaser.js' }, + { src: ['dist/phaser.min.js'], dest: 'build/phaser.min.js' }, + { src: ['dist/phaser.map'], dest: 'build/phaser.map' }, + + { src: ['dist/p2.js'], dest: 'build/custom/p2.js' }, + { src: ['dist/p2.min.js'], dest: 'build/custom/p2.min.js' }, + { src: ['dist/phaser-no-libs.js'], dest: 'build/custom/phaser-no-libs.js' }, + { src: ['dist/phaser-no-libs.min.js'], dest: 'build/custom/phaser-no-libs.min.js' }, + { src: ['dist/pixi.js'], dest: 'build/custom/pixi.js' }, + { src: ['dist/pixi.min.js'], dest: 'build/custom/pixi.min.js' }, + { src: ['dist/ninja.js'], dest: 'build/custom/ninja.js' }, + { src: ['dist/ninja.min.js'], dest: 'build/custom/ninja.min.js' }, + { src: ['dist/phaser-no-physics.js'], dest: 'build/custom/phaser-no-physics.js' }, + { src: ['dist/phaser-no-physics.min.js'], dest: 'build/custom/phaser-no-physics.min.js' } + + ] + } + }, + + connect: { + root: { + options: { + keepalive: true, + hostname: '*' + } + } + }, + + jshint: { + src: { + src: [ + 'plugins/**/*.js', + 'src/**/*.js', + '!src/Intro.js', + '!src/Outro.js', + '!src/pixi/**/*', + '!src/physics/p2/p2.js', + '!plugins/AStar.js' + ], + options: { jshintrc: '.jshintrc' } + }, + + filters: { + src: ['filters/**/*.js'], + options: { jshintrc: 'filters/.jshintrc', } + }, + + tooling: { + src: [ + 'Gruntfile.js', + 'tasks/**/*.js' + ], + options: { jshintrc: 'tasks/.jshintrc' } + }, + + options: { + force: true + } + } + }); + + grunt.registerTask('default', ['build']); + + grunt.registerTask('build', ['clean', 'jshint', 'concat', 'uglify']); + grunt.registerTask('dist', ['build', 'copy']); + +}; diff --git a/bower_components/phaser-official/README.md b/bower_components/phaser-official/README.md new file mode 100644 index 0000000..427b05f --- /dev/null +++ b/bower_components/phaser-official/README.md @@ -0,0 +1,319 @@ +![Phaser 2.0](http://www.phaser.io/images/phaser2-github.png) + +Phaser 2.0.2 +============ + +Phaser is a fast, free and fun open source game framework for making desktop and mobile browser HTML5 games. It uses [Pixi.js](https://github.com/GoodBoyDigital/pixi.js/) internally for fast 2D Canvas and WebGL rendering. + +Version: 2.0.2 "Ghealdan" - Released: 28th March 2014 + +By Richard Davey, [Photon Storm](http://www.photonstorm.com) + +* View the [Official Website](http://phaser.io) +* Follow on [Twitter](https://twitter.com/photonstorm) +* Join the [Forum](http://www.html5gamedevs.com/forum/14-phaser/) +* Source code for 250+ [Phaser Examples](https://github.com/photonstorm/phaser-examples) +* Read the [documentation online](http://docs.phaser.io) +* Browse the [Examples online](http://examples.phaser.io) +* Join our [#phaserio IRC channel](http://www.html5gamedevs.com/topic/4470-official-phaserio-irc-channel-phaserio-on-freenode/) on freenode + +[Subscribe to our new Phaser Newsletter](https://confirmsubscription.com/h/r/369DE48E3E86AF1E). We'll email you when new versions are released as well as send you our regular Phaser game making magazine. + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/photonstorm/phaser/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + + +What's new in 2.0.2? +-------------------- + +The great thing about running an open source project are all the awesome contributions from the community, and this release reflects that. Aside from a raft of small but important ArcadePhysics fixes we've also had the entirely library jshinted and tidied up by xtian. We have had jshint configs in place for a while now, but he's forced us to make them part of the build process and helped tidy-up a number of things that had crept in. + +We've also updated the included tutorial (and the web version) so it's now fully compatible with Phaser 2 and re-published and updated all of the API documentation, which you'll find in the docs folder in this release. + + +Welcome to Phaser +----------------- + +6 months ago we released Phaser 1.0 into the world. Suffice to say that since then we've been overwhelmed at the huge surge of developers taking to it! Our github repository is consistently in the top JavaScript lists, we've over 3200 stars at the time of writing, and a wonderful, vibrant and friendly community. Phaser 2 is a natural evolution of what we started. We've focused specifically on performance and expansion with this release. Lots of developers have already reported to us considerably speed increases just by swapping to Phaser 2 (reports of 200% faster speeds on mobile aren't unheard of!). There's also a full-body physics system available now, in the form of the excellent p2.js. The upgrade to Pixi 1.5 under the hood bought lots of new visual effects in, including blend modes and tints. + +And we're also really pleased to have closed down over 550 issues reported on github. We literally went through every last bug reported to us, and fixed it. All kinds of little things that as a whole make the library that much more solid. With the 2.0 release we're now freezing the API. Before we have to admit that the API changed somewhat on a whim, and we moved things around and changed things without too much consideration for fellow developers. With 2.0 that stops - we've spent long enough on this release that we're now extremely happy with the organisation of classes and methods, and while we may still need to make small tweaks in the future, none of them will be API breaking without prior community approval first. This means if you're using Phaser to teach in classes, or writing a book / tutorials around it, this is the version to base off. + +If you want to port a Phaser 1.x game over to 2 then do read our [Migration Guide](https://github.com/photonstorm/phaser/blob/master/resources/Migration%20Guide.md) first. + +So what's next? We have a roadmap (which you can find at the bottom of this document), but we're going to sit back and take stock for a while, building up the tutorials and sample games. We will of course jump on bug fixes quickly, but this is definitely the best release of Phaser ever. The most features, the fastest, the most stable and just generally the most fun to use. + +Happy coding everyone! See you on the forums. + +![boogie](http://www.phaser.io/images/spacedancer.gif) + + +Getting Started Guides +---------------------- + +We have a new [Getting Started Guide](http://phaser.io/getting-started-js.php) which covers all you need to begin developing games with Phaser. From setting up a web server to picking an IDE. If you're new to HTML5 game development, or are coming from another language like AS3, then we recommend starting there. + +There is a comprehensive [How to Learn Phaser](http://gamedevelopment.tutsplus.com/articles/how-to-learn-the-phaser-html5-game-engine--gamedev-13643) guide on the GameDevTuts+ site which is a great place to learn where to find tutorials, examples and support. + +There is also an [un-official Getting Started Guide](http://www.antonoffplus.com/coding-an-html5-game-for-30-minutes-or-an-introduction-to-the-phaser-framework). + + +![Phaser Logo](http://www.photonstorm.com/wp-content/uploads/2013/09/phaser_10_release.jpg) + +Change Log +---------- + +Version 2.0.2 - "Ghealdan" - 28th March 2014 + +Bug Fixes + +* Sprite would glitch if it had an ArcadePhysics Body that was re-positioned out of loop. +* Sprite would "fly off" if it had an ArcadePhysics Body that was re-positioned during an input handler. +* Tween.generateData would enter an eternal loop if the total resulted in a float. Now wrapped in Math.floor. +* ArcadePhysics.Body preUpdate has been modified to stop Sprites with non-1 scaling from gaining delta and moving off the screen (fix #644). +* ArcadePhysics.Body deltaMaxY wasn't being correctly applied. +* P2.World - Removing tilemap layer retrieval for object layers in convertCollisionObjects() (thanks bmceldowney, fix #653) +* Calling Keyboard.stop() wouldn't let you call Keyboard.start() later on in the same game + + +Updated + +* The "Build your First Phaser Game" Tutorial has been updated for Phaser 2 +* Line.fromSprite now sets "fromCenter" to false by default as Sprite.center is deprecated in 2.x. Documentation and Examples updated to reflect this. +* All the documentation has been re-published for 2.0.2. +* Lots of ArcadePhysics.World methods have been marked as private where they shouldn't be called directly (separateX, etc) +* xtian jshint fixed nearly every single file in the repository! + + +New Features + +* Sprite.overlap lets you quickly check to see if the bounds of two display objects are intersecting or not, without having to use a physics system. +* Keyboard.destroy will now clear all event listeners and any custom set callbacks or Keys. + + +There is an extensive [Migration Guide](https://github.com/photonstorm/phaser/blob/master/resources/Migration%20Guide.md) available for those converting from Phaser 1.x to 2.x. In the guide we detail the API breaking changes and approach to our new physics system. + +The full Change Log is at https://github.com/photonstorm/phaser/blob/master/changelog.md + + +How to Build +------------ + +We provide a fully compiled version of Phaser in the `build` directory, in both plain and minified formats. + +We also provide a Grunt script that will build Phaser from source along with all the examples. + +Run `grunt` to perform a default build to the `dist` folder and update the examples. + +If you replace Pixi or p2 then run `grunt replace` to patch their UMD strings so they work properly with Phaser and requireJS. + + +Koding +------ + +You can [![clone the Phaser repo in Koding](http://learn.koding.com/btn/clone_d.png)][koding] and then start editing and previewing code right away using their web based VM development system. + + +Bower +----- + +If you use bowser you can install phaser with: + +`bower install phaser` + +Nice and easy :) + +![Tanks](http://www.photonstorm.com/wp-content/uploads/2013/10/phaser_tanks-640x480.png) + + +CDNJS +----- + +Thanks to a community member Phaser is now available on [CDNJS](http://cdnjs.com). You can include the following in your html: + +`http://cdnjs.cloudflare.com/ajax/libs/phaser/2.0.2/phaser.min.js` + +Or if you prefer you can leave the protocol off, so it works via http and https: + +`//cdnjs.cloudflare.com/ajax/libs/phaser/2.0.2/phaser.min.js` + + +Requirements +------------ + +Games created with Phaser require a modern web browser that supports the canvas tag. This includes Internet Explorer 9+, Firefox, Chrome, Safari and Opera. It also works on mobile web browsers including stock Android 2.x browser and above and iOS5 Mobile Safari and above. + +If you need to support IE9 or Android 2.x then you must use a build of Phaser that doesn't include P2 physics. You'll find these in the `build/custom` folder. + +For developing with Phaser you can use either a plain-vanilla JavaScript approach or [TypeScript](https://typescript.codeplex.com/) using the provided TypeScript definitions file. We made no assumptions about how you like to code your games, and were careful not to impose any form of class/inheritance/structure upon you. + +Phaser is 576 KB minified (including all 3 physics engines, 311 KB without) and 128 KB gzipped (67 KB without physics libs). + + +Learn By Example +---------------- + +Ever since we started Phaser we've been growing and expanding our extensive set of Examples. Currently over 250 of them! + +They used to be bundled in the main Phaser repo, but because they got so large and in order to help with versioning we've moved them to their own repo. + +Please go and checkout https://github.com/photonstorm/phaser-examples + +Phaser comes with an ever growing suite of Examples. Personally I feel that we learn better by looking at small refined code examples, so we created over 250 of them and create new ones to test every new feature added. Inside the `examples` repo you'll find the current set. If you write a particularly good example then please send it to us. + +The examples need to be run through a local web server (in order to avoid file access permission errors from your browser). You can use your own web server, or start the included web server using grunt. + +Using a locally installed web server browse to the examples folder: + + examples/index.html + +Alternatively in order to start the included web server, after you've cloned the repo, run `npm install` to install all dependencies, then `grunt connect` to start a local server. After running this command you should be able to access your local webserver at `http://127.0.0.1:8000`. Then browse to the examples folder: `http://127.0.0.1:8000/examples/index.html` + +There is a new 'Side View' example viewer as well. This loads all the examples into a left-hand frame for faster navigation. + +You can also browse all [Phaser Examples](http://examples.phaser.io) online. + + +Features +-------- + +**WebGL & Canvas** + +Phaser uses both a Canvas and WebGL renderer internally and can automatically swap between them based on browser support. This allows for lightning fast rendering across Desktop and Mobile. When running under WebGL Phaser now supports shaders, allowing for some incredible in-game effects. Phaser uses and contributes towards the excellent Pixi.js library for rendering. + +**Preloader** + +We've made the loading of assets as simple as one line of code. Images, Sounds, Sprite Sheets, Tilemaps, JSON data, XML and JavaScript files - all parsed and handled automatically, ready for use in game and stored in a global Cache for Sprites to share. + +**Physics** + +Phaser ships with our Arcade Physics system, Ninja Physics and P2.JS - a full body physics system. Arcade Physics is for high-speed AABB collision only. Ninja Physics allows for complex tiles and slopes, perfect for level scenery, and P2.JS is a full-body physics system, with constraints, springs, polygon support and more. + +**Sprites** + +Sprites are the life-blood of your game. Position them, tween them, rotate them, scale them, animate them, collide them, paint them onto custom textures and so much more! +Sprites also have full Input support: click them, touch them, drag them around, snap them - even pixel perfect click detection if needed. + +**Groups** + +Group bundles of Sprites together for easy pooling and recycling, avoiding constant object creation. Groups can also be collided: for example a "Bullets" group checking for collision against the "Aliens" group, with a custom collision callback to handle the outcome. + +**Animation** + +Phaser supports classic Sprite Sheets with a fixed frame size, Texture Packer and Flash CS6/CC JSON files (both Hash and Array formats) and Starling XML files. All of these can be used to easily create animation for Sprites. + +**Particles** + +An Arcade Particle system is built-in, which allows you to create fun particle effects easily. Create explosions or constant streams for effects like rain or fire. Or attach the Emitter to a Sprite for a jet trail. + +**Camera** + +Phaser has a built-in Game World. Objects can be placed anywhere within the world and you've got access to a powerful Camera to look into that world. Pan around and follow Sprites with ease. + +**Input** + +Talk to a Phaser.Pointer and it doesn't matter if the input came from a touch-screen or mouse, it can even change mid-game without dropping a beat. Multi-touch, Mouse, Keyboard and lots of useful functions allow you to code custom gesture recognition. + +**Sound** + +Phaser supports both Web Audio and legacy HTML Audio. It automatically handles mobile device locking, easy Audio Sprite creation, looping, streaming and volume. We know how much of a pain dealing with audio on mobile is, so we did our best to resolve that! + +**Tilemaps** + +Phaser can load, render and collide with a tilemap with just a couple of lines of code. We support CSV and Tiled map data formats with multiple tile layers. There are lots of powerful tile manipulation functions: swap tiles, replace them, delete them, add them and update the map in realtime. + +**Device Scaling** + +Phaser has a built-in Scale Manager which allows you to scale your game to fit any size screen. Control aspect ratios, minimum and maximum scales and full-screen support. + +**Plugin system** + +We are trying hard to keep the core of Phaser limited to only essential classes, so we built a smart Plugin system to handle everything else. Create your own plugins easily and share them with the community. + +**Mobile Browser** + +Phaser was built specifically for Mobile web browsers. Of course it works blazingly fast on Desktop too, but unlike lots of frameworks mobile was our main focus. If it doesn't perform well on mobile then we don't add it into the Core. + +**Developer Support** + +We use Phaser every day on our many client projects. As a result it's constantly evolving and improving and we jump on bugs and pull requests quickly. This is a living, breathing framework maintained by a commercial company with custom feature development and support packages available. We live and breathe HTML5 games. + +**Battle Tested** + +Phaser has been used to create hundreds of games, which receive millions of plays per month. We're not saying it is 100% bug free, but we use it for our client work every day, so issues get resolved fast and we stay on-top of the changing browser landscape. + +![FruitParty](http://www.photonstorm.com/wp-content/uploads/2013/10/phaser_fruit_particles-640x480.png) + + +Road Map +-------- + +Beyond version 2.0 here are some of the features planned for the future: + +Version 2.1 ("Shienar") + +* Enhance the State Management, so you can perform non-destructive State swaps and persistence. +* A more advanced Particle system, one that can render to a single canvas (rather than spawn hundreds of Sprites), more advanced effects, etc. +* Ability to control DOM elements from the core game and layer them into the game. +* Touch Gestures. +* Support for parallel asset loading. + +Version 2.2 ("Tarabon") + +* Comprehensive testing across Firefox OS devices, CocoonJS and Ejecta. +* Integration with third party services like Google Play Game Services and Amazon JS SDK. +* Flash CC HTML5 export integration. +* Massively enhance the audio side of Phaser. Take more advantage of Web Audio: echo effects, positional sound, etc. + +Beyond version 2.2 + +* Test out packaging with Node-webkit. +* Game parameters stored in Google Docs. +* Look at HiDPI Canvas settings. +* Multiple Camera support. +* DragonBones support. +* Cache to localStorage using If-Modified-Since. [See github request](https://github.com/photonstorm/phaser/issues/495) +* Allow for complex assets like Bitmap Fonts to be stored within a texture atlas. +* Look at XDomainRequest for IE9 CORs issues. + + +Nadion +------ + +[Nadion](https://github.com/jcd-as/nadion) is a set of powerful enhancements for Phaser that makes level building even easier. It includes features such as Trigger, Area, Alarms and Emitters, debug panels, state machines, parallax layer scrolling, 'developer mode' short-cuts and more. + + +Contributing +------------ + +We now have a full [Contributors Guide][contribute] which goes into the process in more detail, but here are the headlines: + +- If you find a bug then please report it on [GitHub Issues][issues] or our [Support Forum][forum]. + +- If you have a feature request, or have written a game or demo that shows Phaser in use, then please get in touch. We'd love to hear from you! Either post to our [forum][forum] or email: rich@photonstorm.com + +- If you issue a Pull Request for Phaser, please only do so againt the `dev` branch and *not* against the `master` branch. + +- Before submitting a Pull Request please run your code through [JSHint](http://www.jshint.com/) to check for stylistic or formatting errors. To use JSHint, run `grunt jshint`. This isn't a strict requirement and we are happy to receive Pull Requests that haven't been JSHinted, so don't let it put you off contributing, but do know that we'll reformat your source before going live with it. + + +Bugs? +----- + +Please add them to the [Issue Tracker][issues] with as much info as possible, especially source code demonstrating the issue. + +![Phaser Tilemap](http://www.photonstorm.com/wp-content/uploads/2013/04/phaser_tilemap_collision.png) + +"Being negative is not how we make progress" - Larry Page, Google + + +License +------- + +Phaser is released under the [MIT License](http://opensource.org/licenses/MIT). + +[issues]: https://github.com/photonstorm/phaser/issues +[contribute]: https://github.com/photonstorm/phaser/blob/master/CONTRIBUTING.md +[phaser]: https://github.com/photonstorm/phaser +[forum]: http://www.html5gamedevs.com/forum/14-phaser/ +[koding]: https://koding.com/Teamwork?import=https://github.com/photonstorm/phaser/archive/master.zip&c=git1 + +[![Analytics](https://ga-beacon.appspot.com/UA-44006568-2/phaser/index)](https://github.com/igrigorik/ga-beacon) diff --git a/bower_components/phaser-official/bower.json b/bower_components/phaser-official/bower.json new file mode 100644 index 0000000..5816f22 --- /dev/null +++ b/bower_components/phaser-official/bower.json @@ -0,0 +1,37 @@ +{ + "name": "phaser", + "version": "2.0.2", + "homepage": "http://phaser.io", + "authors": [ + "photonstorm " + ], + "description": "A fun, free and fast 2D game framework for making HTML5 games for desktop and mobile, supporting Canvas and WebGL.", + "main": "build/phaser.js", + "keywords": [ + "html5", + "game", + "games", + "framework", + "canvas", + "WebGL", + "tilemaps", + "physics", + "sprites", + "fonts", + "images", + "audio", + "Web", + "Audio", + "touch", + "input", + "mobile" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/bower_components/phaser-official/build/config.php b/bower_components/phaser-official/build/config.php new file mode 100644 index 0000000..4473f7b --- /dev/null +++ b/bower_components/phaser-official/build/config.php @@ -0,0 +1,215 @@ +"; + } + + echo << + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOL; + + if ($arcade) + { + echo << + +EOL; + } + + + if ($p2) + { + echo << + + + + + + + + + + + + + +EOL; + } + + if ($ninja) + { + echo << + + + + + + + +EOL; + } + +?> \ No newline at end of file diff --git a/bower_components/phaser-official/build/custom/ninja.js b/bower_components/phaser-official/build/custom/ninja.js new file mode 100644 index 0000000..e3db4b0 --- /dev/null +++ b/bower_components/phaser-official/build/custom/ninja.js @@ -0,0 +1,5562 @@ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics. The Ninja Physics system was created in Flash by Metanet Software and ported to JavaScript by Richard Davey. +* +* It allows for AABB and Circle to Tile collision. Tiles can be any of 34 different types, including slopes, convex and concave shapes. +* +* It does what it does very well, but is ripe for expansion and optimisation. Here are some features that I'd love to see the community add: +* +* * AABB to AABB collision +* * AABB to Circle collision +* * AABB and Circle 'immovable' property support +* * n-way collision, so an AABB/Circle could pass through a tile from below and land upon it. +* * QuadTree or spatial grid for faster Body vs. Tile Group look-ups. +* * Optimise the internal vector math and reduce the quantity of temporary vars created. +* * Expand Gravity and Bounce to allow for separate x/y axis values. +* * Support Bodies linked to Sprites that don't have anchor set to 0.5 +* +* Feel free to attempt any of the above and submit a Pull Request with your code! Be sure to include test cases proving they work. +* +* @class Phaser.Physics.Ninja +* @classdesc Ninja Physics Constructor +* @constructor +* @param {Phaser.Game} game reference to the current game instance. +*/ +Phaser.Physics.Ninja = function (game) { + + /** + * @property {Phaser.Game} game - Local reference to game. + */ + this.game = game; + + /** + * @property {Phaser.Time} time - Local reference to game.time. + */ + this.time = this.game.time; + + /** + * @property {number} gravity - The World gravity setting. + */ + this.gravity = 0.2; + + /** + * @property {Phaser.Rectangle} bounds - The bounds inside of which the physics world exists. Defaults to match the world bounds. + */ + this.bounds = new Phaser.Rectangle(0, 0, game.world.width, game.world.height); + + /** + * @property {number} maxObjects - Used by the QuadTree to set the maximum number of objects per quad. + */ + this.maxObjects = 10; + + /** + * @property {number} maxLevels - Used by the QuadTree to set the maximum number of iteration levels. + */ + this.maxLevels = 4; + + /** + * @property {Phaser.QuadTree} quadTree - The world QuadTree. + */ + this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels); + +}; + +Phaser.Physics.Ninja.prototype.constructor = Phaser.Physics.Ninja; + +Phaser.Physics.Ninja.prototype = { + + /** + * This will create a Ninja Physics AABB body on the given game object. Its dimensions will match the width and height of the object at the point it is created. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableAABB + * @param {object|array|Phaser.Group} object - The game object to create the physics body on. Can also be an array or Group of objects, a body will be created on every child that has a `body` property. + * @param {boolean} [children=true] - Should a body be created on all children of this object? If true it will recurse down the display list as far as it can go. + */ + enableAABB: function (object, children) { + + this.enable(object, 1, 0, 0, children); + + }, + + /** + * This will create a Ninja Physics Circle body on the given game object. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableCircle + * @param {object|array|Phaser.Group} object - The game object to create the physics body on. Can also be an array or Group of objects, a body will be created on every child that has a `body` property. + * @param {number} radius - The radius of the Circle. + * @param {boolean} [children=true] - Should a body be created on all children of this object? If true it will recurse down the display list as far as it can go. + */ + enableCircle: function (object, radius, children) { + + this.enable(object, 2, 0, radius, children); + + }, + + /** + * This will create a Ninja Physics Tile body on the given game object. There are 34 different types of tile you can create, including 45 degree slopes, + * convex and concave circles and more. The id parameter controls which Tile type is created, but you can also change it at run-time. + * Note that for all degree based tile types they need to have an equal width and height. If the given object doesn't have equal width and height it will use the width. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enableTile + * @param {object|array|Phaser.Group} object - The game object to create the physics body on. Can also be an array or Group of objects, a body will be created on every child that has a `body` property. + * @param {number} [id=1] - The type of Tile this will use, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. + * @param {boolean} [children=true] - Should a body be created on all children of this object? If true it will recurse down the display list as far as it can go. + */ + enableTile: function (object, id, children) { + + this.enable(object, 3, id, 0, children); + + }, + + /** + * This will create a Ninja Physics body on the given game object or array of game objects. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the object is destroyed. + * + * @method Phaser.Physics.Ninja#enable + * @param {object|array|Phaser.Group} object - The game object to create the physics body on. Can also be an array or Group of objects, a body will be created on every child that has a `body` property. + * @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. + * @param {number} [id=1] - If this body is using a Tile shape, you can set the Tile id here, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. + * @param {number} [radius=0] - If this body is using a Circle shape this controls the radius. + * @param {boolean} [children=true] - Should a body be created on all children of this object? If true it will recurse down the display list as far as it can go. + */ + enable: function (object, type, id, radius, children) { + + if (typeof type === 'undefined') { type = 1; } + if (typeof id === 'undefined') { id = 1; } + if (typeof radius === 'undefined') { radius = 0; } + if (typeof children === 'undefined') { children = true; } + + if (Array.isArray(object)) + { + var i = object.length; + + while (i--) + { + if (object[i] instanceof Phaser.Group) + { + // If it's a Group then we do it on the children regardless + this.enable(object[i].children, type, id, radius, children); + } + else + { + this.enableBody(object[i], type, id, radius); + + if (children && object[i].hasOwnProperty('children') && object[i].children.length > 0) + { + this.enable(object[i], type, id, radius, true); + } + } + } + } + else + { + if (object instanceof Phaser.Group) + { + // If it's a Group then we do it on the children regardless + this.enable(object.children, type, id, radius, children); + } + else + { + this.enableBody(object, type, id, radius); + + if (children && object.hasOwnProperty('children') && object.children.length > 0) + { + this.enable(object.children, type, id, radius, true); + } + } + } + + }, + + /** + * Creates a Ninja Physics body on the given game object. + * A game object can only have 1 physics body active at any one time, and it can't be changed until the body is nulled. + * + * @method Phaser.Physics.Ninja#enableBody + * @param {object} object - The game object to create the physics body on. A body will only be created if this object has a null `body` property. + */ + enableBody: function (object, type, id, radius) { + + if (object.hasOwnProperty('body') && object.body === null) + { + object.body = new Phaser.Physics.Ninja.Body(this, object, type, id, radius); + object.anchor.set(0.5); + } + + }, + + /** + * Updates the size of this physics world. + * + * @method Phaser.Physics.Ninja#setBounds + * @param {number} x - Top left most corner of the world. + * @param {number} y - Top left most corner of the world. + * @param {number} width - New width of the world. Can never be smaller than the Game.width. + * @param {number} height - New height of the world. Can never be smaller than the Game.height. + */ + setBounds: function (x, y, width, height) { + + this.bounds.setTo(x, y, width, height); + + }, + + /** + * Updates the size of this physics world to match the size of the game world. + * + * @method Phaser.Physics.Ninja#setBoundsToWorld + */ + setBoundsToWorld: function () { + + this.bounds.setTo(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height); + + }, + + /** + * Clears all physics bodies from the given TilemapLayer that were created with `World.convertTilemap`. + * + * @method Phaser.Physics.Ninja#clearTilemapLayerBodies + * @param {Phaser.Tilemap} map - The Tilemap to get the map data from. + * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer. + */ + clearTilemapLayerBodies: function (map, layer) { + + layer = map.getLayer(layer); + + var i = map.layers[layer].bodies.length; + + while (i--) + { + map.layers[layer].bodies[i].destroy(); + } + + map.layers[layer].bodies.length = []; + + }, + + /** + * Goes through all tiles in the given Tilemap and TilemapLayer and converts those set to collide into physics tiles. + * Only call this *after* you have specified all of the tiles you wish to collide with calls like Tilemap.setCollisionBetween, etc. + * Every time you call this method it will destroy any previously created bodies and remove them from the world. + * Therefore understand it's a very expensive operation and not to be done in a core game update loop. + * + * In Ninja the Tiles have an ID from 0 to 33, where 0 is 'empty', 1 is a full tile, 2 is a 45-degree slope, etc. You can find the ID + * list either at the very bottom of `Tile.js`, or in a handy visual reference in the `resources/Ninja Physics Debug Tiles` folder in the repository. + * The slopeMap parameter is an array that controls how the indexes of the tiles in your tilemap data will map to the Ninja Tile IDs. + * For example if you had 6 tiles in your tileset: Imagine the first 4 should be converted into fully solid Tiles and the other 2 are 45-degree slopes. + * Your slopeMap array would look like this: `[ 1, 1, 1, 1, 2, 3 ]`. + * Where each element of the array is a tile in your tilemap and the resulting Ninja Tile it should create. + * + * @method Phaser.Physics.Ninja#convertTilemap + * @param {Phaser.Tilemap} map - The Tilemap to get the map data from. + * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to map.currentLayer. + * @param {object} [slopeMap] - The tilemap index to Tile ID map. + * @return {array} An array of the Phaser.Physics.Ninja.Tile objects that were created. + */ + convertTilemap: function (map, layer, slopeMap) { + + layer = map.getLayer(layer); + + // If the bodies array is already populated we need to nuke it + this.clearTilemapLayerBodies(map, layer); + + for (var y = 0, h = map.layers[layer].height; y < h; y++) + { + for (var x = 0, w = map.layers[layer].width; x < w; x++) + { + var tile = map.layers[layer].data[y][x]; + + if (tile && slopeMap.hasOwnProperty(tile.index)) + { + var body = new Phaser.Physics.Ninja.Body(this, null, 3, slopeMap[tile.index], 0, tile.worldX + tile.centerX, tile.worldY + tile.centerY, tile.width, tile.height); + + map.layers[layer].bodies.push(body); + } + } + } + + return map.layers[layer].bodies; + + }, + + /** + * Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters. + * You can perform Sprite vs. Sprite, Sprite vs. Group and Group vs. Group overlap checks. + * Unlike collide the objects are NOT automatically separated or have any physics applied, they merely test for overlap results. + * The second parameter can be an array of objects, of differing types. + * + * @method Phaser.Physics.Ninja#overlap + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter. + * @param {function} [overlapCallback=null] - An optional callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you specified them. + * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then overlapCallback will only be called if processCallback returns true. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @returns {boolean} True if an overlap occured otherwise false. + */ + overlap: function (object1, object2, overlapCallback, processCallback, callbackContext) { + + overlapCallback = overlapCallback || null; + processCallback = processCallback || null; + callbackContext = callbackContext || overlapCallback; + + this._result = false; + this._total = 0; + + if (Array.isArray(object2)) + { + for (var i = 0, len = object2.length; i < len; i++) + { + this.collideHandler(object1, object2[i], overlapCallback, processCallback, callbackContext, true); + } + } + else + { + this.collideHandler(object1, object2, overlapCallback, processCallback, callbackContext, true); + } + + return (this._total > 0); + + }, + + /** + * Checks for collision between two game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions. + * The second parameter can be an array of objects, of differing types. + * The objects are also automatically separated. If you don't require separation then use ArcadePhysics.overlap instead. + * An optional processCallback can be provided. If given this function will be called when two sprites are found to be colliding. It is called before any separation takes place, + * giving you the chance to perform additional checks. If the function returns true then the collision and separation is carried out. If it returns false it is skipped. + * The collideCallback is an optional function that is only called if two sprites collide. If a processCallback has been set then it needs to return true for collideCallback to be called. + * + * @method Phaser.Physics.Ninja#collide + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap. + * @param {function} [collideCallback=null] - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them. + * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them. + * @param {object} [callbackContext] - The context in which to run the callbacks. + * @returns {boolean} True if a collision occured otherwise false. + */ + collide: function (object1, object2, collideCallback, processCallback, callbackContext) { + + collideCallback = collideCallback || null; + processCallback = processCallback || null; + callbackContext = callbackContext || collideCallback; + + this._result = false; + this._total = 0; + + if (Array.isArray(object2)) + { + for (var i = 0, len = object2.length; i < len; i++) + { + this.collideHandler(object1, object2[i], collideCallback, processCallback, callbackContext, false); + } + } + else + { + this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, false); + } + + return (this._total > 0); + + }, + + /** + * Internal collision handler. + * + * @method Phaser.Physics.Ninja#collideHandler + * @private + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap. + * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object2 - The second object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap. Can also be an array of objects to check. + * @param {function} collideCallback - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them. + * @param {function} processCallback - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them. + * @param {object} callbackContext - The context in which to run the callbacks. + * @param {boolean} overlapOnly - Just run an overlap or a full collision. + */ + collideHandler: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly) { + + // Only collide valid objects + if (typeof object2 === 'undefined' && (object1.type === Phaser.GROUP || object1.type === Phaser.EMITTER)) + { + this.collideGroupVsSelf(object1, collideCallback, processCallback, callbackContext, overlapOnly); + return; + } + + if (object1 && object2 && object1.exists && object2.exists) + { + // SPRITES + if (object1.type == Phaser.SPRITE || object1.type == Phaser.TILESPRITE) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsSprite(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideSpriteVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideSpriteVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + // GROUPS + else if (object1.type == Phaser.GROUP) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + // TILEMAP LAYERS + else if (object1.type == Phaser.TILEMAPLAYER) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext); + } + } + // EMITTER + else if (object1.type == Phaser.EMITTER) + { + if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE) + { + this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER) + { + this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly); + } + else if (object2.type == Phaser.TILEMAPLAYER) + { + this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext); + } + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Ninja.collide instead. + * + * @method Phaser.Physics.Ninja#collideSpriteVsSprite + * @private + */ + collideSpriteVsSprite: function (sprite1, sprite2, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (this.separate(sprite1.body, sprite2.body, processCallback, callbackContext, overlapOnly)) + { + if (collideCallback) + { + collideCallback.call(callbackContext, sprite1, sprite2); + } + + this._total++; + } + + }, + + /** + * An internal function. Use Phaser.Physics.Ninja.collide instead. + * + * @method Phaser.Physics.Ninja#collideSpriteVsGroup + * @private + */ + collideSpriteVsGroup: function (sprite, group, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group.length === 0) + { + return; + } + + // What is the sprite colliding with in the quadtree? + // this.quadTree.clear(); + + // this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels); + + // this.quadTree.populate(group); + + // this._potentials = this.quadTree.retrieve(sprite); + + for (var i = 0, len = group.children.length; i < len; i++) + { + // We have our potential suspects, are they in this group? + if (group.children[i].exists && group.children[i].body && this.separate(sprite.body, group.children[i].body, processCallback, callbackContext, overlapOnly)) + { + if (collideCallback) + { + collideCallback.call(callbackContext, sprite, group.children[i]); + } + + this._total++; + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Ninja.collide instead. + * + * @method Phaser.Physics.Ninja#collideGroupVsSelf + * @private + */ + collideGroupVsSelf: function (group, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group.length === 0) + { + return; + } + + var len = group.children.length; + + for (var i = 0; i < len; i++) + { + for (var j = i + 1; j <= len; j++) + { + if (group.children[i] && group.children[j] && group.children[i].exists && group.children[j].exists) + { + this.collideSpriteVsSprite(group.children[i], group.children[j], collideCallback, processCallback, callbackContext, overlapOnly); + } + } + } + + }, + + /** + * An internal function. Use Phaser.Physics.Ninja.collide instead. + * + * @method Phaser.Physics.Ninja#collideGroupVsGroup + * @private + */ + collideGroupVsGroup: function (group1, group2, collideCallback, processCallback, callbackContext, overlapOnly) { + + if (group1.length === 0 || group2.length === 0) + { + return; + } + + for (var i = 0, len = group1.children.length; i < len; i++) + { + if (group1.children[i].exists) + { + this.collideSpriteVsGroup(group1.children[i], group2, collideCallback, processCallback, callbackContext, overlapOnly); + } + } + + }, + + /** + * The core separation function to separate two physics bodies. + * @method Phaser.Physics.Ninja#separate + * @param {Phaser.Physics.Ninja.Body} body1 - The Body object to separate. + * @param {Phaser.Physics.Ninja.Body} body2 - The Body object to separate. + * @returns {boolean} Returns true if the bodies collided, otherwise false. + */ + separate: function (body1, body2) { + + if (body1.type !== Phaser.Physics.NINJA || body2.type !== Phaser.Physics.NINJA) + { + return false; + } + + if (body1.aabb && body2.aabb) + { + return body1.aabb.collideAABBVsAABB(body2.aabb); + } + + if (body1.aabb && body2.tile) + { + return body1.aabb.collideAABBVsTile(body2.tile); + } + + if (body1.tile && body2.aabb) + { + return body2.aabb.collideAABBVsTile(body1.tile); + } + + if (body1.circle && body2.tile) + { + return body1.circle.collideCircleVsTile(body2.tile); + } + + if (body1.tile && body2.circle) + { + return body2.circle.collideCircleVsTile(body1.tile); + } + + } + +}; + +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* The Physics Body is linked to a single Sprite. All physics operations should be performed against the body rather than +* the Sprite itself. For example you can set the velocity, bounce values etc all on the Body. +* +* @class Phaser.Physics.Ninja.Body +* @classdesc Ninja Physics Body Constructor +* @constructor +* @param {Phaser.Physics.Ninja} system - The physics system this Body belongs to. +* @param {Phaser.Sprite} sprite - The Sprite object this physics body belongs to. +* @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. +* @param {number} [id=1] - If this body is using a Tile shape, you can set the Tile id here, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. +* @param {number} [radius=16] - If this body is using a Circle shape this controls the radius. +* @param {number} [x=0] - The x coordinate of this Body. This is only used if a sprite is not provided. +* @param {number} [y=0] - The y coordinate of this Body. This is only used if a sprite is not provided. +* @param {number} [width=0] - The width of this Body. This is only used if a sprite is not provided. +* @param {number} [height=0] - The height of this Body. This is only used if a sprite is not provided. +*/ +Phaser.Physics.Ninja.Body = function (system, sprite, type, id, radius, x, y, width, height) { + + sprite = sprite || null; + + if (typeof type === 'undefined') { type = 1; } + if (typeof id === 'undefined') { id = 1; } + if (typeof radius === 'undefined') { radius = 16; } + + /** + * @property {Phaser.Sprite} sprite - Reference to the parent Sprite. + */ + this.sprite = sprite; + + /** + * @property {Phaser.Game} game - Local reference to game. + */ + this.game = system.game; + + /** + * @property {number} type - The type of physics system this body belongs to. + */ + this.type = Phaser.Physics.NINJA; + + /** + * @property {Phaser.Physics.Ninja} system - The parent physics system. + */ + this.system = system; + + /** + * @property {Phaser.Physics.Ninja.AABB} aabb - The AABB object this body is using for collision. + */ + this.aabb = null; + + /** + * @property {Phaser.Physics.Ninja.Tile} tile - The Tile object this body is using for collision. + */ + this.tile = null; + + /** + * @property {Phaser.Physics.Ninja.Circle} circle - The Circle object this body is using for collision. + */ + this.circle = null; + + /** + * @property {object} shape - A local reference to the body shape. + */ + this.shape = null; + + // Setting drag to 0 and friction to 0 means you get a normalised speed (px psec) + + /** + * @property {number} drag - The drag applied to this object as it moves. + * @default + */ + this.drag = 1; + + /** + * @property {number} friction - The friction applied to this object as it moves. + * @default + */ + this.friction = 0.05; + + /** + * @property {number} gravityScale - How much of the world gravity should be applied to this object? 1 = all of it, 0.5 = 50%, etc. + * @default + */ + this.gravityScale = 1; + + /** + * @property {number} bounce - The bounciness of this object when it collides. A value between 0 and 1. We recommend setting it to 0.999 to avoid jittering. + * @default + */ + this.bounce = 0.3; + + /** + * @property {Phaser.Point} velocity - The velocity in pixels per second sq. of the Body. + */ + this.velocity = new Phaser.Point(); + + /** + * @property {number} facing - A const reference to the direction the Body is traveling or facing. + * @default + */ + this.facing = Phaser.NONE; + + /** + * @property {boolean} immovable - An immovable Body will not receive any impacts from other bodies. Not fully implemented. + * @default + */ + this.immovable = false; + + /** + * A Body can be set to collide against the World bounds automatically and rebound back into the World if this is set to true. Otherwise it will leave the World. + * @property {boolean} collideWorldBounds - Should the Body collide with the World bounds? + */ + this.collideWorldBounds = true; + + /** + * Set the checkCollision properties to control which directions collision is processed for this Body. + * For example checkCollision.up = false means it won't collide when the collision happened while moving up. + * @property {object} checkCollision - An object containing allowed collision. + */ + this.checkCollision = { none: false, any: true, up: true, down: true, left: true, right: true }; + + /** + * This object is populated with boolean values when the Body collides with another. + * touching.up = true means the collision happened to the top of this Body for example. + * @property {object} touching - An object containing touching results. + */ + this.touching = { none: true, up: false, down: false, left: false, right: false }; + + /** + * This object is populated with previous touching values from the bodies previous collision. + * @property {object} wasTouching - An object containing previous touching results. + */ + this.wasTouching = { none: true, up: false, down: false, left: false, right: false }; + + /** + * @property {number} maxSpeed - The maximum speed this body can travel at (taking drag and friction into account) + * @default + */ + this.maxSpeed = 8; + + if (sprite) + { + x = sprite.x; + y = sprite.y; + width = sprite.width; + height = sprite.height; + + if (sprite.anchor.x === 0) + { + x += (sprite.width * 0.5); + } + + if (sprite.anchor.y === 0) + { + y += (sprite.height * 0.5); + } + } + + if (type === 1) + { + this.aabb = new Phaser.Physics.Ninja.AABB(this, x, y, width, height); + this.shape = this.aabb; + } + else if (type === 2) + { + this.circle = new Phaser.Physics.Ninja.Circle(this, x, y, radius); + this.shape = this.circle; + } + else if (type === 3) + { + this.tile = new Phaser.Physics.Ninja.Tile(this, x, y, width, height, id); + this.shape = this.tile; + } + +}; + +Phaser.Physics.Ninja.Body.prototype = { + + /** + * Internal method. + * + * @method Phaser.Physics.Ninja.Body#preUpdate + * @protected + */ + preUpdate: function () { + + // Store and reset collision flags + this.wasTouching.none = this.touching.none; + this.wasTouching.up = this.touching.up; + this.wasTouching.down = this.touching.down; + this.wasTouching.left = this.touching.left; + this.wasTouching.right = this.touching.right; + + this.touching.none = true; + this.touching.up = false; + this.touching.down = false; + this.touching.left = false; + this.touching.right = false; + + this.shape.integrate(); + + if (this.collideWorldBounds) + { + this.shape.collideWorldBounds(); + } + + }, + + /** + * Internal method. + * + * @method Phaser.Physics.Ninja.Body#postUpdate + * @protected + */ + postUpdate: function () { + + if (this.sprite) + { + if (this.sprite.type === Phaser.TILESPRITE) + { + // TileSprites don't use their anchor property, so we need to adjust the coordinates + this.sprite.x = this.shape.pos.x - this.shape.xw; + this.sprite.y = this.shape.pos.y - this.shape.yw; + } + else + { + this.sprite.x = this.shape.pos.x; + this.sprite.y = this.shape.pos.y; + } + } + + if (this.velocity.x < 0) + { + this.facing = Phaser.LEFT; + } + else if (this.velocity.x > 0) + { + this.facing = Phaser.RIGHT; + } + + if (this.velocity.y < 0) + { + this.facing = Phaser.UP; + } + else if (this.velocity.y > 0) + { + this.facing = Phaser.DOWN; + } + + }, + + /** + * Stops all movement of this body. + * + * @method Phaser.Physics.Ninja.Body#setZeroVelocity + */ + setZeroVelocity: function () { + + this.shape.oldpos.x = this.shape.pos.x; + this.shape.oldpos.y = this.shape.pos.y; + + }, + + /** + * Moves the Body forwards based on its current angle and the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveTo + * @param {number} speed - The speed at which it should move forwards. + * @param {number} angle - The angle in which it should move, given in degrees. + */ + moveTo: function (speed, angle) { + + var magnitude = speed * this.game.time.physicsElapsed; + var angle = this.game.math.degToRad(angle); + + this.shape.pos.x = this.shape.oldpos.x + (magnitude * Math.cos(angle)); + this.shape.pos.y = this.shape.oldpos.y + (magnitude * Math.sin(angle)); + + }, + + /** + * Moves the Body backwards based on its current angle and the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveBackward + * @param {number} speed - The speed at which it should move backwards. + * @param {number} angle - The angle in which it should move, given in degrees. + */ + moveFrom: function (speed, angle) { + + var magnitude = -speed * this.game.time.physicsElapsed; + var angle = this.game.math.degToRad(angle); + + this.shape.pos.x = this.shape.oldpos.x + (magnitude * Math.cos(angle)); + this.shape.pos.y = this.shape.oldpos.y + (magnitude * Math.sin(angle)); + + }, + + /** + * If this Body is dynamic then this will move it to the left by setting its x velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveLeft + * @param {number} speed - The speed at which it should move to the left, in pixels per second. + */ + moveLeft: function (speed) { + + var fx = -speed * this.game.time.physicsElapsed; + + this.shape.pos.x = this.shape.oldpos.x + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.x - this.shape.oldpos.x + fx)); + + }, + + /** + * If this Body is dynamic then this will move it to the right by setting its x velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveRight + * @param {number} speed - The speed at which it should move to the right, in pixels per second. + */ + moveRight: function (speed) { + + var fx = speed * this.game.time.physicsElapsed; + + this.shape.pos.x = this.shape.oldpos.x + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.x - this.shape.oldpos.x + fx)); + + }, + + /** + * If this Body is dynamic then this will move it up by setting its y velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveUp + * @param {number} speed - The speed at which it should move up, in pixels per second. + */ + moveUp: function (speed) { + + var fx = -speed * this.game.time.physicsElapsed; + + this.shape.pos.y = this.shape.oldpos.y + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.y - this.shape.oldpos.y + fx)); + + }, + + /** + * If this Body is dynamic then this will move it down by setting its y velocity to the given speed. + * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms). + * + * @method Phaser.Physics.Body#moveDown + * @param {number} speed - The speed at which it should move down, in pixels per second. + */ + moveDown: function (speed) { + + var fx = speed * this.game.time.physicsElapsed; + + this.shape.pos.y = this.shape.oldpos.y + Math.min(this.maxSpeed, Math.max(-this.maxSpeed, this.shape.pos.y - this.shape.oldpos.y + fx)); + + }, + + /** + * Resets all Body values and repositions on the Sprite. + * + * @method Phaser.Physics.Ninja.Body#reset + */ + reset: function () { + + this.velocity.set(0); + + this.shape.pos.x = this.sprite.x; + this.shape.pos.y = this.sprite.y; + + this.shape.oldpos.copyFrom(this.shape.pos); + + }, + + /** + * Returns the absolute delta x value. + * + * @method Phaser.Physics.Ninja.Body#deltaAbsX + * @return {number} The absolute delta value. + */ + deltaAbsX: function () { + return (this.deltaX() > 0 ? this.deltaX() : -this.deltaX()); + }, + + /** + * Returns the absolute delta y value. + * + * @method Phaser.Physics.Ninja.Body#deltaAbsY + * @return {number} The absolute delta value. + */ + deltaAbsY: function () { + return (this.deltaY() > 0 ? this.deltaY() : -this.deltaY()); + }, + + /** + * Returns the delta x value. The difference between Body.x now and in the previous step. + * + * @method Phaser.Physics.Ninja.Body#deltaX + * @return {number} The delta value. Positive if the motion was to the right, negative if to the left. + */ + deltaX: function () { + return this.shape.pos.x - this.shape.oldpos.x; + }, + + /** + * Returns the delta y value. The difference between Body.y now and in the previous step. + * + * @method Phaser.Physics.Ninja.Body#deltaY + * @return {number} The delta value. Positive if the motion was downwards, negative if upwards. + */ + deltaY: function () { + return this.shape.pos.y - this.shape.oldpos.y; + }, + + /** + * Destroys this body's reference to the sprite and system, and destroys its shape. + * + * @method Phaser.Physics.Ninja.Body#destroy + */ + destroy: function() { + this.sprite = null; + this.system = null; + this.aabb = null; + this.tile = null; + this.circle = null; + + this.shape.destroy(); + this.shape = null; + } +}; + +/** +* @name Phaser.Physics.Ninja.Body#x +* @property {number} x - The x position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "x", { + + get: function () { + return this.shape.pos.x; + }, + + set: function (value) { + this.shape.pos.x = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#y +* @property {number} y - The y position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "y", { + + get: function () { + return this.shape.pos.y; + }, + + set: function (value) { + this.shape.pos.y = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#width +* @property {number} width - The width of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "width", { + + get: function () { + return this.shape.width; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#height +* @property {number} height - The height of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "height", { + + get: function () { + return this.shape.height; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#bottom +* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "bottom", { + + get: function () { + return this.shape.pos.y + this.shape.yw; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#right +* @property {number} right - The right value of this Body (same as Body.x + Body.width) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "right", { + + get: function () { + return this.shape.pos.x + this.shape.xw; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#speed +* @property {number} speed - The speed of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "speed", { + + get: function () { + return Math.sqrt(this.shape.velocity.x * this.shape.velocity.x + this.shape.velocity.y * this.shape.velocity.y); + } + +}); + +/** +* @name Phaser.Physics.Ninja.Body#angle +* @property {number} angle - The angle of this Body +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Body.prototype, "angle", { + + get: function () { + return Math.atan2(this.shape.velocity.y, this.shape.velocity.x); + } + +}); + + +/* jshint camelcase: false */ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics AABB constructor. +* Note: This class could be massively optimised and reduced in size. I leave that challenge up to you. +* +* @class Phaser.Physics.Ninja.AABB +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Physics.Ninja.Body} body - The body that owns this shape. +* @param {number} x - The x coordinate to create this shape at. +* @param {number} y - The y coordinate to create this shape at. +* @param {number} width - The width of this AABB. +* @param {number} height - The height of this AABB. +*/ +Phaser.Physics.Ninja.AABB = function (body, x, y, width, height) { + + /** + * @property {Phaser.Physics.Ninja.Body} system - A reference to the body that owns this shape. + */ + this.body = body; + + /** + * @property {Phaser.Physics.Ninja} system - A reference to the physics system. + */ + this.system = body.system; + + /** + * @property {Phaser.Point} pos - The position of this object. + */ + this.pos = new Phaser.Point(x, y); + + /** + * @property {Phaser.Point} oldpos - The position of this object in the previous update. + */ + this.oldpos = new Phaser.Point(x, y); + + /** + * @property {number} xw - Half the width. + * @readonly + */ + this.xw = Math.abs(width / 2); + + /** + * @property {number} xw - Half the height. + * @readonly + */ + this.yw = Math.abs(height / 2); + + /** + * @property {number} width - The width. + * @readonly + */ + this.width = width; + + /** + * @property {number} height - The height. + * @readonly + */ + this.height = height; + + /** + * @property {number} oH - Internal var. + * @private + */ + this.oH = 0; + + /** + * @property {number} oV - Internal var. + * @private + */ + this.oV = 0; + + /** + * @property {Phaser.Point} velocity - The velocity of this object. + */ + this.velocity = new Phaser.Point(); + + /** + * @property {object} aabbTileProjections - All of the collision response handlers. + */ + this.aabbTileProjections = {}; + + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_FULL] = this.projAABB_Full; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_45DEG] = this.projAABB_45Deg; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONCAVE] = this.projAABB_Concave; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONVEX] = this.projAABB_Convex; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGs] = this.projAABB_22DegS; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGb] = this.projAABB_22DegB; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGs] = this.projAABB_67DegS; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGb] = this.projAABB_67DegB; + this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_HALF] = this.projAABB_Half; + +}; + +Phaser.Physics.Ninja.AABB.prototype.constructor = Phaser.Physics.Ninja.AABB; + +Phaser.Physics.Ninja.AABB.COL_NONE = 0; +Phaser.Physics.Ninja.AABB.COL_AXIS = 1; +Phaser.Physics.Ninja.AABB.COL_OTHER = 2; + +Phaser.Physics.Ninja.AABB.prototype = { + + /** + * Updates this AABBs position. + * + * @method Phaser.Physics.Ninja.AABB#integrate + */ + integrate: function () { + + var px = this.pos.x; + var py = this.pos.y; + + // integrate + this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); + this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); + + // store + this.velocity.set(this.pos.x - px, this.pos.y - py); + this.oldpos.set(px, py); + + }, + + /** + * Process a world collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.AABB#reportCollisionVsWorld + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this AABB collided with + */ + reportCollisionVsWorld: function (px, py, dx, dy) { + + var p = this.pos; + var o = this.oldpos; + + // Calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + // Find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; //project velocity onto collision normal + + var ny = dp * dy; //nx,ny is normal velocity + + var tx = vx - nx; //px,py is tangent velocity + var ty = vy - ny; + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; + + if (dp < 0) + { + fx = tx * this.body.friction; + fy = ty * this.body.friction; + + b = 1 + this.body.bounce; + + bx = (nx * b); + by = (ny * b); + + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } + } + else + { + // Moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + // Project object out of collision + p.x += px; + p.y += py; + + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; + o.y += py + by + fy; + + }, + + reverse: function () { + + var vx = this.pos.x - this.oldpos.x; + var vy = this.pos.y - this.oldpos.y; + + if (this.oldpos.x < this.pos.x) + { + this.oldpos.x = this.pos.x + vx; + // this.oldpos.x = this.pos.x + (vx + 1 + this.body.bounce); + } + else if (this.oldpos.x > this.pos.x) + { + this.oldpos.x = this.pos.x - vx; + // this.oldpos.x = this.pos.x - (vx + 1 + this.body.bounce); + } + + if (this.oldpos.y < this.pos.y) + { + this.oldpos.y = this.pos.y + vy; + // this.oldpos.y = this.pos.y + (vy + 1 + this.body.bounce); + } + else if (this.oldpos.y > this.pos.y) + { + this.oldpos.y = this.pos.y - vy; + // this.oldpos.y = this.pos.y - (vy + 1 + this.body.bounce); + } + + }, + + /** + * Process a body collision and apply the resulting forces. Still very much WIP and doesn't work fully. Feel free to fix! + * + * @method Phaser.Physics.Ninja.AABB#reportCollisionVsBody + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this AABB collided with + */ + reportCollisionVsBody: function (px, py, dx, dy, obj) { + + var vx1 = this.pos.x - this.oldpos.x; // Calc velocity of this object + var vy1 = this.pos.y - this.oldpos.y; + var dp1 = (vx1 * dx + vy1 * dy); // Find component of velocity parallel to collision normal + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + if (this.body.immovable && obj.body.immovable) + { + // Split the separation then return, no forces applied as they come to a stand-still + px *= 0.5; + py *= 0.5; + + this.pos.add(px, py); + this.oldpos.set(this.pos.x, this.pos.y); + + obj.pos.subtract(px, py); + obj.oldpos.set(obj.pos.x, obj.pos.y); + + return; + } + else if (!this.body.immovable && !obj.body.immovable) + { + // separate + px *= 0.5; + py *= 0.5; + + this.pos.add(px, py); + obj.pos.subtract(px, py); + + if (dp1 < 0) + { + this.reverse(); + obj.reverse(); + } + } + else if (!this.body.immovable) + { + this.pos.subtract(px, py); + + if (dp1 < 0) + { + this.reverse(); + } + } + else if (!obj.body.immovable) + { + obj.pos.subtract(px, py); + + if (dp1 < 0) + { + obj.reverse(); + } + } + + }, + + /** + * Collides this AABB against the world bounds. + * + * @method Phaser.Physics.Ninja.AABB#collideWorldBounds + */ + collideWorldBounds: function () { + + var dx = this.system.bounds.x - (this.pos.x - this.xw); + + if (0 < dx) + { + this.reportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + dx = (this.pos.x + this.xw) - this.system.bounds.right; + + if (0 < dx) + { + this.reportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + var dy = this.system.bounds.y - (this.pos.y - this.yw); + + if (0 < dy) + { + this.reportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + dy = (this.pos.y + this.yw) - this.system.bounds.bottom; + + if (0 < dy) + { + this.reportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + + }, + + /** + * Collides this AABB against a AABB. + * + * @method Phaser.Physics.Ninja.AABB#collideAABBVsAABB + * @param {Phaser.Physics.Ninja.AABB} aabb - The AABB to collide against. + */ + collideAABBVsAABB: function (aabb) { + + var pos = this.pos; + var c = aabb; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx;//tile->obj delta + var px = (txw + this.xw) - Math.abs(dx);//penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty;//tile->obj delta + var py = (tyw + this.yw) - Math.abs(dy);//pen depth in y + + if (0 < py) + { + //object may be colliding with tile; call tile-specific collision function + + //calculate projection vectors + if (px < py) + { + //project in x + if (dx < 0) + { + //project to the left + px *= -1; + py = 0; + } + else + { + //proj to right + py = 0; + } + } + else + { + //project in y + if (dy < 0) + { + //project up + px = 0; + py *= -1; + } + else + { + //project down + px = 0; + } + } + + var l = Math.sqrt(px * px + py * py); + this.reportCollisionVsBody(px, py, px / l, py / l, c); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + + } + } + + return false; + + }, + + /** + * Collides this AABB against a Tile. + * + * @method Phaser.Physics.Ninja.AABB#collideAABBVsTile + * @param {Phaser.Physics.Ninja.Tile} tile - The Tile to collide against. + */ + collideAABBVsTile: function (tile) { + + var dx = this.pos.x - tile.pos.x; // tile->obj delta + var px = (tile.xw + this.xw) - Math.abs(dx); // penetration depth in x + + if (0 < px) + { + var dy = this.pos.y - tile.pos.y; // tile->obj delta + var py = (tile.yw + this.yw) - Math.abs(dy); // pen depth in y + + if (0 < py) + { + // Calculate projection vectors + if (px < py) + { + // Project in x + if (dx < 0) + { + // Project to the left + px *= -1; + py = 0; + } + else + { + // Project to the right + py = 0; + } + } + else + { + // Project in y + if (dy < 0) + { + // Project up + px = 0; + py *= -1; + } + else + { + // Project down + px = 0; + } + } + + // Object may be colliding with tile; call tile-specific collision function + return this.resolveTile(px, py, this, tile); + } + } + + return false; + + }, + + /** + * Resolves tile collision. + * + * @method Phaser.Physics.Ninja.AABB#resolveTile + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} body - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} tile - The Tile involved in the collision. + * @return {boolean} True if the collision was processed, otherwise false. + */ + resolveTile: function (x, y, body, tile) { + + if (0 < tile.id) + { + return this.aabbTileProjections[tile.type](x, y, body, tile); + } + else + { + // console.warn("Ninja.AABB.resolveTile was called with an empty (or unknown) tile!: id=" + tile.id + ")"); + return false; + } + + }, + + /** + * Resolves Full tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Full + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Full: function (x, y, obj, t) { + + var l = Math.sqrt(x * x + y * y); + obj.reportCollisionVsWorld(x, y, x / l, y / l, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + + }, + + /** + * Resolves Half tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Half + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Half: function (x, y, obj, t) { + + //signx or signy must be 0; the other must be -1 or 1 + //calculate the projection vector for the half-edge, and then + //(if collision is occuring) pick the minimum + + var sx = t.signx; + var sy = t.signy; + + var ox = (obj.pos.x - (sx*obj.xw)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*obj.yw)) - t.pos.y;//point on the AABB, relative to the tile center + + //we perform operations analogous to the 45deg tile, except we're using + //an axis-aligned slope instead of an angled one.. + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + //project along axis; note that we're assuming that this tile is horizontal OR vertical + //relative to the AABB's current tile, and not diagonal OR the current tile. + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //note that we could use -= instead of -dp + obj.reportCollisionVsWorld(sx,sy,t.signx, t.signy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 45 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_45Deg + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_45Deg: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - t.pos.y;//point on the AABB, relative to the tile center + + var sx = t.sx; + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + //project along axis + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //project along slope + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_22DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_22DegS: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + //first we need to check to make sure we're colliding with the slope at all + var py = obj.pos.y - (signy*obj.yw); + var penY = t.pos.y - py;//this is the vector from the innermost point on the box to the highest point on + //the tile; if it is positive, this means the box is above the tile and + //no collision is occuring + if (0 < (penY*signy)) + { + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y - (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + var aY = Math.abs(penY); + + if (lenP < lenN) + { + if (aY < lenP) + { + obj.reportCollisionVsWorld(0, penY, 0, penY/aY, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + } + else + { + if (aY < lenN) + { + obj.reportCollisionVsWorld(0, penY, 0, penY/aY, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + } + } + + //if we've reached this point, no collision has occured + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_22DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_22DegB: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y + (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_67DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_67DegS: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var px = obj.pos.x - (signx*obj.xw); + var penX = t.pos.x - px; + + if (0 < (penX*signx)) + { + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y + (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need to project it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + var aX = Math.abs(penX); + + if (lenP < lenN) + { + if (aX < lenP) + { + obj.reportCollisionVsWorld(penX, 0, penX/aX, 0, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + } + else + { + if (aX < lenN) + { + obj.reportCollisionVsWorld(penX, 0, penX/aX, 0, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + } + } + + //if we've reached this point, no collision has occured + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_67DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_67DegB: function (x, y, obj, t) { + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx*obj.xw)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*obj.yw)) - (t.pos.y - (signy*t.yw));//point on the AABB, relative to a point on the slope + + var sx = t.sx;//get slope unit normal + var sy = t.sy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + }, + + /** + * Resolves Convex tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Convex + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Convex: function (x, y, obj, t) { + + //if distance from "innermost" corner of AABB is less than than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (obj.pos.x - (signx * obj.xw)) - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the circle center to + var oy = (obj.pos.y - (signy * obj.yw)) - (t.pos.y - (signy * t.yw));//the AABB + var len = Math.sqrt(ox * ox + oy * oy); + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var pen = rad - len; + + if (((signx * ox) < 0) || ((signy * oy) < 0)) + { + //the test corner is "outside" the 1/4 of the circle we're interested in + var lenP = Math.sqrt(x * x + y * y); + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS;//we need to report + } + else if (0 < pen) + { + //project along corner->circle vector + ox /= len; + oy /= len; + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Resolves Concave tile collision. + * + * @method Phaser.Physics.Ninja.AABB#projAABB_Concave + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {Phaser.Physics.Ninja.AABB} obj - The AABB involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projAABB_Concave: function (x, y, obj, t) { + + //if distance from "innermost" corner of AABB is further than tile radius, + //collision is occuring and we need to project + + var signx = t.signx; + var signy = t.signy; + + var ox = (t.pos.x + (signx * t.xw)) - (obj.pos.x - (signx * obj.xw));//(ox,oy) is the vector form the innermost AABB corner to the + var oy = (t.pos.y + (signy * t.yw)) - (obj.pos.y - (signy * obj.yw));//circle's center + + var twid = t.xw * 2; + var rad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = len - rad; + + if (0 < pen) + { + //collision; we need to either project along the axes, or project along corner->circlecenter vector + + var lenP = Math.sqrt(x * x + y * y); + + if (lenP < pen) + { + //it's shorter to move along axis directions + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.AABB.COL_AXIS; + } + else + { + //project along corner->circle vector + ox /= len;//len should never be 0, since if it IS 0, rad should be > than len + oy /= len;//and we should never reach here + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.AABB.COL_OTHER; + } + + } + + return Phaser.Physics.Ninja.AABB.COL_NONE; + + }, + + /** + * Destroys this AABB's reference to Body and System + * + * @method Phaser.Physics.Ninja.AABB#destroy + */ + destroy: function() { + this.body = null; + this.system = null; + } + +}; + +/* jshint camelcase: false */ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics Tile constructor. +* A Tile is defined by its width, height and type. It's type can include slope data, such as 45 degree slopes, or convex slopes. +* Understand that for any type including a slope (types 2 to 29) the Tile must be SQUARE, i.e. have an equal width and height. +* Also note that as Tiles are primarily used for levels they have gravity disabled and world bounds collision disabled by default. +* +* Note: This class could be massively optimised and reduced in size. I leave that challenge up to you. +* +* @class Phaser.Physics.Ninja.Tile +* @classdesc The Ninja Physics Tile class. Based on code by Metanet Software. +* @constructor +* @param {Phaser.Physics.Ninja.Body} body - The body that owns this shape. +* @param {number} x - The x coordinate to create this shape at. +* @param {number} y - The y coordinate to create this shape at. +* @param {number} width - The width of this AABB. +* @param {number} height - The height of this AABB. +* @param {number} [type=1] - The type of Ninja shape to create. 1 = AABB, 2 = Circle or 3 = Tile. +*/ +Phaser.Physics.Ninja.Tile = function (body, x, y, width, height, type) { + + if (typeof type === 'undefined') { type = Phaser.Physics.Ninja.Tile.EMPTY; } + + /** + * @property {Phaser.Physics.Ninja.Body} system - A reference to the body that owns this shape. + */ + this.body = body; + + /** + * @property {Phaser.Physics.Ninja} system - A reference to the physics system. + */ + this.system = body.system; + + /** + * @property {number} id - The ID of this Tile. + * @readonly + */ + this.id = type; + + /** + * @property {number} type - The type of this Tile. + * @readonly + */ + this.type = Phaser.Physics.Ninja.Tile.TYPE_EMPTY; + + /** + * @property {Phaser.Point} pos - The position of this object. + */ + this.pos = new Phaser.Point(x, y); + + /** + * @property {Phaser.Point} oldpos - The position of this object in the previous update. + */ + this.oldpos = new Phaser.Point(x, y); + + if (this.id > 1 && this.id < 30) + { + // Tile Types 2 to 29 require square tile dimensions, so use the width as the base + height = width; + } + + /** + * @property {number} xw - Half the width. + * @readonly + */ + this.xw = Math.abs(width / 2); + + /** + * @property {number} xw - Half the height. + * @readonly + */ + this.yw = Math.abs(height / 2); + + /** + * @property {number} width - The width. + * @readonly + */ + this.width = width; + + /** + * @property {number} height - The height. + * @readonly + */ + this.height = height; + + /** + * @property {Phaser.Point} velocity - The velocity of this object. + */ + this.velocity = new Phaser.Point(); + + /** + * @property {number} signx - Internal var. + * @private + */ + this.signx = 0; + + /** + * @property {number} signy - Internal var. + * @private + */ + this.signy = 0; + + /** + * @property {number} sx - Internal var. + * @private + */ + this.sx = 0; + + /** + * @property {number} sy - Internal var. + * @private + */ + this.sy = 0; + + // By default Tiles disable gravity and world bounds collision + this.body.gravityScale = 0; + this.body.collideWorldBounds = false; + + if (this.id > 0) + { + this.setType(this.id); + } + +}; + +Phaser.Physics.Ninja.Tile.prototype.constructor = Phaser.Physics.Ninja.Tile; + +Phaser.Physics.Ninja.Tile.prototype = { + + /** + * Updates this objects position. + * + * @method Phaser.Physics.Ninja.Tile#integrate + */ + integrate: function () { + + var px = this.pos.x; + var py = this.pos.y; + + this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); + this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); + + this.velocity.set(this.pos.x - px, this.pos.y - py); + this.oldpos.set(px, py); + + }, + + /** + * Tiles cannot collide with the world bounds, it's up to you to keep them where you want them. But we need this API stub to satisfy the Body. + * + * @method Phaser.Physics.Ninja.Tile#collideWorldBounds + */ + collideWorldBounds: function () { + + var dx = this.system.bounds.x - (this.pos.x - this.xw); + + if (0 < dx) + { + this.reportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + dx = (this.pos.x + this.xw) - this.system.bounds.right; + + if (0 < dx) + { + this.reportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + var dy = this.system.bounds.y - (this.pos.y - this.yw); + + if (0 < dy) + { + this.reportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + dy = (this.pos.y + this.yw) - this.system.bounds.bottom; + + if (0 < dy) + { + this.reportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + + }, + + /** + * Process a world collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.Tile#reportCollisionVsWorld + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this Tile collided with + */ + reportCollisionVsWorld: function (px, py, dx, dy) { + var p = this.pos; + var o = this.oldpos; + + // Calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + // Find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; //project velocity onto collision normal + + var ny = dp * dy; //nx,ny is normal velocity + + var tx = vx - nx; //px,py is tangent velocity + var ty = vy - ny; + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; + + if (dp < 0) + { + fx = tx * this.body.friction; + fy = ty * this.body.friction; + + b = 1 + this.body.bounce; + + bx = (nx * b); + by = (ny * b); + + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } + } + else + { + // Moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + // Project object out of collision + p.x += px; + p.y += py; + + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; + o.y += py + by + fy; + + }, + + /** + * Tiles cannot collide with the world bounds, it's up to you to keep them where you want them. But we need this API stub to satisfy the Body. + * + * @method Phaser.Physics.Ninja.Tile#setType + * @param {number} id - The type of Tile this will use, i.e. Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn, Phaser.Physics.Ninja.Tile.CONVEXpp, etc. + */ + setType: function (id) { + + if (id === Phaser.Physics.Ninja.Tile.EMPTY) + { + this.clear(); + } + else + { + this.id = id; + this.updateType(); + } + + return this; + + }, + + /** + * Sets this tile to be empty. + * + * @method Phaser.Physics.Ninja.Tile#clear + */ + clear: function () { + + this.id = Phaser.Physics.Ninja.Tile.EMPTY; + this.updateType(); + + }, + + /** + * Destroys this Tiles reference to Body and System. + * + * @method Phaser.Physics.Ninja.Tile#destroy + */ + destroy: function () { + + this.body = null; + this.system = null; + + }, + + /** + * This converts a tile from implicitly-defined (via id), to explicit (via properties). + * Don't call directly, instead of setType. + * + * @method Phaser.Physics.Ninja.Tile#updateType + * @private + */ + updateType: function () { + + if (this.id === 0) + { + //EMPTY + this.type = Phaser.Physics.Ninja.Tile.TYPE_EMPTY; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + + return true; + } + + //tile is non-empty; collide + if (this.id < Phaser.Physics.Ninja.Tile.TYPE_45DEG) + { + //FULL + this.type = Phaser.Physics.Ninja.Tile.TYPE_FULL; + this.signx = 0; + this.signy = 0; + this.sx = 0; + this.sy = 0; + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_CONCAVE) + { + // 45deg + this.type = Phaser.Physics.Ninja.Tile.TYPE_45DEG; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn) + { + this.signx = 1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGnn) + { + this.signx = -1; + this.signy = -1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGnp) + { + this.signx = -1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_45DEGpp) + { + this.signx = 1; + this.signy = 1; + this.sx = this.signx / Math.SQRT2;//get slope _unit_ normal + this.sy = this.signy / Math.SQRT2;//since normal is (1,-1), length is sqrt(1*1 + -1*-1) = sqrt(2) + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_CONVEX) + { + // Concave + this.type = Phaser.Physics.Ninja.Tile.TYPE_CONCAVE; + + if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONCAVEpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_22DEGs) + { + // Convex + this.type = Phaser.Physics.Ninja.Tile.TYPE_CONVEX; + + if (this.id == Phaser.Physics.Ninja.Tile.CONVEXpn) + { + this.signx = 1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXnn) + { + this.signx = -1; + this.signy = -1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXnp) + { + this.signx = -1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else if (this.id == Phaser.Physics.Ninja.Tile.CONVEXpp) + { + this.signx = 1; + this.signy = 1; + this.sx = 0; + this.sy = 0; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_22DEGb) + { + // 22deg small + this.type = Phaser.Physics.Ninja.Tile.TYPE_22DEGs; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_67DEGs) + { + // 22deg big + this.type = Phaser.Physics.Ninja.Tile.TYPE_22DEGb; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_22DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 1) / slen; + this.sy = (this.signy * 2) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_67DEGb) + { + // 67deg small + this.type = Phaser.Physics.Ninja.Tile.TYPE_67DEGs; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnS) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnS) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpS) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGppS) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + return false; + } + } + else if (this.id < Phaser.Physics.Ninja.Tile.TYPE_HALF) + { + // 67deg big + this.type = Phaser.Physics.Ninja.Tile.TYPE_67DEGb; + + if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnB) + { + this.signx = 1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnB) + { + this.signx = -1; + this.signy = -1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpB) + { + this.signx = -1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else if (this.id == Phaser.Physics.Ninja.Tile.SLOPE_67DEGppB) + { + this.signx = 1; + this.signy = 1; + var slen = Math.sqrt(2 * 2 + 1 * 1); + this.sx = (this.signx * 2) / slen; + this.sy = (this.signy * 1) / slen; + } + else + { + return false; + } + } + else + { + // Half-full tile + this.type = Phaser.Physics.Ninja.Tile.TYPE_HALF; + + if (this.id == Phaser.Physics.Ninja.Tile.HALFd) + { + this.signx = 0; + this.signy = -1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFu) + { + this.signx = 0; + this.signy = 1; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFl) + { + this.signx = 1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else if (this.id == Phaser.Physics.Ninja.Tile.HALFr) + { + this.signx = -1; + this.signy = 0; + this.sx = this.signx; + this.sy = this.signy; + } + else + { + return false; + } + } + } + +}; + +/** +* @name Phaser.Physics.Ninja.Tile#x +* @property {number} x - The x position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "x", { + + get: function () { + return this.pos.x - this.xw; + }, + + set: function (value) { + this.pos.x = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#y +* @property {number} y - The y position. +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "y", { + + get: function () { + return this.pos.y - this.yw; + }, + + set: function (value) { + this.pos.y = value; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#bottom +* @property {number} bottom - The bottom value of this Body (same as Body.y + Body.height) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "bottom", { + + get: function () { + return this.pos.y + this.yw; + } + +}); + +/** +* @name Phaser.Physics.Ninja.Tile#right +* @property {number} right - The right value of this Body (same as Body.x + Body.width) +* @readonly +*/ +Object.defineProperty(Phaser.Physics.Ninja.Tile.prototype, "right", { + + get: function () { + return this.pos.x + this.xw; + } + +}); + +Phaser.Physics.Ninja.Tile.EMPTY = 0; +Phaser.Physics.Ninja.Tile.FULL = 1;//fullAABB tile +Phaser.Physics.Ninja.Tile.SLOPE_45DEGpn = 2;//45-degree triangle, whose normal is (+ve,-ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGnn = 3;//(+ve,+ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGnp = 4;//(-ve,+ve) +Phaser.Physics.Ninja.Tile.SLOPE_45DEGpp = 5;//(-ve,-ve) +Phaser.Physics.Ninja.Tile.CONCAVEpn = 6;//1/4-circle cutout +Phaser.Physics.Ninja.Tile.CONCAVEnn = 7; +Phaser.Physics.Ninja.Tile.CONCAVEnp = 8; +Phaser.Physics.Ninja.Tile.CONCAVEpp = 9; +Phaser.Physics.Ninja.Tile.CONVEXpn = 10;//1/4/circle +Phaser.Physics.Ninja.Tile.CONVEXnn = 11; +Phaser.Physics.Ninja.Tile.CONVEXnp = 12; +Phaser.Physics.Ninja.Tile.CONVEXpp = 13; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnS = 14;//22.5 degree slope +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnS = 15; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpS = 16; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGppS = 17; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGpnB = 18; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnnB = 19; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGnpB = 20; +Phaser.Physics.Ninja.Tile.SLOPE_22DEGppB = 21; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnS = 22;//67.5 degree slope +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnS = 23; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpS = 24; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGppS = 25; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGpnB = 26; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnnB = 27; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGnpB = 28; +Phaser.Physics.Ninja.Tile.SLOPE_67DEGppB = 29; +Phaser.Physics.Ninja.Tile.HALFd = 30;//half-full tiles +Phaser.Physics.Ninja.Tile.HALFr = 31; +Phaser.Physics.Ninja.Tile.HALFu = 32; +Phaser.Physics.Ninja.Tile.HALFl = 33; + +Phaser.Physics.Ninja.Tile.TYPE_EMPTY = 0; +Phaser.Physics.Ninja.Tile.TYPE_FULL = 1; +Phaser.Physics.Ninja.Tile.TYPE_45DEG = 2; +Phaser.Physics.Ninja.Tile.TYPE_CONCAVE = 6; +Phaser.Physics.Ninja.Tile.TYPE_CONVEX = 10; +Phaser.Physics.Ninja.Tile.TYPE_22DEGs = 14; +Phaser.Physics.Ninja.Tile.TYPE_22DEGb = 18; +Phaser.Physics.Ninja.Tile.TYPE_67DEGs = 22; +Phaser.Physics.Ninja.Tile.TYPE_67DEGb = 26; +Phaser.Physics.Ninja.Tile.TYPE_HALF = 30; + +/* jshint camelcase: false */ +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Ninja Physics Circle constructor. +* Note: This class could be massively optimised and reduced in size. I leave that challenge up to you. +* +* @class Phaser.Physics.Ninja.Circle +* @classdesc Arcade Physics Constructor +* @constructor +* @param {Phaser.Physics.Ninja.Body} body - The body that owns this shape. +* @param {number} x - The x coordinate to create this shape at. +* @param {number} y - The y coordinate to create this shape at. +* @param {number} radius - The radius of this Circle. +*/ +Phaser.Physics.Ninja.Circle = function (body, x, y, radius) { + + /** + * @property {Phaser.Physics.Ninja.Body} system - A reference to the body that owns this shape. + */ + this.body = body; + + /** + * @property {Phaser.Physics.Ninja} system - A reference to the physics system. + */ + this.system = body.system; + + /** + * @property {Phaser.Point} pos - The position of this object. + */ + this.pos = new Phaser.Point(x, y); + + /** + * @property {Phaser.Point} oldpos - The position of this object in the previous update. + */ + this.oldpos = new Phaser.Point(x, y); + + /** + * @property {number} radius - The radius of this circle shape. + */ + this.radius = radius; + + /** + * @property {number} xw - Half the width. + * @readonly + */ + this.xw = radius; + + /** + * @property {number} xw - Half the height. + * @readonly + */ + this.yw = radius; + + /** + * @property {number} width - The width. + * @readonly + */ + this.width = radius * 2; + + /** + * @property {number} height - The height. + * @readonly + */ + this.height = radius * 2; + + /** + * @property {number} oH - Internal var. + * @private + */ + this.oH = 0; + + /** + * @property {number} oV - Internal var. + * @private + */ + this.oV = 0; + + /** + * @property {Phaser.Point} velocity - The velocity of this object. + */ + this.velocity = new Phaser.Point(); + + /** + * @property {object} circleTileProjections - All of the collision response handlers. + */ + this.circleTileProjections = {}; + + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_FULL] = this.projCircle_Full; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_45DEG] = this.projCircle_45Deg; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONCAVE] = this.projCircle_Concave; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONVEX] = this.projCircle_Convex; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGs] = this.projCircle_22DegS; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGb] = this.projCircle_22DegB; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGs] = this.projCircle_67DegS; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGb] = this.projCircle_67DegB; + this.circleTileProjections[Phaser.Physics.Ninja.Tile.TYPE_HALF] = this.projCircle_Half; + +}; + +Phaser.Physics.Ninja.Circle.prototype.constructor = Phaser.Physics.Ninja.Circle; + +Phaser.Physics.Ninja.Circle.COL_NONE = 0; +Phaser.Physics.Ninja.Circle.COL_AXIS = 1; +Phaser.Physics.Ninja.Circle.COL_OTHER = 2; + +Phaser.Physics.Ninja.Circle.prototype = { + + /** + * Updates this Circles position. + * + * @method Phaser.Physics.Ninja.Circle#integrate + */ + integrate: function () { + + var px = this.pos.x; + var py = this.pos.y; + + // integrate + this.pos.x += (this.body.drag * this.pos.x) - (this.body.drag * this.oldpos.x); + this.pos.y += (this.body.drag * this.pos.y) - (this.body.drag * this.oldpos.y) + (this.system.gravity * this.body.gravityScale); + + // store + this.velocity.set(this.pos.x - px, this.pos.y - py); + this.oldpos.set(px, py); + + }, + + /** + * Process a world collision and apply the resulting forces. + * + * @method Phaser.Physics.Ninja.Circle#reportCollisionVsWorld + * @param {number} px - The tangent velocity + * @param {number} py - The tangent velocity + * @param {number} dx - Collision normal + * @param {number} dy - Collision normal + * @param {number} obj - Object this Circle collided with + */ + reportCollisionVsWorld: function (px, py, dx, dy) { + + var p = this.pos; + var o = this.oldpos; + + // Calc velocity + var vx = p.x - o.x; + var vy = p.y - o.y; + + // Find component of velocity parallel to collision normal + var dp = (vx * dx + vy * dy); + var nx = dp * dx; //project velocity onto collision normal + + var ny = dp * dy; //nx,ny is normal velocity + + var tx = vx - nx; //px,py is tangent velocity + var ty = vy - ny; + + // We only want to apply collision response forces if the object is travelling into, and not out of, the collision + var b, bx, by, fx, fy; + + if (dp < 0) + { + fx = tx * this.body.friction; + fy = ty * this.body.friction; + + b = 1 + this.body.bounce; + + bx = (nx * b); + by = (ny * b); + + if (dx === 1) + { + this.body.touching.left = true; + } + else if (dx === -1) + { + this.body.touching.right = true; + } + + if (dy === 1) + { + this.body.touching.up = true; + } + else if (dy === -1) + { + this.body.touching.down = true; + } + } + else + { + // Moving out of collision, do not apply forces + bx = by = fx = fy = 0; + } + + // Project object out of collision + p.x += px; + p.y += py; + + // Apply bounce+friction impulses which alter velocity + o.x += px + bx + fx; + o.y += py + by + fy; + + }, + + /** + * Collides this Circle against the world bounds. + * + * @method Phaser.Physics.Ninja.Circle#collideWorldBounds + */ + collideWorldBounds: function () { + + var dx = this.system.bounds.x - (this.pos.x - this.radius); + + if (0 < dx) + { + this.reportCollisionVsWorld(dx, 0, 1, 0, null); + } + else + { + dx = (this.pos.x + this.radius) - this.system.bounds.right; + + if (0 < dx) + { + this.reportCollisionVsWorld(-dx, 0, -1, 0, null); + } + } + + var dy = this.system.bounds.y - (this.pos.y - this.radius); + + if (0 < dy) + { + this.reportCollisionVsWorld(0, dy, 0, 1, null); + } + else + { + dy = (this.pos.y + this.radius) - this.system.bounds.bottom; + + if (0 < dy) + { + this.reportCollisionVsWorld(0, -dy, 0, -1, null); + } + } + + }, + + /** + * Collides this Circle with a Tile. + * + * @method Phaser.Physics.Ninja.Circle#collideCircleVsTile + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {boolean} True if they collide, otherwise false. + */ + collideCircleVsTile: function (tile) { + + var pos = this.pos; + var r = this.radius; + var c = tile; + + var tx = c.pos.x; + var ty = c.pos.y; + var txw = c.xw; + var tyw = c.yw; + + var dx = pos.x - tx; // tile->obj delta + var px = (txw + r) - Math.abs(dx); // penetration depth in x + + if (0 < px) + { + var dy = pos.y - ty; // tile->obj delta + var py = (tyw + r) - Math.abs(dy); // pen depth in y + + if (0 < py) + { + // object may be colliding with tile + + // determine grid/voronoi region of circle center + this.oH = 0; + this.oV = 0; + + if (dx < -txw) + { + // circle is on left side of tile + this.oH = -1; + } + else if (txw < dx) + { + // circle is on right side of tile + this.oH = 1; + } + + if (dy < -tyw) + { + // circle is on top side of tile + this.oV = -1; + } + else if (tyw < dy) + { + // circle is on bottom side of tile + this.oV = 1; + } + + return this.resolveCircleTile(px, py, this.oH, this.oV, this, c); + + } + } + }, + + /** + * Resolves tile collision. + * + * @method Phaser.Physics.Ninja.Circle#resolveCircleTile + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + resolveCircleTile: function (x, y, oH, oV, obj, t) { + + if (0 < t.id) + { + return this.circleTileProjections[t.type](x, y, oH, oV, obj, t); + } + else + { + // console.log("ResolveCircleTile() was called with an empty (or unknown) tile!: ID=" + t.id + ")"); + return false; + } + + }, + + /** + * Resolves Full tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_Full + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_Full: function (x, y, oH, oV, obj, t) { + + //if we're colliding vs. the current cell, we need to project along the + //smallest penetration vector. + //if we're colliding vs. horiz. or vert. neighb, we simply project horiz/vert + //if we're colliding diagonally, we need to collide vs. tile corner + + if (oH === 0) + { + if (oV === 0) + { + //collision with current cell + if (x < y) + { + //penetration in x is smaller; project in x + var dx = obj.pos.x - t.pos.x;//get sign for projection along x-axis + + //NOTE: should we handle the delta === 0 case?! and how? (project towards oldpos?) + if (dx < 0) + { + obj.reportCollisionVsWorld(-x, 0, -1, 0, t); + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(x, 0, 1, 0, t); + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + } + else + { + //penetration in y is smaller; project in y + var dy = obj.pos.y - t.pos.y;//get sign for projection along y-axis + + //NOTE: should we handle the delta === 0 case?! and how? (project towards oldpos?) + if (dy < 0) + { + obj.reportCollisionVsWorld(0, -y, 0, -1, t); + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(0, y, 0, 1, t); + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + } + } + else + { + //collision with vertical neighbor + obj.reportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + } + else if (oV === 0) + { + //collision with horizontal neighbor + obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t); + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //diagonal collision + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves 45 Degree tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_45Deg + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_45Deg: function (x, y, oH, oV, obj, t) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb-ve-45deg + //if obj is horiz OR very neighb in direction of slope: collide only vs. slope + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + + var sx = t.sx; + var sy = t.sy; + + var ox = (obj.pos.x - (sx * obj.radius)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy * obj.radius)) - t.pos.y;//point on the circle, relative to the tile center + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the innermost point is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox * sx) + (oy * sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this as the slope penetration vector + sx *= -dp;//(sx,sy) is now the penetration vector + sy *= -dp; + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + var lenN = Math.sqrt(sx * sx + sy * sy); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (oV * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if (0 < (perp * signx * signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + } + else if (oV === 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH * t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox * -sy) + (oy * sx); + if ((perp * signx * signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox * ox + oy * oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox * sx) + (oy * sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx * pen, sy * pen, sx, sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + }, + + /** + * Resolves Concave tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_Concave + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_Concave: function (x, y, oH, oV, obj, t) { + + //if we're colliding diagonally: + // -if obj is in the diagonal pointed to by the slope normal: we can't collide, do nothing + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz OR very neighb in direction of slope: collide vs vert + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + + var ox = (t.pos.x + (signx * t.xw)) - obj.pos.x;//(ox,oy) is the vector from the circle to + var oy = (t.pos.y + (signy * t.yw)) - obj.pos.y;//tile-circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (len + obj.radius) - trad; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we can assume that len >0, because if we're here then + //(len + obj.radius) > trad, and since obj.radius <= trad + //len MUST be > 0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the vertical tip + + //get diag vertex position + var vx = t.pos.x - (signx * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out vertically + dx = 0; + dy = oV; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + else if (oV === 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the horizontal tip + + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y - (signy * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out horizontally + dx = oH; + dy = 0; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal, and + //it cannot possibly reach/touch/penetrate the slope + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves Convex tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_Convex + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_Convex: function (x, y, oH, oV, obj, t) { + + //if the object is horiz AND/OR vertical neighbor in the normal (signx,signy) + //direction, collide vs. tile-circle only. + //if we're colliding diagonally: + // -else, collide vs. the appropriate vertex + //if obj is in this tile: perform collision as for aabb + //if obj is horiz or vert neigh against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + var lenP; + + if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y) < 0) + { + y *= -1; + } + } + + + if (lenP < pen) + { + obj.reportCollisionVsWorld(x, y, x / lenP, y / lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //note: len should NEVER be === 0, because if it is, + //projeciton by an axis shoudl always be shorter, and we should + //never arrive here + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + + } + } + } + else + { + //colliding vertically + if ((signy * oV) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(0, y * oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be === 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + else if (oV === 0) + { + //colliding horizontally + if ((signx * oH) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(x * oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //obj in neighboring cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be === 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + else + { + //colliding diagonally + if (0 < ((signx * oH) + (signy * oV))) + { + //obj in diag neighb cell pointed at by tile normal; + //we could only be colliding vs the tile-circle surface + + var ox = obj.pos.x - (t.pos.x - (signx * t.xw));//(ox,oy) is the vector from the tile-circle to + var oy = obj.pos.y - (t.pos.y - (signy * t.yw));//the circle's center + + var twid = t.xw * 2; + var trad = Math.sqrt(twid * twid + 0);//this gives us the radius of a circle centered on the tile's corner and extending to the opposite edge of the tile; + //note that this should be precomputed at compile-time since it's constant + + var len = Math.sqrt(ox * ox + oy * oy); + var pen = (trad + obj.radius) - len; + + if (0 < pen) + { + + //note: len should NEVER be === 0, because if it is, + //obj is not in a neighboring cell! + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox * pen, oy * pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH * t.xw); + var vy = t.pos.y + (oV * t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx * dx + dy * dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx * pen, dy * pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves Half tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_Half + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_Half: function (x,y,oH,oV,obj,t) { + + //if obj is in a neighbor pointed at by the halfedge normal, + //we'll never collide (i.e if the normal is (0,1) and the obj is in the DL.D, or R neighbors) + // + //if obj is in a neigbor perpendicular to the halfedge normal, it might + //collide with the halfedge-vertex, or with the halfedge side. + // + //if obj is in a neigb pointing opposite the halfedge normal, obj collides with edge + // + //if obj is in a diagonal (pointing away from the normal), obj collides vs vertex + // + //if obj is in the halfedge cell, it collides as with aabb + + var signx = t.signx; + var signy = t.signy; + + var celldp = (oH*signx + oV*signy);//this tells us about the configuration of cell-offset relative to tile normal + if (0 < celldp) + { + //obj is in "far" (pointed-at-by-normal) neighbor of halffull tile, and will never hit + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + var r = obj.radius; + var ox = (obj.pos.x - (signx*r)) - t.pos.x;//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (signy*r)) - t.pos.y;//point on the circle, relative to the tile center + + + //we perform operations analogous to the 45deg tile, except we're using + //an axis-aligned slope instead of an angled one.. + var sx = signx; + var sy = signy; + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the corner is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + + var lenN = Math.sqrt(sx*sx + sy*sy); + var lenP = Math.sqrt(x*x + y*y); + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP,t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.signx,t.signy); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + return true; + } + + } + else + { + //colliding vertically + + if (celldp === 0) + { + + var dx = obj.pos.x - t.pos.x; + + //we're in a cell perpendicular to the normal, and can collide vs. halfedge vertex + //or halfedge side + if ((dx*signx) < 0) + { + //collision with halfedge side + obj.reportCollisionVsWorld(0,y*oV,0,oV,t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //collision with halfedge vertex + var dy = obj.pos.y - (t.pos.y + oV*t.yw);//(dx,dy) is now the vector from the appropriate halfedge vertex to the circle + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = signx / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + else + { + //due to the first conditional (celldp >0), we know we're in the cell "opposite" the normal, and so + //we can only collide with the cell edge + //collision with vertical neighbor + obj.reportCollisionVsWorld(0,y*oV,0,oV,t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + + } + } + else if (oV === 0) + { + //colliding horizontally + if (celldp === 0) + { + + var dy = obj.pos.y - t.pos.y; + + //we're in a cell perpendicular to the normal, and can collide vs. halfedge vertex + //or halfedge side + if ((dy*signy) < 0) + { + //collision with halfedge side + obj.reportCollisionVsWorld(x*oH,0,oH,0,t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //collision with halfedge vertex + var dx = obj.pos.x - (t.pos.x + oH*t.xw);//(dx,dy) is now the vector from the appropriate halfedge vertex to the circle + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = signx / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + else + { + //due to the first conditional (celldp >0), we know w're in the cell "opposite" the normal, and so + //we can only collide with the cell edge + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + } + else + { + //colliding diagonally; we know, due to the initial (celldp >0) test which has failed + //if we've reached this point, that we're in a diagonal neighbor on the non-normal side, so + //we could only be colliding with the cell vertex, if at all. + + //get diag vertex position + var vx = t.pos.x + (oH*t.xw); + var vy = t.pos.y + (oV*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_22DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_22DegS: function (x,y,oH,oV,obj,t) { + + //if the object is in a cell pointed at by signy, no collision will ever occur + //otherwise, + // + //if we're colliding diagonally: + // -collide vs. the appropriate vertex + //if obj is in this tile: collide vs slope or vertex + //if obj is horiz neighb in direction of slope: collide vs. slope or vertex + //if obj is horiz neighb against the slope: + // if (distance in y from circle to 90deg corner of tile < 1/2 tileheight, collide vs. face) + // else(collide vs. corner of slope) (vert collision with a non-grid-aligned vert) + //if obj is vert neighb against direction of slope: collide vs. face + + var lenP; + var signx = t.signx; + var signy = t.signy; + + if (0 < (signy*oV)) + { + //object will never collide vs tile, it can't reach that far + + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var r = obj.radius; + var ox = obj.pos.x - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - t.pos.y;//point on the circle, relative to the tile corner + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal or axially. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + + var perp = (ox*-sy) + (oy*sx); + if (0 < (perp*signx*signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = r - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope or vs axis + ox -= r*sx;//this gives us the vector from + oy -= r*sy;//a point on the slope to the innermost point on the circle + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y)< 0) + { + y *= -1; + } + } + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + + } + else + { + //colliding vertically; we can assume that (signy*oV) < 0 + //due to the first conditional far above + + obj.reportCollisionVsWorld(0,y*oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + } + else if (oV === 0) + { + //colliding horizontally + if ((signx*oH) < 0) + { + //colliding with face/edge OR with corner of wedge, depending on our position vertically + + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x - (signx*t.xw); + var vy = t.pos.y; + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + if ((dy*signy) < 0) + { + //colliding vs face + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding vs. vertex + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x + (oH*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy*t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal, otherwise by the vertex. + //(NOTE: this is the opposite logic of the vertical case; + // for vertical, if the perp prod and the slope's slope agree, it's outside. + // for horizontal, if the perp prod and the slope's slope agree, circle is inside. + // ..but this is only a property of flahs' coord system (i.e the rules might swap + // in righthanded systems)) + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if ((perp*signx*signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen, sx, sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + else + { + + //colliding diagonally; due to the first conditional above, + //obj is vertically offset against slope, and offset in either direction horizontally + + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH*t.xw); + var vy = t.pos.y + (oV*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves 22 Degree tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_22DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_22DegB: function (x,y,oH, oV, obj,t) { + + //if we're colliding diagonally: + // -if we're in the cell pointed at by the normal, collide vs slope, else + // collide vs. the appropriate corner/vertex + // + //if obj is in this tile: collide as with aabb + // + //if obj is horiz or vertical neighbor AGAINST the slope: collide with edge + // + //if obj is horiz neighb in direction of slope: collide vs. slope or vertex or edge + // + //if obj is vert neighb in direction of slope: collide vs. slope or vertex + + var lenP; + var signx = t.signx; + var signy = t.signy; + + if (oH === 0) + { + if (oV === 0) + { + //colliding with current cell + + var sx = t.sx; + var sy = t.sy; + + var r = obj.radius; + var ox = (obj.pos.x - (sx*r)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*r)) - (t.pos.y + (signy*t.yw));//point on the AABB, relative to a point on the slope + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y)< 0) + { + y *= -1; + } + } + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x, y, x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + else + { + //colliding vertically + + if ((signy*oV) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(0, y*oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (signy*t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if (0 < (perp*signx*signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen,sx, sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + } + else if (oV === 0) + { + //colliding horizontally + + if ((signx*oH) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding with edge, slope, or vertex + + var ox = obj.pos.x - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - t.pos.y;//point on the circle, relative to the closest tile vert + + if ((oy*signy) < 0) + { + //we're colliding with the halfface + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding with the vertex or slope + + var sx = t.sx; + var sy = t.sy; + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the slope, otherwise by the vertex. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if ((perp*signx*signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + } + else + { + //colliding diagonally + if ( 0 < ((signx*oH) + (signy*oV)) ) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal. + + //collide vs slope + + //we should really precalc this at compile time, but for now, fuck it + var slen = Math.sqrt(2*2 + 1*1);//the raw slope is (-2,-1) + var sx = (signx*1) / slen;//get slope _unit_ normal; + var sy = (signy*2) / slen;//raw RH normal is (1,-2) + + var r = obj.radius; + var ox = (obj.pos.x - (sx*r)) - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*r)) - (t.pos.y + (signy*t.yw));//point on the circle, relative to a point on the slope + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + //(sx,sy)*-dp is the projection vector + obj.reportCollisionVsWorld(-sx*dp, -sy*dp, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else + { + //collide vs the appropriate vertex + var vx = t.pos.x + (oH*t.xw); + var vy = t.pos.y + (oV*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_67DegS + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_67DegS: function (x,y,oH,oV,obj,t) { + + //if the object is in a cell pointed at by signx, no collision will ever occur + //otherwise, + // + //if we're colliding diagonally: + // -collide vs. the appropriate vertex + //if obj is in this tile: collide vs slope or vertex or axis + //if obj is vert neighb in direction of slope: collide vs. slope or vertex + //if obj is vert neighb against the slope: + // if (distance in y from circle to 90deg corner of tile < 1/2 tileheight, collide vs. face) + // else(collide vs. corner of slope) (vert collision with a non-grid-aligned vert) + //if obj is horiz neighb against direction of slope: collide vs. face + + var signx = t.signx; + var signy = t.signy; + + if (0 < (signx*oH)) + { + //object will never collide vs tile, it can't reach that far + + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else if (oH === 0) + { + if (oV === 0) + { + //colliding with current tile + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var lenP; + var sx = t.sx; + var sy = t.sy; + + var r = obj.radius; + var ox = obj.pos.x - t.pos.x;//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy*t.yw));//point on the circle, relative to the tile corner + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the normal or axis, otherwise by the corner/vertex + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronoi region, or that of the vertex. + + var perp = (ox*-sy) + (oy*sx); + if ((perp*signx*signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = r - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope or vs axis + ox -= r*sx;//this gives us the vector from + oy -= r*sy;//a point on the slope to the innermost point on the circle + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y)< 0) + { + y *= -1; + } + } + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx,sy,t.sx,t.sy,t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + + } + else + { + //colliding vertically + + if ((signy*oV) < 0) + { + //colliding with face/edge OR with corner of wedge, depending on our position vertically + + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x; + var vy = t.pos.y - (signy*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + if ((dx*signx) < 0) + { + //colliding vs face + obj.reportCollisionVsWorld(0, y*oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding vs. vertex + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var sx = t.sx; + var sy = t.sy; + + var ox = obj.pos.x - (t.pos.x - (signx*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (oV*t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the normal. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if (0 < (perp*signx*signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + } + else if (oV === 0) + { + //colliding horizontally; we can assume that (signy*oV) < 0 + //due to the first conditional far above + + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding diagonally; due to the first conditional above, + //obj is vertically offset against slope, and offset in either direction horizontally + + //collide vs. vertex + //get diag vertex position + var vx = t.pos.x + (oH*t.xw); + var vy = t.pos.y + (oV*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + + }, + + /** + * Resolves 67 Degree tile collision. + * + * @method Phaser.Physics.Ninja.Circle#projCircle_67DegB + * @param {number} x - Penetration depth on the x axis. + * @param {number} y - Penetration depth on the y axis. + * @param {number} oH - Grid / voronoi region. + * @param {number} oV - Grid / voronoi region. + * @param {Phaser.Physics.Ninja.Circle} obj - The Circle involved in the collision. + * @param {Phaser.Physics.Ninja.Tile} t - The Tile involved in the collision. + * @return {number} The result of the collision. + */ + projCircle_67DegB: function (x,y,oH, oV, obj,t) { + + //if we're colliding diagonally: + // -if we're in the cell pointed at by the normal, collide vs slope, else + // collide vs. the appropriate corner/vertex + // + //if obj is in this tile: collide as with aabb + // + //if obj is horiz or vertical neighbor AGAINST the slope: collide with edge + // + //if obj is vert neighb in direction of slope: collide vs. slope or vertex or halfedge + // + //if obj is horiz neighb in direction of slope: collide vs. slope or vertex + + var signx = t.signx; + var signy = t.signy; + + if (oH === 0) + { + if (oV === 0) + { + //colliding with current cell + + var lenP; + var sx = t.sx; + var sy = t.sy; + + var r = obj.radius; + var ox = (obj.pos.x - (sx*r)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*r)) - (t.pos.y - (signy*t.yw));//point on the AABB, relative to a point on the slope + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + sx *= -dp;//(sx,sy) is now the projection vector + sy *= -dp; + + var lenN = Math.sqrt(sx*sx + sy*sy); + + //find the smallest axial projection vector + if (x < y) + { + //penetration in x is smaller + lenP = x; + y = 0; + //get sign for projection along x-axis + if ((obj.pos.x - t.pos.x) < 0) + { + x *= -1; + } + } + else + { + //penetration in y is smaller + lenP = y; + x = 0; + //get sign for projection along y-axis + if ((obj.pos.y - t.pos.y)< 0) + { + y *= -1; + } + } + + if (lenP < lenN) + { + obj.reportCollisionVsWorld(x,y,x/lenP, y/lenP, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + obj.reportCollisionVsWorld(sx, sy, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + else + { + //colliding vertically + + if ((signy*oV) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(0, y*oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding with edge, slope, or vertex + + var ox = obj.pos.x - t.pos.x;//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y + (signy*t.yw));//point on the circle, relative to the closest tile vert + + if ((ox*signx) < 0) + { + //we're colliding with the halfface + obj.reportCollisionVsWorld(0, y*oV, 0, oV, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //colliding with the vertex or slope + + var sx = t.sx; + var sy = t.sy; + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the vertex, otherwise by the slope. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if (0 < (perp*signx*signy)) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen, sx, sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + } + } + else if (oV === 0) + { + //colliding horizontally + + if ((signx*oH) < 0) + { + //colliding with face/edge + obj.reportCollisionVsWorld(x*oH, 0, oH, 0, t); + + return Phaser.Physics.Ninja.Circle.COL_AXIS; + } + else + { + //we could only be colliding vs the slope OR a vertex + //look at the vector form the closest vert to the circle to decide + + var slen = Math.sqrt(2*2 + 1*1);//the raw slope is (-2,-1) + var sx = (signx*2) / slen;//get slope _unit_ normal; + var sy = (signy*1) / slen;//raw RH normal is (1,-2) + + var ox = obj.pos.x - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = obj.pos.y - (t.pos.y - (signy*t.yw));//point on the circle, relative to the closest tile vert + + //if the component of (ox,oy) parallel to the normal's righthand normal + //has the same sign as the slope of the slope (the sign of the slope's slope is signx*signy) + //then we project by the slope, otherwise by the vertex. + //note that this is simply a VERY tricky/weird method of determining + //if the circle is in side the slope/face's voronio region, or that of the vertex. + var perp = (ox*-sy) + (oy*sx); + if ((perp*signx*signy) < 0) + { + //collide vs. vertex + var len = Math.sqrt(ox*ox + oy*oy); + var pen = obj.radius - len; + if (0 < pen) + { + //note: if len=0, then perp=0 and we'll never reach here, so don't worry about div-by-0 + ox /= len; + oy /= len; + + obj.reportCollisionVsWorld(ox*pen, oy*pen, ox, oy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + else + { + //collide vs. slope + + //if the component of (ox,oy) parallel to the normal is less than the circle radius, we're + //penetrating the slope. note that this method of penetration calculation doesn't hold + //in general (i.e it won't work if the circle is in the slope), but works in this case + //because we know the circle is in a neighboring cell + var dp = (ox*sx) + (oy*sy); + var pen = obj.radius - Math.abs(dp);//note: we don't need the abs because we know the dp will be positive, but just in case.. + if (0 < pen) + { + //collision; circle out along normal by penetration amount + obj.reportCollisionVsWorld(sx*pen, sy*pen, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + } + } + } + else + { + //colliding diagonally + if ( 0 < ((signx*oH) + (signy*oV)) ) + { + //the dotprod of slope normal and cell offset is strictly positive, + //therefore obj is in the diagonal neighb pointed at by the normal. + + //collide vs slope + + var sx = t.sx; + var sy = t.sy; + + var r = obj.radius; + var ox = (obj.pos.x - (sx*r)) - (t.pos.x + (signx*t.xw));//this gives is the coordinates of the innermost + var oy = (obj.pos.y - (sy*r)) - (t.pos.y - (signy*t.yw));//point on the circle, relative to a point on the slope + + //if the dotprod of (ox,oy) and (sx,sy) is negative, the point on the circle is in the slope + //and we need toproject it out by the magnitude of the projection of (ox,oy) onto (sx,sy) + var dp = (ox*sx) + (oy*sy); + + if (dp < 0) + { + //collision; project delta onto slope and use this to displace the object + //(sx,sy)*-dp is the projection vector + + obj.reportCollisionVsWorld(-sx*dp, -sy*dp, t.sx, t.sy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + return Phaser.Physics.Ninja.Circle.COL_NONE; + } + else + { + + //collide vs the appropriate vertex + var vx = t.pos.x + (oH*t.xw); + var vy = t.pos.y + (oV*t.yw); + + var dx = obj.pos.x - vx;//calc vert->circle vector + var dy = obj.pos.y - vy; + + var len = Math.sqrt(dx*dx + dy*dy); + var pen = obj.radius - len; + if (0 < pen) + { + //vertex is in the circle; project outward + if (len === 0) + { + //project out by 45deg + dx = oH / Math.SQRT2; + dy = oV / Math.SQRT2; + } + else + { + dx /= len; + dy /= len; + } + + obj.reportCollisionVsWorld(dx*pen, dy*pen, dx, dy, t); + + return Phaser.Physics.Ninja.Circle.COL_OTHER; + } + + } + } + + return Phaser.Physics.Ninja.Circle.COL_NONE; + }, + + /** + * Destroys this Circle's reference to Body and System + * + * @method Phaser.Physics.Ninja.Circle#destroy + */ + destroy: function() { + this.body = null; + this.system = null; + } + +}; diff --git a/bower_components/phaser-official/build/custom/ninja.min.js b/bower_components/phaser-official/build/custom/ninja.min.js new file mode 100644 index 0000000..27f501b --- /dev/null +++ b/bower_components/phaser-official/build/custom/ninja.min.js @@ -0,0 +1,3 @@ +/* Ninja Physics for Phaser v2.0.2 - http://phaser.io - @photonstorm - (c) 2014 Photon Storm Ltd. */ +Phaser.Physics.Ninja=function(a){this.game=a,this.time=this.game.time,this.gravity=.2,this.bounds=new Phaser.Rectangle(0,0,a.world.width,a.world.height),this.maxObjects=10,this.maxLevels=4,this.quadTree=new Phaser.QuadTree(this.game.world.bounds.x,this.game.world.bounds.y,this.game.world.bounds.width,this.game.world.bounds.height,this.maxObjects,this.maxLevels)},Phaser.Physics.Ninja.prototype.constructor=Phaser.Physics.Ninja,Phaser.Physics.Ninja.prototype={enableAABB:function(a,b){this.enable(a,1,0,0,b)},enableCircle:function(a,b,c){this.enable(a,2,0,b,c)},enableTile:function(a,b,c){this.enable(a,3,b,0,c)},enable:function(a,b,c,d,e){if("undefined"==typeof b&&(b=1),"undefined"==typeof c&&(c=1),"undefined"==typeof d&&(d=0),"undefined"==typeof e&&(e=!0),Array.isArray(a))for(var f=a.length;f--;)a[f]instanceof Phaser.Group?this.enable(a[f].children,b,c,d,e):(this.enableBody(a[f],b,c,d),e&&a[f].hasOwnProperty("children")&&a[f].children.length>0&&this.enable(a[f],b,c,d,!0));else a instanceof Phaser.Group?this.enable(a.children,b,c,d,e):(this.enableBody(a,b,c,d),e&&a.hasOwnProperty("children")&&a.children.length>0&&this.enable(a.children,b,c,d,!0))},enableBody:function(a,b,c,d){a.hasOwnProperty("body")&&null===a.body&&(a.body=new Phaser.Physics.Ninja.Body(this,a,b,c,d),a.anchor.set(.5))},setBounds:function(a,b,c,d){this.bounds.setTo(a,b,c,d)},setBoundsToWorld:function(){this.bounds.setTo(this.game.world.bounds.x,this.game.world.bounds.y,this.game.world.bounds.width,this.game.world.bounds.height)},clearTilemapLayerBodies:function(a,b){b=a.getLayer(b);for(var c=a.layers[b].bodies.length;c--;)a.layers[b].bodies[c].destroy();a.layers[b].bodies.length=[]},convertTilemap:function(a,b,c){b=a.getLayer(b),this.clearTilemapLayerBodies(a,b);for(var d=0,e=a.layers[b].height;e>d;d++)for(var f=0,g=a.layers[b].width;g>f;f++){var h=a.layers[b].data[d][f];if(h&&c.hasOwnProperty(h.index)){var i=new Phaser.Physics.Ninja.Body(this,null,3,c[h.index],0,h.worldX+h.centerX,h.worldY+h.centerY,h.width,h.height);a.layers[b].bodies.push(i)}}return a.layers[b].bodies},overlap:function(a,b,c,d,e){if(c=c||null,d=d||null,e=e||c,this._result=!1,this._total=0,Array.isArray(b))for(var f=0,g=b.length;g>f;f++)this.collideHandler(a,b[f],c,d,e,!0);else this.collideHandler(a,b,c,d,e,!0);return this._total>0},collide:function(a,b,c,d,e){if(c=c||null,d=d||null,e=e||c,this._result=!1,this._total=0,Array.isArray(b))for(var f=0,g=b.length;g>f;f++)this.collideHandler(a,b[f],c,d,e,!1);else this.collideHandler(a,b,c,d,e,!1);return this._total>0},collideHandler:function(a,b,c,d,e,f){return"undefined"!=typeof b||a.type!==Phaser.GROUP&&a.type!==Phaser.EMITTER?void(a&&b&&a.exists&&b.exists&&(a.type==Phaser.SPRITE||a.type==Phaser.TILESPRITE?b.type==Phaser.SPRITE||b.type==Phaser.TILESPRITE?this.collideSpriteVsSprite(a,b,c,d,e,f):b.type==Phaser.GROUP||b.type==Phaser.EMITTER?this.collideSpriteVsGroup(a,b,c,d,e,f):b.type==Phaser.TILEMAPLAYER&&this.collideSpriteVsTilemapLayer(a,b,c,d,e):a.type==Phaser.GROUP?b.type==Phaser.SPRITE||b.type==Phaser.TILESPRITE?this.collideSpriteVsGroup(b,a,c,d,e,f):b.type==Phaser.GROUP||b.type==Phaser.EMITTER?this.collideGroupVsGroup(a,b,c,d,e,f):b.type==Phaser.TILEMAPLAYER&&this.collideGroupVsTilemapLayer(a,b,c,d,e):a.type==Phaser.TILEMAPLAYER?b.type==Phaser.SPRITE||b.type==Phaser.TILESPRITE?this.collideSpriteVsTilemapLayer(b,a,c,d,e):(b.type==Phaser.GROUP||b.type==Phaser.EMITTER)&&this.collideGroupVsTilemapLayer(b,a,c,d,e):a.type==Phaser.EMITTER&&(b.type==Phaser.SPRITE||b.type==Phaser.TILESPRITE?this.collideSpriteVsGroup(b,a,c,d,e,f):b.type==Phaser.GROUP||b.type==Phaser.EMITTER?this.collideGroupVsGroup(a,b,c,d,e,f):b.type==Phaser.TILEMAPLAYER&&this.collideGroupVsTilemapLayer(a,b,c,d,e)))):void this.collideGroupVsSelf(a,c,d,e,f)},collideSpriteVsSprite:function(a,b,c,d,e,f){this.separate(a.body,b.body,d,e,f)&&(c&&c.call(e,a,b),this._total++)},collideSpriteVsGroup:function(a,b,c,d,e,f){if(0!==b.length)for(var g=0,h=b.children.length;h>g;g++)b.children[g].exists&&b.children[g].body&&this.separate(a.body,b.children[g].body,d,e,f)&&(c&&c.call(e,a,b.children[g]),this._total++)},collideGroupVsSelf:function(a,b,c,d,e){if(0!==a.length)for(var f=a.children.length,g=0;f>g;g++)for(var h=g+1;f>=h;h++)a.children[g]&&a.children[h]&&a.children[g].exists&&a.children[h].exists&&this.collideSpriteVsSprite(a.children[g],a.children[h],b,c,d,e)},collideGroupVsGroup:function(a,b,c,d,e,f){if(0!==a.length&&0!==b.length)for(var g=0,h=a.children.length;h>g;g++)a.children[g].exists&&this.collideSpriteVsGroup(a.children[g],b,c,d,e,f)},separate:function(a,b){return a.type!==Phaser.Physics.NINJA||b.type!==Phaser.Physics.NINJA?!1:a.aabb&&b.aabb?a.aabb.collideAABBVsAABB(b.aabb):a.aabb&&b.tile?a.aabb.collideAABBVsTile(b.tile):a.tile&&b.aabb?b.aabb.collideAABBVsTile(a.tile):a.circle&&b.tile?a.circle.collideCircleVsTile(b.tile):a.tile&&b.circle?b.circle.collideCircleVsTile(a.tile):void 0}},Phaser.Physics.Ninja.Body=function(a,b,c,d,e,f,g,h,i){b=b||null,"undefined"==typeof c&&(c=1),"undefined"==typeof d&&(d=1),"undefined"==typeof e&&(e=16),this.sprite=b,this.game=a.game,this.type=Phaser.Physics.NINJA,this.system=a,this.aabb=null,this.tile=null,this.circle=null,this.shape=null,this.drag=1,this.friction=.05,this.gravityScale=1,this.bounce=.3,this.velocity=new Phaser.Point,this.facing=Phaser.NONE,this.immovable=!1,this.collideWorldBounds=!0,this.checkCollision={none:!1,any:!0,up:!0,down:!0,left:!0,right:!0},this.touching={none:!0,up:!1,down:!1,left:!1,right:!1},this.wasTouching={none:!0,up:!1,down:!1,left:!1,right:!1},this.maxSpeed=8,b&&(f=b.x,g=b.y,h=b.width,i=b.height,0===b.anchor.x&&(f+=.5*b.width),0===b.anchor.y&&(g+=.5*b.height)),1===c?(this.aabb=new Phaser.Physics.Ninja.AABB(this,f,g,h,i),this.shape=this.aabb):2===c?(this.circle=new Phaser.Physics.Ninja.Circle(this,f,g,e),this.shape=this.circle):3===c&&(this.tile=new Phaser.Physics.Ninja.Tile(this,f,g,h,i,d),this.shape=this.tile)},Phaser.Physics.Ninja.Body.prototype={preUpdate:function(){this.wasTouching.none=this.touching.none,this.wasTouching.up=this.touching.up,this.wasTouching.down=this.touching.down,this.wasTouching.left=this.touching.left,this.wasTouching.right=this.touching.right,this.touching.none=!0,this.touching.up=!1,this.touching.down=!1,this.touching.left=!1,this.touching.right=!1,this.shape.integrate(),this.collideWorldBounds&&this.shape.collideWorldBounds()},postUpdate:function(){this.sprite&&(this.sprite.type===Phaser.TILESPRITE?(this.sprite.x=this.shape.pos.x-this.shape.xw,this.sprite.y=this.shape.pos.y-this.shape.yw):(this.sprite.x=this.shape.pos.x,this.sprite.y=this.shape.pos.y)),this.velocity.x<0?this.facing=Phaser.LEFT:this.velocity.x>0&&(this.facing=Phaser.RIGHT),this.velocity.y<0?this.facing=Phaser.UP:this.velocity.y>0&&(this.facing=Phaser.DOWN)},setZeroVelocity:function(){this.shape.oldpos.x=this.shape.pos.x,this.shape.oldpos.y=this.shape.pos.y},moveTo:function(a,b){var c=a*this.game.time.physicsElapsed,b=this.game.math.degToRad(b);this.shape.pos.x=this.shape.oldpos.x+c*Math.cos(b),this.shape.pos.y=this.shape.oldpos.y+c*Math.sin(b)},moveFrom:function(a,b){var c=-a*this.game.time.physicsElapsed,b=this.game.math.degToRad(b);this.shape.pos.x=this.shape.oldpos.x+c*Math.cos(b),this.shape.pos.y=this.shape.oldpos.y+c*Math.sin(b)},moveLeft:function(a){var b=-a*this.game.time.physicsElapsed;this.shape.pos.x=this.shape.oldpos.x+Math.min(this.maxSpeed,Math.max(-this.maxSpeed,this.shape.pos.x-this.shape.oldpos.x+b))},moveRight:function(a){var b=a*this.game.time.physicsElapsed;this.shape.pos.x=this.shape.oldpos.x+Math.min(this.maxSpeed,Math.max(-this.maxSpeed,this.shape.pos.x-this.shape.oldpos.x+b))},moveUp:function(a){var b=-a*this.game.time.physicsElapsed;this.shape.pos.y=this.shape.oldpos.y+Math.min(this.maxSpeed,Math.max(-this.maxSpeed,this.shape.pos.y-this.shape.oldpos.y+b))},moveDown:function(a){var b=a*this.game.time.physicsElapsed;this.shape.pos.y=this.shape.oldpos.y+Math.min(this.maxSpeed,Math.max(-this.maxSpeed,this.shape.pos.y-this.shape.oldpos.y+b))},reset:function(){this.velocity.set(0),this.shape.pos.x=this.sprite.x,this.shape.pos.y=this.sprite.y,this.shape.oldpos.copyFrom(this.shape.pos)},deltaAbsX:function(){return this.deltaX()>0?this.deltaX():-this.deltaX()},deltaAbsY:function(){return this.deltaY()>0?this.deltaY():-this.deltaY()},deltaX:function(){return this.shape.pos.x-this.shape.oldpos.x},deltaY:function(){return this.shape.pos.y-this.shape.oldpos.y},destroy:function(){this.sprite=null,this.system=null,this.aabb=null,this.tile=null,this.circle=null,this.shape.destroy(),this.shape=null}},Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"x",{get:function(){return this.shape.pos.x},set:function(a){this.shape.pos.x=a}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"y",{get:function(){return this.shape.pos.y},set:function(a){this.shape.pos.y=a}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"width",{get:function(){return this.shape.width}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"height",{get:function(){return this.shape.height}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"bottom",{get:function(){return this.shape.pos.y+this.shape.yw}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"right",{get:function(){return this.shape.pos.x+this.shape.xw}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"speed",{get:function(){return Math.sqrt(this.shape.velocity.x*this.shape.velocity.x+this.shape.velocity.y*this.shape.velocity.y)}}),Object.defineProperty(Phaser.Physics.Ninja.Body.prototype,"angle",{get:function(){return Math.atan2(this.shape.velocity.y,this.shape.velocity.x)}}),Phaser.Physics.Ninja.AABB=function(a,b,c,d,e){this.body=a,this.system=a.system,this.pos=new Phaser.Point(b,c),this.oldpos=new Phaser.Point(b,c),this.xw=Math.abs(d/2),this.yw=Math.abs(e/2),this.width=d,this.height=e,this.oH=0,this.oV=0,this.velocity=new Phaser.Point,this.aabbTileProjections={},this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_FULL]=this.projAABB_Full,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_45DEG]=this.projAABB_45Deg,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONCAVE]=this.projAABB_Concave,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_CONVEX]=this.projAABB_Convex,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGs]=this.projAABB_22DegS,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_22DEGb]=this.projAABB_22DegB,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGs]=this.projAABB_67DegS,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_67DEGb]=this.projAABB_67DegB,this.aabbTileProjections[Phaser.Physics.Ninja.Tile.TYPE_HALF]=this.projAABB_Half},Phaser.Physics.Ninja.AABB.prototype.constructor=Phaser.Physics.Ninja.AABB,Phaser.Physics.Ninja.AABB.COL_NONE=0,Phaser.Physics.Ninja.AABB.COL_AXIS=1,Phaser.Physics.Ninja.AABB.COL_OTHER=2,Phaser.Physics.Ninja.AABB.prototype={integrate:function(){var a=this.pos.x,b=this.pos.y;this.pos.x+=this.body.drag*this.pos.x-this.body.drag*this.oldpos.x,this.pos.y+=this.body.drag*this.pos.y-this.body.drag*this.oldpos.y+this.system.gravity*this.body.gravityScale,this.velocity.set(this.pos.x-a,this.pos.y-b),this.oldpos.set(a,b)},reportCollisionVsWorld:function(a,b,c,d){var e,f,g,h,i,j=this.pos,k=this.oldpos,l=j.x-k.x,m=j.y-k.y,n=l*c+m*d,o=n*c,p=n*d,q=l-o,r=m-p;0>n?(h=q*this.body.friction,i=r*this.body.friction,e=1+this.body.bounce,f=o*e,g=p*e,1===c?this.body.touching.left=!0:-1===c&&(this.body.touching.right=!0),1===d?this.body.touching.up=!0:-1===d&&(this.body.touching.down=!0)):f=g=h=i=0,j.x+=a,j.y+=b,k.x+=a+f+h,k.y+=b+g+i},reverse:function(){var a=this.pos.x-this.oldpos.x,b=this.pos.y-this.oldpos.y;this.oldpos.xthis.pos.x&&(this.oldpos.x=this.pos.x-a),this.oldpos.ythis.pos.y&&(this.oldpos.y=this.pos.y-b)},reportCollisionVsBody:function(a,b,c,d,e){var f=this.pos.x-this.oldpos.x,g=this.pos.y-this.oldpos.y,h=f*c+g*d;return this.body.immovable&&e.body.immovable?(a*=.5,b*=.5,this.pos.add(a,b),this.oldpos.set(this.pos.x,this.pos.y),e.pos.subtract(a,b),void e.oldpos.set(e.pos.x,e.pos.y)):void(this.body.immovable||e.body.immovable?this.body.immovable?e.body.immovable||(e.pos.subtract(a,b),0>h&&e.reverse()):(this.pos.subtract(a,b),0>h&&this.reverse()):(a*=.5,b*=.5,this.pos.add(a,b),e.pos.subtract(a,b),0>h&&(this.reverse(),e.reverse())))},collideWorldBounds:function(){var a=this.system.bounds.x-(this.pos.x-this.xw);a>0?this.reportCollisionVsWorld(a,0,1,0,null):(a=this.pos.x+this.xw-this.system.bounds.right,a>0&&this.reportCollisionVsWorld(-a,0,-1,0,null));var b=this.system.bounds.y-(this.pos.y-this.yw);b>0?this.reportCollisionVsWorld(0,b,0,1,null):(b=this.pos.y+this.yw-this.system.bounds.bottom,b>0&&this.reportCollisionVsWorld(0,-b,0,-1,null))},collideAABBVsAABB:function(a){var b=this.pos,c=a,d=c.pos.x,e=c.pos.y,f=c.xw,g=c.yw,h=b.x-d,i=f+this.xw-Math.abs(h);if(i>0){var j=b.y-e,k=g+this.yw-Math.abs(j);if(k>0){k>i?0>h?(i*=-1,k=0):k=0:0>j?(i=0,k*=-1):i=0;var l=Math.sqrt(i*i+k*k);return this.reportCollisionVsBody(i,k,i/l,k/l,c),Phaser.Physics.Ninja.AABB.COL_AXIS}}return!1},collideAABBVsTile:function(a){var b=this.pos.x-a.pos.x,c=a.xw+this.xw-Math.abs(b);if(c>0){var d=this.pos.y-a.pos.y,e=a.yw+this.yw-Math.abs(d);if(e>0)return e>c?0>b?(c*=-1,e=0):e=0:0>d?(c=0,e*=-1):c=0,this.resolveTile(c,e,this,a)}return!1},resolveTile:function(a,b,c,d){return 0i){e*=-i,f*=-i;var j=Math.sqrt(e*e+f*f),k=Math.sqrt(a*a+b*b);return j>k?(c.reportCollisionVsWorld(a,b,a/k,b/k,d),Phaser.Physics.Ninja.AABB.COL_AXIS):(c.reportCollisionVsWorld(e,f,d.signx,d.signy,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_45Deg:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.x-e*c.xw-d.pos.x,h=c.pos.y-f*c.yw-d.pos.y,i=d.sx,j=d.sy,k=g*i+h*j;if(0>k){i*=-k,j*=-k;var l=Math.sqrt(i*i+j*j),m=Math.sqrt(a*a+b*b);return l>m?(c.reportCollisionVsWorld(a,b,a/m,b/m,d),Phaser.Physics.Ninja.AABB.COL_AXIS):(c.reportCollisionVsWorld(i,j,d.sx,d.sy),Phaser.Physics.Ninja.AABB.COL_OTHER)}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_22DegS:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.y-f*c.yw,h=d.pos.y-g;if(h*f>0){var i=c.pos.x-e*c.xw-(d.pos.x+e*d.xw),j=c.pos.y-f*c.yw-(d.pos.y-f*d.yw),k=d.sx,l=d.sy,m=i*k+j*l;if(0>m){k*=-m,l*=-m;var n=Math.sqrt(k*k+l*l),o=Math.sqrt(a*a+b*b),p=Math.abs(h);return n>o?o>p?(c.reportCollisionVsWorld(0,h,0,h/p,d),Phaser.Physics.Ninja.AABB.COL_OTHER):(c.reportCollisionVsWorld(a,b,a/o,b/o,d),Phaser.Physics.Ninja.AABB.COL_AXIS):n>p?(c.reportCollisionVsWorld(0,h,0,h/p,d),Phaser.Physics.Ninja.AABB.COL_OTHER):(c.reportCollisionVsWorld(k,l,d.sx,d.sy,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_22DegB:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.x-e*c.xw-(d.pos.x-e*d.xw),h=c.pos.y-f*c.yw-(d.pos.y+f*d.yw),i=d.sx,j=d.sy,k=g*i+h*j;if(0>k){i*=-k,j*=-k;var l=Math.sqrt(i*i+j*j),m=Math.sqrt(a*a+b*b);return l>m?(c.reportCollisionVsWorld(a,b,a/m,b/m,d),Phaser.Physics.Ninja.AABB.COL_AXIS):(c.reportCollisionVsWorld(i,j,d.sx,d.sy,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_67DegS:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.x-e*c.xw,h=d.pos.x-g;if(h*e>0){var i=c.pos.x-e*c.xw-(d.pos.x-e*d.xw),j=c.pos.y-f*c.yw-(d.pos.y+f*d.yw),k=d.sx,l=d.sy,m=i*k+j*l;if(0>m){k*=-m,l*=-m;var n=Math.sqrt(k*k+l*l),o=Math.sqrt(a*a+b*b),p=Math.abs(h);return n>o?o>p?(c.reportCollisionVsWorld(h,0,h/p,0,d),Phaser.Physics.Ninja.AABB.COL_OTHER):(c.reportCollisionVsWorld(a,b,a/o,b/o,d),Phaser.Physics.Ninja.AABB.COL_AXIS):n>p?(c.reportCollisionVsWorld(h,0,h/p,0,d),Phaser.Physics.Ninja.AABB.COL_OTHER):(c.reportCollisionVsWorld(k,l,d.sx,d.sy,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_67DegB:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.x-e*c.xw-(d.pos.x+e*d.xw),h=c.pos.y-f*c.yw-(d.pos.y-f*d.yw),i=d.sx,j=d.sy,k=g*i+h*j;if(0>k){i*=-k,j*=-k;var l=Math.sqrt(i*i+j*j),m=Math.sqrt(a*a+b*b);return l>m?(c.reportCollisionVsWorld(a,b,a/m,b/m,d),Phaser.Physics.Ninja.AABB.COL_AXIS):(c.reportCollisionVsWorld(i,j,d.sx,d.sy,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}return Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_Convex:function(a,b,c,d){var e=d.signx,f=d.signy,g=c.pos.x-e*c.xw-(d.pos.x-e*d.xw),h=c.pos.y-f*c.yw-(d.pos.y-f*d.yw),i=Math.sqrt(g*g+h*h),j=2*d.xw,k=Math.sqrt(j*j+0),l=k-i;if(0>e*g||0>f*h){var m=Math.sqrt(a*a+b*b);return c.reportCollisionVsWorld(a,b,a/m,b/m,d),Phaser.Physics.Ninja.AABB.COL_AXIS}return l>0?(g/=i,h/=i,c.reportCollisionVsWorld(g*l,h*l,g,h,d),Phaser.Physics.Ninja.AABB.COL_OTHER):Phaser.Physics.Ninja.AABB.COL_NONE},projAABB_Concave:function(a,b,c,d){var e=d.signx,f=d.signy,g=d.pos.x+e*d.xw-(c.pos.x-e*c.xw),h=d.pos.y+f*d.yw-(c.pos.y-f*c.yw),i=2*d.xw,j=Math.sqrt(i*i+0),k=Math.sqrt(g*g+h*h),l=k-j;if(l>0){var m=Math.sqrt(a*a+b*b);return l>m?(c.reportCollisionVsWorld(a,b,a/m,b/m,d),Phaser.Physics.Ninja.AABB.COL_AXIS):(g/=k,h/=k,c.reportCollisionVsWorld(g*l,h*l,g,h,d),Phaser.Physics.Ninja.AABB.COL_OTHER)}return Phaser.Physics.Ninja.AABB.COL_NONE},destroy:function(){this.body=null,this.system=null}},Phaser.Physics.Ninja.Tile=function(a,b,c,d,e,f){"undefined"==typeof f&&(f=Phaser.Physics.Ninja.Tile.EMPTY),this.body=a,this.system=a.system,this.id=f,this.type=Phaser.Physics.Ninja.Tile.TYPE_EMPTY,this.pos=new Phaser.Point(b,c),this.oldpos=new Phaser.Point(b,c),this.id>1&&this.id<30&&(e=d),this.xw=Math.abs(d/2),this.yw=Math.abs(e/2),this.width=d,this.height=e,this.velocity=new Phaser.Point,this.signx=0,this.signy=0,this.sx=0,this.sy=0,this.body.gravityScale=0,this.body.collideWorldBounds=!1,this.id>0&&this.setType(this.id)},Phaser.Physics.Ninja.Tile.prototype.constructor=Phaser.Physics.Ninja.Tile,Phaser.Physics.Ninja.Tile.prototype={integrate:function(){var a=this.pos.x,b=this.pos.y;this.pos.x+=this.body.drag*this.pos.x-this.body.drag*this.oldpos.x,this.pos.y+=this.body.drag*this.pos.y-this.body.drag*this.oldpos.y+this.system.gravity*this.body.gravityScale,this.velocity.set(this.pos.x-a,this.pos.y-b),this.oldpos.set(a,b)},collideWorldBounds:function(){var a=this.system.bounds.x-(this.pos.x-this.xw);a>0?this.reportCollisionVsWorld(a,0,1,0,null):(a=this.pos.x+this.xw-this.system.bounds.right,a>0&&this.reportCollisionVsWorld(-a,0,-1,0,null));var b=this.system.bounds.y-(this.pos.y-this.yw);b>0?this.reportCollisionVsWorld(0,b,0,1,null):(b=this.pos.y+this.yw-this.system.bounds.bottom,b>0&&this.reportCollisionVsWorld(0,-b,0,-1,null))},reportCollisionVsWorld:function(a,b,c,d){var e,f,g,h,i,j=this.pos,k=this.oldpos,l=j.x-k.x,m=j.y-k.y,n=l*c+m*d,o=n*c,p=n*d,q=l-o,r=m-p;0>n?(h=q*this.body.friction,i=r*this.body.friction,e=1+this.body.bounce,f=o*e,g=p*e,1===c?this.body.touching.left=!0:-1===c&&(this.body.touching.right=!0),1===d?this.body.touching.up=!0:-1===d&&(this.body.touching.down=!0)):f=g=h=i=0,j.x+=a,j.y+=b,k.x+=a+f+h,k.y+=b+g+i},setType:function(a){return a===Phaser.Physics.Ninja.Tile.EMPTY?this.clear():(this.id=a,this.updateType()),this},clear:function(){this.id=Phaser.Physics.Ninja.Tile.EMPTY,this.updateType()},destroy:function(){this.body=null,this.system=null},updateType:function(){if(0===this.id)return this.type=Phaser.Physics.Ninja.Tile.TYPE_EMPTY,this.signx=0,this.signy=0,this.sx=0,this.sy=0,!0;if(this.idn?(h=q*this.body.friction,i=r*this.body.friction,e=1+this.body.bounce,f=o*e,g=p*e,1===c?this.body.touching.left=!0:-1===c&&(this.body.touching.right=!0),1===d?this.body.touching.up=!0:-1===d&&(this.body.touching.down=!0)):f=g=h=i=0,j.x+=a,j.y+=b,k.x+=a+f+h,k.y+=b+g+i},collideWorldBounds:function(){var a=this.system.bounds.x-(this.pos.x-this.radius);a>0?this.reportCollisionVsWorld(a,0,1,0,null):(a=this.pos.x+this.radius-this.system.bounds.right,a>0&&this.reportCollisionVsWorld(-a,0,-1,0,null));var b=this.system.bounds.y-(this.pos.y-this.radius);b>0?this.reportCollisionVsWorld(0,b,0,1,null):(b=this.pos.y+this.radius-this.system.bounds.bottom,b>0&&this.reportCollisionVsWorld(0,-b,0,-1,null))},collideCircleVsTile:function(a){var b=this.pos,c=this.radius,d=a,e=d.pos.x,f=d.pos.y,g=d.xw,h=d.yw,i=b.x-e,j=g+c-Math.abs(i);if(j>0){var k=b.y-f,l=h+c-Math.abs(k);if(l>0)return this.oH=0,this.oV=0,-g>i?this.oH=-1:i>g&&(this.oH=1),-h>k?this.oV=-1:k>h&&(this.oV=1),this.resolveCircleTile(j,l,this.oH,this.oV,this,d)}},resolveCircleTile:function(a,b,c,d,e,f){return 0a){var g=e.pos.x-f.pos.x;return 0>g?(e.reportCollisionVsWorld(-a,0,-1,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(a,0,1,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS)}var h=e.pos.y-f.pos.y;return 0>h?(e.reportCollisionVsWorld(0,-b,0,-1,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(0,b,0,1,f),Phaser.Physics.Ninja.Circle.COL_AXIS)}return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS}if(0===d)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var i=f.pos.x+c*f.xw,j=f.pos.y+d*f.yw,g=e.pos.x-i,h=e.pos.y-j,k=Math.sqrt(g*g+h*h),l=e.radius-k;return l>0?(0===k?(g=c/Math.SQRT2,h=d/Math.SQRT2):(g/=k,h/=k),e.reportCollisionVsWorld(g*l,h*l,g,h,f),Phaser.Physics.Ninja.Circle.COL_OTHER):Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_45Deg:function(a,b,c,d,e,f){var g,h=f.signx,i=f.signy;if(0===c)if(0===d){var j=f.sx,k=f.sy,l=e.pos.x-j*e.radius-f.pos.x,m=e.pos.y-k*e.radius-f.pos.y,n=l*j+m*k;if(0>n){j*=-n,k*=-n,b>a?(g=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(g=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1));var o=Math.sqrt(j*j+k*k);return o>g?(e.reportCollisionVsWorld(a,b,a/g,b/g,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(j,k,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}}else{if(0>i*d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,l=e.pos.x-(f.pos.x-h*f.xw),m=e.pos.y-(f.pos.y+d*f.yw),p=l*-k+m*j;if(p*h*i>0){var q=Math.sqrt(l*l+m*m),r=e.radius-q;if(r>0)return l/=q,m/=q,e.reportCollisionVsWorld(l*r,m*r,l,m,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var n=l*j+m*k,r=e.radius-Math.abs(n);if(r>0)return e.reportCollisionVsWorld(j*r,k*r,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else if(0===d){if(0>h*c)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,l=e.pos.x-(f.pos.x+c*f.xw),m=e.pos.y-(f.pos.y-i*f.yw),p=l*-k+m*j; +if(0>p*h*i){var q=Math.sqrt(l*l+m*m),r=e.radius-q;if(r>0)return l/=q,m/=q,e.reportCollisionVsWorld(l*r,m*r,l,m,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var n=l*j+m*k,r=e.radius-Math.abs(n);if(r>0)return e.reportCollisionVsWorld(j*r,k*r,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else{if(h*c+i*d>0)return Phaser.Physics.Ninja.Circle.COL_NONE;var s=f.pos.x+c*f.xw,t=f.pos.y+d*f.yw,u=e.pos.x-s,v=e.pos.y-t,q=Math.sqrt(u*u+v*v),r=e.radius-q;if(r>0)return 0===q?(u=c/Math.SQRT2,v=d/Math.SQRT2):(u/=q,v/=q),e.reportCollisionVsWorld(u*r,v*r,u,v,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_Concave:function(a,b,c,d,e,f){var g,h=f.signx,i=f.signy;if(0===c){if(0===d){var j=f.pos.x+h*f.xw-e.pos.x,k=f.pos.y+i*f.yw-e.pos.y,l=2*f.xw,m=Math.sqrt(l*l+0),n=Math.sqrt(j*j+k*k),o=n+e.radius-m;return o>0?(b>a?(g=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(g=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),o>g?(e.reportCollisionVsWorld(a,b,a/g,b/g,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(j/=n,k/=n,e.reportCollisionVsWorld(j*o,k*o,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER)):Phaser.Physics.Ninja.Circle.COL_NONE}if(0>i*d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var p=f.pos.x-h*f.xw,q=f.pos.y+d*f.yw,r=e.pos.x-p,s=e.pos.y-q,n=Math.sqrt(r*r+s*s),o=e.radius-n;if(o>0)return 0===n?(r=0,s=d):(r/=n,s/=n),e.reportCollisionVsWorld(r*o,s*o,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else if(0===d){if(0>h*c)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var p=f.pos.x+c*f.xw,q=f.pos.y-i*f.yw,r=e.pos.x-p,s=e.pos.y-q,n=Math.sqrt(r*r+s*s),o=e.radius-n;if(o>0)return 0===n?(r=c,s=0):(r/=n,s/=n),e.reportCollisionVsWorld(r*o,s*o,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{if(h*c+i*d>0)return Phaser.Physics.Ninja.Circle.COL_NONE;var p=f.pos.x+c*f.xw,q=f.pos.y+d*f.yw,r=e.pos.x-p,s=e.pos.y-q,n=Math.sqrt(r*r+s*s),o=e.radius-n;if(o>0)return 0===n?(r=c/Math.SQRT2,s=d/Math.SQRT2):(r/=n,s/=n),e.reportCollisionVsWorld(r*o,s*o,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_Convex:function(a,b,c,d,e,f){var g,h=f.signx,i=f.signy;if(0===c)if(0===d){var j=e.pos.x-(f.pos.x-h*f.xw),k=e.pos.y-(f.pos.y-i*f.yw),l=2*f.xw,m=Math.sqrt(l*l+0),n=Math.sqrt(j*j+k*k),o=m+e.radius-n;if(o>0)return b>a?(g=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(g=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),o>g?(e.reportCollisionVsWorld(a,b,a/g,b/g,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(j/=n,k/=n,e.reportCollisionVsWorld(j*o,k*o,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}else{if(0>i*d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=e.pos.x-(f.pos.x-h*f.xw),k=e.pos.y-(f.pos.y-i*f.yw),l=2*f.xw,m=Math.sqrt(l*l+0),n=Math.sqrt(j*j+k*k),o=m+e.radius-n;if(o>0)return j/=n,k/=n,e.reportCollisionVsWorld(j*o,k*o,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else if(0===d){if(0>h*c)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=e.pos.x-(f.pos.x-h*f.xw),k=e.pos.y-(f.pos.y-i*f.yw),l=2*f.xw,m=Math.sqrt(l*l+0),n=Math.sqrt(j*j+k*k),o=m+e.radius-n;if(o>0)return j/=n,k/=n,e.reportCollisionVsWorld(j*o,k*o,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else if(h*c+i*d>0){var j=e.pos.x-(f.pos.x-h*f.xw),k=e.pos.y-(f.pos.y-i*f.yw),l=2*f.xw,m=Math.sqrt(l*l+0),n=Math.sqrt(j*j+k*k),o=m+e.radius-n;if(o>0)return j/=n,k/=n,e.reportCollisionVsWorld(j*o,k*o,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var p=f.pos.x+c*f.xw,q=f.pos.y+d*f.yw,r=e.pos.x-p,s=e.pos.y-q,n=Math.sqrt(r*r+s*s),o=e.radius-n;if(o>0)return 0===n?(r=c/Math.SQRT2,s=d/Math.SQRT2):(r/=n,s/=n),e.reportCollisionVsWorld(r*o,s*o,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_Half:function(a,b,c,d,e,f){var g=f.signx,h=f.signy,i=c*g+d*h;if(i>0)return Phaser.Physics.Ninja.Circle.COL_NONE;if(0===c)if(0===d){var j=e.radius,k=e.pos.x-g*j-f.pos.x,l=e.pos.y-h*j-f.pos.y,m=g,n=h,o=k*m+l*n;if(0>o){m*=-o,n*=-o;var p=Math.sqrt(m*m+n*n),q=Math.sqrt(a*a+b*b);return p>q?(e.reportCollisionVsWorld(a,b,a/q,b/q,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(m,n,f.signx,f.signy),Phaser.Physics.Ninja.Circle.COL_OTHER)}}else{if(0!==i)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var r=e.pos.x-f.pos.x;if(0>r*g)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var s=e.pos.y-(f.pos.y+d*f.yw),t=Math.sqrt(r*r+s*s),u=e.radius-t;if(u>0)return 0===t?(r=g/Math.SQRT2,s=d/Math.SQRT2):(r/=t,s/=t),e.reportCollisionVsWorld(r*u,s*u,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else if(0===d){if(0!==i)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var s=e.pos.y-f.pos.y;if(0>s*h)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var r=e.pos.x-(f.pos.x+c*f.xw),t=Math.sqrt(r*r+s*s),u=e.radius-t;if(u>0)return 0===t?(r=g/Math.SQRT2,s=d/Math.SQRT2):(r/=t,s/=t),e.reportCollisionVsWorld(r*u,s*u,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var v=f.pos.x+c*f.xw,w=f.pos.y+d*f.yw,r=e.pos.x-v,s=e.pos.y-w,t=Math.sqrt(r*r+s*s),u=e.radius-t;if(u>0)return 0===t?(r=c/Math.SQRT2,s=d/Math.SQRT2):(r/=t,s/=t),e.reportCollisionVsWorld(r*u,s*u,r,s,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_22DegS:function(a,b,c,d,e,f){var g,h=f.signx,i=f.signy;if(i*d>0)return Phaser.Physics.Ninja.Circle.COL_NONE;if(0===c){if(0!==d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,l=e.radius,m=e.pos.x-(f.pos.x-h*f.xw),n=e.pos.y-f.pos.y,o=m*-k+n*j;if(o*h*i>0){var p=Math.sqrt(m*m+n*n),q=l-p;if(q>0)return m/=p,n/=p,e.reportCollisionVsWorld(m*q,n*q,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{m-=l*j,n-=l*k;var r=m*j+n*k;if(0>r){j*=-r,k*=-r;var s=Math.sqrt(j*j+k*k);return b>a?(g=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(g=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),s>g?(e.reportCollisionVsWorld(a,b,a/g,b/g,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(j,k,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}}}else if(0===d)if(0>h*c){var t=f.pos.x-h*f.xw,u=f.pos.y,v=e.pos.x-t,w=e.pos.y-u;if(0>w*i)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var p=Math.sqrt(v*v+w*w),q=e.radius-p;if(q>0)return 0===p?(v=c/Math.SQRT2,w=d/Math.SQRT2):(v/=p,w/=p),e.reportCollisionVsWorld(v*q,w*q,v,w,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var j=f.sx,k=f.sy,m=e.pos.x-(f.pos.x+c*f.xw),n=e.pos.y-(f.pos.y-i*f.yw),o=m*-k+n*j;if(0>o*h*i){var p=Math.sqrt(m*m+n*n),q=e.radius-p;if(q>0)return m/=p,n/=p,e.reportCollisionVsWorld(m*q,n*q,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var r=m*j+n*k,q=e.radius-Math.abs(r);if(q>0)return e.reportCollisionVsWorld(j*q,k*q,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else{var t=f.pos.x+c*f.xw,u=f.pos.y+d*f.yw,v=e.pos.x-t,w=e.pos.y-u,p=Math.sqrt(v*v+w*w),q=e.radius-p;if(q>0)return 0===p?(v=c/Math.SQRT2,w=d/Math.SQRT2):(v/=p,w/=p),e.reportCollisionVsWorld(v*q,w*q,v,w,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_22DegB:function(a,b,c,d,e,f){var g,h=f.signx,i=f.signy;if(0===c)if(0===d){var j=f.sx,k=f.sy,l=e.radius,m=e.pos.x-j*l-(f.pos.x-h*f.xw),n=e.pos.y-k*l-(f.pos.y+i*f.yw),o=m*j+n*k;if(0>o){j*=-o,k*=-o;var p=Math.sqrt(j*j+k*k);return b>a?(g=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(g=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),p>g?(e.reportCollisionVsWorld(a,b,a/g,b/g,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(j,k,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}}else{if(0>i*d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,m=e.pos.x-(f.pos.x-h*f.xw),n=e.pos.y-(f.pos.y+i*f.yw),q=m*-k+n*j;if(q*h*i>0){var r=Math.sqrt(m*m+n*n),s=e.radius-r;if(s>0)return m/=r,n/=r,e.reportCollisionVsWorld(m*s,n*s,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var o=m*j+n*k,s=e.radius-Math.abs(o);if(s>0)return e.reportCollisionVsWorld(j*s,k*s,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else if(0===d){if(0>h*c)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var m=e.pos.x-(f.pos.x+h*f.xw),n=e.pos.y-f.pos.y;if(0>n*i)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,q=m*-k+n*j;if(0>q*h*i){var r=Math.sqrt(m*m+n*n),s=e.radius-r;if(s>0)return m/=r,n/=r,e.reportCollisionVsWorld(m*s,n*s,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var o=m*j+n*k,s=e.radius-Math.abs(o);if(s>0)return e.reportCollisionVsWorld(j*s,k*s,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else{if(h*c+i*d>0){var t=Math.sqrt(5),j=1*h/t,k=2*i/t,l=e.radius,m=e.pos.x-j*l-(f.pos.x-h*f.xw),n=e.pos.y-k*l-(f.pos.y+i*f.yw),o=m*j+n*k;return 0>o?(e.reportCollisionVsWorld(-j*o,-k*o,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER):Phaser.Physics.Ninja.Circle.COL_NONE}var u=f.pos.x+c*f.xw,v=f.pos.y+d*f.yw,w=e.pos.x-u,x=e.pos.y-v,r=Math.sqrt(w*w+x*x),s=e.radius-r;if(s>0)return 0===r?(w=c/Math.SQRT2,x=d/Math.SQRT2):(w/=r,x/=r),e.reportCollisionVsWorld(w*s,x*s,w,x,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_67DegS:function(a,b,c,d,e,f){var g=f.signx,h=f.signy;if(g*c>0)return Phaser.Physics.Ninja.Circle.COL_NONE;if(0===c)if(0===d){var i,j=f.sx,k=f.sy,l=e.radius,m=e.pos.x-f.pos.x,n=e.pos.y-(f.pos.y-h*f.yw),o=m*-k+n*j;if(0>o*g*h){var p=Math.sqrt(m*m+n*n),q=l-p;if(q>0)return m/=p,n/=p,e.reportCollisionVsWorld(m*q,n*q,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{m-=l*j,n-=l*k;var r=m*j+n*k;if(0>r){j*=-r,k*=-r;var s=Math.sqrt(j*j+k*k);return b>a?(i=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(i=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),s>i?(e.reportCollisionVsWorld(a,b,a/i,b/i,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(j,k,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}}}else if(0>h*d){var t=f.pos.x,u=f.pos.y-h*f.yw,v=e.pos.x-t,w=e.pos.y-u;if(0>v*g)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var p=Math.sqrt(v*v+w*w),q=e.radius-p;if(q>0)return 0===p?(v=c/Math.SQRT2,w=d/Math.SQRT2):(v/=p,w/=p),e.reportCollisionVsWorld(v*q,w*q,v,w,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var j=f.sx,k=f.sy,m=e.pos.x-(f.pos.x-g*f.xw),n=e.pos.y-(f.pos.y+d*f.yw),o=m*-k+n*j;if(o*g*h>0){var p=Math.sqrt(m*m+n*n),q=e.radius-p;if(q>0)return m/=p,n/=p,e.reportCollisionVsWorld(m*q,n*q,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var r=m*j+n*k,q=e.radius-Math.abs(r);if(q>0)return e.reportCollisionVsWorld(j*q,k*q,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else{if(0===d)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var t=f.pos.x+c*f.xw,u=f.pos.y+d*f.yw,v=e.pos.x-t,w=e.pos.y-u,p=Math.sqrt(v*v+w*w),q=e.radius-p;if(q>0)return 0===p?(v=c/Math.SQRT2,w=d/Math.SQRT2):(v/=p,w/=p),e.reportCollisionVsWorld(v*q,w*q,v,w,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},projCircle_67DegB:function(a,b,c,d,e,f){var g=f.signx,h=f.signy;if(0===c)if(0===d){var i,j=f.sx,k=f.sy,l=e.radius,m=e.pos.x-j*l-(f.pos.x+g*f.xw),n=e.pos.y-k*l-(f.pos.y-h*f.yw),o=m*j+n*k;if(0>o){j*=-o,k*=-o;var p=Math.sqrt(j*j+k*k);return b>a?(i=a,b=0,e.pos.x-f.pos.x<0&&(a*=-1)):(i=b,a=0,e.pos.y-f.pos.y<0&&(b*=-1)),p>i?(e.reportCollisionVsWorld(a,b,a/i,b/i,f),Phaser.Physics.Ninja.Circle.COL_AXIS):(e.reportCollisionVsWorld(j,k,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER)}}else{if(0>h*d)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var m=e.pos.x-f.pos.x,n=e.pos.y-(f.pos.y+h*f.yw);if(0>m*g)return e.reportCollisionVsWorld(0,b*d,0,d,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var j=f.sx,k=f.sy,q=m*-k+n*j;if(q*g*h>0){var r=Math.sqrt(m*m+n*n),s=e.radius-r;if(s>0)return m/=r,n/=r,e.reportCollisionVsWorld(m*s,n*s,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var o=m*j+n*k,s=e.radius-Math.abs(o);if(s>0)return e.reportCollisionVsWorld(j*s,k*s,j,k,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else if(0===d){if(0>g*c)return e.reportCollisionVsWorld(a*c,0,c,0,f),Phaser.Physics.Ninja.Circle.COL_AXIS;var t=Math.sqrt(5),j=2*g/t,k=1*h/t,m=e.pos.x-(f.pos.x+g*f.xw),n=e.pos.y-(f.pos.y-h*f.yw),q=m*-k+n*j;if(0>q*g*h){var r=Math.sqrt(m*m+n*n),s=e.radius-r;if(s>0)return m/=r,n/=r,e.reportCollisionVsWorld(m*s,n*s,m,n,f),Phaser.Physics.Ninja.Circle.COL_OTHER}else{var o=m*j+n*k,s=e.radius-Math.abs(o);if(s>0)return e.reportCollisionVsWorld(j*s,k*s,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER}}else{if(g*c+h*d>0){var j=f.sx,k=f.sy,l=e.radius,m=e.pos.x-j*l-(f.pos.x+g*f.xw),n=e.pos.y-k*l-(f.pos.y-h*f.yw),o=m*j+n*k;return 0>o?(e.reportCollisionVsWorld(-j*o,-k*o,f.sx,f.sy,f),Phaser.Physics.Ninja.Circle.COL_OTHER):Phaser.Physics.Ninja.Circle.COL_NONE}var u=f.pos.x+c*f.xw,v=f.pos.y+d*f.yw,w=e.pos.x-u,x=e.pos.y-v,r=Math.sqrt(w*w+x*x),s=e.radius-r;if(s>0)return 0===r?(w=c/Math.SQRT2,x=d/Math.SQRT2):(w/=r,x/=r),e.reportCollisionVsWorld(w*s,x*s,w,x,f),Phaser.Physics.Ninja.Circle.COL_OTHER}return Phaser.Physics.Ninja.Circle.COL_NONE},destroy:function(){this.body=null,this.system=null}}; \ No newline at end of file diff --git a/bower_components/phaser-official/build/custom/p2.js b/bower_components/phaser-official/build/custom/p2.js new file mode 100644 index 0000000..e13a3e6 --- /dev/null +++ b/bower_components/phaser-official/build/custom/p2.js @@ -0,0 +1,15273 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013 p2.js authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define('p2', (function() { return this.p2 = e(); })()):"undefined"!=typeof window?window.p2=e():"undefined"!=typeof global?self.p2=e():"undefined"!=typeof self&&(self.p2=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; +}; + +/** + * Caclulates the dot product of two vec2's + * + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {Number} dot product of a and b + */ +vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; +}; + +/** + * Computes the cross product of two vec2's + * Note that the cross product must by definition produce a 3D vector + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @returns {vec3} out + */ +vec2.cross = function(out, a, b) { + var z = a[0] * b[1] - a[1] * b[0]; + out[0] = out[1] = 0; + out[2] = z; + return out; +}; + +/** + * Performs a linear interpolation between two vec2's + * + * @param {vec3} out the receiving vector + * @param {vec2} a the first operand + * @param {vec2} b the second operand + * @param {Number} t interpolation amount between the two inputs + * @returns {vec2} out + */ +vec2.lerp = function (out, a, b, t) { + var ax = a[0], + ay = a[1]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + return out; +}; + +/** + * Transforms the vec2 with a mat2 + * + * @param {vec2} out the receiving vector + * @param {vec2} a the vector to transform + * @param {mat2} m matrix to transform with + * @returns {vec2} out + */ +vec2.transformMat2 = function(out, a, m) { + var x = a[0], + y = a[1]; + out[0] = x * m[0] + y * m[1]; + out[1] = x * m[2] + y * m[3]; + return out; +}; + +/** + * Perform some operation over an array of vec2s. + * + * @param {Array} a the array of vectors to iterate over + * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed + * @param {Number} offset Number of elements to skip at the beginning of the array + * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array + * @param {Function} fn Function to call for each vector in the array + * @param {Object} [arg] additional argument to pass to fn + * @returns {Array} a + * @function + */ +vec2.forEach = (function() { + var vec = new Float32Array(2); + + return function(a, stride, offset, count, fn, arg) { + var i, l; + if(!stride) { + stride = 2; + } + + if(!offset) { + offset = 0; + } + + if(count) { + l = Math.min((count * stride) + offset, a.length); + } else { + l = a.length; + } + + for(i = offset; i < l; i += stride) { + vec[0] = a[i]; vec[1] = a[i+1]; + fn(vec, vec, arg); + a[i] = vec[0]; a[i+1] = vec[1]; + } + + return a; + }; +})(); + +/** + * Returns a string representation of a vector + * + * @param {vec2} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; +}; + +if(typeof(exports) !== 'undefined') { + exports.vec2 = vec2; +} + +},{}],3:[function(require,module,exports){ +var Scalar = require('./Scalar'); + +module.exports = Line; + +/** + * Container for line-related functions + * @class Line + */ +function Line(){}; + +/** + * Compute the intersection between two lines. + * @static + * @method lineInt + * @param {Array} l1 Line vector 1 + * @param {Array} l2 Line vector 2 + * @param {Number} precision Precision to use when checking if the lines are parallel + * @return {Array} The intersection point. + */ +Line.lineInt = function(l1,l2,precision){ + precision = precision || 0; + var i = [0,0]; // point + var a1, b1, c1, a2, b2, c2, det; // scalars + a1 = l1[1][1] - l1[0][1]; + b1 = l1[0][0] - l1[1][0]; + c1 = a1 * l1[0][0] + b1 * l1[0][1]; + a2 = l2[1][1] - l2[0][1]; + b2 = l2[0][0] - l2[1][0]; + c2 = a2 * l2[0][0] + b2 * l2[0][1]; + det = a1 * b2 - a2*b1; + if (!Scalar.eq(det, 0, precision)) { // lines are not parallel + i[0] = (b2 * c1 - b1 * c2) / det; + i[1] = (a1 * c2 - a2 * c1) / det; + } + return i; +}; + +/** + * Checks if two line segments intersects. + * @method segmentsIntersect + * @param {Array} p1 The start vertex of the first line segment. + * @param {Array} p2 The end vertex of the first line segment. + * @param {Array} q1 The start vertex of the second line segment. + * @param {Array} q2 The end vertex of the second line segment. + * @return {Boolean} True if the two line segments intersect + */ +Line.segmentsIntersect = function(p1, p2, q1, q2){ + var dx = p2[0] - p1[0]; + var dy = p2[1] - p1[1]; + var da = q2[0] - q1[0]; + var db = q2[1] - q1[1]; + + // segments are parallel + if(da*dy - db*dx == 0) + return false; + + var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx) + var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy) + + return (s>=0 && s<=1 && t>=0 && t<=1); +}; + + +},{"./Scalar":6}],4:[function(require,module,exports){ +module.exports = Point; + +/** + * Point related functions + * @class Point + */ +function Point(){}; + +/** + * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order. + * @static + * @method area + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @return {Number} + */ +Point.area = function(a,b,c){ + return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))); +}; + +Point.left = function(a,b,c){ + return Point.area(a,b,c) > 0; +}; + +Point.leftOn = function(a,b,c) { + return Point.area(a, b, c) >= 0; +}; + +Point.right = function(a,b,c) { + return Point.area(a, b, c) < 0; +}; + +Point.rightOn = function(a,b,c) { + return Point.area(a, b, c) <= 0; +}; + +var tmpPoint1 = [], + tmpPoint2 = []; + +/** + * Check if three points are collinear + * @method collinear + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision. + * @return {Boolean} + */ +Point.collinear = function(a,b,c,thresholdAngle) { + if(!thresholdAngle) + return Point.area(a, b, c) == 0; + else { + var ab = tmpPoint1, + bc = tmpPoint2; + + ab[0] = b[0]-a[0]; + ab[1] = b[1]-a[1]; + bc[0] = c[0]-b[0]; + bc[1] = c[1]-b[1]; + + var dot = ab[0]*bc[0] + ab[1]*bc[1], + magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]), + magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]), + angle = Math.acos(dot/(magA*magB)); + return angle < thresholdAngle; + } +}; + +Point.sqdist = function(a,b){ + var dx = b[0] - a[0]; + var dy = b[1] - a[1]; + return dx * dx + dy * dy; +}; + +},{}],5:[function(require,module,exports){ +var Line = require("./Line") +, Point = require("./Point") +, Scalar = require("./Scalar") + +module.exports = Polygon; + +/** + * Polygon class. + * @class Polygon + * @constructor + */ +function Polygon(){ + + /** + * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..] + * @property vertices + * @type {Array} + */ + this.vertices = []; +} + +/** + * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle. + * @method at + * @param {Number} i + * @return {Array} + */ +Polygon.prototype.at = function(i){ + var v = this.vertices, + s = v.length; + return v[i < 0 ? i % s + s : i % s]; +}; + +/** + * Get first vertex + * @method first + * @return {Array} + */ +Polygon.prototype.first = function(){ + return this.vertices[0]; +}; + +/** + * Get last vertex + * @method last + * @return {Array} + */ +Polygon.prototype.last = function(){ + return this.vertices[this.vertices.length-1]; +}; + +/** + * Clear the polygon data + * @method clear + * @return {Array} + */ +Polygon.prototype.clear = function(){ + this.vertices.length = 0; +}; + +/** + * Append points "from" to "to"-1 from an other polygon "poly" onto this one. + * @method append + * @param {Polygon} poly The polygon to get points from. + * @param {Number} from The vertex index in "poly". + * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. + * @return {Array} + */ +Polygon.prototype.append = function(poly,from,to){ + if(typeof(from) == "undefined") throw new Error("From is not given!"); + if(typeof(to) == "undefined") throw new Error("To is not given!"); + + if(to-1 < from) throw new Error("lol1"); + if(to > poly.vertices.length) throw new Error("lol2"); + if(from < 0) throw new Error("lol3"); + + for(var i=from; i v[br][0])) { + br = i; + } + } + + // reverse poly if clockwise + if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { + this.reverse(); + } +}; + +/** + * Reverse the vertices in the polygon + * @method reverse + */ +Polygon.prototype.reverse = function(){ + var tmp = []; + for(var i=0, N=this.vertices.length; i!==N; i++){ + tmp.push(this.vertices.pop()); + } + this.vertices = tmp; +}; + +/** + * Check if a point in the polygon is a reflex point + * @method isReflex + * @param {Number} i + * @return {Boolean} + */ +Polygon.prototype.isReflex = function(i){ + return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); +}; + +var tmpLine1=[], + tmpLine2=[]; + +/** + * Check if two vertices in the polygon can see each other + * @method canSee + * @param {Number} a Vertex index 1 + * @param {Number} b Vertex index 2 + * @return {Boolean} + */ +Polygon.prototype.canSee = function(a,b) { + var p, dist, l1=tmpLine1, l2=tmpLine2; + + if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) { + return false; + } + dist = Point.sqdist(this.at(a), this.at(b)); + for (var i = 0; i !== this.vertices.length; ++i) { // for each edge + if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges + continue; + if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge + l1[0] = this.at(a); + l1[1] = this.at(b); + l2[0] = this.at(i); + l2[1] = this.at(i + 1); + p = Line.lineInt(l1,l2); + if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b + return false; + } + } + } + + return true; +}; + +/** + * Copy the polygon from vertex i to vertex j. + * @method copy + * @param {Number} i + * @param {Number} j + * @param {Polygon} [targetPoly] Optional target polygon to save in. + * @return {Polygon} The resulting copy. + */ +Polygon.prototype.copy = function(i,j,targetPoly){ + var p = targetPoly || new Polygon(); + p.clear(); + if (i < j) { + // Insert all vertices from i to j + for(var k=i; k<=j; k++) + p.vertices.push(this.vertices[k]); + + } else { + + // Insert vertices 0 to j + for(var k=0; k<=j; k++) + p.vertices.push(this.vertices[k]); + + // Insert vertices i to end + for(var k=i; k 0) + return this.slice(edges); + else + return [this]; +}; + +/** + * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons. + * @method slice + * @param {Array} cutEdges A list of edges, as returned by .getCutEdges() + * @return {Array} + */ +Polygon.prototype.slice = function(cutEdges){ + if(cutEdges.length == 0) return [this]; + if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){ + + var polys = [this]; + + for(var i=0; i maxlevel){ + console.warn("quickDecomp: max level ("+maxlevel+") reached."); + return result; + } + + for (var i = 0; i < this.vertices.length; ++i) { + if (poly.isReflex(i)) { + reflexVertices.push(poly.vertices[i]); + upperDist = lowerDist = Number.MAX_VALUE; + + + for (var j = 0; j < this.vertices.length; ++j) { + if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j)) + && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge + p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection + if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly + d = Point.sqdist(poly.vertices[i], p); + if (d < lowerDist) { // keep only the closest intersection + lowerDist = d; + lowerInt = p; + lowerIndex = j; + } + } + } + if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1)) + && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { + p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1)); + if (Point.left(poly.at(i - 1), poly.at(i), p)) { + d = Point.sqdist(poly.vertices[i], p); + if (d < upperDist) { + upperDist = d; + upperInt = p; + upperIndex = j; + } + } + } + } + + // if there are no vertices to connect to, choose a point in the middle + if (lowerIndex == (upperIndex + 1) % this.vertices.length) { + //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); + p[0] = (lowerInt[0] + upperInt[0]) / 2; + p[1] = (lowerInt[1] + upperInt[1]) / 2; + steinerPoints.push(p); + + if (i < upperIndex) { + //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); + lowerPoly.append(poly, i, upperIndex+1); + lowerPoly.vertices.push(p); + upperPoly.vertices.push(p); + if (lowerIndex != 0){ + //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); + upperPoly.append(poly,lowerIndex,poly.vertices.length); + } + //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); + upperPoly.append(poly,0,i+1); + } else { + if (i != 0){ + //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); + lowerPoly.append(poly,i,poly.vertices.length); + } + //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); + lowerPoly.append(poly,0,upperIndex+1); + lowerPoly.vertices.push(p); + upperPoly.vertices.push(p); + //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); + upperPoly.append(poly,lowerIndex,i+1); + } + } else { + // connect to the closest point within the triangle + //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n"); + + if (lowerIndex > upperIndex) { + upperIndex += this.vertices.length; + } + closestDist = Number.MAX_VALUE; + + if(upperIndex < lowerIndex){ + return result; + } + + for (var j = lowerIndex; j <= upperIndex; ++j) { + if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j)) + && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { + d = Point.sqdist(poly.at(i), poly.at(j)); + if (d < closestDist) { + closestDist = d; + closestIndex = j % this.vertices.length; + } + } + } + + if (i < closestIndex) { + lowerPoly.append(poly,i,closestIndex+1); + if (closestIndex != 0){ + upperPoly.append(poly,closestIndex,v.length); + } + upperPoly.append(poly,0,i+1); + } else { + if (i != 0){ + lowerPoly.append(poly,i,v.length); + } + lowerPoly.append(poly,0,closestIndex+1); + upperPoly.append(poly,closestIndex,i+1); + } + } + + // solve smallest poly first + if (lowerPoly.vertices.length < upperPoly.vertices.length) { + lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + } else { + upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + } + + return result; + } + } + result.push(this); + + return result; +}; + +/** + * Remove collinear points in the polygon. + * @method removeCollinearPoints + * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. + * @return {Number} The number of points removed + */ +Polygon.prototype.removeCollinearPoints = function(precision){ + var num = 0; + for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){ + if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){ + // Remove the middle point + this.vertices.splice(i%this.vertices.length,1); + i--; // Jump one point forward. Otherwise we may get a chain removal + num++; + } + } + return num; +}; + +},{"./Line":3,"./Point":4,"./Scalar":6}],6:[function(require,module,exports){ +module.exports = Scalar; + +/** + * Scalar functions + * @class Scalar + */ +function Scalar(){} + +/** + * Check if two scalars are equal + * @static + * @method eq + * @param {Number} a + * @param {Number} b + * @param {Number} [precision] + * @return {Boolean} + */ +Scalar.eq = function(a,b,precision){ + precision = precision || 0; + return Math.abs(a-b) < precision; +}; + +},{}],7:[function(require,module,exports){ +module.exports = { + Polygon : require("./Polygon"), + Point : require("./Point"), +}; + +},{"./Point":4,"./Polygon":5}],8:[function(require,module,exports){ +module.exports={ + "name": "p2", + "version": "0.4.0", + "description": "A JavaScript 2D physics engine.", + "author": "Stefan Hedman (http://steffe.se)", + "keywords": [ + "p2.js", + "p2", + "physics", + "engine", + "2d" + ], + "main": "./src/p2.js", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/schteppe/p2.js.git" + }, + "bugs": { + "url": "https://github.com/schteppe/p2.js/issues" + }, + "licenses" : [ + { + "type" : "MIT" + } + ], + "devDependencies" : { + "jshint" : "latest", + "nodeunit" : "latest", + "grunt": "~0.4.0", + "grunt-contrib-jshint": "~0.1.1", + "grunt-contrib-nodeunit": "~0.1.2", + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-uglify": "*", + "grunt-browserify" : "*", + "browserify":"*" + }, + "dependencies" : { + "underscore":"*", + "poly-decomp" : "git://github.com/schteppe/poly-decomp.js", + "gl-matrix":"2.0.0", + "jsonschema":"*" + } +} + +},{}],9:[function(require,module,exports){ +var vec2 = require('../math/vec2') +, Utils = require('../utils/Utils') + +module.exports = AABB; + +/** + * Axis aligned bounding box class. + * @class AABB + * @constructor + * @param {Object} options + * @param {Array} upperBound + * @param {Array} lowerBound + */ +function AABB(options){ + + /** + * The lower bound of the bounding box. + * @property lowerBound + * @type {Array} + */ + this.lowerBound = vec2.create(); + if(options && options.lowerBound) vec2.copy(this.lowerBound, options.lowerBound); + + /** + * The upper bound of the bounding box. + * @property upperBound + * @type {Array} + */ + this.upperBound = vec2.create(); + if(options && options.upperBound) vec2.copy(this.upperBound, options.upperBound); +} + +var tmp = vec2.create(); + +/** + * Set the AABB bounds from a set of points. + * @method setFromPoints + * @param {Array} points An array of vec2's. + */ +AABB.prototype.setFromPoints = function(points,position,angle){ + var l = this.lowerBound, + u = this.upperBound; + vec2.set(l, Number.MAX_VALUE, Number.MAX_VALUE); + vec2.set(u, -Number.MAX_VALUE, -Number.MAX_VALUE); + for(var i=0; i u[j]){ + u[j] = p[j]; + } + if(p[j] < l[j]){ + l[j] = p[j]; + } + } + } + + // Add offset + if(position){ + vec2.add(this.lowerBound, this.lowerBound, position); + vec2.add(this.upperBound, this.upperBound, position); + } +}; + +/** + * Copy bounds from an AABB to this AABB + * @method copy + * @param {AABB} aabb + */ +AABB.prototype.copy = function(aabb){ + vec2.copy(this.lowerBound, aabb.lowerBound); + vec2.copy(this.upperBound, aabb.upperBound); +}; + +/** + * Extend this AABB so that it covers the given AABB too. + * @method extend + * @param {AABB} aabb + */ +AABB.prototype.extend = function(aabb){ + // Loop over x and y + for(var i=0; i<2; i++){ + // Extend lower bound + if(aabb.lowerBound[i] < this.lowerBound[i]) + this.lowerBound[i] = aabb.lowerBound[i]; + + // Upper + if(aabb.upperBound[i] > this.upperBound[i]) + this.upperBound[i] = aabb.upperBound[i]; + } +}; + +/** + * Returns true if the given AABB overlaps this AABB. + * @method overlaps + * @param {AABB} aabb + * @return {Boolean} + */ +AABB.prototype.overlaps = function(aabb){ + var l1 = this.lowerBound, + u1 = this.upperBound, + l2 = aabb.lowerBound, + u2 = aabb.upperBound; + + // l2 u2 + // |---------| + // |--------| + // l1 u1 + + return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) && + ((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1])); +}; + +},{"../math/vec2":33,"../utils/Utils":50}],10:[function(require,module,exports){ +var vec2 = require('../math/vec2') +var Body = require('../objects/Body') + +module.exports = Broadphase; + +/** + * Base class for broadphase implementations. + * @class Broadphase + * @constructor + */ +function Broadphase(type){ + + this.type = type; + + /** + * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs(). + * @property result + * @type {Array} + */ + this.result = []; + + /** + * The world to search for collision pairs in. To change it, use .setWorld() + * @property world + * @type {World} + */ + this.world = null; +}; + +/** + * Set the world that we are searching for collision pairs in + * @method setWorld + * @param {World} world + */ +Broadphase.prototype.setWorld = function(world){ + this.world = world; +}; + +/** + * Get all potential intersecting body pairs. + * @method getCollisionPairs + * @param {World} world The world to search in. + * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d). + */ +Broadphase.prototype.getCollisionPairs = function(world){ + throw new Error("getCollisionPairs must be implemented in a subclass!"); +}; + +var dist = vec2.create(); + +/** + * Check whether the bounding radius of two bodies overlap. + * @method boundingRadiusCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.boundingRadiusCheck = function(bodyA, bodyB){ + vec2.sub(dist, bodyA.position, bodyB.position); + var d2 = vec2.squaredLength(dist), + r = bodyA.boundingRadius + bodyB.boundingRadius; + return d2 <= r*r; +}; + +/** + * Check whether the bounding radius of two bodies overlap. + * @method boundingRadiusCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.aabbCheck = function(bodyA, bodyB){ + if(bodyA.aabbNeedsUpdate) bodyA.updateAABB(); + if(bodyB.aabbNeedsUpdate) bodyB.updateAABB(); + return bodyA.aabb.overlaps(bodyB.aabb); +}; + +/** + * Check whether two bodies are allowed to collide at all. + * @method canCollide + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.canCollide = function(bodyA, bodyB){ + + // Cannot collide static bodies + if(bodyA.motionState == Body.STATIC && bodyB.motionState == Body.STATIC) + return false; + + // Cannot collide static vs kinematic bodies + if( (bodyA.motionState == Body.KINEMATIC && bodyB.motionState == Body.STATIC) || + (bodyA.motionState == Body.STATIC && bodyB.motionState == Body.KINEMATIC)) + return false; + + // Cannot collide kinematic vs kinematic + if(bodyA.motionState == Body.KINEMATIC && bodyB.motionState == Body.KINEMATIC) + return false; + + // Cannot collide both sleeping bodies + if(bodyA.sleepState == Body.SLEEPING && bodyB.sleepState == Body.SLEEPING) + return false; + + return true; +}; + +Broadphase.NAIVE = 1; +Broadphase.SAP = 2; + +},{"../math/vec2":33,"../objects/Body":34}],11:[function(require,module,exports){ +var Circle = require('../shapes/Circle') +, Plane = require('../shapes/Plane') +, Particle = require('../shapes/Particle') +, Broadphase = require('../collision/Broadphase') +, vec2 = require('../math/vec2') + +module.exports = GridBroadphase; + +/** + * Broadphase that uses axis-aligned bins. + * @class GridBroadphase + * @constructor + * @extends Broadphase + * @param {number} xmin Lower x bound of the grid + * @param {number} xmax Upper x bound + * @param {number} ymin Lower y bound + * @param {number} ymax Upper y bound + * @param {number} nx Number of bins along x axis + * @param {number} ny Number of bins along y axis + * @todo test + */ +function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){ + Broadphase.apply(this); + + nx = nx || 10; + ny = ny || 10; + + this.binsizeX = (xmax-xmin) / nx; + this.binsizeY = (ymax-ymin) / ny; + this.nx = nx; + this.ny = ny; + this.xmin = xmin; + this.ymin = ymin; + this.xmax = xmax; + this.ymax = ymax; +}; +GridBroadphase.prototype = new Broadphase(); + +/** + * Get a bin index given a world coordinate + * @method getBinIndex + * @param {Number} x + * @param {Number} y + * @return {Number} Integer index + */ +GridBroadphase.prototype.getBinIndex = function(x,y){ + var nx = this.nx, + ny = this.ny, + xmin = this.xmin, + ymin = this.ymin, + xmax = this.xmax, + ymax = this.ymax; + + var xi = Math.floor(nx * (x - xmin) / (xmax-xmin)); + var yi = Math.floor(ny * (y - ymin) / (ymax-ymin)); + return xi*ny + yi; +} + +/** + * Get collision pairs. + * @method getCollisionPairs + * @param {World} world + * @return {Array} + */ +GridBroadphase.prototype.getCollisionPairs = function(world){ + var result = [], + collidingBodies = world.bodies, + Ncolliding = Ncolliding=collidingBodies.length, + binsizeX = this.binsizeX, + binsizeY = this.binsizeY; + + var bins=[], Nbins=nx*ny; + for(var i=0; i= 0 && xi*(ny-1) + yi < Nbins) + bins[ xi*(ny-1) + yi ].push(bi); + } + } + } else if(si instanceof Plane){ + // Put in all bins for now + if(bi.angle == 0){ + var y = bi.position[1]; + for(var j=0; j!==Nbins && ymin+binsizeY*(j-1) id2){ + var tmp = id1; + id1 = id2; + id2 = tmp; + } + return !!this.collidingBodiesLastStep[id1 + " " + id2]; +}; + +// "for in" loops aren't optimised in chrome... is there a better way to handle last-step collision memory? +// Maybe do this: http://jsperf.com/reflection-vs-array-of-keys +function clearObject(obj){ + for(var i = 0, l = obj.keys.length; i < l; i++) { + delete obj[obj.keys[i]]; + } + obj.keys.length = 0; + /* + for(var key in this.collidingBodiesLastStep) + delete this.collidingBodiesLastStep[key]; + */ +} + +/** + * Throws away the old equations and gets ready to create new + * @method reset + * @param {World} world + */ +Narrowphase.prototype.reset = function(world){ + + // Save the colliding bodies data + clearObject(this.collidingBodiesLastStep); + for(var i=0; i!==this.contactEquations.length; i++){ + var eq = this.contactEquations[i], + id1 = eq.bi.id, + id2 = eq.bj.id; + if(id1 > id2){ + var tmp = id1; + id1 = id2; + id2 = tmp; + } + var key = id1 + " " + id2; + if(!this.collidingBodiesLastStep[key]){ + this.collidingBodiesLastStep[key] = true; + this.collidingBodiesLastStep.keys.push(key); + } + } + + if(this.reuseObjects){ + var ce = this.contactEquations, + fe = this.frictionEquations, + rfe = this.reusableFrictionEquations, + rce = this.reusableContactEquations; + Utils.appendArray(rce,ce); + Utils.appendArray(rfe,fe); + } + + // Reset + this.contactEquations.length = this.frictionEquations.length = 0; +}; + +/** + * Creates a ContactEquation, either by reusing an existing object or creating a new one. + * @method createContactEquation + * @param {Body} bodyA + * @param {Body} bodyB + * @return {ContactEquation} + */ +Narrowphase.prototype.createContactEquation = function(bodyA,bodyB,shapeA,shapeB){ + var c = this.reusableContactEquations.length ? this.reusableContactEquations.pop() : new ContactEquation(bodyA,bodyB); + c.bi = bodyA; + c.bj = bodyB; + c.shapeA = shapeA; + c.shapeB = shapeB; + c.restitution = this.restitution; + c.firstImpact = !this.collidedLastStep(bodyA,bodyB); + c.stiffness = this.stiffness; + c.relaxation = this.relaxation; + c.enabled = true; + + if(bodyA.allowSleep && (bodyA.motionState == Body.DYNAMIC) && !(bodyB.motionState == Body.STATIC || bodyB.sleepState === Body.SLEEPY)) + bodyA.wakeUp(); + if(bodyB.allowSleep && (bodyB.motionState == Body.DYNAMIC) && !(bodyA.motionState == Body.STATIC || bodyA.sleepState === Body.SLEEPY)) + bodyB.wakeUp(); + + return c; +}; + +/** + * Creates a FrictionEquation, either by reusing an existing object or creating a new one. + * @method createFrictionEquation + * @param {Body} bodyA + * @param {Body} bodyB + * @return {FrictionEquation} + */ +Narrowphase.prototype.createFrictionEquation = function(bodyA,bodyB,shapeA,shapeB){ + var c = this.reusableFrictionEquations.length ? this.reusableFrictionEquations.pop() : new FrictionEquation(bodyA,bodyB); + c.bi = bodyA; + c.bj = bodyB; + c.shapeA = shapeA; + c.shapeB = shapeB; + c.setSlipForce(this.slipForce); + c.frictionCoefficient = this.frictionCoefficient; + c.relativeVelocity = this.surfaceVelocity; + c.enabled = true; + c.frictionStiffness = this.frictionStiffness; + c.frictionRelaxation = this.frictionRelaxation; + return c; +}; + +/** + * Creates a FrictionEquation given the data in the ContactEquation. Uses same offset vectors ri and rj, but the tangent vector will be constructed from the collision normal. + * @method createFrictionFromContact + * @param {ContactEquation} contactEquation + * @return {FrictionEquation} + */ +Narrowphase.prototype.createFrictionFromContact = function(c){ + var eq = this.createFrictionEquation(c.bi,c.bj,c.shapeA,c.shapeB); + vec2.copy(eq.ri, c.ri); + vec2.copy(eq.rj, c.rj); + vec2.rotate(eq.t, c.ni, -Math.PI / 2); + eq.contactEquation = c; + return eq; +} + +/** + * Convex/line narrowphase + * @method convexLine + * @param {Body} bi + * @param {Convex} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Line} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.LINE | Shape.CONVEX] = +Narrowphase.prototype.convexLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + // TODO + if(justTest) + return false; + else + return 0; +}; + +/** + * Line/rectangle narrowphase + * @method lineRectangle + * @param {Body} bi + * @param {Line} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Rectangle} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.LINE | Shape.RECTANGLE] = +Narrowphase.prototype.lineRectangle = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + // TODO + if(justTest) + return false; + else + return 0; +}; + +function setConvexToCapsuleShapeMiddle(convexShape, capsuleShape){ + vec2.set(convexShape.vertices[0], -capsuleShape.length * 0.5, -capsuleShape.radius); + vec2.set(convexShape.vertices[1], capsuleShape.length * 0.5, -capsuleShape.radius); + vec2.set(convexShape.vertices[2], capsuleShape.length * 0.5, capsuleShape.radius); + vec2.set(convexShape.vertices[3], -capsuleShape.length * 0.5, capsuleShape.radius); +} + +var convexCapsule_tempRect = new Rectangle(1,1), + convexCapsule_tempVec = vec2.create(); + +/** + * Convex/capsule narrowphase + * @method convexCapsule + * @param {Body} bi + * @param {Convex} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Capsule} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.CAPSULE | Shape.CONVEX] = +Narrowphase.prototype[Shape.CAPSULE | Shape.RECTANGLE] = +Narrowphase.prototype.convexCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + + // Check the circles + // Add offsets! + var circlePos = convexCapsule_tempVec; + vec2.set(circlePos, sj.length/2,0); + vec2.rotate(circlePos,circlePos,aj); + vec2.add(circlePos,circlePos,xj); + var result1 = this.circleConvex(bj,sj,circlePos,aj, bi,si,xi,ai, justTest, sj.radius); + + vec2.set(circlePos,-sj.length/2, 0); + vec2.rotate(circlePos,circlePos,aj); + vec2.add(circlePos,circlePos,xj); + var result2 = this.circleConvex(bj,sj,circlePos,aj, bi,si,xi,ai, justTest, sj.radius); + + if(justTest && (result1 || result2)) + return true; + + // Check center rect + var r = convexCapsule_tempRect; + setConvexToCapsuleShapeMiddle(r,sj); + var result = this.convexConvex(bi,si,xi,ai, bj,r,xj,aj, justTest); + + return result + result1 + result2; +}; + +/** + * Capsule/line narrowphase + * @method lineCapsule + * @param {Body} bi + * @param {Line} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Capsule} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.CAPSULE | Shape.LINE] = +Narrowphase.prototype.lineCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + // TODO + if(justTest) + return false; + else + return 0; +}; + +var capsuleCapsule_tempVec1 = vec2.create(); +var capsuleCapsule_tempVec2 = vec2.create(); +var capsuleCapsule_tempRect1 = new Rectangle(1,1); + +/** + * Capsule/capsule narrowphase + * @method capsuleCapsule + * @param {Body} bi + * @param {Capsule} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Capsule} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.CAPSULE | Shape.CAPSULE] = +Narrowphase.prototype.capsuleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + + // Check the circles + // Add offsets! + var circlePosi = capsuleCapsule_tempVec1, + circlePosj = capsuleCapsule_tempVec2; + + var numContacts = 0; + + // Need 4 circle checks, between all + for(var i=0; i<2; i++){ + + vec2.set(circlePosi,(i==0?-1:1)*si.length/2,0); + vec2.rotate(circlePosi,circlePosi,ai); + vec2.add(circlePosi,circlePosi,xi); + + for(var j=0; j<2; j++){ + + vec2.set(circlePosj,(j==0?-1:1)*sj.length/2, 0); + vec2.rotate(circlePosj,circlePosj,aj); + vec2.add(circlePosj,circlePosj,xj); + + var result = this.circleCircle(bi,si,circlePosi,ai, bj,sj,circlePosj,aj, justTest, si.radius, sj.radius); + + if(justTest && result) + return true; + + numContacts += result; + } + } + + // Check circles against the center rectangles + var rect = capsuleCapsule_tempRect1; + setConvexToCapsuleShapeMiddle(rect,si); + var result1 = this.convexCapsule(bi,rect,xi,ai, bj,sj,xj,aj, justTest); + + if(justTest && result1) return true; + numContacts += result1; + + setConvexToCapsuleShapeMiddle(rect,sj); + var result2 = this.convexCapsule(bj,rect,xj,aj, bi,si,xi,ai, justTest); + + if(justTest && result2) return true; + numContacts += result2; + + return numContacts; +}; + +/** + * Line/line narrowphase + * @method lineLine + * @param {Body} bi + * @param {Line} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Line} sj + * @param {Array} xj + * @param {Number} aj + * @todo Implement me! + */ +Narrowphase.prototype[Shape.LINE | Shape.LINE] = +Narrowphase.prototype.lineLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){ + // TODO + if(justTest) + return false; + else + return 0; +}; + +/** + * Plane/line Narrowphase + * @method planeLine + * @param {Body} planeBody + * @param {Plane} planeShape + * @param {Array} planeOffset + * @param {Number} planeAngle + * @param {Body} lineBody + * @param {Line} lineShape + * @param {Array} lineOffset + * @param {Number} lineAngle + */ +Narrowphase.prototype[Shape.PLANE | Shape.LINE] = +Narrowphase.prototype.planeLine = function(planeBody, planeShape, planeOffset, planeAngle, + lineBody, lineShape, lineOffset, lineAngle, justTest){ + var worldVertex0 = tmp1, + worldVertex1 = tmp2, + worldVertex01 = tmp3, + worldVertex11 = tmp4, + worldEdge = tmp5, + worldEdgeUnit = tmp6, + dist = tmp7, + worldNormal = tmp8, + worldTangent = tmp9, + verts = tmpArray + numContacts = 0; + + // Get start and end points + vec2.set(worldVertex0, -lineShape.length/2, 0); + vec2.set(worldVertex1, lineShape.length/2, 0); + + // Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired. + vec2.rotate(worldVertex01, worldVertex0, lineAngle); + vec2.rotate(worldVertex11, worldVertex1, lineAngle); + + add(worldVertex01, worldVertex01, lineOffset); + add(worldVertex11, worldVertex11, lineOffset); + + vec2.copy(worldVertex0,worldVertex01); + vec2.copy(worldVertex1,worldVertex11); + + // Get vector along the line + sub(worldEdge, worldVertex1, worldVertex0); + vec2.normalize(worldEdgeUnit, worldEdge); + + // Get tangent to the edge. + vec2.rotate(worldTangent, worldEdgeUnit, -Math.PI/2); + + vec2.rotate(worldNormal, yAxis, planeAngle); + + // Check line ends + verts[0] = worldVertex0; + verts[1] = worldVertex1; + for(var i=0; i pos0 && pos < pos1){ + // We got contact! + + if(justTest) return true; + + var c = this.createContactEquation(circleBody,lineBody,si,sj); + + vec2.scale(c.ni, orthoDist, -1); + vec2.normalize(c.ni, c.ni); + + vec2.scale( c.ri, c.ni, circleRadius); + add(c.ri, c.ri, circleOffset); + sub(c.ri, c.ri, circleBody.position); + + sub(c.rj, projectedPoint, lineOffset); + add(c.rj, c.rj, lineOffset); + sub(c.rj, c.rj, lineBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + + return 1; + } + } + + // Add corner + // @todo reuse array object + verts[0] = worldVertex0; + verts[1] = worldVertex1; + + for(var i=0; i 0){ + + // Now project the circle onto the edge + vec2.scale(orthoDist, worldTangent, d); + sub(projectedPoint, circleOffset, orthoDist); + + + // Check if the point is within the edge span + var pos = dot(worldEdgeUnit, projectedPoint); + var pos0 = dot(worldEdgeUnit, worldVertex0); + var pos1 = dot(worldEdgeUnit, worldVertex1); + + if(pos > pos0 && pos < pos1){ + // We got contact! + + if(justTest) return true; + + if(closestEdgeDistance === null || d*d 0){ + for(var i=0; i= 0){ + + // Now project the particle onto the edge + vec2.scale(orthoDist, worldTangent, d); + sub(projectedPoint, particleOffset, orthoDist); + + // Check if the point is within the edge span + var pos = dot(worldEdgeUnit, projectedPoint); + var pos0 = dot(worldEdgeUnit, worldVertex0); + var pos1 = dot(worldEdgeUnit, worldVertex1); + + if(pos > pos0 && pos < pos1){ + // We got contact! + if(justTest) return true; + + if(closestEdgeDistance === null || d*d r*r) + return 0; + + if(justTest) return true; + + var c = this.createContactEquation(bodyA,bodyB,si,sj); + sub(c.ni, offsetB, offsetA); + vec2.normalize(c.ni,c.ni); + + vec2.scale( c.ri, c.ni, radiusA); + vec2.scale( c.rj, c.ni, -radiusB); + + add(c.ri, c.ri, offsetA); + sub(c.ri, c.ri, bodyA.position); + + add(c.rj, c.rj, offsetB); + sub(c.rj, c.rj, bodyB.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + return 1; +}; + +/** + * Plane/Convex Narrowphase + * @method planeConvex + * @param {Body} bi + * @param {Plane} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Convex} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] = +Narrowphase.prototype[Shape.PLANE | Shape.RECTANGLE] = +Narrowphase.prototype.planeConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var convexBody = bj, + convexOffset = xj, + convexShape = sj, + convexAngle = aj, + planeBody = bi, + planeShape = si, + planeOffset = xi, + planeAngle = ai; + + var worldVertex = tmp1, + worldNormal = tmp2, + dist = tmp3; + + var numReported = 0; + vec2.rotate(worldNormal, yAxis, planeAngle); + + for(var i=0; i= 2) + break; + } + } + + return numReported; +}; + +/** + * @method convexPlane + * @deprecated Use .planeConvex() instead! + */ +Narrowphase.prototype.convexPlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + console.warn("Narrowphase.prototype.convexPlane is deprecated. Use planeConvex instead!"); + return this.planeConvex( bj,sj,xj,aj, bi,si,xi,ai, justTest ); +} + +/** + * Narrowphase for particle vs plane + * @method particlePlane + * @param {Body} bi The particle body + * @param {Particle} si Particle shape + * @param {Array} xi World position for the particle + * @param {Number} ai World angle for the particle + * @param {Body} bj Plane body + * @param {Plane} sj Plane shape + * @param {Array} xj World position for the plane + * @param {Number} aj World angle for the plane + */ +Narrowphase.prototype[Shape.PARTICLE | Shape.PLANE] = +Narrowphase.prototype.particlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var particleBody = bi, + particleShape = si, + particleOffset = xi, + planeBody = bj, + planeShape = sj, + planeOffset = xj, + planeAngle = aj; + + var dist = tmp1, + worldNormal = tmp2; + + planeAngle = planeAngle || 0; + + sub(dist, particleOffset, planeOffset); + vec2.rotate(worldNormal, yAxis, planeAngle); + + var d = dot(dist, worldNormal); + + if(d > 0) return 0; + if(justTest) return true; + + var c = this.createContactEquation(planeBody,particleBody,sj,si); + + vec2.copy(c.ni, worldNormal); + vec2.scale( dist, c.ni, d ); + // dist is now the distance vector in the normal direction + + // ri is the particle position projected down onto the plane, from the plane center + sub( c.ri, particleOffset, dist); + sub( c.ri, c.ri, planeBody.position); + + // rj is from the body center to the particle center + sub( c.rj, particleOffset, particleBody.position ); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + return 1; +}; + +/** + * Circle/Particle Narrowphase + * @method circleParticle + * @param {Body} bi + * @param {Circle} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Particle} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.PARTICLE] = +Narrowphase.prototype.circleParticle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var circleBody = bi, + circleShape = si, + circleOffset = xi, + particleBody = bj, + particleShape = sj, + particleOffset = xj, + dist = tmp1; + + sub(dist, particleOffset, circleOffset); + if(vec2.squaredLength(dist) > circleShape.radius*circleShape.radius) return 0; + if(justTest) return true; + + var c = this.createContactEquation(circleBody,particleBody,si,sj); + vec2.copy(c.ni, dist); + vec2.normalize(c.ni,c.ni); + + // Vector from circle to contact point is the normal times the circle radius + vec2.scale(c.ri, c.ni, circleShape.radius); + add(c.ri, c.ri, circleOffset); + sub(c.ri, c.ri, circleBody.position); + + // Vector from particle center to contact point is zero + sub(c.rj, particleOffset, particleBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + + return 1; +}; + +var capsulePlane_tmpCircle = new Circle(1), + capsulePlane_tmp1 = vec2.create(), + capsulePlane_tmp2 = vec2.create(), + capsulePlane_tmp3 = vec2.create(); + +Narrowphase.prototype[Shape.PLANE | Shape.CAPSULE] = +Narrowphase.prototype.planeCapsule = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var end1 = capsulePlane_tmp1, + end2 = capsulePlane_tmp2, + circle = capsulePlane_tmpCircle, + dst = capsulePlane_tmp3; + + // Compute world end positions + vec2.set(end1, -sj.length/2, 0); + vec2.rotate(end1,end1,aj); + add(end1,end1,xj); + + vec2.set(end2, sj.length/2, 0); + vec2.rotate(end2,end2,aj); + add(end2,end2,xj); + + circle.radius = sj.radius; + + // Do Narrowphase as two circles + var numContacts1 = this.circlePlane(bj,circle,end1,0, bi,si,xi,ai, justTest), + numContacts2 = this.circlePlane(bj,circle,end2,0, bi,si,xi,ai, justTest); + + if(justTest) + return numContacts1 || numContacts2; + else + return numContacts1 + numContacts2; +}; + +/** + * @method capsulePlane + * @deprecated Use .planeCapsule() instead! + */ +Narrowphase.prototype.capsulePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + console.warn("Narrowphase.prototype.capsulePlane() is deprecated. Use .planeCapsule() instead!"); + return this.planeCapsule( bj,sj,xj,aj, bi,si,xi,ai, justTest ); +} + +/** + * Creates ContactEquations and FrictionEquations for a collision. + * @method circlePlane + * @param {Body} bi The first body that should be connected to the equations. + * @param {Circle} si The circle shape participating in the collision. + * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none. + * @param {Body} bj The second body that should be connected to the equations. + * @param {Plane} sj The Plane shape that is participating + * @param {Array} xj Extra offset for the plane shape. + * @param {Number} aj Extra angle to apply to the plane + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.PLANE] = +Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var circleBody = bi, + circleShape = si, + circleOffset = xi, // Offset from body center, rotated! + planeBody = bj, + shapeB = sj, + planeOffset = xj, + planeAngle = aj; + + planeAngle = planeAngle || 0; + + // Vector from plane to circle + var planeToCircle = tmp1, + worldNormal = tmp2, + temp = tmp3; + + sub(planeToCircle, circleOffset, planeOffset); + + // World plane normal + vec2.rotate(worldNormal, yAxis, planeAngle); + + // Normal direction distance + var d = dot(worldNormal, planeToCircle); + + if(d > circleShape.radius) return 0; // No overlap. Abort. + + if(justTest) return true; + + // Create contact + var contact = this.createContactEquation(planeBody,circleBody,sj,si); + + // ni is the plane world normal + vec2.copy(contact.ni, worldNormal); + + // rj is the vector from circle center to the contact point + vec2.scale(contact.rj, contact.ni, -circleShape.radius); + add(contact.rj, contact.rj, circleOffset); + sub(contact.rj, contact.rj, circleBody.position); + + // ri is the distance from plane center to contact. + vec2.scale(temp, contact.ni, d); + sub(contact.ri, planeToCircle, temp ); // Subtract normal distance vector from the distance vector + add(contact.ri, contact.ri, planeOffset); + sub(contact.ri, contact.ri, planeBody.position); + + this.contactEquations.push(contact); + + if(this.enableFriction){ + this.frictionEquations.push( this.createFrictionFromContact(contact) ); + } + + return 1; +}; + +Narrowphase.convexPrecision = 1e-10; + +/** + * Convex/convex Narrowphase.See this article for more info. + * @method convexConvex + * @param {Body} bi + * @param {Convex} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Convex} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.CONVEX] = +Narrowphase.prototype[Shape.CONVEX | Shape.RECTANGLE] = +Narrowphase.prototype[Shape.RECTANGLE] = +Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, precision ){ + var sepAxis = tmp1, + worldPoint = tmp2, + worldPoint0 = tmp3, + worldPoint1 = tmp4, + worldEdge = tmp5, + projected = tmp6, + penetrationVec = tmp7, + dist = tmp8, + worldNormal = tmp9, + numContacts = 0, + precision = precision || Narrowphase.convexPrecision; + + var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis); + if(!found) return 0; + + // Make sure the separating axis is directed from shape i to shape j + sub(dist,xj,xi); + if(dot(sepAxis,dist) > 0){ + vec2.scale(sepAxis,sepAxis,-1); + } + + // Find edges with normals closest to the separating axis + var closestEdge1 = Narrowphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis + closestEdge2 = Narrowphase.getClosestEdge(sj,aj,sepAxis); + + if(closestEdge1==-1 || closestEdge2==-1) return 0; + + // Loop over the shapes + for(var k=0; k<2; k++){ + + var closestEdgeA = closestEdge1, + closestEdgeB = closestEdge2, + shapeA = si, shapeB = sj, + offsetA = xi, offsetB = xj, + angleA = ai, angleB = aj, + bodyA = bi, bodyB = bj; + + if(k==0){ + // Swap! + var tmp; + tmp = closestEdgeA; closestEdgeA = closestEdgeB; closestEdgeB = tmp; + tmp = shapeA; shapeA = shapeB; shapeB = tmp; + tmp = offsetA; offsetA = offsetB; offsetB = tmp; + tmp = angleA; angleA = angleB; angleB = tmp; + tmp = bodyA; bodyA = bodyB; bodyB = tmp; + } + + // Loop over 2 points in convex B + for(var j=closestEdgeB; j max) max = value; + if(min === null || value < min) min = value; + } + + if(min > max){ + var t = min; + min = max; + max = t; + } + + // Project the position of the body onto the axis - need to add this to the result + var offset = dot(convexOffset, worldAxis); + + vec2.set( result, min + offset, max + offset); +}; + +// .findSeparatingAxis is called by other functions, need local tmp vectors +var fsa_tmp1 = vec2.fromValues(0,0) +, fsa_tmp2 = vec2.fromValues(0,0) +, fsa_tmp3 = vec2.fromValues(0,0) +, fsa_tmp4 = vec2.fromValues(0,0) +, fsa_tmp5 = vec2.fromValues(0,0) +, fsa_tmp6 = vec2.fromValues(0,0) + +/** + * Find a separating axis between the shapes, that maximizes the separating distance between them. + * @method findSeparatingAxis + * @static + * @param {Convex} c1 + * @param {Array} offset1 + * @param {Number} angle1 + * @param {Convex} c2 + * @param {Array} offset2 + * @param {Number} angle2 + * @param {Array} sepAxis The resulting axis + * @return {Boolean} Whether the axis could be found. + */ +Narrowphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){ + var maxDist = null, + overlap = false, + found = false, + edge = fsa_tmp1, + worldPoint0 = fsa_tmp2, + worldPoint1 = fsa_tmp3, + normal = fsa_tmp4, + span1 = fsa_tmp5, + span2 = fsa_tmp6; + + for(var j=0; j!==2; j++){ + var c = c1, + angle = angle1; + if(j===1){ + c = c2; + angle = angle2; + } + + for(var i=0; i!==c.vertices.length; i++){ + // Get the world edge + vec2.rotate(worldPoint0, c.vertices[i], angle); + vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle); + + sub(edge, worldPoint1, worldPoint0); + + // Get normal - just rotate 90 degrees since vertices are given in CCW + vec2.rotate(normal, edge, -Math.PI / 2); + vec2.normalize(normal,normal); + + // Project hulls onto that normal + Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1); + Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2); + + // Order by span position + var a=span1, + b=span2, + swapped = false; + if(span1[0] > span2[0]){ + b=span1; + a=span2; + swapped = true; + } + + // Get separating distance + var dist = b[0] - a[1]; + overlap = dist < 0; + + if(maxDist===null || dist > maxDist){ + vec2.copy(sepAxis, normal); + maxDist = dist; + found = overlap; + } + } + } + + return found; +}; + +// .getClosestEdge is called by other functions, need local tmp vectors +var gce_tmp1 = vec2.fromValues(0,0) +, gce_tmp2 = vec2.fromValues(0,0) +, gce_tmp3 = vec2.fromValues(0,0) + +/** + * Get the edge that has a normal closest to an axis. + * @method getClosestEdge + * @static + * @param {Convex} c + * @param {Number} angle + * @param {Array} axis + * @param {Boolean} flip + * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed. + */ +Narrowphase.getClosestEdge = function(c,angle,axis,flip){ + var localAxis = gce_tmp1, + edge = gce_tmp2, + normal = gce_tmp3; + + // Convert the axis to local coords of the body + vec2.rotate(localAxis, axis, -angle); + if(flip){ + vec2.scale(localAxis,localAxis,-1); + } + + var closestEdge = -1, + N = c.vertices.length, + halfPi = Math.PI / 2; + for(var i=0; i!==N; i++){ + // Get the edge + sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]); + + // Get normal - just rotate 90 degrees since vertices are given in CCW + vec2.rotate(normal, edge, -halfPi); + vec2.normalize(normal,normal); + + var d = dot(normal,localAxis); + if(closestEdge == -1 || d > maxDot){ + closestEdge = i % N; + maxDot = d; + } + } + + return closestEdge; +}; + +var circleHeightfield_candidate = vec2.create(), + circleHeightfield_dist = vec2.create(), + circleHeightfield_v0 = vec2.create(), + circleHeightfield_v1 = vec2.create(), + circleHeightfield_minCandidate = vec2.create(), + circleHeightfield_worldNormal = vec2.create(), + circleHeightfield_minCandidateNormal = vec2.create(); + +/** + * @method circleHeightfield + * @param {Body} bi + * @param {Circle} si + * @param {Array} xi + * @param {Body} bj + * @param {Heightfield} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.HEIGHTFIELD] = +Narrowphase.prototype.circleHeightfield = function( circleBody,circleShape,circlePos,circleAngle, + hfBody,hfShape,hfPos,hfAngle, justTest, radius ){ + var data = hfShape.data, + radius = radius || circleShape.radius, + w = hfShape.elementWidth, + dist = circleHeightfield_dist, + candidate = circleHeightfield_candidate, + minCandidate = circleHeightfield_minCandidate, + minCandidateNormal = circleHeightfield_minCandidateNormal, + worldNormal = circleHeightfield_worldNormal, + v0 = circleHeightfield_v0, + v1 = circleHeightfield_v1; + + // Get the index of the points to test against + var idxA = Math.floor( (circlePos[0] - radius - hfPos[0]) / w ), + idxB = Math.ceil( (circlePos[0] + radius - hfPos[0]) / w ); + + /*if(idxB < 0 || idxA >= data.length) + return justTest ? false : 0;*/ + + if(idxA < 0) idxA = 0; + if(idxB >= data.length) idxB = data.length-1; + + // Get max and min + var max = data[idxA], + min = data[idxB]; + for(var i=idxA; i max) max = data[i]; + } + + if(circlePos[1]-radius > max) + return justTest ? false : 0; + + if(circlePos[1]+radius < min){ + // Below the minimum point... We can just guess. + // TODO + } + + // 1. Check so center of circle is not inside the field. If it is, this wont work... + // 2. For each edge + // 2. 1. Get point on circle that is closest to the edge (scale normal with -radius) + // 2. 2. Check if point is inside. + + var found = false, + minDist = false; + + // Check all edges first + for(var i=idxA; i= v0[0] && candidate[0] < v1[0] && d <= 0){ + + if(minDist === false || Math.abs(d) < minDist){ + + // Store the candidate point, projected to the edge + vec2.scale(dist,worldNormal,-d); + vec2.add(minCandidate,candidate,dist); + vec2.copy(minCandidateNormal,worldNormal); + + found = true; + minDist = Math.abs(d); + + if(justTest) + return true; + } + } + } + + if(found){ + + var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape); + + // Normal is out of the heightfield + vec2.copy(c.ni, minCandidateNormal); + + // Vector from circle to heightfield + vec2.scale(c.rj, c.ni, -radius); + add(c.rj, c.rj, circlePos); + sub(c.rj, c.rj, circleBody.position); + + vec2.copy(c.ri, minCandidate); + //vec2.sub(c.ri, c.ri, hfPos); + vec2.sub(c.ri, c.ri, hfBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction) + this.frictionEquations.push( this.createFrictionFromContact(c) ); + + return 1; + } + + + // Check all vertices + if(radius > 0){ + for(var i=idxA; i<=idxB; i++){ + + // Get point + vec2.set(v0, i*w, data[i]); + vec2.add(v0,v0,hfPos); + + vec2.sub(dist, circlePos, v0); + + if(vec2.squaredLength(dist) < radius*radius){ + + if(justTest) return true; + + var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape); + + // Construct normal - out of heightfield + vec2.copy(c.ni, dist); + vec2.normalize(c.ni,c.ni); + + vec2.scale(c.rj, c.ni, -radius); + add(c.rj, c.rj, circlePos); + sub(c.rj, c.rj, circleBody.position); + + sub(c.ri, v0, hfPos); + add(c.ri, c.ri, hfPos); + sub(c.ri, c.ri, hfBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + + return 1; + } + } + } + + return 0; + +}; + +},{"../equations/ContactEquation":23,"../equations/FrictionEquation":25,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":38,"../shapes/Rectangle":44,"../shapes/Shape":45,"../utils/Utils":50}],14:[function(require,module,exports){ +var Plane = require("../shapes/Plane"); +var Broadphase = require("../collision/Broadphase"); + +module.exports = { + QuadTree : QuadTree, + Node : Node, + BoundsNode : BoundsNode, +}; + +/** + * QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree + * @class QuadTree + * @constructor + * @param {Object} An object representing the bounds of the top level of the QuadTree. The object + * should contain the following properties : x, y, width, height + * @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds + * (width / height)(false). Default value is false. + * @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4. + * @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes. + */ +function QuadTree(bounds, pointQuad, maxDepth, maxChildren){ + var node; + if(pointQuad){ + node = new Node(bounds, 0, maxDepth, maxChildren); + } else { + node = new BoundsNode(bounds, 0, maxDepth, maxChildren); + } + + /** + * The root node of the QuadTree which covers the entire area being segmented. + * @property root + * @type Node + */ + this.root = node; +} + +/** + * Inserts an item into the QuadTree. + * @method insert + * @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y + * properties that represents its position in 2D space. + */ +QuadTree.prototype.insert = function(item){ + if(item instanceof Array){ + var len = item.length; + for(var i = 0; i < len; i++){ + this.root.insert(item[i]); + } + } else { + this.root.insert(item); + } +} + +/** + * Clears all nodes and children from the QuadTree + * @method clear + */ +QuadTree.prototype.clear = function(){ + this.root.clear(); +} + +/** + * Retrieves all items / points in the same node as the specified item / point. If the specified item + * overlaps the bounds of a node, then all children in both nodes will be returned. + * @method retrieve + * @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape + * with dimensions (x, y, width, height) properties. + */ +QuadTree.prototype.retrieve = function(item){ + //get a copy of the array of items + var out = this.root.retrieve(item).slice(0); + return out; +} + +QuadTree.prototype.getCollisionPairs = function(world){ + + var result = []; + + // Add all bodies + this.insert(world.bodies); + + /* + console.log("bodies",world.bodies.length); + console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren); + */ + + for(var i=0; i!==world.bodies.length; i++){ + var b = world.bodies[i], + items = this.retrieve(b); + + //console.log("items",items.length); + + // Check results + for(var j=0, len=items.length; j!==len; j++){ + var item = items[j]; + + if(b === item) continue; // Do not add self + + // Check if they were already added + var found = false; + for(var k=0, numAdded=result.length; k= this.maxDepth) && len > this.maxChildren) { + this.subdivide(); + + for(var i = 0; i < len; i++){ + this.insert(this.children[i]); + } + + this.children.length = 0; + } +} + +Node.prototype.retrieve = function(item){ + if(this.nodes.length){ + var index = this.findIndex(item); + return this.nodes[index].retrieve(item); + } + + return this.children; +} + +Node.prototype.findIndex = function(item){ + var b = this.bounds; + var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true; + var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true; + + if(item instanceof Plane){ + left = top = false; // Will overlap the left/top boundary since it is infinite + } + + //top left + var index = Node.TOP_LEFT; + if(left){ + if(!top){ + index = Node.BOTTOM_LEFT; + } + } else { + if(top){ + index = Node.TOP_RIGHT; + } else { + index = Node.BOTTOM_RIGHT; + } + } + + return index; +} + + +Node.prototype.subdivide = function(){ + var depth = this.depth + 1; + + var bx = this.bounds.x; + var by = this.bounds.y; + + //floor the values + var b_w_h = (this.bounds.width / 2); + var b_h_h = (this.bounds.height / 2); + var bx_b_w_h = bx + b_w_h; + var by_b_h_h = by + b_h_h; + + //top left + this.nodes[Node.TOP_LEFT] = new this.classConstructor({ + x:bx, + y:by, + width:b_w_h, + height:b_h_h + }, + depth); + + //top right + this.nodes[Node.TOP_RIGHT] = new this.classConstructor({ + x:bx_b_w_h, + y:by, + width:b_w_h, + height:b_h_h + }, + depth); + + //bottom left + this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({ + x:bx, + y:by_b_h_h, + width:b_w_h, + height:b_h_h + }, + depth); + + + //bottom right + this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({ + x:bx_b_w_h, + y:by_b_h_h, + width:b_w_h, + height:b_h_h + }, + depth); +} + +Node.prototype.clear = function(){ + this.children.length = 0; + + var len = this.nodes.length; + for(var i = 0; i < len; i++){ + this.nodes[i].clear(); + } + + this.nodes.length = 0; +} + + +// BoundsQuadTree + +function BoundsNode(bounds, depth, maxChildren, maxDepth){ + Node.call(this, bounds, depth, maxChildren, maxDepth); + this.stuckChildren = []; +} + +BoundsNode.prototype = new Node(); +BoundsNode.prototype.classConstructor = BoundsNode; +BoundsNode.prototype.stuckChildren = null; + +//we use this to collect and conctenate items being retrieved. This way +//we dont have to continuously create new Array instances. +//Note, when returned from QuadTree.retrieve, we then copy the array +BoundsNode.prototype.out = []; + +BoundsNode.prototype.insert = function(item){ + if(this.nodes.length){ + var index = this.findIndex(item); + var node = this.nodes[index]; + + /* + console.log("radius:",item.boundingRadius); + console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width); + console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height); + */ + + //todo: make _bounds bounds + if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child + item.position[0] - item.boundingRadius >= node.bounds.x && + item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width && + item.position[1] - item.boundingRadius >= node.bounds.y && + item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){ + this.nodes[index].insert(item); + } else { + this.stuckChildren.push(item); + } + + return; + } + + this.children.push(item); + + var len = this.children.length; + + if(this.depth < this.maxDepth && len > this.maxChildren){ + this.subdivide(); + + for(var i=0; i