├── src
├── index.js
├── constants.js
├── utils.js
├── physics.js
├── browser.js
└── tiled
│ ├── Tile.js
│ ├── Tileset.js
│ ├── Objectlayer.js
│ └── TilemapParser.js
├── testmaps
├── maps
│ ├── gfx
│ │ └── png
│ │ │ ├── blue_x10.png
│ │ │ ├── ughTiles.png
│ │ │ ├── 32_3_tiles.png
│ │ │ ├── greyBlack.png
│ │ │ ├── miniGreen.png
│ │ │ ├── formosa
│ │ │ ├── tiles.png
│ │ │ ├── moon_overlay.png
│ │ │ └── 16x16-overworld.png
│ │ │ ├── greenBlue_x0_y32.png
│ │ │ ├── singleTallBall.png
│ │ │ ├── darkworld-tileset.png
│ │ │ ├── ortho
│ │ │ ├── yellowCircle.png
│ │ │ ├── miniGreenOrtho.png
│ │ │ └── miniBlueOrthoTall.png
│ │ │ ├── isometric_grass_and_water.png
│ │ │ ├── 32x16
│ │ │ ├── red_xoffset_0__yoffset_0.png
│ │ │ ├── green_xoffset_16__yoffset_0.png
│ │ │ ├── salmon_xoffset_13__yoffset0.png
│ │ │ └── white_grey_xoffset_0__yoffset_0.png
│ │ │ ├── 64x32
│ │ │ ├── blue_xoffset_0__yoffset_0.png
│ │ │ ├── aqua_xoffset_0__yoffset_17.png
│ │ │ ├── green_xoffset_0__yoffset_16.png
│ │ │ ├── pink_xoffset_0__yoffset_20.png
│ │ │ └── salmon_xoffset_0__yoffset_15.png
│ │ │ └── 32x16_XY_offset
│ │ │ ├── aqua_xoffset_5__yoffset_1.png
│ │ │ ├── red_xoffset_5__yoffset_45.png
│ │ │ ├── blue_xoffset_17__yoffset_5.png
│ │ │ ├── yellow_xoffset_0__yoffset_0.png
│ │ │ └── black_xoffset_32__yoffset_32.png
│ ├── ortho
│ │ ├── basicRotate.tmx
│ │ ├── Ortho_1_32__32.tmx
│ │ ├── formosa_zlib.tmx
│ │ ├── formosa_gzip.tmx
│ │ └── Ortho_1_32__32_objects.tmx
│ └── iso
│ │ ├── Isometric_32x16_nooffset.tmx
│ │ ├── Isometric_cubes.tmx
│ │ ├── Isometric_cubes_large.tmx
│ │ ├── Isometric_land.tmx
│ │ ├── Isometric_32x16_with_offset_x.tmx
│ │ ├── Isometric_64_32_with_offset_y_evensize.tmx
│ │ ├── Isometric_cubes_with_objects_large.tmx
│ │ ├── Isometric_64_32_with_offset_y.tmx
│ │ ├── Isometric_32x16_with_offset_x_y_odd.tmx
│ │ ├── Isometric_32x16_with_offset_x_y_even.tmx
│ │ └── Isometric_cubes_with_objects.tmx
├── README.md
├── css
│ └── style.css
├── index.html
└── js
│ ├── resources.js
│ ├── main.js
│ └── vendor
│ ├── jquery.query-object.js
│ └── require-2.1.15.min.js
├── .gitignore
├── .editorconfig
├── LICENSE
├── package.json
├── gulpfile.js
├── CONTRIBUTING.md
├── .jscsrc
├── .eslintrc.json
├── typescript
└── phaser-tiled.d.ts
└── README.md
/src/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | utils: require('./utils')
3 | };
4 |
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/blue_x10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/blue_x10.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/ughTiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/ughTiles.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32_3_tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32_3_tiles.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/greyBlack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/greyBlack.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/miniGreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/miniGreen.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/formosa/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/formosa/tiles.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/greenBlue_x0_y32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/greenBlue_x0_y32.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/singleTallBall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/singleTallBall.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/darkworld-tileset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/darkworld-tileset.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/ortho/yellowCircle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/ortho/yellowCircle.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/formosa/moon_overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/formosa/moon_overlay.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/ortho/miniGreenOrtho.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/ortho/miniGreenOrtho.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/formosa/16x16-overworld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/formosa/16x16-overworld.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/ortho/miniBlueOrthoTall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/ortho/miniBlueOrthoTall.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/isometric_grass_and_water.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/isometric_grass_and_water.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16/red_xoffset_0__yoffset_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16/red_xoffset_0__yoffset_0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/64x32/blue_xoffset_0__yoffset_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/64x32/blue_xoffset_0__yoffset_0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16/green_xoffset_16__yoffset_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16/green_xoffset_16__yoffset_0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16/salmon_xoffset_13__yoffset0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16/salmon_xoffset_13__yoffset0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/64x32/aqua_xoffset_0__yoffset_17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/64x32/aqua_xoffset_0__yoffset_17.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/64x32/green_xoffset_0__yoffset_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/64x32/green_xoffset_0__yoffset_16.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/64x32/pink_xoffset_0__yoffset_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/64x32/pink_xoffset_0__yoffset_20.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/64x32/salmon_xoffset_0__yoffset_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/64x32/salmon_xoffset_0__yoffset_15.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16/white_grey_xoffset_0__yoffset_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16/white_grey_xoffset_0__yoffset_0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16_XY_offset/aqua_xoffset_5__yoffset_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16_XY_offset/aqua_xoffset_5__yoffset_1.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16_XY_offset/red_xoffset_5__yoffset_45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16_XY_offset/red_xoffset_5__yoffset_45.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16_XY_offset/blue_xoffset_17__yoffset_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16_XY_offset/blue_xoffset_17__yoffset_5.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16_XY_offset/yellow_xoffset_0__yoffset_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16_XY_offset/yellow_xoffset_0__yoffset_0.png
--------------------------------------------------------------------------------
/testmaps/maps/gfx/png/32x16_XY_offset/black_xoffset_32__yoffset_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/englercj/phaser-tiled/master/testmaps/maps/gfx/png/32x16_XY_offset/black_xoffset_32__yoffset_32.png
--------------------------------------------------------------------------------
/testmaps/README.md:
--------------------------------------------------------------------------------
1 | # MapTests
2 |
3 | A bunch of [Tiled Editor][0] maps to test [phaser-tiled][1] features.
4 |
5 | [0]: http://mapeditor.org/
6 | [1]: https://github.com/englercj/phaser-tiled
7 |
8 | ## Running
9 |
10 | First clone this repository, then run `npm install` in the cloned directory.
11 |
12 | You can run the packaged server by running `npm dev`.
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # sublime text 2 files
2 | *.sublime*
3 | *.*~*.TMP
4 |
5 | # temp files
6 | .DS_Store
7 | Thumbs.db
8 | Desktop.ini
9 | npm-debug.log
10 |
11 | # vim swap files
12 | *.sw*
13 |
14 | # emacs temp files
15 | *~
16 | \#*#
17 |
18 | # project ignores
19 | !.gitkeep
20 | *__temp
21 | node_modules
22 | testmaps/maps/tilemap-assets.json
23 | testmaps/node_modules
24 | build
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://EditorConfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | indent_style = space
10 | indent_size = 4
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [{package.json,bower.json,.travis.yml}]
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/testmaps/css/style.css:
--------------------------------------------------------------------------------
1 | html,body {
2 | height: 100%;
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | #viewporter,
8 | #game {
9 | height: 100%;
10 | margin: 0;
11 | padding: 0;
12 | cursor: move;
13 | }
14 |
15 | #switch,
16 | #dirty,
17 | #fullscreen {
18 | position: absolute;
19 | }
20 |
21 | #switch {
22 | top: 5px;
23 | right: 5px;
24 | }
25 |
26 | #fullscreen {
27 | top: 30px;
28 | right: 5px;
29 | }
30 |
--------------------------------------------------------------------------------
/testmaps/maps/ortho/basicRotate.tmx:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/testmaps/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Map Tests
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_32x16_nooffset.tmx:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_cubes.tmx:
--------------------------------------------------------------------------------
1 |
2 |
16 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_cubes_large.tmx:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * @property CSV
4 | * @type {Number}
5 | * @static
6 | * @final
7 | */
8 | CSV: 0,
9 |
10 | /**
11 | * @property CSV
12 | * @type {Number}
13 | * @static
14 | * @final
15 | */
16 | TILED_JSON: 1,
17 |
18 | /**
19 | * @property CSV
20 | * @type {Number}
21 | * @static
22 | * @final
23 | */
24 | TILED_XML: 2,
25 |
26 | /**
27 | * @property CSV
28 | * @type {Number}
29 | * @static
30 | * @final
31 | */
32 | NORTH: 0,
33 |
34 | /**
35 | * @property CSV
36 | * @type {Number}
37 | * @static
38 | * @final
39 | */
40 | EAST: 1,
41 |
42 | /**
43 | * @property CSV
44 | * @type {Number}
45 | * @static
46 | * @final
47 | */
48 | SOUTH: 2,
49 |
50 | /**
51 | * @property CSV
52 | * @type {Number}
53 | * @static
54 | * @final
55 | */
56 | WEST: 3
57 | };
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2014 Chad Engler
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_land.tmx:
--------------------------------------------------------------------------------
1 |
2 |
16 |
--------------------------------------------------------------------------------
/testmaps/maps/ortho/Ortho_1_32__32.tmx:
--------------------------------------------------------------------------------
1 |
2 |
25 |
--------------------------------------------------------------------------------
/testmaps/js/resources.js:
--------------------------------------------------------------------------------
1 | define(function () {
2 | var resources = {
3 | worlds: [
4 | // Orthogonal test maps
5 | 'maps/ortho/formosa_gzip.tmx',
6 | 'maps/ortho/formosa_zlib.tmx',
7 | 'maps/ortho/Ortho_1_16__16_large.json',
8 | 'maps/ortho/Ortho_1_16__16_large.tmx',
9 | 'maps/ortho/Ortho_1_32__32.tmx',
10 | 'maps/ortho/Ortho_1_32__32_objects.tmx',
11 | 'maps/ortho/Ortho_1_32__32_objects.json',
12 | 'maps/ortho/basicRotate.tmx',
13 |
14 | // Isometric test maps
15 | 'maps/iso/Isometric_32x16_nooffset.tmx',
16 | 'maps/iso/Isometric_32x16_with_offset_x.tmx',
17 | 'maps/iso/Isometric_32x16_with_offset_x_y_even.tmx',
18 | 'maps/iso/Isometric_32x16_with_offset_x_y_odd.tmx',
19 | 'maps/iso/Isometric_64_32_with_offset_y.tmx',
20 | 'maps/iso/Isometric_64_32_with_offset_y_evensize.tmx',
21 | 'maps/iso/Isometric_cubes.tmx',
22 | 'maps/iso/Isometric_cubes_large.tmx',
23 | 'maps/iso/Isometric_cubes_with_objects.tmx',
24 | 'maps/iso/Isometric_cubes_with_objects_large.tmx',
25 | 'maps/iso/Isometric_land.tmx'
26 | ]
27 | };
28 | return resources;
29 | });
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser-tiled",
3 | "version": "2.0.4",
4 | "description": "A tilemap implementation for phaser focusing on large complex maps",
5 | "author": "Chad Engler ",
6 | "license": "MIT",
7 | "homepage": "https://github.com/englercj/phaser-tiled",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/englercj/phaser-tiled.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/englercj/phaser-tiled/issues"
14 | },
15 | "keywords": [
16 | "phaser",
17 | "html5",
18 | "tilemap",
19 | "tile",
20 | "map",
21 | "2d",
22 | "tiled"
23 | ],
24 | "files": [
25 | "build/",
26 | "src/",
27 | "typescript/",
28 | "LICENSE",
29 | "README.md",
30 | "package.json"
31 | ],
32 | "dependencies": {
33 | "Base64": "^0.3.0",
34 | "xmldom": "^0.1.21",
35 | "zlibjs": "^0.2.0"
36 | },
37 | "devDependencies": {
38 | "browserify": "^13.0.0",
39 | "code-style": "englercj/code-style",
40 | "eslint": "^1.10.3",
41 | "event-stream": "^3.3.2",
42 | "gulp": "^3.9.0",
43 | "gulp-eslint": "^1.1.1",
44 | "gulp-jscs": "^3.0.2",
45 | "gulp-util": "^3.0.7",
46 | "jscs": "^2.8.0",
47 | "vinyl-source-stream": "^1.1.0",
48 | "watchify": "^3.7.0",
49 | "gulp-connect": "^2.0.6",
50 | "gulp-phaser-tiled-pack": "^1.1.0"
51 | },
52 | "peerDependencies": {
53 | "phaser": "^2.4.4"
54 | },
55 | "main": "./src/index.js",
56 | "browser": {
57 | "./src/index.js": "./src/browser.js",
58 | "xmldom": false
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_32x16_with_offset_x.tmx:
--------------------------------------------------------------------------------
1 |
2 |
33 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_64_32_with_offset_y_evensize.tmx:
--------------------------------------------------------------------------------
1 |
2 |
42 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_cubes_with_objects_large.tmx:
--------------------------------------------------------------------------------
1 |
2 |
41 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_64_32_with_offset_y.tmx:
--------------------------------------------------------------------------------
1 |
2 |
51 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_32x16_with_offset_x_y_odd.tmx:
--------------------------------------------------------------------------------
1 |
2 |
51 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_32x16_with_offset_x_y_even.tmx:
--------------------------------------------------------------------------------
1 |
2 |
51 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var path = require('path');
3 | var gutil = require('gulp-util');
4 | var eslint = require('gulp-eslint');
5 | var jscs = require('gulp-jscs');
6 | var tiledmapPack = require('gulp-phaser-tiled-pack');
7 | var connect = require('gulp-connect');
8 |
9 | var source = require('vinyl-source-stream');
10 | var watchify = require('watchify');
11 | var browserify = require('browserify');
12 |
13 | var index = './src/index.js';
14 | var outdir = './build';
15 | var bundle = 'Phaser.Plugin.Tiled';
16 | var outfile = 'phaser-tiled.js';
17 |
18 | function rebundle(file) {
19 | if (file) {
20 | gutil.log('Rebundling,', path.basename(file[0]), 'has changes.');
21 | }
22 |
23 | return this.bundle()
24 | // log errors if they happen
25 | .on('error', gutil.log.bind(gutil, 'Browserify Error'))
26 | .pipe(source(outfile))
27 | .pipe(gulp.dest(outdir));
28 | }
29 |
30 | function createBundler(args) {
31 | args = args || {};
32 | args.standalone = bundle;
33 |
34 | return browserify(index, args);
35 | }
36 |
37 | /*****
38 | * Dev task, incrementally rebuilds the output bundle as the the sources change
39 | *****/
40 | gulp.task('watch', function () {
41 | var bundler = watchify(createBundler());
42 |
43 | bundler.on('update', function (file) {
44 | gulp.start('lint');
45 | rebundle.call(this, file).pipe(connect.reload());
46 | });
47 |
48 | gulp.watch('testmaps/maps/**/*', ['packmaps']);
49 | gulp.watch('testmaps/js/*', function (file) {
50 | gulp.start('lint');
51 | gulp.src('testmaps/js/*').pipe(connect.reload(file));
52 | });
53 |
54 | connect.server({
55 | root: '',
56 | livereload: true
57 | });
58 |
59 | return rebundle.call(bundler);
60 | });
61 |
62 | /*****
63 | * Build task, builds the output bundle
64 | *****/
65 | gulp.task('build', function () {
66 | return rebundle.call(createBundler());
67 | });
68 |
69 | /*****
70 | * ESLint task, lints the lib and test *.js files.
71 | *****/
72 | gulp.task('lint', function () {
73 | return gulp.src([
74 | './src/**/*.js',
75 | 'testmaps/js/*.js',
76 | 'gulpfile.js',
77 | '!node_modules/**'
78 | ])
79 | .pipe(eslint())
80 | .pipe(eslint.format())
81 | .pipe(jscs())
82 | .pipe(jscs.reporter());
83 | });
84 |
85 | gulp.task('packmaps', function () {
86 | return gulp.src('./testmaps/maps/{iso,ortho}/*.{json,tmx}')
87 | .pipe(tiledmapPack({ useExtInKey: true, baseUrl: 'maps' }))
88 | .pipe(gulp.dest('./testmaps/maps'))
89 | .pipe(connect.reload());
90 | });
91 |
92 | gulp.task('dev', ['packmaps', 'lint', 'build', 'watch']);
93 |
94 | /*****
95 | * Base task
96 | *****/
97 | gulp.task('default', ['lint', 'build']);
98 |
--------------------------------------------------------------------------------
/testmaps/maps/ortho/formosa_zlib.tmx:
--------------------------------------------------------------------------------
1 |
2 |
82 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | Please read this short guide to contributing before performing pull requests or reporting issues. The purpose
4 | of this guide is to ensure the best experience for all involved and make development as smooth as possible.
5 |
6 |
7 | ## Reporting issues
8 |
9 | To report a bug, request a feature, or even ask a question, make use of the [GitHub Issues][10] in this repo.
10 | When submitting an issue please take the following steps:
11 |
12 | **1. Search for existing issues.** Your bug may have already been fixed or addressed in an unreleased version, so
13 | be sure to search the issues first before putting in a duplicate issue.
14 |
15 | **2. Create an isolated and reproducible test case.** If you are reporting a bug, make sure you also have a minimal,
16 | runnable, code example that reproduces the problem you have.
17 |
18 | **3. Include a live example.** After narrowing your code down to only the problem areas, make use of [jsFiddle][11],
19 | [jsBin][12], or a link to your live site so that we can view a live example of the problem.
20 |
21 | **4. Share as much information as possible.** Include browser version affected, your OS, version of the library,
22 | steps to reproduce, etc. "X isn't working!!!1!" will probably just be closed.
23 |
24 | [10]: https://github.com/englercj/phaser-tiled/issues
25 | [11]: http://jsfiddle.net
26 | [12]: http://jsbin.com/
27 |
28 |
29 | ## Making Changes
30 |
31 | To build the library you will need to download node.js from [nodejs.org][20]. After it has been installed open a
32 | console and run `npm install -g gulp` to install the global `gulp` executable.
33 |
34 | After that you can clone the repository and run `npm install` inside the cloned folder. This will install
35 | dependencies necessary for building the project. You can rebuild the project by running `gulp` in the cloned
36 | folder.
37 |
38 | Once that is ready, you can make your changes and submit a Pull Request:
39 |
40 | - **Send Pull Requests to the `master` branch.** All Pull Requests must be sent to the `master` branch.
41 |
42 | - **Ensure changes are jshint validated.** Our JSHint configuration file is provided in the repository and you
43 | should check against it before submitting. This should happen automatically when running `gulp` in the repo directory.
44 |
45 | - **Never commit new builds.** When making a code change you should always run `gulp` which will rebuild the project
46 | so you can test, *however* please do not commit the new builds placed in `dist/` or your PR will be closed. By default
47 | the build process will output to an ignored folder (`build/`) you should be fine.
48 |
49 | - **Only commit relevant changes.** Don't include changes that are not directly relevant to the fix you are making.
50 | The more focused a PR is, the faster it will get attention and be merged. Extra files changing only whitespace or
51 | trash files will likely get your PR closed.
52 |
53 | [20]: http://nodejs.org
54 |
55 |
56 | ## Quickie Code Style Guide
57 |
58 | Use EditorConfig and JSHint! Both tools will ensure your code is in the required styles! Either way, here are some tips:
59 |
60 | - Use 4 spaces for tabs, never tab characters.
61 |
62 | - No trailing whitespace, blank lines should have no whitespace.
63 |
64 | - Always favor strict equals `===` unless you *need* to use type coercion.
65 |
66 | - Follow conventions already in the code, and listen to jshint. Our config is set-up for a reason.
67 |
--------------------------------------------------------------------------------
/testmaps/maps/ortho/formosa_gzip.tmx:
--------------------------------------------------------------------------------
1 |
2 |
82 |
--------------------------------------------------------------------------------
/testmaps/js/main.js:
--------------------------------------------------------------------------------
1 | require.config({
2 | shim: {
3 | 'jquery.query': {
4 | deps: ['jquery'],
5 | exports: 'jQuery.fn.query'
6 | }
7 | },
8 | paths: {
9 | jquery: 'vendor/jquery-2.1.1.min',
10 | 'jquery.query': 'vendor/jquery.query-object'
11 | }
12 | });
13 |
14 | require([
15 | 'jquery',
16 | 'resources',
17 | 'jquery.query'
18 | ], function ($, resources) {
19 |
20 | var $game;
21 | var game;
22 | var packDataKey = 'tiledmaps_pack_data';
23 | var packData;
24 |
25 | // when DOM is ready, create the game instance
26 | $(function () {
27 | $game = $('#game');
28 | window.game = game = new Phaser.Game($game.width(),
29 | $game.height(), Phaser.AUTO, 'game', { preload: gamePreload, create: gameSetup }, false, false);
30 | });
31 |
32 | function gamePreload() {
33 | game.load.json(packDataKey, 'maps/tilemap-assets.json');
34 | }
35 |
36 | function gameSetup() {
37 |
38 | game.add.plugin(Phaser.Plugin.Tiled);
39 |
40 | packData = game.cache.getJSON(packDataKey);
41 |
42 | var $sw = $('#switch');
43 | var $fullscreen = $('#fullscreen');
44 | var map = $.query.get('map');
45 |
46 | // create a game state for each of the worlds
47 | resources.worlds.forEach(function (w) {
48 | var key = w.substring(w.lastIndexOf('/') + 1);
49 |
50 | game.state.add(key, {
51 | preload: function () {
52 | this.load.pack(key, null, packData);
53 | },
54 | create: function () {
55 | this.add.tiledmap(key).spawnObjects(function (obj) {
56 | if (obj.type !== Phaser.SPRITE) return;
57 |
58 | obj.inputEnabled = true;
59 | obj.input.enableDrag();
60 | });
61 | },
62 | update: function () {
63 | if (game.input.mousePointer.active) {
64 | moveCamera(game.input.mousePointer);
65 | }
66 |
67 | if (game.input.pointer1.active) {
68 | moveCamera(game.input.pointer1);
69 | }
70 | }
71 | }, false);
72 |
73 | $('', {
74 | value: key,
75 | text: key,
76 | selected: key === map
77 | }).appendTo($sw);
78 | });
79 |
80 | // use url parameter 'map' as starting map, if available
81 | if (map && game.state.checkState(map)) {
82 | game.state.start(map);
83 | }
84 | else {
85 | game.state.start(resources.worlds[0].substring(resources.worlds[0].lastIndexOf('/') + 1));
86 | }
87 |
88 | $sw.on('change', function () {
89 | window.location.search = $.query.set('map', $sw.val());
90 | game.state.start($sw.val());
91 | });
92 |
93 | $fullscreen.on('click', function () {
94 | game.scale.startFullScreen();
95 | });
96 | }
97 |
98 | var lastPos = new Phaser.Point();
99 | var pan = false;
100 |
101 | function moveCamera(pointer) {
102 | if (!pointer.timeDown) return;
103 |
104 | if (pointer.isDown && !pointer.targetObject) {
105 | if (pan) {
106 | game.camera.x += lastPos.x - pointer.position.x;
107 | game.camera.y += lastPos.y - pointer.position.y;
108 | }
109 |
110 | lastPos.copyFrom(pointer.position);
111 | pan = true;
112 | }
113 |
114 | if (pointer.isUp) {
115 | pan = false;
116 | }
117 | }
118 | });
119 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "excludeFiles": [
3 | "src/vendor/*"
4 | ],
5 |
6 | "disallowEmptyBlocks": true,
7 | "disallowIdenticalDestructuringNames": true,
8 | "disallowImplicitTypeConversion": [ "numeric", "string" ],
9 | "disallowKeywords": [ "with" ],
10 | // "disallowKeywordsOnNewLine": [ "else", "while" ],
11 | "disallowMixedSpacesAndTabs": true,
12 | "disallowMultiLineTernary": false,
13 | "disallowMultipleLineBreaks": true,
14 | "disallowMultipleLineStrings": true,
15 | "disallowMultipleVarDecl": true,
16 | "disallowNestedTernaries": true,
17 | "disallowOperatorBeforeLineBreak": [ "." ],
18 | "disallowQuotedKeysInObjects": true,
19 | "disallowShorthandArrowFunctions": true,
20 | "disallowSpaceBeforeComma": true,
21 | "disallowSpaceBeforePostfixUnaryOperators": true,
22 | "disallowSpaceBeforeSemicolon": true,
23 | "disallowSpacesInCallExpression": true,
24 | "disallowSpacesInFunctionDeclaration": {
25 | "beforeOpeningRoundBrace": true
26 | },
27 | "disallowSpacesInsideBrackets": true,
28 | "disallowSpacesInsideParentheses": true,
29 | "disallowSpacesInsideParenthesizedExpression": true,
30 | "disallowTrailingComma": true,
31 | "disallowTrailingWhitespace": true,
32 | "disallowUnusedParams": true,
33 | "disallowYodaConditions": true,
34 |
35 | // "jsDoc": {
36 | // "checkAnnotations": "jsdoc3",
37 | // "checkParamExistence": true,
38 | // "checkParamNames": true,
39 | // "requireParamTypes": true,
40 | // "checkRedundantParams": true,
41 | // "checkReturnTypes": true,
42 | // "checkRedundantReturns": true,
43 | // "checkTypes": true,
44 | // "checkRedundantAccess": true,
45 | // "leadingUnderscoreAccess": "private",
46 | // "requireHyphenBeforeDescription": true,
47 | // "requireNewlineAfterDescription": true
48 | // },
49 |
50 | "maximumLineLength": 140,
51 |
52 | "requireAlignedMultilineParams": "firstParam",
53 | "requireBlocksOnNewline": false,
54 | "requireCamelCaseOrUpperCaseIdentifiers": false,
55 | // "requireNewlineBeforeBlockStatements": true,
56 | "requireCapitalizedConstructors": true,
57 | "requireCommaBeforeLineBreak": true,
58 | "requireCurlyBraces": [
59 | // "if",
60 | // "else",
61 | "for",
62 | "while",
63 | "do",
64 | "try",
65 | "catch"
66 | ],
67 | "requireDollarBeforejQueryAssignment": true,
68 | "requireDotNotation": true,
69 | "requireLineBreakAfterVariableAssignment": true,
70 | "requireLineFeedAtFileEnd": true,
71 | "requireNumericLiterals": true,
72 | "requirePaddingNewLinesAfterUseStrict": true,
73 | "requireParenthesesAroundIIFE": true,
74 | "requireSemicolons": true,
75 | "requireSpaceAfterBinaryOperators": true,
76 | "requireSpaceAfterComma": true,
77 | "requireSpaceAfterKeywords": [
78 | "do",
79 | "for",
80 | "if",
81 | "else",
82 | "switch",
83 | "case",
84 | "try",
85 | "catch",
86 | "void",
87 | "while",
88 | "with",
89 | "return",
90 | "typeof",
91 | "function"
92 | ],
93 | "requireSpaceAfterLineComment": true,
94 | "requireSpaceBeforeBinaryOperators": true,
95 | "requireSpaceBeforeBlockStatements": true,
96 | "requireSpaceBeforeKeywords": [
97 | "else"
98 | ],
99 | "requireSpaceBeforeObjectValues": true,
100 | "requireSpaceBetweenArguments": true,
101 | "requireSpacesInAnonymousFunctionExpression": {
102 | "beforeOpeningRoundBrace": true,
103 | "beforeOpeningCurlyBrace": true,
104 | "allExcept": ["shorthand"]
105 | },
106 | "requireSpacesInConditionalExpression": true,
107 | "requireSpacesInForStatement": true,
108 | "requireSpacesInFunctionDeclaration": {
109 | "beforeOpeningCurlyBrace": true
110 | },
111 | "requireSpacesInsideObjectBrackets": "all",
112 |
113 | "safeContextKeyword": [ "self" ],
114 | "validateIndentation": 4,
115 | "validateLineBreaks": "LF",
116 | "validateQuoteMarks": "'"
117 | }
118 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "amd": true
6 | },
7 | "globals": {
8 | "PIXI": false,
9 | "Phaser": false
10 | },
11 | "ecmaFeatures": {
12 | "modules": true
13 | },
14 | "rules": {
15 | // Possible Errors
16 | "comma-dangle": [2, "never"],
17 | "no-cond-assign": [2, "except-parens"],
18 | "no-console": 0,
19 | "no-constant-condition": 2,
20 | "no-control-regex": 2,
21 | "no-debugger": 2,
22 | "no-dupe-args": 2,
23 | "no-dupe-keys": 2,
24 | "no-duplicate-case": 2,
25 | "no-empty-character-class": 2,
26 | "no-empty": 2,
27 | "no-ex-assign": 2,
28 | "no-extra-boolean-cast": 2,
29 | "no-extra-parens": 0,
30 | "no-extra-semi": 2,
31 | "no-func-assign": 2,
32 | "no-inner-declarations": 2,
33 | "no-invalid-regexp": 2,
34 | "no-irregular-whitespace": 2,
35 | "no-negated-in-lhs": 2,
36 | "no-obj-calls": 2,
37 | "no-regex-spaces": 2,
38 | "no-sparse-arrays": 2,
39 | "no-unexpected-multiline": 2,
40 | "no-unreachable": 2,
41 | "use-isnan": 2,
42 | "valid-jsdoc": 0, // jscs does this already.
43 | "valid-typeof": 2,
44 |
45 | // Best Practices
46 | "accessor-pairs": 2,
47 | "block-scoped-var": 2,
48 | "complexity": 0,
49 | "consistent-return": 2,
50 | "curly": 0, // jscs does this already.
51 | "default-case": 0,
52 | "dot-location": 0, // jscs does this already.
53 | "dot-notation": 2,
54 | "eqeqeq": 2,
55 | "guard-for-in": 0,
56 | "no-alert": 2,
57 | "no-caller": 2,
58 | "no-case-declarations": 2,
59 | "no-div-regex": 2,
60 | "no-else-return": 2,
61 | "no-empty-label": 2,
62 | "no-empty-pattern": 2,
63 | "no-eq-null": 2,
64 | "no-eval": 2,
65 | "no-extend-native": 2,
66 | "no-extra-bind": 2,
67 | "no-fallthrough": 2,
68 | "no-floating-decimal": 2,
69 | "no-implicit-coercion": 0, // jscs does this already.
70 | "no-implied-eval": 2,
71 | "no-invalid-this": 0,
72 | "no-iterator": 2,
73 | "no-labels": 2,
74 | "no-lone-blocks": 2,
75 | "no-loop-func": 0,
76 | "no-magic-numbers": 0,
77 | "no-multi-spaces": 0, // jscs does this already.
78 | "no-multi-str": 2,
79 | "no-native-reassign": 2,
80 | "no-new-func": 2,
81 | "no-new-wrappers": 2,
82 | "no-new": 2,
83 | "no-octal-escape": 2,
84 | "no-octal": 2,
85 | "no-param-reassign": 0,
86 | "no-process-env": 0,
87 | "no-proto": 2,
88 | "no-redeclare": 2,
89 | "no-return-assign": 2,
90 | "no-script-url": 2,
91 | "no-self-compare": 2,
92 | "no-sequences": 2,
93 | "no-throw-literal": 2,
94 | "no-unused-expressions": 2,
95 | "no-useless-call": 2,
96 | "no-useless-concat": 2,
97 | "no-void": 2,
98 | "no-warning-comments": 0,
99 | "no-with": 0, // jscs does this already.
100 | "radix": 2,
101 | "vars-on-top": 0,
102 | "wrap-iife": 0, // jscs does this already.
103 | "yoda": 0, // jscs does this already.
104 |
105 | // Strict Mode
106 | "strict": [2, "global"],
107 |
108 | // Variables
109 | "init-declarations": 0,
110 | "no-catch-shadow": 2,
111 | "no-delete-var": 2,
112 | "no-label-var": 2,
113 | "no-shadow-restricted-names": 2,
114 | "no-shadow": 0,
115 | "no-undef-init": 2,
116 | "no-undef": 2,
117 | "no-undefined": 0,
118 | "no-unused-vars": 2,
119 | "no-use-before-define": [2, "nofunc"],
120 |
121 | // Node.js and CommonJS
122 | "callback-return": 0,
123 | "global-require": 0,
124 | "handle-callback-err": 2,
125 | "no-mixed-requires": 2,
126 | "no-new-require": 2,
127 | "no-path-concat": 2,
128 | "no-process-exit": 0,
129 | "no-restricted-modules": 0,
130 | "no-sync": 0
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | var zlib = require('zlibjs');
2 | var Buffer = require('buffer').Buffer;
3 |
4 | var decodeB64 = (typeof window !== 'undefined' && window.atob) || require('Base64').atob;
5 |
6 | var utils = module.exports = {};
7 |
8 | utils.destroyTexture = function (texture, callDestroy) {
9 | if (callDestroy !== false) {
10 | texture.destroy();
11 | }
12 |
13 | texture.baseTexture = null;
14 | texture.frame = null;
15 | texture.trim = null;
16 | texture.crop = null;
17 | texture._uvs = null;
18 | };
19 |
20 | utils.stringToBuffer = function (str) {
21 | var len = str.length;
22 | var buf = new Buffer(len);
23 |
24 | for (var i = 0; i < len; i++) {
25 | buf[i] = str.charCodeAt(i);
26 | }
27 |
28 | return buf;
29 | };
30 |
31 | utils.cacheKey = function (key, type, name) {
32 | return key + '_' + type + (name ? '_' + name : '');
33 | };
34 |
35 | utils.decompressBase64Data = function (raw, encoding, compression) {
36 | // TODO: This assumes base64 encoding, need to check encoding param
37 | var str = decodeB64(raw);
38 | var buf = utils.stringToBuffer(str);
39 |
40 | // decompress
41 | if (compression === 'gzip') {
42 | return zlib.gunzipSync(buf);
43 | }
44 | else if (compression === 'zlib') {
45 | return zlib.inflateSync(buf);
46 | }
47 |
48 | return buf;
49 | };
50 |
51 | /**
52 | * Parses an array of numbers that represent a hitArea into the actual shape.
53 | *
54 | * For example: `[1, 1, 15]` is a Circle (`[x, y, radius]`); `[1, 1, 15, 15]` is a Rectangle
55 | * (`[x, y, width, height]`); and any length >= 5 is a polygon in the form `[x1, y1, x2, y2, ..., xN, yN]`.
56 | *
57 | * @method parseHitArea
58 | * @param value {Array} The array to parse
59 | * @return {Circle|Rectangle|Polygon} The parsed out shape
60 | */
61 | utils.parseHitArea = function (hv) {
62 | var shape;
63 |
64 | // odd number of values
65 | if (hv.length % 2 !== 0 && hv.length !== 3) {
66 | throw new RangeError(
67 | 'Strange number of values for hitArea! Should be a flat array of values, like: ' +
68 | '[cx,cy,di] for a circle, [x,y,w,h] for a rectangle, or [x,y,x,y,...] for other polygons.'
69 | );
70 | }
71 |
72 | // a circle x,y,r
73 | if (hv.length === 3) {
74 | shape = new Phaser.Circle(hv[0], hv[1], hv[2]);
75 | }
76 | // a rectangle x,y,w,h
77 | else if (hv.length === 4) {
78 | shape = new Phaser.Rectangle(hv[0], hv[1], hv[2], hv[3]);
79 | }
80 | // generic polygon
81 | else {
82 | shape = new Phaser.Polygon(hv);
83 | }
84 |
85 | return shape;
86 | };
87 |
88 | /**
89 | * Parses an object of string properties into potential javascript types. First it attempts to
90 | * convert to a number, if that fails it checks for the string 'true' or 'false' and changes it
91 | * to the actual Boolean value, then it attempts to parse a string as JSON.
92 | *
93 | * @method parseTiledProperties
94 | * @param value {Array} The array to parse
95 | * @return {Circle|Rectangle|Polygon} The parsed out shape
96 | */
97 | utils.parseTiledProperties = function (obj) {
98 | obj = obj || {};
99 |
100 | if (!obj || obj.__tiledparsed) {
101 | return obj;
102 | }
103 |
104 | for (var k in obj) {
105 | var v = obj[k];
106 | var n = parseFloat(v, 10);
107 |
108 | // try to massage numbers
109 | if (n === 0 || n) {
110 | obj[k] = n;
111 | }
112 | // true values
113 | else if (v === 'true') {
114 | obj[k] = true;
115 | }
116 | // false values
117 | else if (v === 'false') {
118 | obj[k] = false;
119 | }
120 | // anything else is either a string or json
121 | else {
122 | try {
123 | v = JSON.parse(v);
124 | obj[k] = v;
125 | } catch (e) {
126 | // ignore error
127 | }
128 | }
129 | }
130 |
131 | if (obj.hitArea) {
132 | obj.hitArea = utils.parseHitArea(obj.hitArea);
133 | }
134 |
135 | if (obj.body === 'static' || obj.sensor) {
136 | obj.mass = Infinity;
137 | obj.inertia = Infinity;
138 | }
139 |
140 | obj.__tiledparsed = true;
141 |
142 | return obj;
143 | };
144 |
--------------------------------------------------------------------------------
/testmaps/maps/iso/Isometric_cubes_with_objects.tmx:
--------------------------------------------------------------------------------
1 |
2 |
97 |
--------------------------------------------------------------------------------
/testmaps/maps/ortho/Ortho_1_32__32_objects.tmx:
--------------------------------------------------------------------------------
1 |
2 |
109 |
--------------------------------------------------------------------------------
/typescript/phaser-tiled.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module Phaser {
4 | module Plugin {
5 | class Tiled implements Phaser.Plugin {
6 |
7 | constructor(game: Phaser.Game, parent: PIXI.DisplayObject);
8 |
9 | active: boolean;
10 | game: Phaser.Game;
11 | hasPostRender: boolean;
12 | hasPostUpdate: boolean;
13 | hasPreUpdate: boolean;
14 | hasRender: boolean;
15 | hasUpdate: boolean;
16 | parent: PIXI.DisplayObject;
17 | visible: boolean;
18 |
19 | destroy(): void;
20 | postRender(): void;
21 | preUpdate(): void;
22 | render(): void;
23 | update(): void;
24 | }
25 |
26 | module Tiled {
27 | interface ITiledObject {
28 | id: number;
29 | name: string;
30 | type: string;
31 | x: number;
32 | y: number;
33 | width: number;
34 | height: number;
35 | rotation: number;
36 | gid: number;
37 | visible: boolean;
38 | properties: any;
39 | }
40 |
41 | interface ITileAnimation {
42 | rate: number;
43 | data: Phaser.FrameData;
44 | }
45 |
46 | class Tile extends Phaser.Sprite {
47 | constructor(game: Phaser.Game, x: number, y: number, tileId: number, tileset: Tileset, layer: Tilelayer);
48 |
49 | layer: Tilelayer;
50 | tileset: Tileset;
51 | tilePosition: Phaser.Point;
52 | centerX: number;
53 | centerY: number;
54 | properties: any;
55 | scanned: boolean;
56 | faceTop: boolean;
57 | faceBottom: boolean;
58 | faceLeft: boolean;
59 | faceRight: boolean;
60 | collideLeft: boolean;
61 | collideRight: boolean;
62 | collideUp: boolean;
63 | collideDown: boolean;
64 | collisionCallback: () => any;
65 | collisionCallbackContext: any;
66 |
67 | worldX: number;
68 | worldY: number;
69 | collides: boolean;
70 | canCollide: boolean;
71 | left: number;
72 | right: number;
73 | top: number;
74 | bottom: number;
75 |
76 | containsPoint(x: number, y: number): boolean;
77 | intersects(x: number, y: number, right: number, bottom: number): boolean;
78 | setCollisionCallback(callback: () => any, context?: any): void;
79 | setCollision(left: boolean, right: boolean, up: boolean, down: boolean): void;
80 | resetCollision(): void;
81 | isInteresting(collides: boolean, faces: boolean): boolean;
82 | copy(tile: Tile): void;
83 | }
84 |
85 | class Tileset extends PIXI.Texture {
86 | constructor(game: Phaser.Game, key: string, settings: any);
87 |
88 | game: Phaser.Game;
89 | multiImage: boolean;
90 | firstgid: number;
91 | lastgid: number;
92 | name: string;
93 | tileWidth: number;
94 | tileHeight: number;
95 | spacing: number;
96 | margin: number;
97 | tileoffset: Phaser.Point;
98 | numTiles: Phaser.Point;
99 | properties: any;
100 | tileproperties: any;
101 | size: Phaser.Point;
102 | textures: PIXI.Texture[];
103 | tileanimations: { [key: string]: ITileAnimation };
104 |
105 | getTileProperties(tileId: number): any;
106 | getTileAnimations(tileId: number): ITileAnimation;
107 | getTileTexture(tileId: number): PIXI.Texture;
108 | contains(tileId: number): boolean;
109 | }
110 |
111 | class Tilemap extends Phaser.Group {
112 | key: string;
113 | size: Phaser.Point;
114 | tileWidth: number;
115 | tileHeight: number;
116 | scaledTileWidth: number;
117 | scaledTileHeight: number;
118 | orientation: string;
119 | format: number;
120 | version: number;
121 | properties: any;
122 | widthInPixels: number;
123 | heightInPixels: number;
124 | layers: Tilelayer[];
125 | tilesets: Tileset[];
126 | objects: Objectlayer[];
127 | images: Phaser.Sprite[];
128 | collideIndexes: number[];
129 | currentLayer: number;
130 | preventingRecalculate: boolean;
131 | needToRecalculate: boolean;
132 | dirty: boolean;
133 |
134 | setTileSize(tileWidth: number, tileHeight: number): void;
135 | getTilelayerIndex(name: string): number;
136 | getTilesetIndex(name: string): number;
137 | getImagelayerIndex(name: string): number;
138 | getObjectlayerIndex(name: string): number;
139 | getTilelayer(name: string): Tilelayer;
140 | getTileset(name: string): Tileset;
141 | getImagelayer(name: string): Phaser.Sprite;
142 | getObjectlayer(name: string): Objectlayer;
143 | }
144 |
145 | class Tilelayer extends Phaser.Group {
146 | constructor(game: Phaser.Game, map: Tilemap, layer: any, index: number);
147 |
148 | map: Tilemap;
149 | fixedToCamera: boolean;
150 | cameraOffset: Phaser.Point;
151 | tiles: any;
152 | scrollFactor: Phaser.Point;
153 | name: string;
154 | size: Phaser.Point;
155 | tileIds: number[];
156 | properties: any;
157 | layerType: string;
158 | rayStepRate: number;
159 | bodies: any[];
160 | dirty: boolean;
161 |
162 | scrollX: number;
163 | scrollY: number;
164 | widthInPixels: number;
165 | heightInPixels: number;
166 | collisionWidth: number;
167 | collisionHeight: number;
168 |
169 | setupRenderArea(): void;
170 | resizeWorld(): void;
171 | private setupTiles(): void;
172 | clearTiles(): Tilelayer;
173 | clearTile(tile: Tile): void;
174 | moveTileSprite(fromTileX: number, fromTileY: number, toTileX: number, toTileY: number): void;
175 | updatePan(): void;
176 | getRayCastTiles(line: Phaser.Line, stepRate?: number, collides?: boolean, interestingFace?: boolean): Tile[];
177 | getTiles(x: number, y: number, width: number, height: number, collides?: boolean, interestingFace?: boolean): Tile[];
178 | }
179 |
180 | class Objectlayer extends Phaser.Group {
181 | constructor(game: Phaser.Game, map: Tilemap, layer: any, index: number);
182 |
183 | index: number;
184 | map: Tilemap;
185 | name: string;
186 | color: string;
187 | properties: any;
188 | objects: ITiledObject[];
189 | layerType: string;
190 | bodies: any[];
191 |
192 | spawn(physicsBodyType?: number, spawnCallback?: () => void): Objectlayer;
193 | despawn(destroy?: boolean): Objectlayer;
194 | getObject(name: string): ITiledObject;
195 | }
196 |
197 | class utils {
198 | static stringToBuffer(str: string): any;
199 | static cacheKey(key: string, type: string, name?: string): string;
200 | static decompressBase64Data(raw: string, encoding: string, compression: string): any;
201 | static parseHitArea(value: number[]): (Phaser.Circle|Phaser.Rectangle|Phaser.Polygon);
202 | static parseTiledProperties(obj: any): any;
203 | }
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/testmaps/js/vendor/jquery.query-object.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jQuery.query - Query String Modification and Creation for jQuery
3 | * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
4 | * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
5 | * Date: 2009/8/13
6 | *
7 | * @author Blair Mitchelmore
8 | * @version 2.2.3
9 | *
10 | **/
11 | new function(settings) {
12 | // Various Settings
13 | var $separator = settings.separator || '&';
14 | var $spaces = settings.spaces === false ? false : true;
15 | var $suffix = settings.suffix === false ? '' : '[]';
16 | var $prefix = settings.prefix === false ? false : true;
17 | var $hash = $prefix ? settings.hash === true ? "#" : "?" : "";
18 | var $numbers = settings.numbers === false ? false : true;
19 |
20 | jQuery.query = new function() {
21 | var is = function(o, t) {
22 | return o != undefined && o !== null && (!!t ? o.constructor == t : true);
23 | };
24 | var parse = function(path) {
25 | var m, rx = /\[([^[]*)\]/g, match = /^([^[]+)(\[.*\])?$/.exec(path), base = match[1], tokens = [];
26 | while (m = rx.exec(match[2])) tokens.push(m[1]);
27 | return [base, tokens];
28 | };
29 | var set = function(target, tokens, value) {
30 | var o, token = tokens.shift();
31 | if (typeof target != 'object') target = null;
32 | if (token === "") {
33 | if (!target) target = [];
34 | if (is(target, Array)) {
35 | target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value));
36 | } else if (is(target, Object)) {
37 | var i = 0;
38 | while (target[i++] != null);
39 | target[--i] = tokens.length == 0 ? value : set(target[i], tokens.slice(0), value);
40 | } else {
41 | target = [];
42 | target.push(tokens.length == 0 ? value : set(null, tokens.slice(0), value));
43 | }
44 | } else if (token && token.match(/^\s*[0-9]+\s*$/)) {
45 | var index = parseInt(token, 10);
46 | if (!target) target = [];
47 | target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value);
48 | } else if (token) {
49 | var index = token.replace(/^\s*|\s*$/g, "");
50 | if (!target) target = {};
51 | if (is(target, Array)) {
52 | var temp = {};
53 | for (var i = 0; i < target.length; ++i) {
54 | temp[i] = target[i];
55 | }
56 | target = temp;
57 | }
58 | target[index] = tokens.length == 0 ? value : set(target[index], tokens.slice(0), value);
59 | } else {
60 | return value;
61 | }
62 | return target;
63 | };
64 |
65 | var queryObject = function(a) {
66 | var self = this;
67 | self.keys = {};
68 |
69 | if (a.queryObject) {
70 | jQuery.each(a.get(), function(key, val) {
71 | self.SET(key, val);
72 | });
73 | } else {
74 | self.parseNew.apply(self, arguments);
75 | }
76 | return self;
77 | };
78 |
79 | queryObject.prototype = {
80 | queryObject: true,
81 | parseNew: function(){
82 | var self = this;
83 | self.keys = {};
84 | jQuery.each(arguments, function() {
85 | var q = "" + this;
86 | q = q.replace(/^[?#]/,''); // remove any leading ? || #
87 | q = q.replace(/[;&]$/,''); // remove any trailing & || ;
88 | if ($spaces) q = q.replace(/[+]/g,' '); // replace +'s with spaces
89 |
90 | jQuery.each(q.split(/[&;]/), function(){
91 | var key = decodeURIComponent(this.split('=')[0] || "");
92 | var val = decodeURIComponent(this.split('=')[1] || "");
93 |
94 | if (!key) return;
95 |
96 | if ($numbers) {
97 | if (/^[+-]?[0-9]+\.[0-9]*$/.test(val)) // simple float regex
98 | val = parseFloat(val);
99 | else if (/^[+-]?[1-9][0-9]*$/.test(val)) // simple int regex
100 | val = parseInt(val, 10);
101 | }
102 |
103 | val = (!val && val !== 0) ? true : val;
104 |
105 | self.SET(key, val);
106 | });
107 | });
108 | return self;
109 | },
110 | has: function(key, type) {
111 | var value = this.get(key);
112 | return is(value, type);
113 | },
114 | GET: function(key) {
115 | if (!is(key)) return this.keys;
116 | var parsed = parse(key), base = parsed[0], tokens = parsed[1];
117 | var target = this.keys[base];
118 | while (target != null && tokens.length != 0) {
119 | target = target[tokens.shift()];
120 | }
121 | return typeof target == 'number' ? target : target || "";
122 | },
123 | get: function(key) {
124 | var target = this.GET(key);
125 | if (is(target, Object))
126 | return jQuery.extend(true, {}, target);
127 | else if (is(target, Array))
128 | return target.slice(0);
129 | return target;
130 | },
131 | SET: function(key, val) {
132 | var value = !is(val) ? null : val;
133 | var parsed = parse(key), base = parsed[0], tokens = parsed[1];
134 | var target = this.keys[base];
135 | this.keys[base] = set(target, tokens.slice(0), value);
136 | return this;
137 | },
138 | set: function(key, val) {
139 | return this.copy().SET(key, val);
140 | },
141 | REMOVE: function(key, val) {
142 | if (val) {
143 | var target = this.GET(key);
144 | if (is(target, Array)) {
145 | for (tval in target) {
146 | target[tval] = target[tval].toString();
147 | }
148 | var index = $.inArray(val, target);
149 | if (index >= 0) {
150 | key = target.splice(index, 1);
151 | key = key[index];
152 | } else {
153 | return;
154 | }
155 | } else if (val != target) {
156 | return;
157 | }
158 | }
159 | return this.SET(key, null).COMPACT();
160 | },
161 | remove: function(key, val) {
162 | return this.copy().REMOVE(key, val);
163 | },
164 | EMPTY: function() {
165 | var self = this;
166 | jQuery.each(self.keys, function(key, value) {
167 | delete self.keys[key];
168 | });
169 | return self;
170 | },
171 | load: function(url) {
172 | var hash = url.replace(/^.*?[#](.+?)(?:\?.+)?$/, "$1");
173 | var search = url.replace(/^.*?[?](.+?)(?:#.+)?$/, "$1");
174 | return new queryObject(url.length == search.length ? '' : search, url.length == hash.length ? '' : hash);
175 | },
176 | empty: function() {
177 | return this.copy().EMPTY();
178 | },
179 | copy: function() {
180 | return new queryObject(this);
181 | },
182 | COMPACT: function() {
183 | function build(orig) {
184 | var obj = typeof orig == "object" ? is(orig, Array) ? [] : {} : orig;
185 | if (typeof orig == 'object') {
186 | function add(o, key, value) {
187 | if (is(o, Array))
188 | o.push(value);
189 | else
190 | o[key] = value;
191 | }
192 | jQuery.each(orig, function(key, value) {
193 | if (!is(value)) return true;
194 | add(obj, key, build(value));
195 | });
196 | }
197 | return obj;
198 | }
199 | this.keys = build(this.keys);
200 | return this;
201 | },
202 | compact: function() {
203 | return this.copy().COMPACT();
204 | },
205 | toString: function() {
206 | var i = 0, queryString = [], chunks = [], self = this;
207 | var encode = function(str) {
208 | str = str + "";
209 | str = encodeURIComponent(str);
210 | if ($spaces) str = str.replace(/%20/g, "+");
211 | return str;
212 | };
213 | var addFields = function(arr, key, value) {
214 | if (!is(value) || value === false) return;
215 | var o = [encode(key)];
216 | if (value !== true) {
217 | o.push("=");
218 | o.push(encode(value));
219 | }
220 | arr.push(o.join(""));
221 | };
222 | var build = function(obj, base) {
223 | var newKey = function(key) {
224 | return !base || base == "" ? [key].join("") : [base, "[", key, "]"].join("");
225 | };
226 | jQuery.each(obj, function(key, value) {
227 | if (typeof value == 'object')
228 | build(value, newKey(key));
229 | else
230 | addFields(chunks, newKey(key), value);
231 | });
232 | };
233 |
234 | build(this.keys);
235 |
236 | if (chunks.length > 0) queryString.push($hash);
237 | queryString.push(chunks.join($separator));
238 |
239 | return queryString.join("");
240 | }
241 | };
242 |
243 | return new queryObject(location.search, location.hash);
244 | };
245 | }(jQuery.query || {}); // Pass in jQuery.query as settings object
246 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Phaser Tiled
2 |
3 | This is a replacement for the tilemap implementation in the [Phaser][0] game framework.
4 | The purpose of this plugin is to optimize the tilemaps for large complex maps built with
5 | the [Tiled Map Editor][1].
6 |
7 | This plugin optimizes the rendering of large scrolling tilemaps. It also adds support for
8 | many more Tiled features and lots of advanced functionality. You can read [Why use this plugin?][2]
9 | below for more details.
10 |
11 | **Note**: The recommended version of Tiled for use with this plugin is `v0.15`. Any other version
12 | is not officially supported.
13 |
14 | [0]: https://github.com/photonstorm/phaser
15 | [1]: http://www.mapeditor.org/
16 | [2]: #why-use-this-plugin
17 |
18 | ## Usage
19 |
20 | Simply download the `phaser-tiled.js` script from the [latest release](https://github.com/englercj/phaser-tiled/releases) and include it on your page after including Phaser:
21 |
22 | ```html
23 |
24 |
25 | ```
26 |
27 | ### Javascript
28 | After adding the script to the page you can activate it by enabling the plugin:
29 |
30 | ```js
31 | game.add.plugin(Phaser.Plugin.Tiled);
32 | ```
33 |
34 | Then you can add a Tiled map to your game like this:
35 |
36 | ```js
37 | // By using the built-in cache key creator, the plugin can automagically
38 | // find all the necessary items in the cache.
39 | // The params are (map-key, type, tiled-name). The `key` is an arbitrary key
40 | // you make up to identify the map it belongs to. The `type` is the type of
41 | // resource it is loading. The `tiled-name` is for layers, the name should match
42 | // the name of the layer in the map.
43 | var cacheKey = Phaser.Plugin.Tiled.utils.cacheKey;
44 |
45 | // load the tiled map, notice it is "tiledmap" and not "tilemap"
46 | game.load.tiledmap(cacheKey('my-tiledmap', 'tiledmap'), 'assets/levels/tilemap.json', null, Phaser.Tilemap.TILED_JSON);
47 |
48 | // load the images for your tilesets, make sure the last param to "cacheKey" is
49 | // the name of the tileset in your map so the plugin can find it later
50 | game.load.image(cacheKey('my-tiledmap', 'tileset', 'tileset1-name'), 'assets/levels/tileset1.png');
51 | game.load.image(cacheKey('my-tiledmap', 'tileset', 'tileset2-name'), 'assets/levels/tileset2.png');
52 |
53 | // if you have image layers, be sure to load those too! Again,
54 | // make sure the last param is the name of your layer in the map.
55 | game.load.image(cacheKey('my-tiledmap', 'layer', 'layer-name'), 'assets/levels/layer.png');
56 |
57 | ////////////
58 | // Later after loading is complete:
59 |
60 | // add the tiledmap to the game
61 | // this method takes the key for the tiledmap which has been used in the cacheKey calls
62 | // earlier, and an optional group to add the tilemap to (defaults to game.world).
63 | var map = game.add.tiledmap('my-tiledmap');
64 | ```
65 |
66 | That can get pretty heavy, and hardcoding what to load and how to name it can stink! Luckily, there is an easier
67 | way to handle it. Instead of hard-coding what the tilemap should load and be named, this plugin has a gulp task
68 | that can generate a Phaser Asset Pack that describes what and how to load the tiledmap. If you have this pack
69 | it becomes trivial to load and create a tiledmap:
70 |
71 | ```js
72 | // the key will be the filename of the map without the extension.
73 | game.load.pack('my-tiledmap', 'assets/levels/tilemap-assets.json');
74 |
75 | ////////////
76 | // Later after loading is complete:
77 | var map = game.add.tiledmap('my-tiledmap');
78 | ```
79 |
80 | Wow, that was a lot easier! You can find out more about the generator on [it's GitHub page][10].
81 |
82 | [10]: https://github.com/englercj/gulp-phaser-tiled-pack
83 |
84 | ### Typescript
85 |
86 | Download the phaser-tiled.d.ts and add it to your project. (The file is located in the typescript folder)
87 | You also need the normal [phaser-tiled.js](https://github.com/englercj/phaser-tiled/releases) file.
88 |
89 | First of all you need to make sure you add a reference to the phaser and phaser-tiled typescript files.
90 |
91 | ```ts
92 | ///
93 | ///
94 | ```
95 |
96 | Now you need to load the plugin. This needs to be done in the preload function.
97 |
98 | ```ts
99 | //This is in the preload function
100 | this.game.add.plugin(new Tiled(this.game, this.game.stage));
101 |
102 | //Now you need to load the tiledmap. Notice that it says tiledmap.
103 | var cacheKey = Phaser.Plugin.Tiled.utils.cacheKey;
104 | //Since you can't add methods to existing classes in typescript you need to cast to any.
105 | (this.game.load).tiledmap(cacheKey('myTiledMap', 'tiledmap'), 'maps/myTiledMap.json', null, Phaser.Tilemap.TILED_JSON);
106 |
107 | //Now you need to load the tilesets.
108 | this.game.load.image(cacheKey('myTiledMap', 'tileset', 'Grass'), 'images/tilesets/Grass.png');
109 | this.game.load.image(cacheKey('myTiledMap', 'tileset', 'Dirt'), 'images/tilesets/Dirt.png');
110 |
111 | // You can load image layers like this:
112 | this.game.load.image(cacheKey('myTiledMap', 'layer', 'yourLayerName'), 'images/imageLayers/layer.png');
113 | //The last parameter should be the name of your layer in your tiled map
114 |
115 | ```
116 |
117 | Now the loading part is done and we will continue after the loading is complete.
118 |
119 | ```ts
120 | //This is normally in the create method
121 | //Make sure you cast it to any again.
122 | var map = (this.game.add).tiledmap('myTiledMap');
123 | ```
124 |
125 |
126 | ### Physics
127 |
128 | This plugin comes with a couple ways to implement physics for your games. Right now the only officially supported engine
129 | is p2.js, but hopefully arcade and others can join the party soon (need it now? submit a PR!).
130 |
131 | #### Using the object tools
132 |
133 | To create the physics bodies based on a tilemap, the simplest way is to create an object layer in Tiled Editor and use
134 | the object tools to draw physics. You can use the rectangle, ellipse, polygon, or polyline tools. The only caveats are
135 | that circles (not ellipses) are supported so height and width of the ellipse must be the same, and if you use the polyline
136 | tool be sure to close the path to make a convex polygon!
137 |
138 | Here is how you can convert your objects into collision bodies:
139 |
140 | ```js
141 | var map = game.add.tiledmap('tilemap-key');
142 |
143 | game.physics.p2.convertTiledCollisionObjects(map, 'objectlayer-name');
144 | ```
145 |
146 | That is it! All the objects in the layer named `objectlayer-name` will be converted to p2 physics bodies and added
147 | to the simulation.
148 |
149 | #### Using collidable tiles
150 |
151 | The second method is to set the `collides` custom property on tiles in a tileset to `true`. This tells the engine that
152 | that specific tile is collidable wherever it is in the map, and a body will be created for it.
153 |
154 | Here is how you can convert your collidable tiles into collision bodies:
155 |
156 | ```js
157 | var map = game.add.tiledmap('tilemap-key');
158 |
159 | game.physics.p2.convertTiledmap(map, 'tilelayer-name');
160 | ```
161 |
162 | That is it! All the collidable tiles in the layer named `tilelayer-name` will be converted to p2 physics bodies
163 | and added to the simulation. This will also try to combine bodies that are adjacent on the same X axis into a single
164 | body to reduce the total number of bodies that are created. This algorithm can (and should) be much improved.
165 |
166 | ## Why use this plugin?
167 |
168 | Here are some features this plugin has that Phaser doesn't, or that this plugin
169 | tries to do better:
170 |
171 | 1. Faster render times
172 | 2. Support for Tiled XML format
173 | 3. Support for tile flipping
174 | 4. Support for animated tiles (added in Tiled v0.10.0)
175 | 5. Automatic layer creation from tiled data
176 | 6. Automatic tileset creation from tiled data
177 |
178 | ## Show me the performance!
179 |
180 | Using a large test map with 256x256 tiles, each 16x16 pixels, and 3 layers of them. [phaser-debug][20]
181 | gives me this performance graph for the core phaser tilemap implementation:
182 |
183 | ![Slow][21]
184 |
185 | The spikes you see there are when I pan around the map. Using the same map with this plugin I get this:
186 |
187 | ![Fast][22]
188 |
189 | [20]: https://github.com/englercj/phaser-debug
190 | [21]: http://static.pantherdev.com/misc/slow_lttp_debug.png
191 | [22]: http://static.pantherdev.com/misc/fast_lttp_debug.png
192 |
193 |
194 | ## Supported custom properties
195 |
196 | #### Tilemap
197 | None, yet.
198 |
199 | #### Tileset
200 | None, yet.
201 |
202 | #### Tileset Tile (specific tile in the tileset)
203 | - `collideLeft` - true will make this tile collide on the left
204 | - `collideRight` - true will make this tile collide on the right
205 | - `collideUp` - true will make this tile collide on the top
206 | - `collideDown` - true will make this tile collide on the bottom
207 | - `collides` - true will set all collision sides to true, if that collision side doesn't have a specific override
208 | - `blendMode` - string of the blendMode constant to use for this tile (e.g. 'NORMAL')
209 |
210 | #### Tile Layer
211 | - `batch` - true will place tile sprites into a SpriteBatch container.
212 |
213 | #### Object Layer
214 | - `batch` - true will place object sprites into a SpriteBatch container.
215 | - `blendMode` - string of the blendMode constant to use for all objects in this layer (e.g. 'NORMAL').
216 |
217 | #### Object Layer Object (specific object in the layer)
218 | - `blendMode` - string of the blendMode constant to use for this object (e.g. 'NORMAL')
219 | - `texture` - string of the texture to load from the cache, usually the URL you would load the texture with.
220 | - `collides` - true/false whether this object is collidable, falls back to the tileset tile collides property.
221 | - `sensor` - Makes the physics shape a sensor shape when `collides` is true.
222 | - `anchor` - A custom anchor override for a tile in array format, e.g. "[0,1]"
223 |
224 | #### Image Layer
225 | None, yet.
226 |
227 | ## Tiled features not yet implemented:
228 |
229 | 1. Object layers
230 | 2. Image layers
231 | 3. Multi-image tilesets
232 |
233 | ## Phaser Tilemap API features still needed:
234 |
235 | Layer Properties:
236 | - tileColor
237 | - wrap
238 | - scrollFactor
239 |
240 | Map Methods:
241 | - setTileIndexCallback
242 | - setTileLocationCallback
243 | - setCollision
244 | - setCollisionBetween
245 | - setCollisionByExclusion
246 | - setCollisionByIndex
247 | - copy
248 | - paste
249 | - swap
250 | - swapHandler
251 | - forEach
252 | - replace
253 | - random
254 | - shuffle
255 | - fill
256 |
257 | Object Layer:
258 | - object spritepool to pull custom object from
259 | - Test, only minimally implemented right now
260 |
261 | Image Layer:
262 | - Completely unimplemented
263 |
264 | General:
265 | - Rerender on resize/rescale seems off
266 | - Tile render debug stuff (edges, physics, etc)
267 | - Memory optimizations
268 |
--------------------------------------------------------------------------------
/src/physics.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | p2: {
3 | /**
4 | * Goes through all tiles in the given Tilemap and TilemapLayer and converts those set to collide into physics
5 | * bodies. Only call this *after* you have specified all of the tiles you wish to collide with calls like
6 | * Tilemap.setCollisionBetween, etc. Every time you call this method it will destroy any previously created
7 | * bodies and remove them from the world. Therefore understand it's a very expensive operation and not to be
8 | * done in a core game update loop.
9 | *
10 | * @method Phaser.Physics.P2#convertTilemap
11 | * @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
12 | * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default
13 | * to map.currentLayer.
14 | * @param {boolean} [addToWorld=true] - If true it will automatically add each body to the world, otherwise
15 | * it's up to you to do so.
16 | * @param {boolean} [optimize=true] - If true adjacent colliding tiles will be combined into a single body
17 | * to save processing. However it means you cannot perform specific Tile to Body collision responses.
18 | * @return {array} An array of the Phaser.Physics.P2.Body objects that were created.
19 | */
20 | // convertTiledmap: function (map, layer, addToWorld, optimize) {
21 | //
22 | // if (typeof addToWorld === 'undefined') { addToWorld = true; }
23 | // if (typeof optimize === 'undefined') { optimize = true; }
24 | // if (typeof layer === 'undefined') { layer = map.currentLayer; }
25 | //
26 | // layer = map.getTilelayer(layer);
27 | //
28 | // if (!layer) {
29 | // return;
30 | // }
31 | //
32 | // // If the bodies array is already populated we need to nuke it
33 | // this.clearTilemapLayerBodies(map, layer.index);
34 | //
35 | // var width = 0,
36 | // sx = 0,
37 | // sy = 0,
38 | // tile, body, right;
39 | //
40 | // for (var y = 0, h = layer.size.y; y < h; y++)
41 | // {
42 | // width = 0;
43 | //
44 | // for (var x = 0, w = layer.size.x; x < w; x++)
45 | // {
46 | // if (!layer.tiles[y]) {
47 | // continue;
48 | // }
49 | //
50 | // tile = layer.tiles[y][x];
51 | //
52 | // if (tile && tile.collides)
53 | // {
54 | // if (optimize)
55 | // {
56 | // right = map.getTileRight(layer.index, x, y);
57 | //
58 | // if (width === 0)
59 | // {
60 | // sx = tile.x;
61 | // sy = tile.y;
62 | // width = tile.width;
63 | // }
64 | //
65 | // if (right && right.collides)
66 | // {
67 | // width += tile.width;
68 | // }
69 | // else
70 | // {
71 | // body = this.createBody(sx, sy, 0, false);
72 | //
73 | // body.addRectangle(width, tile.height, width / 2, tile.height / 2, 0);
74 | //
75 | // if (addToWorld)
76 | // {
77 | // this.addBody(body);
78 | // }
79 | //
80 | // layer.bodies.push(body);
81 | //
82 | // width = 0;
83 | // }
84 | // }
85 | // else
86 | // {
87 | // body = this.createBody(tile.x, tile.y, 0, false);
88 | //
89 | // body.clearShapes();
90 | // body.addRectangle(tile.width, tile.height, tile.width / 2, tile.height / 2, tile.rotation);
91 | //
92 | // if (addToWorld)
93 | // {
94 | // this.addBody(body);
95 | // }
96 | //
97 | // layer.bodies.push(body);
98 | // }
99 | // }
100 | // }
101 | // }
102 | //
103 | // return layer.bodies;
104 | //
105 | // },
106 | /**
107 | * Converts all of the polylines objects inside a Tiled ObjectGroup into physics bodies that are added to the world.
108 | * Note that the polylines must be created in such a way that they can withstand polygon decomposition.
109 | *
110 | * @method Phaser.Physics.P2#convertCollisionObjects
111 | * @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
112 | * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on, defaults to map.currentLayer.
113 | * @param {boolean} [addToWorld=true] - If true it will automatically add each body to the world.
114 | * @return {array} An array of the Phaser.Physics.Body objects that have been created.
115 | */
116 | convertTiledCollisionObjects: function (map, layer, addToWorld) {
117 |
118 | if (typeof addToWorld === 'undefined') { addToWorld = true; }
119 | if (typeof layer === 'undefined') { layer = map.currentLayer; }
120 |
121 | layer = map.getObjectlayer(layer);
122 |
123 | if (!layer) {
124 | return;
125 | }
126 |
127 | for (var i = 0, len = layer.objects.length; i < len; i++)
128 | {
129 | var object = layer.objects[i];
130 |
131 | var body = this.createBody(object.x, object.y, 0, false);
132 |
133 | // polygon defined area
134 | if (object.polygon || object.polyline) {
135 | if (!body.addPolygon(null, (object.polygon || object.polyline).map(mapPointToArray))) {
136 | console.warn('Unable to add poly collision body for object:', object);
137 | continue;
138 | }
139 | }
140 | // currently only circles are supported by P2, so we just use the width
141 | else if (object.ellipse) {
142 | var radius = (object.width + object.height) / 4;
143 | body.addCircle(radius, object.width / 2, object.height / 2, object.rotation);
144 | }
145 | // no polygon, use rectangle defined by object itself
146 | else {
147 | body.addRectangle(object.width, object.height, object.width / 2, object.height / 2, object.rotation);
148 | }
149 |
150 | if (!body.data.shapes[0]) {
151 | console.warn('No shape created for object:', object);
152 | continue;
153 | }
154 |
155 | body.data.shapes[0].sensor = !!(object.properties && object.properties.sensor);
156 |
157 | if (object.properties && typeof object.properties.collisionResponse === 'boolean') {
158 | body.data.shapes[0].collisionResponse = object.properties.collisionResponse;
159 | }
160 |
161 | var bodyType = object.properties && object.properties.bodyType || 'static';
162 |
163 | body[bodyType] = true;
164 |
165 | body.tiledObject = object;
166 |
167 | if (addToWorld) {
168 | this.addBody(body);
169 | }
170 |
171 | layer.bodies.push(body);
172 | }
173 | }
174 | },
175 |
176 | ninja: {
177 | /**
178 | * Goes through all tiles in the given Tilemap and TilemapLayer and converts those set to collide into physics
179 | * bodies. Only call this *after* you have specified all of the tiles you wish to collide with calls like
180 | * Tilemap.setCollisionBetween, etc. Every time you call this method it will destroy any previously created
181 | * bodies and remove them from the world. Therefore understand it's a very expensive operation and not to be
182 | * done in a core game update loop.
183 | *
184 | * 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,
185 | * etc. You can find the ID list either at the very bottom of `Tile.js`, or in a handy visual reference in the
186 | * `resources/Ninja Physics Debug Tiles` folder in the repository. The slopeMap parameter is an array that controls
187 | * how the indexes of the tiles in your tilemap data will map to the Ninja Tile IDs. For example if you had 6
188 | * tiles in your tileset: Imagine the first 4 should be converted into fully solid Tiles and the other 2 are 45-degree
189 | * slopes. Your slopeMap array would look like this: `[ 1, 1, 1, 1, 2, 3 ]`. Where each element of the array is
190 | * a tile in your tilemap and the resulting Ninja Tile it should create.
191 | *
192 | * @method Phaser.Physics.Ninja#convertTilemap
193 | * @param {Phaser.Tilemap} map - The Tilemap to get the map data from.
194 | * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. Defaults to map.currentLayer.
195 | * @param {object} [slopeMap] - The tilemap index to Tile ID map.
196 | * @return {array} An array of the Phaser.Physics.Ninja.Tile objects that were created.
197 | */
198 | // convertTiledmap: function (map, layer, slopeMap) {
199 | //
200 | // layer = map.getTilelayer(layer);
201 | //
202 | // if (!layer) {
203 | // return;
204 | // }
205 | //
206 | // // If the bodies array is already populated we need to nuke it
207 | // this.clearTilemapLayerBodies(map, layer);
208 | //
209 | // for (var y = 0, h = layer.size.y; y < h; y++)
210 | // {
211 | // if (!layer.tiles[y]) {
212 | // continue;
213 | // }
214 | //
215 | // for (var x = 0, w = layer.size.x; x < w; x++)
216 | // {
217 | // var tile = layer.tiles[y][x],
218 | // index = (y * layer.size.x) + x;
219 | //
220 | // if (tile && slopeMap.hasOwnProperty(index))
221 | // {
222 | // var body = new Phaser.Physics.Ninja.Body(
223 | // this,
224 | // null,
225 | // 3,
226 | // slopeMap[index],
227 | // 0,
228 | // tile.worldX + tile.centerX,
229 | // tile.worldY + tile.centerY,
230 | // tile.width,
231 | // tile.height
232 | // );
233 | //
234 | // layer.bodies.push(body);
235 | // }
236 | // }
237 | // }
238 | //
239 | // return layer.bodies;
240 | //
241 | // }
242 | }
243 | };
244 |
245 | function mapPointToArray(obj) {
246 | return [obj.x, obj.y];
247 | }
248 |
--------------------------------------------------------------------------------
/src/browser.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils');
2 | var physics = require('./physics');
3 |
4 | /**
5 | * @class Phaser.Plugin.Tiled
6 | * @classdesc Phaser - Tiled Plugin
7 | *
8 | * @constructor
9 | * @extends Phaser.Plugin
10 | *
11 | * @param {Phaser.Game} game - A reference to the currently running game.
12 | * @param {Any} parent - The object that owns this plugin, usually Phaser.PluginManager.
13 | */
14 | function Tiled(game, parent) {
15 | Phaser.Plugin.call(this, game, parent);
16 | }
17 |
18 | // Extends the Phaser.Plugin template, setting up values we need
19 | Tiled.prototype = Object.create(Phaser.Plugin.prototype);
20 | Tiled.prototype.constructor = Tiled;
21 |
22 | module.exports = Tiled;
23 |
24 | // Tiled.Tile = require('./tiled/Tile');
25 | Tiled.Tileset = require('./tiled/Tileset');
26 | Tiled.Tilemap = require('./tiled/Tilemap');
27 | Tiled.Tilelayer = require('./tiled/Tilelayer');
28 | Tiled.Objectlayer = require('./tiled/Objectlayer');
29 | Tiled.utils = utils;
30 |
31 | var originals = {
32 | gameObjectFactory: {
33 | tiledmap: Phaser.GameObjectFactory.prototype.tiledmap
34 | },
35 | loader: {
36 | tiledmap: Phaser.Loader.prototype.tiledmap,
37 | loadFile: Phaser.Loader.prototype.loadFile,
38 | jsonLoadComplete: Phaser.Loader.prototype.jsonLoadComplete,
39 | xmlLoadComplete: Phaser.Loader.prototype.xmlLoadComplete,
40 | processPack: Phaser.Loader.prototype.processPack
41 | },
42 | physics: {
43 | p2: {
44 | convertTiledmap: Phaser.Physics.P2 ? Phaser.Physics.P2.prototype.convertTiledmap : null,
45 | convertTiledCollisionObjects: Phaser.Physics.P2 ? Phaser.Physics.P2.prototype.convertTiledCollisionObjects : null
46 | },
47 | ninja: {
48 | convertTiledmap: Phaser.Physics.Ninja ? Phaser.Physics.Ninja.prototype.convertTiledmap : null
49 | }
50 | }
51 | };
52 |
53 | Tiled.prototype.init = function () {
54 | Phaser.GameObjectFactory.prototype.tiledmap = GameObjectFactory_tiledmap;
55 | Phaser.Loader.prototype.tiledmap = Loader_tiledmap;
56 | Phaser.Loader.prototype.loadFile = Loader_loadFile;
57 | Phaser.Loader.prototype.jsonLoadComplete = Loader_jsonLoadComplete;
58 | Phaser.Loader.prototype.xmlLoadComplete = Loader_xmlLoadComplete;
59 | Phaser.Loader.prototype.processPack = Loader_processPack;
60 |
61 | if (Phaser.Physics.P2) {
62 | Phaser.Physics.P2.prototype.convertTiledmap = physics.p2.convertTiledmap;
63 | Phaser.Physics.P2.prototype.convertTiledCollisionObjects = physics.p2.convertTiledCollisionObjects;
64 | }
65 |
66 | if (Phaser.Physics.Ninja) {
67 | Phaser.Physics.Ninja.prototype.convertTiledmap = physics.ninja.convertTiledmap;
68 | }
69 | };
70 |
71 | Tiled.prototype.destroy = function () {
72 | Phaser.Plugin.prototype.destroy.apply(this, arguments);
73 |
74 | Phaser.GameObjectFactory.prototype.tiledmap = originals.gameObjectFactory.tiledmap;
75 | Phaser.Loader.prototype.tiledmap = originals.loader.tiledmap;
76 | Phaser.Loader.prototype.loadFile = originals.loader.loadFile;
77 | Phaser.Loader.prototype.jsonLoadComplete = originals.loader.jsonLoadComplete;
78 | Phaser.Loader.prototype.xmlLoadComplete = originals.loader.xmlLoadComplete;
79 | Phaser.Loader.prototype.processPack = originals.loader.processPack;
80 |
81 | if (originals.physics.p2.convertTiledmap) {
82 | Phaser.Physics.P2.prototype.convertTiledmap = originals.physics.p2.convertTiledmap;
83 | Phaser.Physics.P2.prototype.convertTiledCollisionObjects = originals.physics.p2.convertTiledCollisionObjects;
84 | }
85 |
86 | if (originals.physics.ninja.convertTiledmap) {
87 | Phaser.Physics.Ninja.prototype.convertTiledmap = originals.physics.ninja.convertTiledmap;
88 | }
89 | };
90 |
91 | function GameObjectFactory_tiledmap(key, group) {
92 | return new Tiled.Tilemap(this.game, key, group);
93 | }
94 |
95 | /**
96 | * Add a new tilemap loading request.
97 | *
98 | * @method Phaser.Loader#tilemap
99 | * @param {string} key - Unique asset key of the tilemap data.
100 | * @param {string} [url] - The url of the map data file (csv/json)
101 | * @param {object} [data] - An optional JSON data object. If given then the url is ignored and this JSON
102 | * object is used for map data instead.
103 | * @param {number} [format=Tiled.Tilemap.CSV] - The format of the map data. Either Tiled.Tilemap.CSV
104 | * or Tiled.Tilemap.TILED_JSON.
105 | * @return {Phaser.Loader} This Loader instance.
106 | */
107 | function Loader_tiledmap(key, url, data, format) {
108 | if (typeof format === 'undefined') { format = Tiled.Tilemap.CSV; }
109 |
110 | /*eslint-disable no-eq-null, eqeqeq */
111 | if (url == null && data == null) {
112 | console.warn('Phaser.Loader.tiledmap - Both url and data are null. One must be set.');
113 |
114 | return this;
115 | }
116 | /*eslint-enable no-eq-null, eqeqeq */
117 |
118 | // A map data object has been given
119 | if (data) {
120 | switch (format) {
121 | // A csv string or object has been given
122 | case Tiled.Tilemap.CSV:
123 | break;
124 |
125 | // A json string or object has been given
126 | case Tiled.Tilemap.TILED_JSON:
127 | if (typeof data === 'string') {
128 | data = JSON.parse(data);
129 | }
130 | break;
131 |
132 | // An xml string or document has been given
133 | case Tiled.Tilemap.TILED_XML:
134 | if (typeof data === 'string') {
135 | data = utils.parseXML(data);
136 | }
137 | break;
138 | }
139 |
140 | this.game.cache.addTilemap(key, null, data, format);
141 | }
142 | else {
143 | this.addToFileList('tiledmap', key, url, { format: format });
144 | }
145 |
146 | return this;
147 | }
148 |
149 | function Loader_loadFile(file) {
150 | originals.loader.loadFile.apply(this, arguments);
151 |
152 | if (file.type === 'tiledmap') {
153 | if (file.format === Tiled.Tilemap.TILED_JSON) {
154 | this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.jsonLoadComplete);
155 | }
156 | else if (file.format === Tiled.Tilemap.CSV) {
157 | this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.csvLoadComplete);
158 | }
159 | else if (file.format === Tiled.Tilemap.TILED_XML) {
160 | this.xhrLoad(file, this.transformUrl(file.url, file), 'text', this.xmlLoadComplete);
161 | }
162 | else {
163 | this.asyncComplete(file, 'invalid Tilemap format: ' + file.format);
164 | }
165 | }
166 | }
167 |
168 | /**
169 | * Successfully loaded a JSON file.
170 | *
171 | * @method Phaser.Loader#jsonLoadComplete
172 | * @param {object} file - File associated with this request
173 | * @param {XMLHttpRequest} xhr
174 | */
175 | function Loader_jsonLoadComplete(file, xhr) {
176 | var data = JSON.parse(xhr.responseText);
177 |
178 | if (file.type === 'tilemap' || file.type === 'tiledmap')
179 | {
180 | this.game.cache.addTilemap(file.key, file.url, data, file.format);
181 | }
182 | else if (file.type === 'json')
183 | {
184 | this.game.cache.addJSON(file.key, file.url, data);
185 | }
186 | else
187 | {
188 | this.game.cache.addTextureAtlas(file.key, file.url, file.data, data, file.format);
189 | }
190 |
191 | this.asyncComplete(file);
192 | }
193 |
194 | /**
195 | * Successfully loaded an XML file.
196 | *
197 | * @method Phaser.Loader#xmlLoadComplete
198 | * @param {object} file - File associated with this request
199 | * @param {XMLHttpRequest} xhr
200 | */
201 | function Loader_xmlLoadComplete(file, xhr) {
202 | // Always try parsing the content as XML, regardless of actually response type
203 | var data = xhr.responseText;
204 | var xml = this.parseXml(data);
205 |
206 | if (!xml)
207 | {
208 | var responseType = xhr.responseType || xhr.contentType; // contentType for MS-XDomainRequest
209 | console.warn('Phaser.Loader - ' + file.key + ': invalid XML (' + responseType + ')');
210 | this.asyncComplete(file, 'invalid XML');
211 | return;
212 | }
213 |
214 | if (file.type === 'tilemap' || file.type === 'tiledmap') {
215 | this.game.cache.addTilemap(file.key, file.url, xml, file.format);
216 | }
217 | else if (file.type === 'bitmapfont')
218 | {
219 | this.game.cache.addBitmapFont(file.key, file.url, file.data, xml, file.xSpacing, file.ySpacing);
220 | }
221 | else if (file.type === 'textureatlas')
222 | {
223 | this.game.cache.addTextureAtlas(file.key, file.url, file.data, xml, file.format);
224 | }
225 | else if (file.type === 'xml')
226 | {
227 | this.game.cache.addXML(file.key, file.url, xml);
228 | }
229 |
230 | this.asyncComplete(file);
231 |
232 | }
233 |
234 | // the same as the core one, but we add 'tiledmap'
235 | function Loader_processPack(pack) {
236 | var packData = pack.data[pack.key];
237 |
238 | if (!packData)
239 | {
240 | console.warn('Phaser.Loader - ' + pack.key + ': pack has data, but not for pack key');
241 | return;
242 | }
243 |
244 | for (var i = 0; i < packData.length; i++)
245 | {
246 | var file = packData[i];
247 |
248 | switch (file.type)
249 | {
250 | case 'image':
251 | this.image(file.key, file.url, file.overwrite);
252 | break;
253 |
254 | case 'text':
255 | this.text(file.key, file.url, file.overwrite);
256 | break;
257 |
258 | case 'json':
259 | this.json(file.key, file.url, file.overwrite);
260 | break;
261 |
262 | case 'script':
263 | this.script(file.key, file.url, file.callback, pack.callbackContext || this);
264 | break;
265 |
266 | case 'binary':
267 | this.binary(file.key, file.url, file.callback, pack.callbackContext || this);
268 | break;
269 |
270 | case 'spritesheet':
271 | this.spritesheet(file.key, file.url, file.frameWidth, file.frameHeight,
272 | file.frameMax, file.margin, file.spacing);
273 | break;
274 |
275 | case 'audio':
276 | this.audio(file.key, file.urls, file.autoDecode);
277 | break;
278 |
279 | case 'audiosprite':
280 | this.audio(file.key, file.urls, file.jsonURL);
281 | break;
282 |
283 | case 'tilemap':
284 | this.tilemap(file.key, file.url, file.data, Phaser.Tilemap[file.format]);
285 | break;
286 |
287 | case 'tiledmap':
288 | this.tiledmap(file.key, file.url, file.data, Tiled.Tilemap[file.format]);
289 | break;
290 |
291 | case 'physics':
292 | this.physics(file.key, file.url, file.data, Phaser.Loader[file.format]);
293 | break;
294 |
295 | case 'bitmapFont':
296 | this.bitmapFont(file.key, file.textureURL, file.xmlURL, file.xmlData, file.xSpacing, file.ySpacing);
297 | break;
298 |
299 | case 'atlasJSONArray':
300 | this.atlasJSONArray(file.key, file.textureURL, file.atlasURL, file.atlasData);
301 | break;
302 |
303 | case 'atlasJSONHash':
304 | this.atlasJSONHash(file.key, file.textureURL, file.atlasURL, file.atlasData);
305 | break;
306 |
307 | case 'atlasXML':
308 | this.atlasXML(file.key, file.textureURL, file.atlasURL, file.atlasData);
309 | break;
310 |
311 | case 'atlas':
312 | this.atlas(file.key, file.textureURL, file.atlasURL, file.atlasData, Phaser.Loader[file.format]);
313 | break;
314 | }
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/src/tiled/Tile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Base Tile implementation, a tile is a single tile in a tilemap layer
3 | *
4 | * @class Tile
5 | * @extends Phaser.Sprite
6 | * @constructor
7 | */
8 | function Tile(game, x, y, tileId, tileset, layer) {
9 | Phaser.Sprite.call(this,
10 | game,
11 | (x * tileset.tileWidth) + tileset.tileoffset.x,
12 | (y * tileset.tileHeight) + tileset.tileoffset.y,
13 | tileset.getTileTexture(tileId)
14 | );
15 |
16 | this.type = Phaser.TILESPRITE;
17 |
18 | /**
19 | * @property {object} layer - The layer in the Tilemap data that this tile belongs to.
20 | */
21 | this.layer = layer;
22 |
23 | /**
24 | * @property {object} tileset - The tileset that this tile's texture is from.
25 | */
26 | this.tileset = tileset;
27 |
28 | /**
29 | * @property {Phaser.Point} tilePosition - The position of the tile in 'tile coords'
30 | */
31 | this.tilePosition = new Phaser.Point(x, y);
32 |
33 | /**
34 | * @property {number} centerX - The center of the tile.
35 | */
36 | this.centerX = Math.abs(tileset.tileWidth / 2);
37 |
38 | /**
39 | * @property {number} centerY - The height of the tile in pixels.
40 | */
41 | this.centerY = Math.abs(tileset.tileHeight / 2);
42 |
43 | /**
44 | * @property {object} properties - Tile specific properties.
45 | */
46 | this.properties = tileset.getTileProperties(tileId);
47 |
48 | /**
49 | * @property {boolean} scanned - Has this tile been walked / turned into a poly?
50 | */
51 | this.scanned = false;
52 |
53 | /**
54 | * @property {boolean} faceTop - Is the top of this tile an interesting edge?
55 | */
56 | this.faceTop = false;
57 |
58 | /**
59 | * @property {boolean} faceBottom - Is the bottom of this tile an interesting edge?
60 | */
61 | this.faceBottom = false;
62 |
63 | /**
64 | * @property {boolean} faceLeft - Is the left of this tile an interesting edge?
65 | */
66 | this.faceLeft = false;
67 |
68 | /**
69 | * @property {boolean} faceRight - Is the right of this tile an interesting edge?
70 | */
71 | this.faceRight = false;
72 |
73 | /**
74 | * @property {boolean} collideLeft - Indicating collide with any object on the left.
75 | * @default
76 | */
77 | this.collideLeft = this.properties.collideLeft !== undefined ?
78 | this.properties.collideLeft : (this.properties.collides || false);
79 |
80 | /**
81 | * @property {boolean} collideRight - Indicating collide with any object on the right.
82 | * @default
83 | */
84 | this.collideRight = this.properties.collideRight !== undefined ?
85 | this.properties.collideRight : (this.properties.collides || false);
86 |
87 | /**
88 | * @property {boolean} collideUp - Indicating collide with any object on the top.
89 | * @default
90 | */
91 | this.collideUp = this.properties.collideUp !== undefined ?
92 | this.properties.collideUp : (this.properties.collides || false);
93 |
94 | /**
95 | * @property {boolean} collideDown - Indicating collide with any object on the bottom.
96 | * @default
97 | */
98 | this.collideDown = this.properties.collideDown !== undefined ?
99 | this.properties.collideDown : (this.properties.collides || false);
100 |
101 | /**
102 | * @property {function} collisionCallback - Tile collision callback.
103 | * @default
104 | */
105 | this.collisionCallback = null;
106 |
107 | /**
108 | * @property {object} collisionCallbackContext - The context in which the collision callback will be called.
109 | * @default
110 | */
111 | this.collisionCallbackContext = this;
112 |
113 | // load animation data
114 | var animData = tileset.getTileAnimations(tileId);
115 | if (animData) {
116 | this.animations.copyFrameData(animData.data, 0);
117 | this.animations.add('tile', null, animData.rate, true).play();
118 | }
119 |
120 | // set the blend mode
121 | var blendMode = this.properties.blendMode || layer.properties.blendMode;
122 | this.blendMode = blendMode ? Phaser.blendModes[blendMode] : Phaser.blendModes.NORMAL;
123 |
124 | // setup the flipped states
125 | if (this.properties.flippedX) {
126 | this.scale.x = -1;
127 | this.position.x += tileset.tileWidth;
128 | }
129 |
130 | if (this.properties.flippedY) {
131 | this.scale.y = -1;
132 | this.position.y += tileset.tileHeight;
133 | }
134 |
135 | // from Tiled Editor:
136 | // https://github.com/bjorn/tiled/blob/b059a13b2864ea029fb741a90780d31cf5b67043/src/libtiled/maprenderer.cpp#L135-L145
137 | if (this.properties.flippedAD) {
138 | this.rotation = this.game.math.degToRad(90);
139 | this.scale.x *= -1;
140 |
141 | var sx = this.scale.x;
142 | this.scale.x = this.scale.y;
143 | this.scale.y = sx;
144 |
145 | var halfDiff = Math.abs(this.height / 2) - Math.abs(this.width / 2);
146 | this.position.y += halfDiff;
147 | this.position.x += halfDiff;
148 | }
149 | }
150 |
151 | Tile.prototype = Object.create(Phaser.Sprite.prototype);
152 | Tile.prototype.constructor = Tile;
153 |
154 | module.exports = Tile;
155 |
156 | /**
157 | * Check if the given x and y world coordinates are within this Tile.
158 | *
159 | * @method Phaser.Tile#containsPoint
160 | * @param {number} x - The x coordinate to test.
161 | * @param {number} y - The y coordinate to test.
162 | * @return {boolean} True if the coordinates are within this Tile, otherwise false.
163 | */
164 | Tile.prototype.containsPoint = function (x, y) {
165 |
166 | return !(x < this.worldX || y < this.worldY || x > this.right || y > this.bottom);
167 |
168 | };
169 |
170 | /**
171 | * Check for intersection with this tile.
172 | *
173 | * @method Phaser.Tile#intersects
174 | * @param {number} x - The x axis in pixels.
175 | * @param {number} y - The y axis in pixels.
176 | * @param {number} right - The right point.
177 | * @param {number} bottom - The bottom point.
178 | * @return {boolean} True if the coordinates are within this Tile, otherwise false.
179 | */
180 | Tile.prototype.intersects = function (x, y, right, bottom) {
181 |
182 | if (right <= this.worldX)
183 | {
184 | return false;
185 | }
186 |
187 | if (bottom <= this.worldY)
188 | {
189 | return false;
190 | }
191 |
192 | if (x >= this.worldX + this.width)
193 | {
194 | return false;
195 | }
196 |
197 | if (y >= this.worldY + this.height)
198 | {
199 | return false;
200 | }
201 |
202 | return true;
203 |
204 | };
205 |
206 | /**
207 | * Set a callback to be called when this tile is hit by an object.
208 | * The callback must true true for collision processing to take place.
209 | *
210 | * @method Phaser.Tile#setCollisionCallback
211 | * @param {function} callback - Callback function.
212 | * @param {object} context - Callback will be called within this context.
213 | */
214 | Tile.prototype.setCollisionCallback = function (callback, context) {
215 |
216 | this.collisionCallback = callback;
217 | this.collisionCallbackContext = context;
218 |
219 | };
220 |
221 | /**
222 | * Clean up memory.
223 | *
224 | * @method Phaser.Tile#destroy
225 | */
226 | Tile.prototype.destroy = function () {
227 | Phaser.Sprite.prototype.destroy.apply(this, arguments);
228 |
229 | this.layer = null;
230 | this.tileset = null;
231 | this.tilePosition = null;
232 |
233 | this.properties = null;
234 |
235 | this.collisionCallback = null;
236 |
237 | this.collisionCallbackContext = null;
238 | };
239 |
240 | /**
241 | * Sets the collision flags for each side of this tile and updates the interesting faces list.
242 | *
243 | * @method Phaser.Tile#setCollision
244 | * @param {boolean} left - Indicating collide with any object on the left.
245 | * @param {boolean} right - Indicating collide with any object on the right.
246 | * @param {boolean} up - Indicating collide with any object on the top.
247 | * @param {boolean} down - Indicating collide with any object on the bottom.
248 | */
249 | Tile.prototype.setCollision = function (left, right, up, down) {
250 |
251 | this.collideLeft = left;
252 | this.collideRight = right;
253 | this.collideUp = up;
254 | this.collideDown = down;
255 |
256 | this.faceLeft = left;
257 | this.faceRight = right;
258 | this.faceTop = up;
259 | this.faceBottom = down;
260 |
261 | };
262 |
263 | /**
264 | * Reset collision status flags.
265 | *
266 | * @method Phaser.Tile#resetCollision
267 | */
268 | Tile.prototype.resetCollision = function () {
269 |
270 | this.collideLeft = false;
271 | this.collideRight = false;
272 | this.collideUp = false;
273 | this.collideDown = false;
274 |
275 | this.faceLeft = false;
276 | this.faceRight = false;
277 | this.faceTop = false;
278 | this.faceBottom = false;
279 |
280 | };
281 |
282 | /**
283 | * Is this tile interesting?
284 | *
285 | * @method Phaser.Tile#isInteresting
286 | * @param {boolean} collides - If true will check any collides value.
287 | * @param {boolean} faces - If true will check any face value.
288 | * @return {boolean} True if the Tile is interesting, otherwise false.
289 | */
290 | Tile.prototype.isInteresting = function (collides, faces) {
291 |
292 | if (collides && faces)
293 | {
294 | // Does this tile have any collide flags OR interesting face?
295 | return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown ||
296 | this.faceTop || this.faceBottom || this.faceLeft || this.faceRight || this.collisionCallback);
297 | }
298 | else if (collides)
299 | {
300 | // Does this tile collide?
301 | return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown);
302 | }
303 | else if (faces)
304 | {
305 | // Does this tile have an interesting face?
306 | return (this.faceTop || this.faceBottom || this.faceLeft || this.faceRight);
307 | }
308 |
309 | return false;
310 |
311 | };
312 |
313 | /**
314 | * Copies the tile data and properties from the given tile to this tile.
315 | *
316 | * @method Phaser.Tile#copy
317 | * @param {Phaser.Tile} tile - The tile to copy from.
318 | */
319 | Tile.prototype.copy = function (tile) {
320 |
321 | this.index = tile.index;
322 | this.alpha = tile.alpha;
323 | this.properties = tile.properties;
324 |
325 | this.collideUp = tile.collideUp;
326 | this.collideDown = tile.collideDown;
327 | this.collideLeft = tile.collideLeft;
328 | this.collideRight = tile.collideRight;
329 |
330 | this.collisionCallback = tile.collisionCallback;
331 | this.collisionCallbackContext = tile.collisionCallbackContext;
332 |
333 | };
334 |
335 | Object.defineProperty(Tile.prototype, 'worldX', {
336 | get: function () {
337 | return this.position.x;
338 | },
339 | set: function (val) {
340 | this.position.x = val;
341 | }
342 | });
343 |
344 | Object.defineProperty(Tile.prototype, 'worldY', {
345 | get: function () {
346 | return this.position.y;
347 | },
348 | set: function (val) {
349 | this.position.y = val;
350 | }
351 | });
352 |
353 | /**
354 | * @name Phaser.Tile#collides
355 | * @property {boolean} collides - True if this tile can collide on any of its faces.
356 | * @readonly
357 | */
358 | Object.defineProperty(Tile.prototype, 'collides', {
359 |
360 | get: function () {
361 | return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown);
362 | }
363 |
364 | });
365 |
366 | /**
367 | * @name Phaser.Tile#canCollide
368 | * @property {boolean} canCollide - True if this tile can collide on any of its faces or has a collision callback set.
369 | * @readonly
370 | */
371 | Object.defineProperty(Tile.prototype, 'canCollide', {
372 |
373 | get: function () {
374 | return (this.collideLeft || this.collideRight || this.collideUp || this.collideDown || this.collisionCallback);
375 | }
376 |
377 | });
378 |
379 | /**
380 | * @name Phaser.Tile#left
381 | * @property {number} left - The x value in pixels.
382 | * @readonly
383 | */
384 | Object.defineProperty(Tile.prototype, 'left', {
385 |
386 | get: function () {
387 | return this.worldX;
388 | }
389 |
390 | });
391 |
392 | /**
393 | * @name Phaser.Tile#right
394 | * @property {number} right - The sum of the x and width properties.
395 | * @readonly
396 | */
397 | Object.defineProperty(Tile.prototype, 'right', {
398 |
399 | get: function () {
400 | return this.worldX + this.width;
401 | }
402 |
403 | });
404 |
405 | /**
406 | * @name Phaser.Tile#top
407 | * @property {number} top - The y value.
408 | * @readonly
409 | */
410 | Object.defineProperty(Tile.prototype, 'top', {
411 |
412 | get: function () {
413 | return this.worldY;
414 | }
415 |
416 | });
417 |
418 | /**
419 | * @name Phaser.Tile#bottom
420 | * @property {number} bottom - The sum of the y and height properties.
421 | * @readonly
422 | */
423 | Object.defineProperty(Tile.prototype, 'bottom', {
424 |
425 | get: function () {
426 | return this.worldY + this.height;
427 | }
428 |
429 | });
430 |
--------------------------------------------------------------------------------
/src/tiled/Tileset.js:
--------------------------------------------------------------------------------
1 | var utils = require('../utils');
2 |
3 | /**
4 | * This object represents a tileset used by a Tilemap.
5 | * There can be multiple Tilesets in a map
6 | *
7 | * @class Tileset
8 | * @extends Texture
9 | * @constructor
10 | * @param game {Phaser.Game} Phaser game this belongs to.
11 | * @param key {string} The name of the tiledmap, this is usually the filename without the extension.
12 | * @param settings {Object} All the settings for the tileset
13 | * @param settings.tilewidth {Number} The width of a single tile in the set
14 | * @param settings.tileheight {Number} The height of a single tile in the set
15 | * @param [settings.firstgid=1] {Number} The id of the first tile in the set, defaults to 1
16 | * @param [settings.spacing=0] {Number} The spacing around tiles in the tileset (in pixels)
17 | * @param [settings.margin=0] {Number} The margin around a tile in the tileset (in pixels)
18 | * @param [settings.tileoffset] {Object} The offset to apply to a tile rendered from this tileset
19 | * @param [settings.tileoffset.x=0] {Number} The X offset to apply to the tile
20 | * @param [settings.tileoffset.y=0] {Number} The Y offset to apply to the tile
21 | * @param [settings.properties] {Object} User-defined, custom properties that apply to the tileset
22 | * @param [settings.tileproperties] {Object} User-defined, custom properties that apply to tiles in the tileset.
23 | * The keys of this object should the tile id of the properties
24 | * @param [settings.tiles] {Object} Extra metadata about specific tiles
25 | * @param [settings.imagewidth] {Number} An override for the image width
26 | * @param [settings.imageheight] {Number} An override for the image height
27 | */
28 | // TODO: Support external tilesets (TSX files) via the "source" attribute
29 | // see: https://github.com/bjorn/tiled/wiki/TMX-Map-Format#tileset
30 | function Tileset(game, key, settings) {
31 | var txkey = utils.cacheKey(key, 'tileset', settings.name);
32 | var tx = game.cache.getBaseTexture(txkey);
33 | var ids;
34 | var ttxkey;
35 | var ttx;
36 | var tileTextures = {};
37 | var numTileTextures = 0;
38 |
39 | // if no main texture, check if multi-image tileset
40 | if (!tx && settings.tiles) {
41 | // need to sort because order matters here, and can't guarantee that the object's keys will be ordered.
42 | // We need a custom comparator because .sort() is lexagraphic, not numeric.
43 | ids = Object.keys(settings.tiles).sort(function (a, b) { return parseInt(a, 10) - parseInt(b, 10); });
44 |
45 | for (var i = 0; i < ids.length; ++i) {
46 | if (settings.tiles[ids[i]].image) {
47 | ttxkey = utils.cacheKey(key, 'tileset_image_' + ids[i], settings.name);
48 | ttx = game.cache.getPixiTexture(ttxkey);
49 |
50 | if (!ttx) {
51 | console.warn(
52 | 'Tileset "' + settings.name + '" unable to find texture cached by key "' +
53 | ttxkey + '", using blank texture.'
54 | );
55 | ttx = PIXI.Texture.emptyTexture;
56 | }
57 |
58 | tileTextures[numTileTextures++] = ttx;
59 | }
60 | }
61 | }
62 |
63 | // if no main texture, and we didn't find any image tiles then warn about blank tileset
64 | if (!tx && numTileTextures === 0) {
65 | console.warn(
66 | 'Tileset "' + settings.name + '" unable to find texture cached by key "' +
67 | txkey + '", using blank texture.'
68 | );
69 | }
70 |
71 | PIXI.Texture.call(this, tx || PIXI.Texture.emptyTexture.baseTexture);
72 |
73 | this.game = game;
74 |
75 | this.multiImage = numTileTextures > 0;
76 |
77 | // Tiled Editor properties
78 |
79 | /**
80 | * The first tileId in the tileset
81 | *
82 | * @property firstgid
83 | * @type Number
84 | */
85 | this.firstgid = settings.firstgid || 1;
86 |
87 | /**
88 | * The name of the tileset
89 | *
90 | * @property name
91 | * @type String
92 | */
93 | this.name = settings.name;
94 |
95 | /**
96 | * The width of a tile in the tileset
97 | *
98 | * @property tileWidth
99 | * @type Number
100 | */
101 | this.tileWidth = settings.tilewidth;
102 |
103 | /**
104 | * The height of a tile in the tileset
105 | *
106 | * @property tileHeight
107 | * @type Number
108 | */
109 | this.tileHeight = settings.tileheight;
110 |
111 | /**
112 | * The spacing around a tile in the tileset
113 | *
114 | * @property spacing
115 | * @type Number
116 | */
117 | this.spacing = settings.spacing || 0;
118 |
119 | /**
120 | * The margin around a tile in the tileset
121 | *
122 | * @property margin
123 | * @type Number
124 | */
125 | this.margin = settings.margin || 0;
126 |
127 | /**
128 | * The offset of tile positions when rendered
129 | *
130 | * @property tileoffset
131 | * @type Phaser.Point
132 | */
133 | this.tileoffset = new Phaser.Point(
134 | settings.tileoffset ? settings.tileoffset.x : 0,
135 | settings.tileoffset ? settings.tileoffset.y : 0
136 | );
137 |
138 | // TODO: Support for "terraintypes," "image"
139 | // see: https://github.com/bjorn/tiled/wiki/TMX-Map-Format#tileset
140 |
141 | // Custom/Optional properties
142 |
143 | /**
144 | * The number of tiles calculated based on size, margin, and spacing
145 | *
146 | * @property numTiles
147 | * @type Vector
148 | */
149 | this.numTiles = this.multiImage ? tileTextures.length : new Phaser.Point(
150 | Math.round((this.baseTexture.width - this.margin) / (this.tileWidth + this.spacing)),
151 | Math.round((this.baseTexture.height - this.margin) / (this.tileHeight + this.spacing))
152 | );
153 |
154 | /**
155 | * The last tileId in the tileset
156 | *
157 | * @property lastgid
158 | * @type Number
159 | */
160 | this.lastgid = this.firstgid + (this.multiImage ? numTileTextures : ((this.numTiles.x * this.numTiles.y) || 1)) - 1;
161 |
162 | /**
163 | * The properties of the tileset
164 | *
165 | * @property properties
166 | * @type Object
167 | */
168 | this.properties = utils.parseTiledProperties(settings.properties);
169 |
170 | /**
171 | * The properties of the tiles in the tileset
172 | *
173 | * @property tileproperties
174 | * @type Object
175 | */
176 | this.tileproperties = {};
177 |
178 | // massage tile tileproperties
179 | for (var k in settings.tileproperties) {
180 | this.tileproperties[k] = utils.parseTiledProperties(settings.tileproperties[k]);
181 | }
182 |
183 | /**
184 | * The size of the tileset
185 | *
186 | * @property size
187 | * @type Vector
188 | */
189 | this.size = this.multiImage ? new Phaser.Point(0, 0) : new Phaser.Point(
190 | settings.imagewidth || this.baseTexture.width,
191 | settings.imageheight || this.baseTexture.height
192 | );
193 |
194 | /**
195 | * The texture instances for each tile in the set
196 | *
197 | * @property textures
198 | * @type Array
199 | */
200 | this.textures = this.multiImage ? tileTextures : {};
201 |
202 | /**
203 | * The animation data for tile animations in the set
204 | *
205 | * @property tileanimations
206 | * @type Object
207 | */
208 | this.tileanimations = {};
209 |
210 | /**
211 | * Internal tiles config data.
212 | *
213 | * @property _tilesData
214 | * @type Object
215 | * @private
216 | */
217 | this._tilesData = settings.tiles || {};
218 | }
219 |
220 | Tileset.prototype = Object.create(PIXI.Texture.prototype);
221 | Tileset.prototype.constructor = Tileset;
222 |
223 | module.exports = Tileset;
224 |
225 | /**
226 | * Gets the tile properties for a tile based on it's ID
227 | *
228 | * @method getTileProperties
229 | * @param tileId {Number} The id of the tile to get the properties for
230 | * @return {Object} The properties of the tile
231 | */
232 | Tileset.prototype.getTileProperties = function (tileId) {
233 | if (!tileId) {
234 | return null;
235 | }
236 |
237 | var flags = Tileset.FLAGS;
238 | var flippedX = tileId & flags.FLIPPED_HORZ;
239 | var flippedY = tileId & flags.FLIPPED_VERT;
240 | var flippedAD = tileId & flags.FLIPPED_ANTI_DIAG;
241 |
242 | tileId = (tileId & ~Tileset.FLAGS.ALL) - this.firstgid;
243 |
244 | // if less than 0, then this id isn't in this tileset
245 | if (tileId < 0) {
246 | return null;
247 | }
248 |
249 | var props = this.tileproperties[tileId] ?
250 | // get this value
251 | this.tileproperties[tileId] :
252 | // set this id to default values and cache
253 | this.tileproperties[tileId] = {
254 | collides: false
255 | };
256 |
257 | props.flippedX = flippedX;
258 | props.flippedY = flippedY;
259 | props.flippedAD = flippedAD;
260 |
261 | return props;
262 | };
263 |
264 | /**
265 | * Gets the tile animations for a tile based on it's ID
266 | *
267 | * @method getTileProperties
268 | * @param tileId {Number} The id of the tile to get the animation frames for
269 | * @return {Phaser.FrameData} The frame data of the tile
270 | */
271 | Tileset.prototype.getTileAnimations = function (tileId) {
272 | if (!tileId) {
273 | return null;
274 | }
275 |
276 | tileId = (tileId & ~Tileset.FLAGS.ALL) - this.firstgid;
277 |
278 | // if less than 0, then this id isn't in this tileset
279 | if (tileId < 0) {
280 | return null;
281 | }
282 |
283 | // if we have already created the animation data
284 | if (this.tileanimations[tileId]) {
285 | return this.tileanimations[tileId];
286 | }
287 |
288 | if (this._tilesData[tileId] && this._tilesData[tileId].animation) {
289 | this.tileanimations[tileId] = {
290 | rate: 1000 / this._tilesData[tileId].animation[0].duration,
291 | data: new Phaser.FrameData()
292 | };
293 |
294 | for (var i = 0; i < this._tilesData[tileId].animation.length; ++i) {
295 | var frame = this.getTileTexture(this._tilesData[tileId].animation[i].tileid + this.firstgid).frame;
296 |
297 | this.tileanimations[tileId].data.addFrame(
298 | new Phaser.Frame(i, frame.x, frame.y, frame.width, frame.height)
299 | );
300 | }
301 |
302 | return this.tileanimations[tileId];
303 | }
304 |
305 | return null;
306 | };
307 |
308 | /**
309 | * Gets the tile texture for a tile based on it's ID
310 | *
311 | * @method getTileTexture
312 | * @param tileId {Number} The id of the tile to get the texture for
313 | * @return {Texture} The texture for the tile
314 | */
315 | Tileset.prototype.getTileTexture = function (tileId) {
316 | if (!tileId) {
317 | return null;
318 | }
319 |
320 | // get the internal ID of the tile in this set (0 indexed)
321 | tileId = (tileId & ~Tileset.FLAGS.ALL) - this.firstgid;
322 |
323 | // if less than 0, then this id isn't in this tileset
324 | if (tileId < 0) {
325 | return null;
326 | }
327 |
328 | // multi image set or we have cached the texture, just return it
329 | if (this.multiImage || this.textures[tileId]) {
330 | return this.textures[tileId];
331 | }
332 |
333 | // generate this tile's texture then cache it.
334 | // convert the tileId to x,y coords of the tile in the Texture
335 | var y = Phaser.Math.floorTo(tileId / this.numTiles.x);
336 | var x = (tileId - (y * this.numTiles.x));
337 |
338 | // get location in pixels
339 | x = (x * this.tileWidth) + (x * this.spacing) + this.margin;
340 | y = (y * this.tileHeight) + (y * this.spacing) + this.margin;
341 |
342 | return (this.textures[tileId] = new PIXI.Texture(
343 | this.baseTexture,
344 | new Phaser.Rectangle(x, y, this.tileWidth, this.tileHeight)
345 | ));
346 | };
347 |
348 | /**
349 | * Returns whether or not this tileset contains the given tile guid
350 | *
351 | * @method contains
352 | * @param tileId {Number} The ID of the tile to check
353 | * @return {Boolean}
354 | */
355 | Tileset.prototype.contains = function (tileId) {
356 | if (!tileId) {
357 | return false;
358 | }
359 |
360 | tileId &= ~Tileset.FLAGS.ALL;
361 |
362 | return (tileId >= this.firstgid && tileId <= this.lastgid);
363 | };
364 |
365 | Tileset.prototype.destroy = function () {
366 | utils.destroyTexture(this, false);
367 |
368 | // destroy tile textures
369 | for (var id in this.textures) {
370 | utils.destroyTexture(this.textures[id]);
371 | }
372 |
373 | // destroy tile animations
374 | for (var jd in this.tileanimations) {
375 | this.tileanimations[jd]._frames = null;
376 | this.tileanimations[jd]._frameNames = null;
377 | }
378 |
379 | this.tileoffset = null;
380 | this.numTiles = null;
381 | this.properties = null;
382 | this.tileproperties = null;
383 | this.size = null;
384 | this.textures = null;
385 | this.tileanimations = null;
386 | };
387 |
388 | /**
389 | * Tileset GID flags, these flags are set on a tile's ID to give it a special property
390 | *
391 | * @property FLAGS
392 | * @static
393 | */
394 | Tileset.FLAGS = {
395 | FLIPPED_HORZ: 0x80000000,
396 | FLIPPED_VERT: 0x40000000,
397 | FLIPPED_ANTI_DIAG: 0x20000000
398 | };
399 |
400 | var mask = 0;
401 | for (var f in Tileset.FLAGS) {
402 | mask |= Tileset.FLAGS[f];
403 | }
404 |
405 | Tileset.FLAGS.ALL = mask;
406 |
--------------------------------------------------------------------------------
/src/tiled/Objectlayer.js:
--------------------------------------------------------------------------------
1 | var utils = require('../utils');
2 |
3 | /**
4 | * Tiled object group is a special layer that contains entities
5 | *
6 | * @class Objectlayer
7 | * @extends Phaser.Group
8 | * @constructor
9 | * @param map {Tilemap} The tilemap instance that this belongs to
10 | * @param group {Object} All the settings for the layer
11 | */
12 | function Objectlayer(game, map, layer, index) {
13 | Phaser.Group.call(this, game, map);
14 |
15 | this.index = index;
16 |
17 | // Non-Tiled related properties
18 |
19 | /**
20 | * The map instance this object group belongs to
21 | *
22 | * @property map
23 | * @type Tilemap
24 | */
25 | this.map = map;
26 |
27 | /**
28 | * The const type of this object.
29 | *
30 | * @property type
31 | * @type Number
32 | * @default
33 | */
34 | this.type = Phaser.TILEMAPLAYER;
35 |
36 | /**
37 | * The name of the group
38 | *
39 | * @property name
40 | * @type String
41 | * @default ''
42 | */
43 | this.name = layer.name || '';
44 |
45 | // Tiled related properties
46 |
47 | /**
48 | * The color of this group in the Tiled Editor,
49 | *
50 | * @property color
51 | * @type
52 | */
53 | this.color = layer.color;
54 |
55 | /**
56 | * The user-defined properties of this group. Usually defined in the TiledEditor
57 | *
58 | * @property properties
59 | * @type Object
60 | */
61 | this.properties = utils.parseTiledProperties(layer.properties);
62 |
63 | /**
64 | * The objects in this group that can be spawned
65 | *
66 | * @property objects
67 | * @type Array
68 | */
69 | this.objects = layer.objects;
70 |
71 | for (var i = 0; i < this.objects.length; ++i) {
72 | utils.parseTiledProperties(this.objects[i].properties);
73 | }
74 |
75 | /**
76 | * The Tiled type of tile layer, should always be 'objectgroup'
77 | *
78 | * @property layerType
79 | * @type String
80 | * @default 'objectgroup'
81 | * @readOnly
82 | */
83 | this.layerType = layer.type || 'objectgroup';
84 |
85 | // translate some tiled properties to our inherited properties
86 | this.position.x = layer.x || 0;
87 | this.position.y = layer.y || 0;
88 | this.alpha = layer.opacity !== undefined ? layer.opacity : 1;
89 | this.visible = layer.visible !== undefined ? layer.visible : true;
90 |
91 | // physics bodies in this layer
92 | this.bodies = [];
93 |
94 | if (this.properties.batch) {
95 | this.container = this.addChild(new Phaser.SpriteBatch(game));
96 | }
97 | else {
98 | this.container = this;
99 | }
100 | }
101 |
102 | Objectlayer.prototype = Object.create(Phaser.Group.prototype);
103 | Objectlayer.prototype.constructor = Objectlayer;
104 |
105 | module.exports = Objectlayer;
106 |
107 | /**
108 | * Spawns all the entities associated with this layer, and properly sets their attributes
109 | *
110 | * @chainable
111 | * @param [physicsBodyType=Phaser.Physics.ARCADE] {number} The physics system to create stuff on.
112 | * @param [spawnCallback] {function} A function to call for each object spawned.
113 | * @return {Objectlayer} Returns itself.
114 | */
115 | Objectlayer.prototype.spawn = function (physicsBodyType, spawnCallback) {
116 | // we go through these backwards so that things that are higher in the
117 | // list of object gets rendered on top.
118 | for (var i = this.objects.length - 1; i >= 0; --i) {
119 | var o = this.objects[i];
120 | var props = o.properties;
121 | var set;
122 | // var interactive;
123 | var obj;
124 |
125 | props.tileprops = {};
126 | props.animation = null;
127 |
128 | var texture = props.texture;
129 | // gid means a sprite from a tileset texture
130 | if (o.gid) {
131 | set = this.map.getTileset(o.gid);
132 |
133 | // if the tileset exists
134 | if (set) {
135 | props.texture = set.getTileTexture(o.gid);
136 | props.tileprops = set.getTileProperties(o.gid);
137 | props.animation = set.getTileAnimations(o.gid);
138 | }
139 | }
140 |
141 | o.name = o.name || props.name || props.tileprops.name || '';
142 | o.type = o.type || props.type || props.tileprops.type || '';
143 |
144 | // a manually specified string texture
145 | if (typeof texture === 'string') {
146 | props.texture = this.game.cache.getPixiTexture(texture);
147 | }
148 |
149 | if (typeof props.tileprops.texture === 'string') {
150 | props.texture = this.game.cache.getPixiTexture(props.tileprops.texture);
151 | }
152 |
153 | // when props.texture is empty it will just create an empty sprite.
154 | obj = this.game.add.sprite(o.x, o.y, props.texture, null, this.container);
155 |
156 | // setup the properties of the sprite
157 | obj.name = o.name;
158 | obj.rotation = o.rotation;
159 | obj.objectType = o.type;
160 |
161 | if (!props.texture) {
162 | obj.width = o.width;
163 | obj.height = o.height;
164 | }
165 |
166 | var blendMode = props.blendMode || this.properties.blendMode;
167 | obj.blendMode = blendMode ? Phaser.blendModes[blendMode] : Phaser.blendModes.NORMAL;
168 |
169 | // create physics if this body is physical.
170 | if (props.collides || props.tileprops.collides) {
171 | this.game.physics.enable(obj, physicsBodyType, props.debug || props.tileprops.debug);
172 |
173 | obj.body.setRectangle(obj.width, obj.height, obj.width / 2, -obj.height / 2, obj.rotation);
174 |
175 | obj.body[props.bodyType || props.tileprops.bodyType || 'static'] = true;
176 |
177 | if (props.sensor) {
178 | obj.body.data.shapes[0].sensor = true;
179 | }
180 | }
181 |
182 | var a = props.anchor || props.tileprops.anchor;
183 | obj.anchor.x = a ? a[0] : 0;
184 | obj.anchor.y = a ? a[1] : 1;
185 |
186 | if (props.tileprops.flippedX) {
187 | obj.scale.x = -1;
188 | obj.position.x += Math.abs(obj.width);
189 | }
190 |
191 | if (props.tileprops.flippedY) {
192 | obj.scale.y = -1;
193 | obj.position.y += Math.abs(obj.height);
194 | }
195 |
196 | // from Tiled Editor:
197 | // https://github.com/bjorn/tiled/blob/b059a13b2864ea029fb741a90780d31cf5b67043/src/libtiled/maprenderer.cpp#L135-L145
198 | if (props.tileprops.flippedAD) {
199 | obj.rotation = this.game.math.degToRad(90);
200 | obj.scale.x *= -1;
201 |
202 | var sx = obj.scale.x;
203 | obj.scale.x = obj.scale.y;
204 | obj.scale.y = sx;
205 |
206 | var halfDiff = Math.abs(o.height / 2) - Math.abs(o.width / 2);
207 | obj.position.y += halfDiff;
208 | obj.position.x += halfDiff;
209 | }
210 |
211 | if (props.animation && obj.animations) {
212 | obj.animations.copyFrameData(props.animation.data, 0);
213 | obj.animations.add('tile', null, props.animation.rate, true).play();
214 | // obj.animations.play(props.animation || props.tileprops.animation);
215 | }
216 |
217 | if (typeof o.rotation === 'number') {
218 | obj.rotation = o.rotation;
219 | }
220 |
221 | // visible was recently added to Tiled, default old versions to true
222 | obj.visible = o.visible !== undefined ? !!o.visible : true;
223 |
224 | // if (this.map.orientation === 'isometric') {
225 | // var toTileX = o.x / this.map.tileWidth,
226 | // toTileY = o.y / this.map.tileWidth;
227 |
228 | // //This cannot be the simplest form of this...
229 | // o.x = (toTileX * this.map.tileWidth) - ((toTileY - 1) * (this.map.tileWidth / 2));
230 | // o.y = (toTileY * this.map.tileWidth / 2) + (toTileX * this.map.tileWidth);
231 | // }
232 |
233 | // interactive = this._getInteractive(set, props);
234 |
235 | // //pass through all events
236 | // if (interactive) {
237 | // obj.interactive = interactive;
238 |
239 | // obj.click = this.onObjectEvent.bind(this, 'click', obj);
240 | // obj.mousedown = this.onObjectEvent.bind(this, 'mousedown', obj);
241 | // obj.mouseup = this.onObjectEvent.bind(this, 'mouseup', obj);
242 | // obj.mousemove = this.onObjectEvent.bind(this, 'mousemove', obj);
243 | // obj.mouseout = this.onObjectEvent.bind(this, 'mouseout', obj);
244 | // obj.mouseover = this.onObjectEvent.bind(this, 'mouseover', obj);
245 | // obj.mouseupoutside = this.onObjectEvent.bind(this, 'mouseupoutside', obj);
246 | // }
247 |
248 | // set custom properties
249 | obj.properties = {};
250 | for (var t in props.tileprops) {
251 | obj.properties[t] = props.tileprops[t];
252 | }
253 |
254 | for (var k in props) {
255 | if (k !== 'tileprops') {
256 | obj.properties[k] = props[k];
257 | }
258 | }
259 |
260 | obj._objIndex = i;
261 |
262 | if (spawnCallback) {
263 | spawnCallback(obj);
264 | }
265 | }
266 |
267 | return this;
268 | };
269 |
270 | Objectlayer.prototype.getObject = function (name) {
271 | for (var i = 0; i < this.objects.length; ++i) {
272 | if (this.objects[i].name === name) {
273 | return this.objects[i];
274 | }
275 | }
276 | };
277 |
278 | /**
279 | * Called internally whenever an event happens on an object, used to echo to the map.
280 | *
281 | * @method onObjectEvent
282 | * @param eventName {String} The name of the event
283 | * @param obj {Container|Sprite} The object the event happened to
284 | * @param data {mixed} The event data that was passed along
285 | * @private
286 | */
287 | Objectlayer.prototype.onObjectEvent = function (eventName, obj, data) {
288 | this.map.onObjectEvent(eventName, obj, data);
289 | };
290 |
291 | /**
292 | * Creates a polygon from the vertices in a polygon Tiled property
293 | *
294 | * @method _getPolygon
295 | * @param obj {Object} The polygon Tiled object
296 | * @return {Polygon} The polygon created
297 | * @private
298 | */
299 | Objectlayer.prototype._getPolygon = function (o) {
300 | var points = [];
301 | for (var i = 0, il = o.polygon.length; i < il; ++i) {
302 | points.push(new Phaser.Point(o.polygon[i].x, o.polygon[i].y));
303 | }
304 |
305 | return new Phaser.Polygon(points);
306 | };
307 |
308 | /**
309 | * Creates a polyline from the vertices in a polyline Tiled property
310 | *
311 | * @method _getPolyline
312 | * @param obj {Object} The polyline Tiled object
313 | * @return {Polygon} The polyline created
314 | * @private
315 | */
316 | Objectlayer.prototype._getPolyline = function (o) {
317 | var points = [];
318 | for (var i = 0, il = o.polyline.length; i < il; ++i) {
319 | points.push(new Phaser.Point(o.polyline[i].x, o.polyline[i].y));
320 | }
321 |
322 | return new Phaser.Polygon(points);
323 | };
324 |
325 | /**
326 | * Creates a ellipse from the vertices in a ellipse Tiled property
327 | *
328 | * @method _getEllipse
329 | * @param obj {Object} The ellipse Tiled object
330 | * @return {Ellipse} The ellipse created
331 | * @private
332 | */
333 | Objectlayer.prototype._getEllipse = function (o) {
334 | return new Phaser.Ellipse(0, 0, o.width, o.height);
335 | };
336 |
337 | /**
338 | * Creates a rectangle from the vertices in a rectangle Tiled property
339 | *
340 | * @method _getRectangle
341 | * @param obj {Object} The rectangle Tiled object
342 | * @return {Rectangle} The rectangle created
343 | * @private
344 | */
345 | Objectlayer.prototype._getRectangle = function (o) {
346 | return new Phaser.Rectangle(0, 0, o.width, o.height);
347 | };
348 |
349 | /**
350 | * Checks if an object should be marked as interactive
351 | *
352 | * @method _getInteractive
353 | * @param set {Tileset} The tileset for the object
354 | * @param props {Object} The Tiled properties object
355 | * @return {Boolean} Whether or not the item is interactive
356 | * @private
357 | */
358 | Objectlayer.prototype._getInteractive = function (set, props) {
359 | // TODO: This is wrong, if 'false' is set on a lower level a higher level will override
360 | // first check the lowest level value (on the tile iteself)
361 | return props.interactive || // obj interactive
362 | props.tileprops.interactive || // tile object interactive
363 | (set && set.properties.interactive) || // tileset interactive
364 | this.properties.interactive || // layer interactive
365 | this.map.properties.interactive; // map interactive
366 | };
367 |
368 | /**
369 | * Despawns all the sprites associated with this layer
370 | *
371 | * @method despawn
372 | * @param destroy {Boolean} Should we destroy the children as well?
373 | * @return {Objectlayer} Returns itself.
374 | * @chainable
375 | */
376 | Objectlayer.prototype.despawn = function (destroy) {
377 | return Phaser.Group.prototype.removeAll.call(this, destroy);
378 | };
379 |
380 | /**
381 | * Destroys the group completely
382 | *
383 | * @method destroy
384 | */
385 | Objectlayer.prototype.destroy = function () {
386 | Phaser.Group.prototype.destroy.apply(this, arguments);
387 |
388 | // destroy bodies
389 | for (var i = 0; i < this.bodies.length; ++i) {
390 | this.bodies[i].destroy();
391 | }
392 |
393 | this.bodies = null;
394 |
395 | this.map = null;
396 | this.properties = null;
397 | this.objects = null;
398 | };
399 |
--------------------------------------------------------------------------------
/testmaps/js/vendor/require-2.1.15.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
3 | Available via the MIT or new BSD license.
4 | see: http://github.com/jrburke/requirejs for details
5 | */
6 | var requirejs,require,define;
7 | (function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
19 | (f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
20 | this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
21 | if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
22 | "fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
23 | a);this.check()}));this.errback&&q(a,"error",u(this,this.errback))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,
24 | nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,
25 | a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=
26 | !0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==
27 | e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&&!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):
34 | (e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=
35 | O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b}),e=N;e&&(b||
36 | (b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);
37 |
--------------------------------------------------------------------------------
/src/tiled/TilemapParser.js:
--------------------------------------------------------------------------------
1 | /* jshint maxlen:200 */
2 | var utils = require('../utils');
3 | var C = require('../constants');
4 |
5 | var TilemapParser = {
6 | /**
7 | * Parse tilemap data from the cache and creates a Tilemap object.
8 | *
9 | * @method parse
10 | * @param {Phaser.Game} game - Game reference to the currently running game.
11 | * @param {string} key - The key of the tilemap in the Cache.
12 | * @param {number} [tileWidth=32] - The pixel width of a single map tile. If using CSV data you must
13 | * specify this. Not required if using Tiled map data.
14 | * @param {number} [tileHeight=32] - The pixel height of a single map tile. If using CSV data you must
15 | * specify this. Not required if using Tiled map data.
16 | * @param {number} [width=10] - The width of the map in tiles. If this map is created from Tiled or
17 | * CSV data you don't need to specify this.
18 | * @param {number} [height=10] - The height of the map in tiles. If this map is created from Tiled or
19 | * CSV data you don't need to specify this.
20 | * @return {object} The parsed map object.
21 | */
22 | parse: function (game, key, tileWidth, tileHeight, width, height) {
23 | if (typeof tileWidth === 'undefined') { tileWidth = 32; }
24 | if (typeof tileHeight === 'undefined') { tileHeight = 32; }
25 | if (typeof width === 'undefined') { width = 10; }
26 | if (typeof height === 'undefined') { height = 10; }
27 |
28 | if (typeof key === 'undefined') {
29 | return this.getEmptyData();
30 | }
31 |
32 | if (!key) {
33 | return this.getEmptyData(tileWidth, tileHeight, width, height);
34 | }
35 |
36 | var map = game.cache.getTilemapData(utils.cacheKey(key, 'tiledmap'));
37 |
38 | if (map) {
39 | if (map.format === C.CSV) {
40 | return this.parseCSV(key, map.data, tileWidth, tileHeight);
41 | }
42 | else if (map.format === C.TILED_XML) {
43 | return this.parseTiledXML(map.data);
44 | }
45 | else if (!map.format || map.format === C.TILED_JSON) {
46 | return this.parseTiledJSON(map.data);
47 | }
48 | }
49 | else {
50 | console.warn('Phaser.TilemapParser.parse - No map data found for key ' + key);
51 | }
52 | },
53 |
54 | parseCSV: Phaser.TilemapParser.parseCSV,
55 |
56 | getEmptyData: function () {
57 | var map = Phaser.TilemapParser.getEmptyData.apply(this, arguments);
58 |
59 | map.tilewidth = map.tileWidth;
60 | map.tileheight = map.tileHeight;
61 |
62 | return map;
63 | },
64 |
65 | /**
66 | * Parses a Tiled JSON file into valid map data.
67 | *
68 | * @method parseTiledJSON
69 | * @param {object} data - The JSON map data.
70 | * @return {object} Generated and parsed map data.
71 | */
72 | parseTiledJSON: function (data) {
73 | if (data.orientation !== 'orthogonal') {
74 | console.warn('TilemapParser.parseTiledJSON: Only orthogonal map types are supported in this version of Phaser');
75 | return null;
76 | }
77 |
78 | data.format = Phaser.TILED_JSON;
79 |
80 | var layers = data.layers;
81 |
82 | // decode any encoded/compressed layers
83 | if (Array.isArray(layers)) {
84 | for (var i = 0; i < layers.length; i++) {
85 | var layer = layers[i];
86 |
87 | if (layer && layer.encoding === 'base64') {
88 | var decomp = utils.decompressBase64Data(layer.data, layer.encoding, layer.compression);
89 |
90 | layer.data = new Uint32Array(decomp.buffer, 0, decomp.length / 4);
91 |
92 | // remove metadata as layer is no longer encoded or compressed
93 | delete layer.encoding;
94 | delete layer.compression;
95 | }
96 | }
97 | }
98 |
99 | return data;
100 | },
101 |
102 | /**
103 | * Parses a Tiled JSON file into valid map data.
104 | *
105 | * @method parseTiledXML
106 | * @param {object} data - The JSON map data.
107 | * @return {object} Generated and parsed map data.
108 | */
109 | parseTiledXML: function (data) {
110 | var mapElement = data.getElementsByTagName('map')[0];
111 | var map = {
112 | version: parseFloat(mapElement.attributes.getNamedItem('version').value, 10),
113 | width: parseInt(mapElement.attributes.getNamedItem('width').value, 10),
114 | height: parseInt(mapElement.attributes.getNamedItem('height').value, 10),
115 | tilewidth: parseInt(mapElement.attributes.getNamedItem('tilewidth').value, 10),
116 | tileheight: parseInt(mapElement.attributes.getNamedItem('tileheight').value, 10),
117 | orientation: mapElement.attributes.getNamedItem('orientation').value,
118 | renderorder: mapElement.attributes.getNamedItem('renderorder').value,
119 | format: Phaser.Tilemap.TILED_XML,
120 | properties: {},
121 | layers: [],
122 | tilesets: []
123 | };
124 | var i = 0;
125 | var il = 0;
126 |
127 | // add the properties
128 | var mapprops = mapElement.getElementsByTagName('properties');
129 | for (i = 0, il = mapprops.length; i < il; ++i) {
130 | if (mapprops[i].parentNode === mapElement) {
131 | mapprops = mapprops[i].getElementsByTagName('property');
132 |
133 | for (var mp = 0; mp < mapprops.length; ++mp) {
134 | var mappropName = mapprops[mp].attributes.getNamedItem('name').value;
135 | map.properties[mappropName] = mapprops[mp].attributes.getNamedItem('value').value;
136 | }
137 |
138 | break;
139 | }
140 | }
141 |
142 | // add the layers
143 | var layers = mapElement.childNodes; // getElementsByTagName('layer');
144 |
145 | for (i = 0, il = layers.length; i < il; ++i) {
146 | var node = layers[i];
147 |
148 | if (node.nodeName === 'layer') {
149 | var lyr = node;
150 | var layer = {
151 | type: 'tilelayer',
152 | name: lyr.attributes.getNamedItem('name').value,
153 | width: parseInt(lyr.attributes.getNamedItem('width').value, 10) || map.width,
154 | height: parseInt(lyr.attributes.getNamedItem('height').value, 10) || map.height,
155 | visible: lyr.attributes.getNamedItem('visible') ? lyr.attributes.getNamedItem('visible').value === '1' : true,
156 | opacity: lyr.attributes.getNamedItem('opacity') ? parseFloat(lyr.attributes.getNamedItem('opacity').value, 10) : 1,
157 | encoding: 'base64',
158 | compression: '',
159 | rawData: '',
160 | data: '',
161 | x: 0,
162 | y: 0
163 | };
164 |
165 | // set encoding
166 | var dataElement = lyr.getElementsByTagName('data')[0];
167 | layer.encoding = dataElement.attributes.getNamedItem('encoding').value;
168 |
169 | // set data from the text node of the element
170 | layer.rawData = dataElement.firstChild.nodeValue.trim();
171 |
172 | // set compression
173 | if (dataElement.attributes.getNamedItem('compression')) {
174 | layer.compression = dataElement.attributes.getNamedItem('compression').value;
175 | }
176 |
177 | if (layer.encoding === 'base64') {
178 | var decomp = utils.decompressBase64Data(layer.rawData, layer.encoding, layer.compression);
179 |
180 | layer.data = new Uint32Array(decomp.buffer, 0, decomp.length / 4);
181 | }
182 | else if (layer.encoding === 'csv') {
183 | layer.data = JSON.parse('[' + layer.rawData + ']');
184 | }
185 |
186 | map.layers.push(layer);
187 | }
188 | else if (node.nodeName === 'objectgroup') {
189 | var grp = node;
190 | var group = {
191 | type: 'objectgroup',
192 | draworder: 'topdown', // TODO: support custom draworders
193 | name: grp.attributes.getNamedItem('name').value,
194 | width: 0,
195 | height: 0,
196 | objects: [],
197 | visible: grp.attributes.getNamedItem('visible') ? grp.attributes.getNamedItem('visible').value === '0' : true,
198 | opacity: grp.attributes.getNamedItem('opacity') ? parseFloat(grp.attributes.getNamedItem('opacity').value, 10) : 1,
199 | x: 0,
200 | y: 0
201 | };
202 |
203 | var objects = grp.getElementsByTagName('object');
204 | for (var oj = 0; oj < objects.length; ++oj) {
205 | var obj = objects[oj];
206 | var object = {
207 | /* jscs:disable maximumLineLength */
208 | gid: obj.attributes.getNamedItem('gid') ? parseInt(obj.attributes.getNamedItem('gid').value, 10) : null,
209 | name: obj.attributes.getNamedItem('name') ? obj.attributes.getNamedItem('name').value : '',
210 | type: obj.attributes.getNamedItem('type') ? obj.attributes.getNamedItem('type').value : '',
211 | width: obj.attributes.getNamedItem('width') ? parseFloat(obj.attributes.getNamedItem('width').value, 10) : 0,
212 | height: obj.attributes.getNamedItem('height') ? parseFloat(obj.attributes.getNamedItem('height').value, 10) : 0,
213 | rotation: obj.attributes.getNamedItem('rotation') ? parseFloat(obj.attributes.getNamedItem('rotation').value, 10) : 0,
214 | visible: obj.attributes.getNamedItem('visible') ? obj.attributes.getNamedItem('visible').value === '1' : true,
215 | x: parseFloat(obj.attributes.getNamedItem('x').value, 10),
216 | y: parseFloat(obj.attributes.getNamedItem('y').value, 10),
217 | properties: {}
218 | /* jscs:enable maximumLineLength */
219 | };
220 | var poly;
221 |
222 | if (object.gid === null) {
223 | delete object.gid;
224 | }
225 |
226 | poly = obj.getElementsByTagName('polygon');
227 | if (poly.length) {
228 | object.polygon = poly[0].attributes.getNamedItem('points').value.split(' ').map(csvToXY);
229 | }
230 |
231 | poly = obj.getElementsByTagName('polyline');
232 | if (poly.length) {
233 | object.polyline = poly[0].attributes.getNamedItem('points').value.split(' ').map(csvToXY);
234 | }
235 |
236 | poly = obj.getElementsByTagName('ellipse');
237 | if (poly.length) {
238 | object.ellipse = true;
239 | }
240 |
241 | var props = obj.getElementsByTagName('properties');
242 | if (props.length) {
243 | props = props[0].getElementsByTagName('property');
244 | for (var pr = 0; pr < props.length; ++pr) {
245 | var propName = props[pr].attributes.getNamedItem('name').value;
246 | object.properties[propName] = props[pr].attributes.getNamedItem('value').value;
247 | }
248 | }
249 |
250 | group.objects.push(object);
251 | }
252 |
253 | map.layers.push(group);
254 | }
255 | else if (node.nodeName === 'imagelayer') {
256 | var ilyr = node;
257 | var imglayer = {
258 | type: 'imagelayer',
259 | image: ilyr.getElementsByTagName('image')[0].attributes.getNamedItem('source').value,
260 | name: ilyr.attributes.getNamedItem('name').value,
261 | width: 0, // always 0 for imagelayers
262 | height: 0, // always 0 for imagelayers
263 | visible: ilyr.attributes.getNamedItem('visible') ? ilyr.attributes.getNamedItem('visible').value === '1' : true,
264 | opacity: ilyr.attributes.getNamedItem('opacity') ? parseFloat(ilyr.attributes.getNamedItem('opacity').value, 10) : 1,
265 | x: ilyr.attributes.getNamedItem('x') ? parseInt(ilyr.attributes.getNamedItem('x').value, 10) : 0,
266 | y: ilyr.attributes.getNamedItem('y') ? parseInt(ilyr.attributes.getNamedItem('y').value, 10) : 0,
267 | properties: {}
268 | };
269 |
270 | var iprops = ilyr.getElementsByTagName('properties');
271 | if (iprops.length) {
272 | iprops = iprops[0].getElementsByTagName('property');
273 | for (var ip = 0; ip < iprops.length; ++ip) {
274 | var ipropName = iprops[ip].attributes.getNamedItem('name').value;
275 | imglayer.properties[ipropName] = iprops[ip].attributes.getNamedItem('value').value;
276 | }
277 | }
278 |
279 | map.layers.push(imglayer);
280 | }
281 | }
282 |
283 | // add the tilesets
284 | var tilesets = mapElement.getElementsByTagName('tileset');
285 |
286 | for (i = 0, il = tilesets.length; i < il; ++i) {
287 | var tset = tilesets[i];
288 | var tiles = tset.getElementsByTagName('tile');
289 | var tileset = {
290 | name: tset.attributes.getNamedItem('name').value,
291 | firstgid: parseInt(tset.attributes.getNamedItem('firstgid').value, 10),
292 | tilewidth: parseInt(tset.attributes.getNamedItem('tilewidth').value, 10),
293 | tileheight: parseInt(tset.attributes.getNamedItem('tileheight').value, 10),
294 | margin: 0,
295 | spacing: 0,
296 | tileoffset: { x: 0, y: 0 },
297 | terrains: [],
298 | properties: {},
299 | tileproperties: {},
300 | tiles: {}
301 | };
302 |
303 | // add spacing / margin attributes if exist
304 | var spacing = tset.attributes.getNamedItem('spacing');
305 | if (spacing) {
306 | tileset.spacing = parseInt(spacing.value, 10);
307 | }
308 |
309 | var margin = tset.attributes.getNamedItem('margin');
310 | if (margin) {
311 | tileset.margin = parseInt(margin.value, 10);
312 | }
313 |
314 | // add .properties if element exists
315 | var tsetprops = tset.getElementsByTagName('properties');
316 | for (var tsp = 0; tsp < tsetprops.length; ++tsp) {
317 | if (tsetprops[tsp].parentNode === tset) {
318 | tsetprops = tsetprops[tsp].getElementsByTagName('property');
319 |
320 | if (tsetprops.length) {
321 | for (var p = 0; p < tsetprops.length; ++p) {
322 | var tsetprop = tsetprops[p];
323 | var tsetpropName = tsetprop.attributes.getNamedItem('name').value;
324 |
325 | tileset.properties[tsetpropName] = tsetprop.attributes.getNamedItem('value').value;
326 | }
327 | }
328 |
329 | break;
330 | }
331 | }
332 |
333 | // add .tiles if there are tile-specific properties
334 | for (var t = 0; t < tiles.length; ++t) {
335 | var tile = tiles[t];
336 | var id = tile.attributes.getNamedItem('id').value;
337 | var img = tile.getElementsByTagName('image');
338 |
339 | tileset.tiles[id] = {};
340 |
341 | // add attributes into the object
342 | for (var ta = 0; ta < tile.attributes.length; ++ta) {
343 | var tileatr = tile.attributes[ta];
344 |
345 | if (tileatr.name === 'id') {
346 | continue;
347 | }
348 |
349 | switch (tileatr.name) {
350 | case 'terrain':
351 | tileset.tiles[id].terrain = tileatr.value.sply(',');
352 | break;
353 |
354 | case 'probability':
355 | tileset.tiles[id].probability = parseFloat(tileatr.value, 10);
356 | break;
357 | }
358 | }
359 |
360 | // check if it has an image child
361 | if (img.length) {
362 | tileset.tiles[id] = tileset.tiles[id] || {};
363 | tileset.tiles[id].image = img[0].attributes.getNamedItem('source').value;
364 | }
365 |
366 | // add all the tile properties
367 | var tileprops = tile.getElementsByTagName('properties');
368 | if (tileprops.length) {
369 | tileset.tileproperties[id] = {};
370 | tileprops = tileprops[0].getElementsByTagName('property');
371 | for (var tp = 0; tp < tileprops.length; ++tp) {
372 | var tileprop = tileprops[tp];
373 | var tilepropName = tileprop.attributes.getNamedItem('name').value;
374 | tileset.tileproperties[id][tilepropName] = tileprop.attributes.getNamedItem('value').value;
375 | }
376 | }
377 |
378 | // add all the tile animations
379 | var tileanims = tile.getElementsByTagName('animation');
380 | if (tileanims.length) {
381 | tileset.tiles[id].animation = [];
382 | tileanims = tileanims[0].getElementsByTagName('frame');
383 | for (var tn = 0; tn < tileanims.length; ++tn) {
384 | var tileanim = tileanims[tn].attributes;
385 | var animObj = {};
386 |
387 | for (var tna = 0; tna < tileanim.length; ++tna) {
388 | animObj[tileanim[tna].name] = tileanim[tna].value;
389 | }
390 |
391 | tileset.tiles[id].animation.push(animObj);
392 | }
393 | }
394 | }
395 |
396 | // check for terraintypes and add those
397 | var terrains = tset.getElementsByTagName('terraintypes');
398 | if (terrains.length) {
399 | terrains = terrains[0].getElementsByTagName('terrain');
400 | for (var tr = 0; tr < terrains.length; ++tr) {
401 | tileset.terrains.push({
402 | name: terrains[tr].attributes.getNamedItem('name').value,
403 | tile: parseInt(terrains[tr].attributes.getNamedItem('tile').value, 10)
404 | });
405 | }
406 | }
407 |
408 | // check for tileoffset and add that
409 | var offset = tset.getElementsByTagName('tileoffset');
410 | if (offset.length) {
411 | tileset.tileoffset.x = parseInt(offset[0].attributes.getNamedItem('x').value, 10);
412 | tileset.tileoffset.y = parseInt(offset[0].attributes.getNamedItem('y').value, 10);
413 | }
414 |
415 | // add image, imagewidth, imageheight
416 | var image = tset.getElementsByTagName('image');
417 | if (image.length === 1 && image[0].parentNode === tset) {
418 | tileset.image = image[0].attributes.getNamedItem('source').value;
419 | tileset.imagewidth = parseInt(image[0].attributes.getNamedItem('width').value, 10);
420 | tileset.imageheight = parseInt(image[0].attributes.getNamedItem('height').value, 10);
421 | }
422 |
423 | map.tilesets.push(tileset);
424 | }
425 |
426 | return map;
427 | }
428 | };
429 |
430 | module.exports = TilemapParser;
431 |
432 | function csvToXY(pt) {
433 | var points = pt.split(',');
434 | return {
435 | x: parseInt(points[0], 10),
436 | y: parseInt(points[1], 10)
437 | };
438 | }
439 |
--------------------------------------------------------------------------------