From 1a4271921314b40ffd107565dd8ec6ee86b89175 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sun, 9 Jan 2022 19:31:27 -0600 Subject: [PATCH 01/16] feat: [#283] Implements Tiled Data Component --- src/tiled-data-component.ts | 9 +++++++++ src/tiled-map-resource.ts | 12 ++++++++---- src/tiled-types.ts | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 src/tiled-data-component.ts diff --git a/src/tiled-data-component.ts b/src/tiled-data-component.ts new file mode 100644 index 00000000..91094271 --- /dev/null +++ b/src/tiled-data-component.ts @@ -0,0 +1,9 @@ +import { Component } from "excalibur"; +import { TiledObject } from "./tiled-object"; + +export class TiledDataComponent extends Component<'ex.tiled'> { + public readonly type = "ex.tiled"; + constructor(public tiledObject: TiledObject) { + super(); + } +} \ No newline at end of file diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index df753aca..6d4596fb 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -26,6 +26,7 @@ import { TiledMap } from './tiled-map'; import { parseExternalTsx } from './tiled-tileset'; import { getCanonicalGid, isFlippedDiagonally, isFlippedHorizontally, isFlippedVertically } from './tiled-layer'; import { getProperty } from './tiled-entity'; +import { TiledDataComponent } from './tiled-data-component'; export enum TiledMapFormat { @@ -117,9 +118,8 @@ export class TiledMapResource implements Loadable { if (collider.type === 'circle') { actor.collider.useCircleCollider(collider.radius); } - + actor.addComponent(new TiledDataComponent(collider.tiled)); scene.add(actor); - if (collider.zIndex) { actor.z = collider.zIndex; } @@ -147,6 +147,7 @@ export class TiledMapResource implements Loadable { label.rotation = text.rotation, label.color = Color.fromHex(text.text?.color ?? '#000000'), label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); + label.addComponent(new TiledDataComponent(text)); scene.add(label); } } @@ -173,6 +174,7 @@ export class TiledMapResource implements Loadable { rotation: tile.rotation, collisionType }); + actor.addComponent(new TiledDataComponent(tile)); if (Flags.isEnabled('use-legacy-drawing')) { actor.addDrawing(Sprite.toLegacySprite(sprite)); } else { @@ -249,7 +251,8 @@ export class TiledMapResource implements Loadable { color, zIndex: +(zIndex?.value ?? 0), radius: 0, - type: 'box' + type: 'box', + tiled: box }); } @@ -267,7 +270,8 @@ export class TiledMapResource implements Loadable { zIndex: +(zIndex?.value ?? 0), width: circle.width ?? 0, height: circle.height ?? 0, - type: 'circle' + type: 'circle', + tiled: circle }) } } diff --git a/src/tiled-types.ts b/src/tiled-types.ts index ec3717d9..ad609777 100644 --- a/src/tiled-types.ts +++ b/src/tiled-types.ts @@ -1,6 +1,7 @@ import { CollisionType } from 'excalibur'; +import { TiledObject } from '.'; /** * Tiled Map Interface @@ -103,6 +104,7 @@ export interface ExcaliburCollider { width: number; height: number; radius: number; + tiled: TiledObject; } export interface TiledProperty { /** From 84d3458711a4d4ca522111e42e6ff15ff79af283 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Tue, 11 Jan 2022 20:17:09 -0600 Subject: [PATCH 02/16] Add tests + Add TiledLayerComponent + Expose raw tiled data on parsed objects --- karma.conf.js | 6 ++ src/index.ts | 4 +- src/tiled-data-component.ts | 9 --- src/tiled-layer-component.ts | 9 +++ src/tiled-layer.ts | 6 ++ src/tiled-map-resource.ts | 26 +++++---- src/tiled-map.ts | 6 ++ src/tiled-object-component.ts | 9 +++ src/tiled-object.ts | 5 ++ .../kenny-rpg-urban-pack/Instructions.url | 6 ++ .../assets/kenny-rpg-urban-pack/Kenney.url | 2 + .../Kenny RPG Urban Pack.tsx | 4 ++ .../assets/kenny-rpg-urban-pack/License.txt | 23 ++++++++ .../assets/kenny-rpg-urban-pack/Patreon.url | 2 + .../kenny-rpg-urban-pack/tilemap_packed.png | Bin 0 -> 17300 bytes test/unit/objects.tmx | 11 +++- test/unit/tiled-layer-component.spec.ts | 25 ++++++++ test/unit/tiled-map-resource.spec.ts | 54 +++++++++++++++++- test/unit/tiled-map.spec.ts | 2 +- test/unit/tiled-object-component.spec.ts | 25 ++++++++ 20 files changed, 210 insertions(+), 24 deletions(-) delete mode 100644 src/tiled-data-component.ts create mode 100644 src/tiled-layer-component.ts create mode 100644 src/tiled-object-component.ts create mode 100644 test/unit/assets/kenny-rpg-urban-pack/Instructions.url create mode 100644 test/unit/assets/kenny-rpg-urban-pack/Kenney.url create mode 100644 test/unit/assets/kenny-rpg-urban-pack/Kenny RPG Urban Pack.tsx create mode 100644 test/unit/assets/kenny-rpg-urban-pack/License.txt create mode 100644 test/unit/assets/kenny-rpg-urban-pack/Patreon.url create mode 100644 test/unit/assets/kenny-rpg-urban-pack/tilemap_packed.png create mode 100644 test/unit/tiled-layer-component.spec.ts create mode 100644 test/unit/tiled-object-component.spec.ts diff --git a/karma.conf.js b/karma.conf.js index 8f55c6b1..73be873a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -16,6 +16,12 @@ module.exports = function(config) { // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['jasmine'], + proxies: { + // smooths over loading files because karma prepends '/base/' to everything + '/src/' : '/base/src/', + '/test/' : '/base/test/', + }, + // list of files / patterns to load in the browser files: [ diff --git a/src/index.ts b/src/index.ts index d3a0c906..316d4e89 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,4 +5,6 @@ export * from './tiled-map'; export * from './tiled-entity'; export * from './tiled-layer'; export * from './tiled-object'; -export * from './tiled-tileset'; \ No newline at end of file +export * from './tiled-tileset'; +export * from './tiled-object-component'; +export * from './tiled-layer-component'; \ No newline at end of file diff --git a/src/tiled-data-component.ts b/src/tiled-data-component.ts deleted file mode 100644 index 91094271..00000000 --- a/src/tiled-data-component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Component } from "excalibur"; -import { TiledObject } from "./tiled-object"; - -export class TiledDataComponent extends Component<'ex.tiled'> { - public readonly type = "ex.tiled"; - constructor(public tiledObject: TiledObject) { - super(); - } -} \ No newline at end of file diff --git a/src/tiled-layer-component.ts b/src/tiled-layer-component.ts new file mode 100644 index 00000000..b8b70fe9 --- /dev/null +++ b/src/tiled-layer-component.ts @@ -0,0 +1,9 @@ +import { Component } from "excalibur"; +import { TiledLayer } from "./tiled-layer"; + +export class TiledLayerComponent extends Component<'ex.tiledlayer'> { + public readonly type = "ex.tiledlayer"; + constructor(public layer: TiledLayer) { + super(); + } +} \ No newline at end of file diff --git a/src/tiled-layer.ts b/src/tiled-layer.ts index 4f518126..f174c2dd 100644 --- a/src/tiled-layer.ts +++ b/src/tiled-layer.ts @@ -1,5 +1,6 @@ import { TiledCompression, TiledEncoding, TiledProperty } from "./tiled-types"; import { TiledEntity } from "./tiled-entity"; +import { RawTiledLayer } from "."; // Most significant byte of 32 bit id contains flags for flipping // See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tile-flipping @@ -76,4 +77,9 @@ export class TiledLayer extends TiledEntity { * Original compression of the Tiled layer if any */ public compression?: TiledCompression; + + /** + * Reference to the raw tiled layer data + */ + public rawLayer!: RawTiledLayer; } \ No newline at end of file diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 6d4596fb..88426d37 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -26,7 +26,8 @@ import { TiledMap } from './tiled-map'; import { parseExternalTsx } from './tiled-tileset'; import { getCanonicalGid, isFlippedDiagonally, isFlippedHorizontally, isFlippedVertically } from './tiled-layer'; import { getProperty } from './tiled-entity'; -import { TiledDataComponent } from './tiled-data-component'; +import { TiledObjectComponent } from './tiled-object-component'; +import { TiledLayerComponent } from './tiled-layer-component'; export enum TiledMapFormat { @@ -118,7 +119,7 @@ export class TiledMapResource implements Loadable { if (collider.type === 'circle') { actor.collider.useCircleCollider(collider.radius); } - actor.addComponent(new TiledDataComponent(collider.tiled)); + actor.addComponent(new TiledObjectComponent(collider.tiled)); scene.add(actor); if (collider.zIndex) { actor.z = collider.zIndex; @@ -147,7 +148,7 @@ export class TiledMapResource implements Loadable { label.rotation = text.rotation, label.color = Color.fromHex(text.text?.color ?? '#000000'), label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); - label.addComponent(new TiledDataComponent(text)); + label.addComponent(new TiledObjectComponent(text)); scene.add(label); } } @@ -174,7 +175,7 @@ export class TiledMapResource implements Loadable { rotation: tile.rotation, collisionType }); - actor.addComponent(new TiledDataComponent(tile)); + actor.addComponent(new TiledObjectComponent(tile)); if (Flags.isEnabled('use-legacy-drawing')) { actor.addDrawing(Sprite.toLegacySprite(sprite)); } else { @@ -433,20 +434,23 @@ export class TiledMapResource implements Loadable { } // Create Excalibur sprites for each cell - for (var rawLayer of this.data.rawMap.layers) { - if (rawLayer.type === "tilelayer") { - const layer = new TileMap(0, 0, this.data.rawMap.tilewidth, this.data.rawMap.tileheight, this.data.height, this.data.width); + for (var layer of this.data.layers) { + if (layer.rawLayer.type === "tilelayer") { + const rawLayer = layer.rawLayer; + const tileMapLayer = new TileMap(0, 0, this.data.rawMap.tilewidth, this.data.rawMap.tileheight, this.data.height, this.data.width); + tileMapLayer.addComponent(new TiledLayerComponent(layer)); + const zindex = getProperty(rawLayer.properties, 'zindex')?.value || layerZIndexBase++; - layer.z = zindex; + tileMapLayer.z = zindex; for (var i = 0; i < rawLayer.data.length; i++) { let gid = rawLayer.data[i]; if (gid !== 0) { const sprite = this.getSpriteForGid(gid) - layer.data[i].addGraphic(sprite); + tileMapLayer.data[i].addGraphic(sprite); } } - this._mapToRawLayer.set(layer, rawLayer); - this.layers?.push(layer); + this._mapToRawLayer.set(tileMapLayer, rawLayer); + this.layers?.push(tileMapLayer); } } } diff --git a/src/tiled-map.ts b/src/tiled-map.ts index 18a61bc1..5835799a 100644 --- a/src/tiled-map.ts +++ b/src/tiled-map.ts @@ -11,6 +11,9 @@ import { TiledObject, TiledObjectGroup } from "./tiled-object"; import { TiledTileset } from './tiled-tileset'; import { Util } from 'excalibur'; +/** + * Responsible for representing the Tiled TileMap in total and parsing from the source Tiled files (tmx) + */ export class TiledMap { /** * Raw tilemap data @@ -205,6 +208,7 @@ export class TiledMap { resultLayer.encoding = layer.encoding ?? 'csv'; resultLayer.compression = layer.compression; resultLayer.properties = layer.properties ?? []; + resultLayer.rawLayer = layer; resultMap.layers.push(resultLayer); } @@ -214,6 +218,7 @@ export class TiledMap { resultObjectGroup.id = +objectlayer.id; resultObjectGroup.name = objectlayer.name; resultObjectGroup.properties = objectlayer.properties ?? []; + resultObjectGroup.rawObjectGroup = objectlayer; for (let object of objectlayer.objects) { const resultObject = new TiledObject(); resultObject.id = +object.id; @@ -230,6 +235,7 @@ export class TiledMap { resultObject.ellipse = object.ellipse; resultObject.polyline = object.polyline; resultObject.polygon = object.polygon; + resultObject.rawObject = object; if (object.text) { resultObject.text = { ...object.text, diff --git a/src/tiled-object-component.ts b/src/tiled-object-component.ts new file mode 100644 index 00000000..8b3518e9 --- /dev/null +++ b/src/tiled-object-component.ts @@ -0,0 +1,9 @@ +import { Component } from "excalibur"; +import { TiledObject } from "./tiled-object"; + +export class TiledObjectComponent extends Component<'ex.tiledobject'> { + public readonly type = "ex.tiledobject"; + constructor(public object: TiledObject) { + super(); + } +} \ No newline at end of file diff --git a/src/tiled-object.ts b/src/tiled-object.ts index 8d1b9a48..3765c717 100644 --- a/src/tiled-object.ts +++ b/src/tiled-object.ts @@ -1,9 +1,12 @@ import { ExcaliburCamera, TiledPoint } from "./tiled-types"; import { TiledEntity } from "./tiled-entity"; +import { RawTiledLayer, RawTiledObject } from "."; export class TiledObjectGroup extends TiledEntity { public objects: TiledObject[] = []; + public rawObjectGroup!: RawTiledLayer; + public getCamera(): ExcaliburCamera | undefined { const camera = this.getObjectByType('camera'); if (camera) { @@ -92,6 +95,8 @@ export class TiledObject extends TiledEntity { * Present on inserted tile objects */ public gid?: number; + + public rawObject!: RawTiledObject; } export interface TiledText { diff --git a/test/unit/assets/kenny-rpg-urban-pack/Instructions.url b/test/unit/assets/kenny-rpg-urban-pack/Instructions.url new file mode 100644 index 00000000..75c728fe --- /dev/null +++ b/test/unit/assets/kenny-rpg-urban-pack/Instructions.url @@ -0,0 +1,6 @@ +[InternetShortcut] +URL=https://kenney.nl/documentation/game-assets/tilemaps +IDList= +HotKey=0 +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 diff --git a/test/unit/assets/kenny-rpg-urban-pack/Kenney.url b/test/unit/assets/kenny-rpg-urban-pack/Kenney.url new file mode 100644 index 00000000..fbdde435 --- /dev/null +++ b/test/unit/assets/kenny-rpg-urban-pack/Kenney.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=http://www.kenney.nl/ \ No newline at end of file diff --git a/test/unit/assets/kenny-rpg-urban-pack/Kenny RPG Urban Pack.tsx b/test/unit/assets/kenny-rpg-urban-pack/Kenny RPG Urban Pack.tsx new file mode 100644 index 00000000..53440680 --- /dev/null +++ b/test/unit/assets/kenny-rpg-urban-pack/Kenny RPG Urban Pack.tsx @@ -0,0 +1,4 @@ + + + + diff --git a/test/unit/assets/kenny-rpg-urban-pack/License.txt b/test/unit/assets/kenny-rpg-urban-pack/License.txt new file mode 100644 index 00000000..4116e893 --- /dev/null +++ b/test/unit/assets/kenny-rpg-urban-pack/License.txt @@ -0,0 +1,23 @@ + + + RPG Urban Pack 1.0 + + Created/distributed by Kenney (www.kenney.nl) + Creation date: 05-01-2019 + + ------------------------------ + + License: (Creative Commons Zero, CC0) + http://creativecommons.org/publicdomain/zero/1.0/ + + This content is free to use in personal, educational and commercial projects. + Support us by crediting Kenney or www.kenney.nl (this is not mandatory) + + ------------------------------ + + Donate: http://support.kenney.nl + Request: http://request.kenney.nl + Patreon: http://patreon.com/kenney/ + + Follow on Twitter for updates: + http://twitter.com/KenneyNL \ No newline at end of file diff --git a/test/unit/assets/kenny-rpg-urban-pack/Patreon.url b/test/unit/assets/kenny-rpg-urban-pack/Patreon.url new file mode 100644 index 00000000..439ef262 --- /dev/null +++ b/test/unit/assets/kenny-rpg-urban-pack/Patreon.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://www.patreon.com/kenney/ \ No newline at end of file diff --git a/test/unit/assets/kenny-rpg-urban-pack/tilemap_packed.png b/test/unit/assets/kenny-rpg-urban-pack/tilemap_packed.png new file mode 100644 index 0000000000000000000000000000000000000000..66bf1156a23e2436473f96cf2240c301e4309faf GIT binary patch literal 17300 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∓V7`Zm(8 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

urW0yQ{r12)tk5X@S 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 M07*qoM6N<$f_km2z5oCK literal 0 HcmV?d00001 diff --git a/test/unit/objects.tmx b/test/unit/objects.tmx index 12526eb9..90d7d412 100644 --- a/test/unit/objects.tmx +++ b/test/unit/objects.tmx @@ -3,7 +3,8 @@ - + + 0,0,0,0,0, @@ -14,6 +15,9 @@ + + + @@ -31,5 +35,10 @@ + + + + + diff --git a/test/unit/tiled-layer-component.spec.ts b/test/unit/tiled-layer-component.spec.ts new file mode 100644 index 00000000..bc343693 --- /dev/null +++ b/test/unit/tiled-layer-component.spec.ts @@ -0,0 +1,25 @@ +import { TiledObjectComponent, TiledObject, TiledLayerComponent, TiledLayer } from '@excalibur-tiled'; +import { Actor, TileMap } from 'excalibur'; + +describe('A TiledLayerComponent', () => { + it('should exist', () => { + expect(TiledLayerComponent).toBeDefined(); + }); + + it('can be created', () => { + expect(() => { + const sut = new TiledLayerComponent(new TiledLayer()); + }).not.toThrow(); + }); + + it('can be retrieved from a TileMap', () => { + const tm = new TileMap({x: 0, y: 0, cellHeight: 10, cellWidth: 10, cols: 10, rows: 10}); + const sut = new TiledLayerComponent(new TiledLayer()); + + expect(tm.get(TiledLayerComponent)).toBeUndefined(); + + tm.addComponent(sut); + + expect(tm.get(TiledLayerComponent)).toBe(sut); + }); +}); \ No newline at end of file diff --git a/test/unit/tiled-map-resource.spec.ts b/test/unit/tiled-map-resource.spec.ts index 1d9eb0e0..ad6e6c70 100644 --- a/test/unit/tiled-map-resource.spec.ts +++ b/test/unit/tiled-map-resource.spec.ts @@ -1,4 +1,5 @@ -import { TiledMapResource } from "@excalibur-tiled"; +import { TiledObjectComponent, TiledMapResource, TiledLayerComponent } from "@excalibur-tiled"; +import { Scene } from "excalibur"; describe('A Tiled Map Excalibur Resource', () => { @@ -78,4 +79,55 @@ describe('A Tiled Map Excalibur Resource', () => { expect(tiled.ex.camera?.y).toBe(50); expect(tiled.ex.camera?.zoom).toBe(4); }); + + it('will parse inserted tile entities and include their TiledObject in the TileObjectComponent', async () => { + const tiled = new TiledMapResource('test/unit/objects.tmx') + await tiled.load(); + expect(tiled.isLoaded()); + + const scene = new Scene(); + tiled.addTiledMapToScene(scene); + + const objects = scene.actors.filter(a => a.get(TiledObjectComponent)); + + const insertedTile = objects.find(a => a.get(TiledObjectComponent)?.object.id === 8); + + const component = insertedTile?.get(TiledObjectComponent); + + // + // + // + // + // + expect(component?.object).not.toBeNull(); + expect(component?.object).not.toBeUndefined(); + expect(component?.object.gid).toBe(1073741992) + expect(component?.object.visible).toBe(true) + expect(component?.object.x).toBe(42); + expect(component?.object.y).toBe(42); + expect(component?.object.width).toBe(16); + expect(component?.object.height).toBe(16); + expect(component?.object.getProperty("zindex")?.value).toBe(3); + expect(component?.object.rawObject).toBeDefined(); + }); + + it('will parse inserted tile entities and include their TiledObject in the TileLayerComponent', async () => { + const tiled = new TiledMapResource('test/unit/objects.tmx') + await tiled.load(); + expect(tiled.isLoaded()); + + const scene = new Scene(); + tiled.addTiledMapToScene(scene); + + const tm = scene.tileMaps[0]; + + const component = tm?.get(TiledLayerComponent); + + expect(component?.layer.id).toBe(1); + expect(component?.layer.name).toBe("Tile Layer 1"); + expect(component?.layer.width).toBe(5); + expect(component?.layer.height).toBe(5); + expect(component?.layer.encoding).toBe('csv'); + expect(component?.layer.rawLayer).toBeDefined(); + }); }); \ No newline at end of file diff --git a/test/unit/tiled-map.spec.ts b/test/unit/tiled-map.spec.ts index 79a9e9e7..76936454 100644 --- a/test/unit/tiled-map.spec.ts +++ b/test/unit/tiled-map.spec.ts @@ -111,7 +111,7 @@ describe('A Tiled Map', () => { expect(tiledMap.objectGroups[0].getPolyLines().length).toBe(1); expect(tiledMap.objectGroups[0].getPolygons().length).toBe(1); expect(tiledMap.objectGroups[0].getText().length).toBe(1); - expect(tiledMap.objectGroups[0].getInsertedTiles().length).toBe(1); + expect(tiledMap.objectGroups[0].getInsertedTiles().length).toBe(2); expect(tiledMap.objectGroups[0].getCamera()).toEqual({x: 16, y: 16, zoom: 1}); }); diff --git a/test/unit/tiled-object-component.spec.ts b/test/unit/tiled-object-component.spec.ts new file mode 100644 index 00000000..d000c70a --- /dev/null +++ b/test/unit/tiled-object-component.spec.ts @@ -0,0 +1,25 @@ +import { TiledObjectComponent, TiledObject } from '@excalibur-tiled'; +import { Actor } from 'excalibur'; + +describe('A TiledObjectComponent', () => { + it('should exist', () => { + expect(TiledObjectComponent).toBeDefined(); + }); + + it('can be created', () => { + expect(() => { + const sut = new TiledObjectComponent(new TiledObject()); + }).not.toThrow(); + }); + + it('can be retrieved from an actor', () => { + const actor = new Actor({x: 0, y: 0}); + const sut = new TiledObjectComponent(new TiledObject()); + + expect(actor.get(TiledObjectComponent)).toBeUndefined(); + + actor.addComponent(sut); + + expect(actor.get(TiledObjectComponent)).toBe(sut); + }); +}); \ No newline at end of file From 36a9f8a28308890f7d919038e6a937b31f14d5f0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 11 Jan 2022 00:09:52 +0000 Subject: [PATCH 03/16] chore: Update Node.js to v16.13.2 --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 6e9d5a1e..d7cb9ec3 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.13.1 \ No newline at end of file +16.13.2 \ No newline at end of file From ff90fff83e0970157f531cbdadff8dae1bb6874e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 11 Jan 2022 18:03:58 +0000 Subject: [PATCH 04/16] chore: Update dependency webpack-dev-server to v4.7.3 --- package-lock.json | 196 ++++++++++------------------------------------ package.json | 2 +- 2 files changed, 41 insertions(+), 157 deletions(-) diff --git a/package-lock.json b/package-lock.json index dcba150f..7ea81211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "webpack": "5.65.0", "webpack-bundle-analyzer": "4.5.0", "webpack-cli": "4.9.1", - "webpack-dev-server": "4.7.2" + "webpack-dev-server": "4.7.3" }, "peerDependencies": { "excalibur": "0.25.x" @@ -374,9 +374,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "node_modules/@types/mime": { @@ -1699,12 +1699,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del/node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1901,12 +1895,6 @@ "node": ">=10.13.0" } }, - "node_modules/enhanced-resolve/node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "node_modules/ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", @@ -2436,12 +2424,6 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/fs-extra/node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, "node_modules/fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -2565,13 +2547,10 @@ } }, "node_modules/graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true }, "node_modules/gzip-size": { "version": "6.0.0", @@ -3238,12 +3217,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/karma/node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, "node_modules/karma/node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -3547,12 +3520,12 @@ } }, "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.0.tgz", + "integrity": "sha512-M4AsdaP0bGNaSPtatd/+f76asocI0cFaURRdeQVZvrJBrYp2Qohv5hDbGHykuNqCb1BYjWHjdS6HlN50qbztwA==", "dev": true, "engines": { - "node": ">= 6.0.0" + "node": ">= 6.13.0" } }, "node_modules/node-releases": { @@ -4406,12 +4379,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -4419,12 +4386,15 @@ "dev": true }, "node_modules/selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", "dev": true, "dependencies": { - "node-forge": "^0.10.0" + "node-forge": "^1.2.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver": { @@ -5520,12 +5490,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "node_modules/webpack-dev-middleware/node_modules/ajv": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", @@ -5601,9 +5565,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.2.tgz", - "integrity": "sha512-s6yEOSfPpB6g1T2+C5ZOUt5cQOMhjI98IVmmvMNb5cdiqHoxSUfACISHqU/wZy+q4ar/A9jW0pbNj7sa50XRVA==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", "dev": true, "dependencies": { "@types/bonjour": "^3.5.9", @@ -5628,7 +5592,7 @@ "p-retry": "^4.5.0", "portfinder": "^1.0.28", "schema-utils": "^4.0.0", - "selfsigned": "^1.10.11", + "selfsigned": "^2.0.0", "serve-index": "^1.9.1", "sockjs": "^0.3.21", "spdy": "^4.0.2", @@ -5651,12 +5615,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "node_modules/webpack-dev-server/node_modules/ajv": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", @@ -5685,12 +5643,6 @@ "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -5747,12 +5699,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -6213,9 +6159,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/mime": { @@ -7257,14 +7203,6 @@ "p-map": "^4.0.0", "rimraf": "^3.0.2", "slash": "^3.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - } } }, "depd": { @@ -7434,14 +7372,6 @@ "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - } } }, "ent": { @@ -7853,14 +7783,6 @@ "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - } } }, "fs-monkey": { @@ -7955,9 +7877,9 @@ } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "gzip-size": { @@ -8420,12 +8342,6 @@ "path-is-absolute": "^1.0.0" } }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -8684,9 +8600,9 @@ "dev": true }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.0.tgz", + "integrity": "sha512-M4AsdaP0bGNaSPtatd/+f76asocI0cFaURRdeQVZvrJBrYp2Qohv5hDbGHykuNqCb1BYjWHjdS6HlN50qbztwA==", "dev": true }, "node-releases": { @@ -9299,14 +9215,6 @@ "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - } } }, "select-hose": { @@ -9316,12 +9224,12 @@ "dev": true }, "selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", "dev": true, "requires": { - "node-forge": "^0.10.0" + "node-forge": "^1.2.0" } }, "semver": { @@ -10043,12 +9951,6 @@ "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true } } }, @@ -10135,12 +10037,6 @@ "schema-utils": "^4.0.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "ajv": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", @@ -10198,9 +10094,9 @@ } }, "webpack-dev-server": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.2.tgz", - "integrity": "sha512-s6yEOSfPpB6g1T2+C5ZOUt5cQOMhjI98IVmmvMNb5cdiqHoxSUfACISHqU/wZy+q4ar/A9jW0pbNj7sa50XRVA==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", @@ -10225,7 +10121,7 @@ "p-retry": "^4.5.0", "portfinder": "^1.0.28", "schema-utils": "^4.0.0", - "selfsigned": "^1.10.11", + "selfsigned": "^2.0.0", "serve-index": "^1.9.1", "sockjs": "^0.3.21", "spdy": "^4.0.2", @@ -10234,12 +10130,6 @@ "ws": "^8.1.0" }, "dependencies": { - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, "ajv": { "version": "8.8.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", @@ -10261,12 +10151,6 @@ "fast-deep-equal": "^3.1.3" } }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", diff --git a/package.json b/package.json index 9b12e341..03458d57 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "webpack": "5.65.0", "webpack-bundle-analyzer": "4.5.0", "webpack-cli": "4.9.1", - "webpack-dev-server": "4.7.2" + "webpack-dev-server": "4.7.3" }, "dependencies": { "fast-xml-parser": "3.21.1", From 4cf84b151cb709068f5f069404849c338f264282 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Tue, 11 Jan 2022 20:57:45 -0600 Subject: [PATCH 05/16] Tag original layer order --- src/tiled-map-resource.ts | 182 ++++++++++++++++++++++---------------- src/tiled-map.ts | 6 ++ src/tiled-types.ts | 5 ++ 3 files changed, 115 insertions(+), 78 deletions(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 88426d37..b59d90d1 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -42,6 +42,18 @@ export enum TiledMapFormat { JSON = 'JSON' } +export interface TiledMapOptions { + /** + * Optionally override the map format, by default files ending in .tmx are treated as TMX format, otherise treated as JSON format + */ + mapFormatOverride?: TiledMapFormat; + + /** + * Optionally Specify the first layer z-index value, each layer will increment up 1 unless overridden (default -1) + */ + firstLayerZIndex?: number; +} + export class TiledMapResource implements Loadable { private _resource: Resource; public data!: TiledMap; @@ -51,6 +63,7 @@ export class TiledMapResource implements Loadable { public imageMap: Record; public sheetMap: Record; public layers?: TileMap[] = []; + private _layerZIndexStart = -1; private _mapToRawLayer = new Map(); @@ -59,8 +72,15 @@ export class TiledMapResource implements Loadable { */ public convertPath: (originPath: string, relativePath: string) => string; - constructor(public path: string, mapFormatOverride?: TiledMapFormat, private readonly layerZIndexStart = -1) { - const detectedType = mapFormatOverride ?? (path.includes('.tmx') ? TiledMapFormat.TMX : TiledMapFormat.JSON); + /** + * + * @param path Specify a path to your Tiled map source files (usually path/to/my_map.tmx) + * @param options Optionally configure other aspecits + */ + constructor(public path: string, options?: TiledMapOptions) { + const { mapFormatOverride, firstLayerZIndex } = { ...options}; + this._layerZIndexStart = firstLayerZIndex ?? this._layerZIndexStart; + const detectedType = mapFormatOverride ?? (path.includes('.tmx') ? TiledMapFormat.TMX : TiledMapFormat.JSON); switch (detectedType) { case TiledMapFormat.TMX: this._resource = new Resource(path, 'text'); @@ -108,12 +128,12 @@ export class TiledMapResource implements Loadable { pos: vec(collider.x, collider.y), collisionType: collider.collisionType ?? CollisionType.Fixed }); - + if (collider.color) { actor.color = Color.fromHex(collider.color.value); } - - if (collider.type === 'box') { + + if (collider.type === 'box') { actor.collider.useBoxCollider(collider.width, collider.height, Vector.Zero); } if (collider.type === 'circle') { @@ -146,8 +166,8 @@ export class TiledMapResource implements Loadable { label.font.textAlign = TextAlign.Left; label.font.baseAlign = BaseAlign.Top; label.rotation = text.rotation, - label.color = Color.fromHex(text.text?.color ?? '#000000'), - label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); + label.color = Color.fromHex(text.text?.color ?? '#000000'), + label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); label.addComponent(new TiledObjectComponent(text)); scene.add(label); } @@ -155,37 +175,39 @@ export class TiledMapResource implements Loadable { } private _addTiledInsertedTiles(scene: Scene) { - const excalibur = this.data?.getExcaliburObjects(); - if (excalibur.length > 0) { - const inserted = excalibur.flatMap(o => o.getInsertedTiles()); - for (const tile of inserted) { - const collisionTypeProp = tile.getProperty('collisionType'); - let collisionType = CollisionType.PreventCollision; - if (collisionTypeProp) { - collisionType = collisionTypeProp.value; - } - if (tile.gid) { - const sprite = this.getSpriteForGid(tile.gid); - const actor = new Actor({ - x: tile.x, - y: tile.y, - width: tile.width, - height: tile.height, - anchor: vec(0, 1), - rotation: tile.rotation, - collisionType - }); - actor.addComponent(new TiledObjectComponent(tile)); - if (Flags.isEnabled('use-legacy-drawing')) { - actor.addDrawing(Sprite.toLegacySprite(sprite)); - } else { - actor.graphics.anchor = vec(0, 1); - actor.graphics.use(sprite); + const excaliburObjectLayers = this.data?.getExcaliburObjects(); + if (excaliburObjectLayers.length > 0) { + for (const objectLayer of excaliburObjectLayers) { + const inserted = objectLayer.getInsertedTiles(); + for (const tile of inserted) { + const collisionTypeProp = tile.getProperty('collisionType'); + let collisionType = CollisionType.PreventCollision; + if (collisionTypeProp) { + collisionType = collisionTypeProp.value; } - scene.add(actor); - const z = tile.getProperty('zindex'); - if (z) { - actor.z = +z.value; + if (tile.gid) { + const sprite = this.getSpriteForGid(tile.gid); + const actor = new Actor({ + x: tile.x, + y: tile.y, + width: tile.width, + height: tile.height, + anchor: vec(0, 1), + rotation: tile.rotation, + collisionType + }); + actor.addComponent(new TiledObjectComponent(tile)); + if (Flags.isEnabled('use-legacy-drawing')) { + actor.addDrawing(Sprite.toLegacySprite(sprite)); + } else { + actor.graphics.anchor = vec(0, 1); + actor.graphics.use(sprite); + } + scene.add(actor); + const z = tile.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); + if (z) { + actor.z = +z; + } } } } @@ -231,49 +253,53 @@ export class TiledMapResource implements Loadable { private _parseExcaliburInfo() { // Tiled+Excalibur smarts - const excalibur = this.data?.getExcaliburObjects(); + const excaliburObjectLayers = this.data?.getExcaliburObjects(); const ex: ExcaliburData = {}; - if (excalibur.length > 0) { + if (excaliburObjectLayers.length > 0) { // Parse cameras find the first - ex.camera = excalibur.find(objectlayer => objectlayer.getObjectByType('camera'))?.getCamera(); + ex.camera = excaliburObjectLayers.find(objectlayer => objectlayer.getObjectByType('camera'))?.getCamera(); // Parse colliders ex.colliders = []; - const boxColliders = excalibur.flatMap(o => o.getObjectsByType('boxcollider')); - for (let box of boxColliders) { - const collisionType = box.getProperty('collisiontype'); - const color = box.getProperty('color'); - const zIndex = box.getProperty('zindex'); - ex.colliders.push({ - ...box, - width: +(box.width ?? 0), - height: +(box.height ?? 0), - collisionType: collisionType?.value ?? CollisionType.Fixed, - color, - zIndex: +(zIndex?.value ?? 0), - radius: 0, - type: 'box', - tiled: box - }); - } + for (let objectLayer of excaliburObjectLayers) { + + const boxColliders = objectLayer.getObjectsByType('boxcollider'); + + for (let box of boxColliders) { + const collisionType = box.getProperty('collisiontype'); + const color = box.getProperty('color'); + const zIndex = box.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); + ex.colliders.push({ + ...box, + width: +(box.width ?? 0), + height: +(box.height ?? 0), + collisionType: collisionType?.value ?? CollisionType.Fixed, + color, + zIndex: zIndex, + radius: 0, + type: 'box', + tiled: box + }); + } - const circleColliders = excalibur.flatMap(o => o.getObjectsByType('circlecollider')); - for (let circle of circleColliders) { - var collisionType = circle.getProperty('collisiontype'); - var color = circle.getProperty('color'); - var zIndex = circle.getProperty('zindex'); - ex.colliders.push({ - x: circle.x, - y: circle.y, - radius: Math.max(circle.width ?? 0, circle.height?? 0), - collisionType: collisionType?.value ?? CollisionType.Fixed, - color, - zIndex: +(zIndex?.value ?? 0), - width: circle.width ?? 0, - height: circle.height ?? 0, - type: 'circle', - tiled: circle - }) + const circleColliders = objectLayer.getObjectsByType('circlecollider'); + for (let circle of circleColliders) { + var collisionType = circle.getProperty('collisiontype'); + var color = circle.getProperty('color'); + var zIndex = circle.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); + ex.colliders.push({ + x: circle.x, + y: circle.y, + radius: Math.max(circle.width ?? 0, circle.height ?? 0), + collisionType: collisionType?.value ?? CollisionType.Fixed, + color, + zIndex: zIndex, + width: circle.width ?? 0, + height: circle.height ?? 0, + type: 'circle', + tiled: circle + }) + } } } this.ex = ex; @@ -369,7 +395,7 @@ export class TiledMapResource implements Loadable { if (this.data) { for (var i = this.data.rawMap.tilesets.length - 1; i >= 0; i--) { var ts = this.data.rawMap.tilesets[i]; - + if (ts.firstgid <= gid) { return ts; } @@ -415,14 +441,14 @@ export class TiledMapResource implements Loadable { * Creates the Excalibur tile map representation */ private _createTileMap() { - let layerZIndexBase = this.layerZIndexStart; + let layerZIndexBase = this._layerZIndexStart; // register sprite sheets for each tileset in map for (const tileset of this.data.rawMap.tilesets) { const cols = Math.floor(tileset.imagewidth / tileset.tilewidth); const rows = Math.floor(tileset.imageheight / tileset.tileheight); const ss = SpriteSheet.fromImageSource({ - image:this.imageMap[tileset.firstgid], + image: this.imageMap[tileset.firstgid], grid: { columns: cols, rows: rows, @@ -440,7 +466,7 @@ export class TiledMapResource implements Loadable { const tileMapLayer = new TileMap(0, 0, this.data.rawMap.tilewidth, this.data.rawMap.tileheight, this.data.height, this.data.width); tileMapLayer.addComponent(new TiledLayerComponent(layer)); - const zindex = getProperty(rawLayer.properties, 'zindex')?.value || layerZIndexBase++; + const zindex = getProperty(rawLayer.properties, 'zindex')?.value ?? (layer.rawLayer.order + layerZIndexBase); tileMapLayer.z = zindex; for (var i = 0; i < rawLayer.data.length; i++) { let gid = rawLayer.data[i]; diff --git a/src/tiled-map.ts b/src/tiled-map.ts index 5835799a..b316b90f 100644 --- a/src/tiled-map.ts +++ b/src/tiled-map.ts @@ -197,6 +197,12 @@ export class TiledMap { resultMap.tileWidth = +rawMap.tilewidth; resultMap.tileHeight = +rawMap.tileheight; + // Tag the raw layers with their original order + let order = 0; + for (let layer of rawMap.layers) { + layer.order = order++; + } + for (let layer of rawMap.layers) { if (layer.type !== 'tilelayer') continue; const resultLayer = new TiledLayer(); diff --git a/src/tiled-types.ts b/src/tiled-types.ts index ad609777..1fbd12f7 100644 --- a/src/tiled-types.ts +++ b/src/tiled-types.ts @@ -184,6 +184,11 @@ export interface RawTiledLayer { * Vertical layer offset in tiles. Always 0. */ y: number; + + /** + * Layer order in the original Tiled source + */ + order: number; /** * Horizontal layer offset in pixels (default: 0) */ From bc34bd6f25df0eb4a91c7d8c8a0d6b0a05d076af Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Tue, 11 Jan 2022 21:33:12 -0600 Subject: [PATCH 06/16] Remove explicit zindex from example --- example/example-city.tmx | 28 ++++------------------------ example/game.ts | 2 +- src/tiled-map-resource.ts | 2 +- test/unit/tiled-map-resource.spec.ts | 4 +++- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/example/example-city.tmx b/example/example-city.tmx index 72b6803c..b7bb5478 100644 --- a/example/example-city.tmx +++ b/example/example-city.tmx @@ -1,12 +1,9 @@ - + - - - 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,462,463,464,36,37,37,37,37,37,37,37,37,37,37,38,462,463,464,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,462,469,464,36,37,37,37,37,37,37,37,37,37,37,38,462,469,464,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, @@ -111,9 +108,6 @@ - - - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -220,7 +214,6 @@ - 236,236,236,236,236,236,236,236,236,236,236,236,236,237,0,0,0,0,0,0,0,109,110,110,110,111,0,90,91,92,0,400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -328,7 +321,6 @@ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,334,0,0,335,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -659,20 +651,8 @@ - - - - - - - - - - - - - - - + + + diff --git a/example/game.ts b/example/game.ts index 9c91eca0..f4494cf6 100644 --- a/example/game.ts +++ b/example/game.ts @@ -58,7 +58,7 @@ const start = (mapFile: string) => { } game.add(player); - const map = new TiledMapResource(mapFile); + const map = new TiledMapResource(mapFile, { firstLayerZIndex: -2 }); const loader = new ex.Loader([map]); game.start(loader).then(() => { player.pos = ex.vec(100, 100); diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index b59d90d1..e5fa5ba0 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -75,7 +75,7 @@ export class TiledMapResource implements Loadable { /** * * @param path Specify a path to your Tiled map source files (usually path/to/my_map.tmx) - * @param options Optionally configure other aspecits + * @param options Optionally configure other aspects of the tilemap like start layer z-index and map format */ constructor(public path: string, options?: TiledMapOptions) { const { mapFormatOverride, firstLayerZIndex } = { ...options}; diff --git a/test/unit/tiled-map-resource.spec.ts b/test/unit/tiled-map-resource.spec.ts index ad6e6c70..11ebdb97 100644 --- a/test/unit/tiled-map-resource.spec.ts +++ b/test/unit/tiled-map-resource.spec.ts @@ -54,7 +54,9 @@ describe('A Tiled Map Excalibur Resource', () => { }); it('can overwrite the base z-index start value', async () => { - const tiled = new TiledMapResource('base/test/unit/layer-zindex.tmx', undefined, -5); + const tiled = new TiledMapResource('base/test/unit/layer-zindex.tmx', { + firstLayerZIndex: -5 + }); await tiled.load(); expect(tiled.isLoaded()).toBe(true); From 17c9f7652db54155020b1c79f8c74d7a2ba78d3c Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 07:56:11 -0600 Subject: [PATCH 07/16] Update src/tiled-map-resource.ts Co-authored-by: Kamran Ayub --- src/tiled-map-resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index c9fbff0f..688be6a7 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -44,7 +44,7 @@ export enum TiledMapFormat { export interface TiledMapOptions { /** - * Optionally override the map format, by default files ending in .tmx are treated as TMX format, otherise treated as JSON format + * By default files ending in .tmx are treated as TMX format, otherwise treated as JSON format */ mapFormatOverride?: TiledMapFormat; From 770572c90cc9f0d654423b1d083b18f68e760d00 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 08:03:44 -0600 Subject: [PATCH 08/16] Update src/tiled-map-resource.ts Co-authored-by: Kamran Ayub --- src/tiled-map-resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 688be6a7..48943248 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -49,7 +49,7 @@ export interface TiledMapOptions { mapFormatOverride?: TiledMapFormat; /** - * Optionally Specify the first layer z-index value, each layer will increment up 1 unless overridden (default -1) + * Override the starting auto-incrementing z-index value (default: `-1`). Each layer will increment this number by 1 unless the layer specifies it's own custom `zindex` property. */ firstLayerZIndex?: number; } From c99c0463e887877da8f68528a0611a1d63dde6b4 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 08:29:53 -0600 Subject: [PATCH 09/16] Update per feedback --- src/tiled-map-resource.ts | 69 +++++++++++++++++++++++---------------- src/tiled-map.ts | 13 +++++--- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 48943248..85aa1173 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -25,9 +25,10 @@ import { ExcaliburData, RawTiledLayer, RawTiledMap, RawTiledTileset } from './ti import { TiledMap } from './tiled-map'; import { parseExternalTsx } from './tiled-tileset'; import { getCanonicalGid, isFlippedDiagonally, isFlippedHorizontally, isFlippedVertically } from './tiled-layer'; -import { getProperty } from './tiled-entity'; +import { getProperty, TiledEntity } from './tiled-entity'; import { TiledObjectComponent } from './tiled-object-component'; import { TiledLayerComponent } from './tiled-layer-component'; +import { TiledLayer, TiledObjectGroup } from '.'; export enum TiledMapFormat { @@ -149,27 +150,30 @@ export class TiledMapResource implements Loadable { } private _addTiledText(scene: Scene) { - const excalibur = this.data?.getExcaliburObjects(); - if (excalibur.length > 0) { - const textobjects = excalibur.flatMap(o => o.getText()); - for (const text of textobjects) { - const label = new Label({ - x: text.x, - y: text.y + ((text.height ?? 0) - (text.text?.pixelSize ?? 0)), - text: text.text?.text ?? '', - font: new Font({ - family: text.text?.fontFamily, - size: text.text?.pixelSize, - unit: FontUnit.Px - }) - }); - label.font.textAlign = TextAlign.Left; - label.font.baseAlign = BaseAlign.Top; - label.rotation = text.rotation, + const excaliburObjectLayers = this.data?.getExcaliburObjects(); + if (excaliburObjectLayers.length > 0) { + for (const objectLayer of excaliburObjectLayers) { + const textobjects = objectLayer.getText(); + for (const text of textobjects) { + const label = new Label({ + x: text.x, + y: text.y + ((text.height ?? 0) - (text.text?.pixelSize ?? 0)), + text: text.text?.text ?? '', + font: new Font({ + family: text.text?.fontFamily, + size: text.text?.pixelSize, + unit: FontUnit.Px + }) + }); + label.font.textAlign = TextAlign.Left; + label.font.baseAlign = BaseAlign.Top; + label.rotation = text.rotation; label.color = Color.fromHex(text.text?.color ?? '#000000'), label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); - label.addComponent(new TiledObjectComponent(text)); - scene.add(label); + label.addComponent(new TiledObjectComponent(text)); + scene.add(label); + label.z = this._calculateZIndex(text, objectLayer); + } } } } @@ -204,10 +208,7 @@ export class TiledMapResource implements Loadable { actor.graphics.use(sprite); } scene.add(actor); - const z = tile.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); - if (z) { - actor.z = +z; - } + actor.z = this._calculateZIndex(tile, objectLayer); } } } @@ -268,7 +269,7 @@ export class TiledMapResource implements Loadable { for (let box of boxColliders) { const collisionType = box.getProperty('collisiontype'); const color = box.getProperty('color'); - const zIndex = box.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); + const zIndex = this._calculateZIndex(box, objectLayer); ex.colliders.push({ ...box, width: +(box.width ?? 0), @@ -284,9 +285,9 @@ export class TiledMapResource implements Loadable { const circleColliders = objectLayer.getObjectsByType('circlecollider'); for (let circle of circleColliders) { - var collisionType = circle.getProperty('collisiontype'); - var color = circle.getProperty('color'); - var zIndex = circle.getProperty('zindex')?.value ?? (objectLayer.rawObjectGroup.order + this._layerZIndexStart); + const collisionType = circle.getProperty('collisiontype'); + const color = circle.getProperty('color'); + const zIndex = this._calculateZIndex(circle, objectLayer); ex.colliders.push({ x: circle.x, y: circle.y, @@ -437,6 +438,18 @@ export class TiledMapResource implements Loadable { throw new Error(`Could not find sprite for gid: [${gid}] normalized gid: [${normalizedGid}]`); } + private _calculateZIndex(entity: TiledEntity, tileLayerOrObjectGroup: TiledLayer | TiledObjectGroup): number { + let finalZ = entity.getProperty('z')?.value ?? entity.getProperty('zindex')?.value; + + if (tileLayerOrObjectGroup instanceof TiledLayer) { + finalZ ??= (tileLayerOrObjectGroup.rawLayer.order + this._layerZIndexStart); + } else { + finalZ ??= (tileLayerOrObjectGroup.rawObjectGroup.order + this._layerZIndexStart); + } + // coerce to integer + return +finalZ + } + /** * Creates the Excalibur tile map representation */ diff --git a/src/tiled-map.ts b/src/tiled-map.ts index b316b90f..d5b04ff3 100644 --- a/src/tiled-map.ts +++ b/src/tiled-map.ts @@ -197,11 +197,7 @@ export class TiledMap { resultMap.tileWidth = +rawMap.tilewidth; resultMap.tileHeight = +rawMap.tileheight; - // Tag the raw layers with their original order - let order = 0; - for (let layer of rawMap.layers) { - layer.order = order++; - } + tagOriginalLayerOrder(rawMap); for (let layer of rawMap.layers) { if (layer.type !== 'tilelayer') continue; @@ -308,6 +304,13 @@ export class TiledMap { } } +const tagOriginalLayerOrder = (rawMap: RawTiledMap) => { + let order = 0; + for (let layer of rawMap.layers) { + layer.order = order++; + } +} + /** * Decompression implementations */ From bb2e76632364ae53a443400876a6dec706fffe82 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 08:43:26 -0600 Subject: [PATCH 10/16] Simplify parsing with a small refactoring while we are here --- src/tiled-layer.ts | 15 ++++++++++++ src/tiled-map.ts | 58 +++++++-------------------------------------- src/tiled-object.ts | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/tiled-layer.ts b/src/tiled-layer.ts index f174c2dd..de9122c3 100644 --- a/src/tiled-layer.ts +++ b/src/tiled-layer.ts @@ -82,4 +82,19 @@ export class TiledLayer extends TiledEntity { * Reference to the raw tiled layer data */ public rawLayer!: RawTiledLayer; + + public static parse(layer: RawTiledLayer): TiledLayer { + if (layer.type !== 'tilelayer') throw Error('Cannot parse a non tilelayer type layer'); + const resultLayer = new TiledLayer(); + resultLayer.id = +layer.id; + resultLayer.name = layer.name; + resultLayer.data = (layer.data as number[]); + resultLayer.width = layer.width; + resultLayer.height = layer.height; + resultLayer.encoding = layer.encoding ?? 'csv'; + resultLayer.compression = layer.compression; + resultLayer.properties = layer.properties ?? []; + resultLayer.rawLayer = layer; + return resultLayer + } } \ No newline at end of file diff --git a/src/tiled-map.ts b/src/tiled-map.ts index d5b04ff3..4d56c560 100644 --- a/src/tiled-map.ts +++ b/src/tiled-map.ts @@ -197,58 +197,16 @@ export class TiledMap { resultMap.tileWidth = +rawMap.tilewidth; resultMap.tileHeight = +rawMap.tileheight; - tagOriginalLayerOrder(rawMap); + tagLayerWithOriginalOrder(rawMap); for (let layer of rawMap.layers) { - if (layer.type !== 'tilelayer') continue; - const resultLayer = new TiledLayer(); - resultLayer.id = +layer.id; - resultLayer.name = layer.name; - resultLayer.data = (layer.data as number[]); - resultLayer.width = layer.width; - resultLayer.height = layer.height; - resultLayer.encoding = layer.encoding ?? 'csv'; - resultLayer.compression = layer.compression; - resultLayer.properties = layer.properties ?? []; - resultLayer.rawLayer = layer; - resultMap.layers.push(resultLayer); - } + if (layer.type == 'tilelayer') { + resultMap.layers.push(TiledLayer.parse(layer)); + } - for (let objectlayer of rawMap.layers) { - if (objectlayer.type !== 'objectgroup') continue; - const resultObjectGroup = new TiledObjectGroup(); - resultObjectGroup.id = +objectlayer.id; - resultObjectGroup.name = objectlayer.name; - resultObjectGroup.properties = objectlayer.properties ?? []; - resultObjectGroup.rawObjectGroup = objectlayer; - for (let object of objectlayer.objects) { - const resultObject = new TiledObject(); - resultObject.id = +object.id; - resultObject.gid = object.gid; - resultObject.visible = object.visible ?? true; - resultObject.name = object.name; - resultObject.type = object.type; - resultObject.x = +object.x; - resultObject.y = +object.y; - resultObject.rotation = object.rotation ? Util.toRadians(object.rotation) : 0; - resultObject.width = object.width ?? 0; - resultObject.height = object.height ?? 0; - resultObject.point = object.point; - resultObject.ellipse = object.ellipse; - resultObject.polyline = object.polyline; - resultObject.polygon = object.polygon; - resultObject.rawObject = object; - if (object.text) { - resultObject.text = { - ...object.text, - pixelSize: object.text.pixelsize, - fontFamily: object.text.fontfamily - } - } - resultObject.properties = object.properties ?? []; - resultObjectGroup.objects.push(resultObject); - } - resultMap.objectGroups.push(resultObjectGroup); + if (layer.type == 'objectgroup') { + resultMap.objectGroups.push(TiledObjectGroup.parse(layer)); + }; } for(let tileset of rawMap.tilesets) { @@ -304,7 +262,7 @@ export class TiledMap { } } -const tagOriginalLayerOrder = (rawMap: RawTiledMap) => { +const tagLayerWithOriginalOrder = (rawMap: RawTiledMap) => { let order = 0; for (let layer of rawMap.layers) { layer.order = order++; diff --git a/src/tiled-object.ts b/src/tiled-object.ts index 3765c717..81df1058 100644 --- a/src/tiled-object.ts +++ b/src/tiled-object.ts @@ -1,6 +1,7 @@ import { ExcaliburCamera, TiledPoint } from "./tiled-types"; import { TiledEntity } from "./tiled-entity"; import { RawTiledLayer, RawTiledObject } from "."; +import { Util } from "excalibur"; export class TiledObjectGroup extends TiledEntity { public objects: TiledObject[] = []; @@ -58,6 +59,19 @@ export class TiledObjectGroup extends TiledEntity { public getInsertedTiles(): TiledObject[] { return this.objects.filter(o => !!o.gid); } + + public static parse(objectGroup: RawTiledLayer): TiledObjectGroup { + if (objectGroup.type !== 'objectgroup') throw Error('Cannot parse non objectgroup type layer'); + const resultObjectGroup = new TiledObjectGroup(); + resultObjectGroup.id = +objectGroup.id; + resultObjectGroup.name = objectGroup.name; + resultObjectGroup.properties = objectGroup.properties ?? []; + resultObjectGroup.rawObjectGroup = objectGroup; + for (let object of objectGroup.objects) { + resultObjectGroup.objects.push(TiledObject.parse(object)); + } + return resultObjectGroup; + } } export class TiledObject extends TiledEntity { @@ -97,6 +111,34 @@ export class TiledObject extends TiledEntity { public gid?: number; public rawObject!: RawTiledObject; + + public static parse(object: RawTiledObject): TiledObject { + const resultObject = new TiledObject(); + resultObject.id = +object.id; + resultObject.gid = object.gid; + resultObject.visible = object.visible ?? true; + resultObject.name = object.name; + resultObject.type = object.type; + resultObject.x = +object.x; + resultObject.y = +object.y; + resultObject.rotation = object.rotation ? Util.toRadians(object.rotation) : 0; + resultObject.width = object.width ?? 0; + resultObject.height = object.height ?? 0; + resultObject.point = object.point; + resultObject.ellipse = object.ellipse; + resultObject.polyline = object.polyline; + resultObject.polygon = object.polygon; + resultObject.rawObject = object; + if (object.text) { + resultObject.text = { + ...object.text, + pixelSize: object.text.pixelsize, + fontFamily: object.text.fontfamily + } + } + resultObject.properties = object.properties ?? []; + return resultObject + } } export interface TiledText { From 4d6108fbb0fd25a865780263722596fed93cbc17 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 08:51:54 -0600 Subject: [PATCH 11/16] Missed one --- src/tiled-map-resource.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 85aa1173..7718ce5b 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -479,8 +479,8 @@ export class TiledMapResource implements Loadable { const tileMapLayer = new TileMap(0, 0, this.data.rawMap.tilewidth, this.data.rawMap.tileheight, this.data.height, this.data.width); tileMapLayer.addComponent(new TiledLayerComponent(layer)); - const zindex = getProperty(rawLayer.properties, 'zindex')?.value ?? (layer.rawLayer.order + layerZIndexBase); - tileMapLayer.z = zindex; + // I know this looks goofy, but the entity and the layer "it belongs" to are the same here + tileMapLayer.z = this._calculateZIndex(layer, layer); for (var i = 0; i < rawLayer.data.length; i++) { let gid = rawLayer.data[i]; if (gid !== 0) { From 0f90d50847ab188053ce6bac059d3d796f9b8fd9 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 08:56:38 -0600 Subject: [PATCH 12/16] fix typo --- src/tiled-map-resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 7718ce5b..e4bbe9f3 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -168,7 +168,7 @@ export class TiledMapResource implements Loadable { label.font.textAlign = TextAlign.Left; label.font.baseAlign = BaseAlign.Top; label.rotation = text.rotation; - label.color = Color.fromHex(text.text?.color ?? '#000000'), + label.color = Color.fromHex(text.text?.color ?? '#000000'); label.collider.set(Shape.Box(text.width ?? 0, text.height ?? 0)); label.addComponent(new TiledObjectComponent(text)); scene.add(label); From 3971deb1c8a760a584325dedf671ecdd104d6f2b Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 20:25:49 -0600 Subject: [PATCH 13/16] Add some tests --- test/unit/objects.tmx | 14 ++++++++++++- test/unit/tiled-map-resource.spec.ts | 30 +++++++++++++++++++++++++++- test/unit/tiled-map.spec.ts | 5 +++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/test/unit/objects.tmx b/test/unit/objects.tmx index 90d7d412..79801982 100644 --- a/test/unit/objects.tmx +++ b/test/unit/objects.tmx @@ -37,8 +37,20 @@ - + + + + + + + + + + + + + diff --git a/test/unit/tiled-map-resource.spec.ts b/test/unit/tiled-map-resource.spec.ts index 11ebdb97..a81c3903 100644 --- a/test/unit/tiled-map-resource.spec.ts +++ b/test/unit/tiled-map-resource.spec.ts @@ -82,6 +82,33 @@ describe('A Tiled Map Excalibur Resource', () => { expect(tiled.ex.camera?.zoom).toBe(4); }); + it('will support "z" and "zindex" otherwise default to owning layer', async () => { + const tiled = new TiledMapResource('test/unit/objects.tmx') + await tiled.load(); + expect(tiled.isLoaded()); + + const scene = new Scene(); + tiled.addTiledMapToScene(scene); + + const objects = scene.actors.filter(a => a.get(TiledObjectComponent)); + + // z + const insertedTile1 = objects.find(a => a.get(TiledObjectComponent)?.object.id === 8); + expect(insertedTile1?.z).toBe(3); + + // z index + const insertedTile2 = objects.find(a => a.get(TiledObjectComponent)?.object.id === 9); + expect(insertedTile2?.z).toBe(3); + + // layer order + const insertedTile3 = objects.find(a => a.get(TiledObjectComponent)?.object.id === 10); + expect(insertedTile3?.z).toBe(0); + + // layer order + const insertedTile4 = objects.find(a => a.get(TiledObjectComponent)?.object.id === 11); + expect(insertedTile4?.z).toBe(1); + }); + it('will parse inserted tile entities and include their TiledObject in the TileObjectComponent', async () => { const tiled = new TiledMapResource('test/unit/objects.tmx') await tiled.load(); @@ -109,7 +136,8 @@ describe('A Tiled Map Excalibur Resource', () => { expect(component?.object.y).toBe(42); expect(component?.object.width).toBe(16); expect(component?.object.height).toBe(16); - expect(component?.object.getProperty("zindex")?.value).toBe(3); + expect(insertedTile?.z).toBe(3); + expect(component?.object.getProperty("z")?.value).toBe(3); expect(component?.object.rawObject).toBeDefined(); }); diff --git a/test/unit/tiled-map.spec.ts b/test/unit/tiled-map.spec.ts index 76936454..1dcb9116 100644 --- a/test/unit/tiled-map.spec.ts +++ b/test/unit/tiled-map.spec.ts @@ -103,15 +103,16 @@ describe('A Tiled Map', () => { expect(tiledMap).toBeDefined(); expect(tiledMap.layers.length).toBe(1); expect(tiledMap.layers[0].name).toBe('Tile Layer 1'); - expect(tiledMap.objectGroups.length).toBe(1); + expect(tiledMap.objectGroups.length).toBe(2); expect(tiledMap.objectGroups[0].name).toBe('Object Layer 1'); + expect(tiledMap.objectGroups[1].name).toBe('Object Layer 2'); expect(tiledMap.objectGroups[0].getPoints().length).toBe(1); expect(tiledMap.objectGroups[0].getEllipses().length).toBe(1); expect(tiledMap.objectGroups[0].getPolyLines().length).toBe(1); expect(tiledMap.objectGroups[0].getPolygons().length).toBe(1); expect(tiledMap.objectGroups[0].getText().length).toBe(1); - expect(tiledMap.objectGroups[0].getInsertedTiles().length).toBe(2); + expect(tiledMap.objectGroups[0].getInsertedTiles().length).toBe(4); expect(tiledMap.objectGroups[0].getCamera()).toEqual({x: 16, y: 16, zoom: 1}); }); From 5df06dde0502bcb7ce1bd8701626983f1408afd2 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Wed, 12 Jan 2022 20:32:45 -0600 Subject: [PATCH 14/16] Small refactoring + tests --- src/tiled-layer.ts | 6 ++++++ src/tiled-map-resource.ts | 7 ++----- src/tiled-object.ts | 3 +++ test/unit/tiled-map.spec.ts | 3 +++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/tiled-layer.ts b/src/tiled-layer.ts index de9122c3..0b682ae4 100644 --- a/src/tiled-layer.ts +++ b/src/tiled-layer.ts @@ -78,6 +78,11 @@ export class TiledLayer extends TiledEntity { */ public compression?: TiledCompression; + /** + * Original order of the Tiled layer + */ + public order!: number; + /** * Reference to the raw tiled layer data */ @@ -93,6 +98,7 @@ export class TiledLayer extends TiledEntity { resultLayer.height = layer.height; resultLayer.encoding = layer.encoding ?? 'csv'; resultLayer.compression = layer.compression; + resultLayer.order = layer.order; resultLayer.properties = layer.properties ?? []; resultLayer.rawLayer = layer; return resultLayer diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index e4bbe9f3..53d91f72 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -441,11 +441,8 @@ export class TiledMapResource implements Loadable { private _calculateZIndex(entity: TiledEntity, tileLayerOrObjectGroup: TiledLayer | TiledObjectGroup): number { let finalZ = entity.getProperty('z')?.value ?? entity.getProperty('zindex')?.value; - if (tileLayerOrObjectGroup instanceof TiledLayer) { - finalZ ??= (tileLayerOrObjectGroup.rawLayer.order + this._layerZIndexStart); - } else { - finalZ ??= (tileLayerOrObjectGroup.rawObjectGroup.order + this._layerZIndexStart); - } + finalZ ??= (tileLayerOrObjectGroup.order + this._layerZIndexStart); + // coerce to integer return +finalZ } diff --git a/src/tiled-object.ts b/src/tiled-object.ts index 81df1058..217ba4e9 100644 --- a/src/tiled-object.ts +++ b/src/tiled-object.ts @@ -8,6 +8,8 @@ export class TiledObjectGroup extends TiledEntity { public rawObjectGroup!: RawTiledLayer; + public order!: number; + public getCamera(): ExcaliburCamera | undefined { const camera = this.getObjectByType('camera'); if (camera) { @@ -67,6 +69,7 @@ export class TiledObjectGroup extends TiledEntity { resultObjectGroup.name = objectGroup.name; resultObjectGroup.properties = objectGroup.properties ?? []; resultObjectGroup.rawObjectGroup = objectGroup; + resultObjectGroup.order = objectGroup.order; for (let object of objectGroup.objects) { resultObjectGroup.objects.push(TiledObject.parse(object)); } diff --git a/test/unit/tiled-map.spec.ts b/test/unit/tiled-map.spec.ts index 1dcb9116..34d042dd 100644 --- a/test/unit/tiled-map.spec.ts +++ b/test/unit/tiled-map.spec.ts @@ -103,9 +103,12 @@ describe('A Tiled Map', () => { expect(tiledMap).toBeDefined(); expect(tiledMap.layers.length).toBe(1); expect(tiledMap.layers[0].name).toBe('Tile Layer 1'); + expect(tiledMap.layers[0].order).toBe(0); expect(tiledMap.objectGroups.length).toBe(2); expect(tiledMap.objectGroups[0].name).toBe('Object Layer 1'); + expect(tiledMap.objectGroups[0].order).toBe(1); expect(tiledMap.objectGroups[1].name).toBe('Object Layer 2'); + expect(tiledMap.objectGroups[1].order).toBe(2); expect(tiledMap.objectGroups[0].getPoints().length).toBe(1); expect(tiledMap.objectGroups[0].getEllipses().length).toBe(1); From a213dfeb00042326fdb097702df378d29601fbf8 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 14 Jan 2022 08:08:09 -0600 Subject: [PATCH 15/16] Update src/tiled-map-resource.ts Co-authored-by: Kamran Ayub --- src/tiled-map-resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 53d91f72..3c5e1a82 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -52,7 +52,7 @@ export interface TiledMapOptions { /** * Override the starting auto-incrementing z-index value (default: `-1`). Each layer will increment this number by 1 unless the layer specifies it's own custom `zindex` property. */ - firstLayerZIndex?: number; + startingLayerZIndex?: number; } export class TiledMapResource implements Loadable { From f86ae5d8d05ab33dec359b0928834fbacdf26394 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 14 Jan 2022 08:12:13 -0600 Subject: [PATCH 16/16] fix tests --- example/game.ts | 2 +- src/tiled-map-resource.ts | 4 ++-- test/unit/tiled-map-resource.spec.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/game.ts b/example/game.ts index f4494cf6..806bd3ab 100644 --- a/example/game.ts +++ b/example/game.ts @@ -58,7 +58,7 @@ const start = (mapFile: string) => { } game.add(player); - const map = new TiledMapResource(mapFile, { firstLayerZIndex: -2 }); + const map = new TiledMapResource(mapFile, { startingLayerZIndex: -2 }); const loader = new ex.Loader([map]); game.start(loader).then(() => { player.pos = ex.vec(100, 100); diff --git a/src/tiled-map-resource.ts b/src/tiled-map-resource.ts index 3c5e1a82..7333f069 100644 --- a/src/tiled-map-resource.ts +++ b/src/tiled-map-resource.ts @@ -79,8 +79,8 @@ export class TiledMapResource implements Loadable { * @param options Optionally configure other aspects of the tilemap like start layer z-index and map format */ constructor(public path: string, options?: TiledMapOptions) { - const { mapFormatOverride, firstLayerZIndex } = { ...options }; - this._layerZIndexStart = firstLayerZIndex ?? this._layerZIndexStart; + const { mapFormatOverride, startingLayerZIndex } = { ...options }; + this._layerZIndexStart = startingLayerZIndex ?? this._layerZIndexStart; const detectedType = mapFormatOverride ?? (path.includes('.tmx') ? TiledMapFormat.TMX : TiledMapFormat.JSON); switch (detectedType) { case TiledMapFormat.TMX: diff --git a/test/unit/tiled-map-resource.spec.ts b/test/unit/tiled-map-resource.spec.ts index a81c3903..76190d70 100644 --- a/test/unit/tiled-map-resource.spec.ts +++ b/test/unit/tiled-map-resource.spec.ts @@ -55,7 +55,7 @@ describe('A Tiled Map Excalibur Resource', () => { it('can overwrite the base z-index start value', async () => { const tiled = new TiledMapResource('base/test/unit/layer-zindex.tmx', { - firstLayerZIndex: -5 + startingLayerZIndex: -5 }); await tiled.load();