@canvasengine/tiled 2.0.0-beta.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/classes/Gid.d.ts +13 -0
  2. package/dist/classes/Layer.d.ts +26 -0
  3. package/dist/classes/Map.d.ts +232 -0
  4. package/dist/classes/Object.d.ts +8 -0
  5. package/dist/classes/Properties.d.ts +11 -0
  6. package/dist/classes/Tile.d.ts +19 -0
  7. package/dist/classes/Tileset.d.ts +13 -0
  8. package/dist/generate/tileset.d.ts +11 -0
  9. package/dist/generate/wangtile.d.ts +21 -0
  10. package/dist/index.d.ts +16 -0
  11. package/dist/index.js +6205 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/parser/open-file.d.ts +24 -0
  14. package/dist/parser/parser.d.ts +27 -0
  15. package/dist/types/Layer.d.ts +116 -0
  16. package/dist/types/Map.d.ts +72 -0
  17. package/dist/types/Objects.d.ts +57 -0
  18. package/dist/types/Text.d.ts +47 -0
  19. package/dist/types/Tile.d.ts +17 -0
  20. package/dist/types/Tileset.d.ts +92 -0
  21. package/dist/types/Types.d.ts +147 -0
  22. package/dist/types/WorldMaps.d.ts +12 -0
  23. package/dist/utils.d.ts +10 -0
  24. package/package.json +31 -0
  25. package/readme.md +1 -0
  26. package/src/classes/Gid.ts +46 -0
  27. package/src/classes/Layer.ts +135 -0
  28. package/src/classes/Map.ts +443 -0
  29. package/src/classes/Object.ts +16 -0
  30. package/src/classes/Properties.ts +30 -0
  31. package/src/classes/Tile.ts +22 -0
  32. package/src/classes/Tileset.ts +34 -0
  33. package/src/generate/tileset.ts +35 -0
  34. package/src/generate/wangtile.ts +166 -0
  35. package/src/index.ts +16 -0
  36. package/src/parser/open-file.ts +155 -0
  37. package/src/parser/parser.ts +309 -0
  38. package/src/types/Layer.ts +127 -0
  39. package/src/types/Map.ts +83 -0
  40. package/src/types/Objects.ts +64 -0
  41. package/src/types/Text.ts +47 -0
  42. package/src/types/Tile.ts +19 -0
  43. package/src/types/Tileset.ts +99 -0
  44. package/src/types/Types.ts +157 -0
  45. package/src/types/WorldMaps.ts +13 -0
  46. package/src/utils.ts +22 -0
  47. package/tests/class.spec.ts +88 -0
  48. package/tests/data.ts +5440 -0
  49. package/tests/parser.spec.ts +112 -0
  50. package/tests/tile-properties.spec.ts +99 -0
  51. package/tests/tiledmap-multi-layers.spec.ts +99 -0
  52. package/tests/tiledmap.spec.ts +223 -0
  53. package/tsconfig.json +28 -0
  54. package/vite.config.ts +21 -0
@@ -0,0 +1,34 @@
1
+ import { TilesetTile } from "../types/Tile"
2
+ import { TiledTileset } from "../types/Tileset"
3
+ import { TiledProperties } from "./Properties"
4
+ import { Tile } from "./Tile"
5
+
6
+ export class Tileset extends TiledProperties {
7
+ private cacheTileId: Map<number, Tile> = new Map()
8
+
9
+ constructor(private tileset: TiledTileset) {
10
+ super(tileset)
11
+ Object.assign(this, tileset)
12
+ this.margin = this.margin ?? 0
13
+ this.spacing = this.spacing ?? 0
14
+ // Handle both 'tiles' and 'tile' property names
15
+ const tilesArray = tileset.tiles || (tileset as any).tile || []
16
+ for (let tile of tilesArray) {
17
+ this.addTile(tile)
18
+ }
19
+ Reflect.deleteProperty(this, 'tiles')
20
+ Reflect.deleteProperty(this, 'tile')
21
+ }
22
+
23
+ addTile(tileObj: TilesetTile): Tile {
24
+ const tile = new Tile(tileObj)
25
+ this.cacheTileId.set(tile.id, tile)
26
+ return tile
27
+ }
28
+
29
+ getTile(id: number): Tile | undefined {
30
+ return this.cacheTileId.get(+id)
31
+ }
32
+ }
33
+
34
+ export interface Tileset extends TiledTileset {}
@@ -0,0 +1,35 @@
1
+ import builder from 'xmlbuilder'
2
+ import { TiledTileset } from '../types/Tileset'
3
+
4
+ export class Tileset {
5
+ constructor(protected nbTilesWidth: number, protected nbTilesHeight: number) {}
6
+
7
+ generate(attr: {
8
+ root: any,
9
+ image: any
10
+ }): builder.XMLElement {
11
+ const root = builder.create('tileset')
12
+ for (let param in attr.root) {
13
+ root.att(param, attr.root[param])
14
+ }
15
+ root.att('tilecount', this.nbTilesWidth * this.nbTilesHeight)
16
+ root.att('columns', this.nbTilesWidth)
17
+ root.ele('image', attr.image)
18
+ return root
19
+ }
20
+
21
+ createTile(id: number, properties): builder.XMLElement {
22
+ const tile = builder.create('tile', { headless: true })
23
+ tile.att('id', id)
24
+ const elProperties = tile.ele('properties')
25
+ for (let key in properties) {
26
+ const value = properties[key]
27
+ let type
28
+ if (typeof value == 'boolean') {
29
+ type = 'bool'
30
+ }
31
+ elProperties.ele('property', { name: key, type, value })
32
+ }
33
+ return tile
34
+ }
35
+ }
@@ -0,0 +1,166 @@
1
+ import builder from 'xmlbuilder'
2
+ import { Tileset } from './tileset'
3
+
4
+ export class Autotile extends Tileset {
5
+ static readonly HEIGHT_TILES: number = 6
6
+ static readonly WIDTH_TILES: number = 8
7
+
8
+ constructor(private nbGroupTilesWidth: number, private nbGroupTilesHeight: number, private nbAnimation: number = 1) {
9
+ const totalWidth = nbGroupTilesWidth * Autotile.WIDTH_TILES * nbAnimation
10
+ const totalHeight = nbGroupTilesHeight * Autotile.HEIGHT_TILES
11
+ super(totalWidth, totalHeight)
12
+ }
13
+
14
+ static getWangTiles(id: number): [number, number, number, number, number, number, number, number][] {
15
+ return [
16
+ [0,0,id,0,0,0,0,0],
17
+ [0,0,id,0,0,0,id,0],
18
+ [0,0,0,0,0,0,id,0],
19
+ [0,0,0,0,id,0,0,0],
20
+ [0,0,id,id,id,0,0,0],
21
+ [0,0,id,id,id,id,id,0],
22
+ [0,0,0,0,id,id,id,0],
23
+ [0,0,id,0,id,0,0,0],
24
+ [0,0,0,0,id,0,id,0],
25
+ [id,0,id,0,id,0,0,0],
26
+ [0,0,id,0,id,0,id,0],
27
+ [id,0,0,0,id,0,0,0],
28
+ [id,id,id,id,id,0,0,0],
29
+ [id,id,id,id,id,id,id,id],
30
+ [id,0,0,0,id,id,id,id],
31
+ [id,0,id,0,0,0,0,0],
32
+ [id,0,0,0,0,0,id,0],
33
+ [id,0,id,0,0,0,id,0],
34
+ [id,0,0,0,id,0,id,0],
35
+ [id,0,0,0,0,0,0,0],
36
+ [id,id,id,0,0,0,0,0],
37
+ [id,id,id,0,0,0,id,id],
38
+ [id,0,0,0,0,0,id,id],
39
+ [id,id,id,0,id,0,0,0],
40
+ [id,0,0,0,id,0,id,id],
41
+ [0,0,id,0,id,id,id,0],
42
+ [0,0,id,id,id,0,id,0],
43
+ [id,id,id,0,id,id,id,id],
44
+ [id,id,id,id,id,0,id,id],
45
+ [id,0,id,0,id,0,id,id],
46
+ [id,id,id,0,id,0,id,0],
47
+ [id,0,id,id,id,0,0,0],
48
+ [id,0,0,0,id,id,id,0],
49
+ [id,0,id,0,0,0,id,id],
50
+ [id,id,id,0,0,0,id,0],
51
+ [id,0,id,id,id,id,id,id],
52
+ [id,id,id,id,id,id,id,0],
53
+ [id,0,id,0,id,id,id,0],
54
+ [id,0,id,id,id,0,id,0],
55
+ [id,0,id,id,id,id,id,0],
56
+ [id,id,id,0,id,0,id,id],
57
+ [id,0,id,0,id,id,id,id],
58
+ [id,id,id,id,id,0,id,0],
59
+ [id,0,id,id,id,0,id,id],
60
+ [id,id,id,0,id,id,id,0],
61
+ [id,0,id,0,id,0,id,0]
62
+ ]
63
+ }
64
+
65
+ static getRandomColor() {
66
+ const letters = '0123456789ABCDEF'
67
+ let color = '#';
68
+ for (var i = 0; i < 6; i++) {
69
+ color += letters[Math.floor(Math.random() * 16)]
70
+ }
71
+ return color
72
+ }
73
+
74
+ get hasAnimation(): boolean {
75
+ return this.nbAnimation > 1
76
+ }
77
+
78
+ getIndex(x: number, y: number): number {
79
+ return x + y * this.nbTilesWidth
80
+ }
81
+
82
+ generate(attr: {
83
+ root: any,
84
+ image: any,
85
+ tile: any
86
+ }): builder.XMLElement {
87
+ const root = super.generate({
88
+ root: attr.root,
89
+ image: attr.image
90
+ })
91
+ for (let i=0 ; i < this.nbTilesHeight ; i++) {
92
+ for (let j=0 ; j < this.nbTilesWidth ; j++) {
93
+ const tileId = this.getIndex(j, i)
94
+ const nbProp = Object.keys(attr.tile).length
95
+ const hasAnimation = this.hasAnimation && j < Autotile.WIDTH_TILES
96
+ if (nbProp == 0 && !hasAnimation) {
97
+ continue
98
+ }
99
+ const xmlTile = this.createTile(tileId, attr.tile)
100
+ if (hasAnimation) {
101
+ const xmlAnimation = this.generateAnimationTile(tileId)
102
+ xmlTile.importDocument(xmlAnimation)
103
+ }
104
+ root.importDocument(xmlTile)
105
+ }
106
+ }
107
+ const xmlWang = this.generateWangTiles()
108
+ root.importDocument(xmlWang)
109
+ return root
110
+ }
111
+
112
+ generateAnimationTile(tileId: number): builder.XMLElement {
113
+ let xml = builder.create('animation', { headless: true })
114
+ for (let i=0 ; i < this.nbAnimation ; i++) {
115
+ xml.ele('frame', { tileid: tileId + i * Autotile.WIDTH_TILES, duration: 100 })
116
+ }
117
+ return xml
118
+ }
119
+
120
+ generateWangTiles(tileId: number = 0, name = 'Autotile'): builder.XMLElement {
121
+ let xml = builder.create('wangsets', { headless: true })
122
+ const xmlWangset = xml.ele('wangset', { name, type: 'mixed', tile: tileId })
123
+ const getOrigin = (i, j) => i* Autotile.WIDTH_TILES + (j* Autotile.HEIGHT_TILES * this.nbTilesWidth)
124
+ for (let i=0 ; i < this.nbGroupTilesWidth ; i++) {
125
+ for (let j=0 ; j < this.nbGroupTilesHeight ; j++) {
126
+ xmlWangset.ele('wangcolor', { color: Autotile.getRandomColor(), tile: getOrigin(i, j), probability: 1 })
127
+ }
128
+ }
129
+ let k=0
130
+ for (let i=0 ; i < this.nbGroupTilesWidth ; i++) {
131
+ for (let j=0 ; j < this.nbGroupTilesHeight ; j++) {
132
+ const wangTiles = Autotile.getWangTiles(k+1)
133
+ const origin = getOrigin(i, j)
134
+ for (let l=0 ; l <= wangTiles.length ; l++) {
135
+ const wangTile = wangTiles[l-1]
136
+ if (!wangTile) continue
137
+ const x = l % Autotile.WIDTH_TILES
138
+ const y = Math.floor(l / Autotile.WIDTH_TILES)
139
+ const index = this.getIndex(x, y)
140
+ const tileId = origin + index
141
+ xmlWangset.ele('wangtile', { tileid: tileId, wangid: wangTile.join(',') })
142
+ }
143
+ k++
144
+ }
145
+ }
146
+ return xml
147
+ }
148
+ }
149
+ /*
150
+ const str = new Autotile(1, 2).generate({
151
+ root: {
152
+ version: '1.8',
153
+ tiledversion: "1.8.2",
154
+ name: "[A]Wall-Up_pipo",
155
+ tilewidth: "32",
156
+ tileheight: "32"
157
+ },
158
+ image: {
159
+ source: '../../../client/maps/assets/[A]Wall-Up_pipo.png',
160
+ width: "256",
161
+ height: "384"
162
+ },
163
+ tile: {}
164
+ }).end({ pretty: true })
165
+ console.log(str)
166
+ */
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type { TiledWorld, TiledWorldMap } from './types/WorldMaps'
2
+ export type { TiledTileset } from './types/Tileset'
3
+ export type { TiledObject } from './types/Objects'
4
+ export type { TiledImage } from './types/Types'
5
+ export { type TiledLayer, TiledLayerType } from './types/Layer'
6
+ export type { TiledText } from './types/Text'
7
+ export type { TiledMap } from './types/Map'
8
+ export { isTiledFormat } from './utils'
9
+ export { TiledParser } from './parser/parser'
10
+ export { TiledProperties } from './classes/Properties'
11
+ export { Tile } from './classes/Tile'
12
+ export { Layer } from './classes/Layer'
13
+ export { Tileset } from './classes/Tileset'
14
+ export { TiledObjectClass } from './classes/Object'
15
+ export { TiledParserFile } from './parser/open-file'
16
+ export { MapClass } from './classes/Map'
@@ -0,0 +1,155 @@
1
+ import { TiledParser } from "./parser"
2
+ import { TiledMap } from "../types/Map"
3
+ import { TiledTileset } from "../types/Tileset"
4
+ import { joinPath } from "../utils"
5
+
6
+ type ParseOptions = { getOnlyBasename?: boolean }
7
+
8
+
9
+ export class TiledParserFile {
10
+ private basePath: string
11
+
12
+ constructor(private file: string, {
13
+ basePath = '',
14
+ staticDir = '',
15
+ relativePath = ''
16
+ } = {}) {
17
+ this.basePath = basePath
18
+ }
19
+
20
+ static isBrowser() {
21
+ // @ts-ignore
22
+ return (typeof window !== 'undefined' && !window.useFileSystem)
23
+ }
24
+
25
+ static typeOfFile(file: string): {
26
+ isXml: boolean
27
+ isObject: boolean
28
+ isHttp: boolean
29
+ isPath: boolean
30
+ } {
31
+ file = file.trim()
32
+ const isString = typeof file == 'string'
33
+ const info = {
34
+ isXml: isString && file.startsWith('<?xml'),
35
+ isObject: !!file['version'],
36
+ isHttp: isString && file.startsWith('http')
37
+ }
38
+ return {
39
+ ...info,
40
+ isPath: !info.isXml && !info.isObject && !info.isHttp
41
+ }
42
+ }
43
+
44
+ private _parseFile<T>(file: string, type: string, cb: Function) {
45
+ const isXml = content => TiledParserFile.typeOfFile(content).isXml
46
+
47
+ const loadContent = (content) => {
48
+ if (!content) {
49
+ return cb(null)
50
+ }
51
+ if (isXml(content)) {
52
+ const parser = new TiledParser(content, file, this.basePath)
53
+ if (type == 'map') {
54
+ const json = parser.parseMap() as any
55
+ return cb(json)
56
+ }
57
+ else if (type == 'tileset') {
58
+ const json = parser.parseTileset() as any
59
+ return cb(json)
60
+ }
61
+ }
62
+
63
+ return cb(JSON.parse(content))
64
+ }
65
+
66
+ if (TiledParserFile.typeOfFile(file).isObject) {
67
+ return cb(file)
68
+ }
69
+
70
+ const { isHttp } = TiledParserFile.typeOfFile(file)
71
+ if (isXml(file)) {
72
+ loadContent(file)
73
+ }
74
+ else if (isHttp || (TiledParserFile.isBrowser() && process.env.NODE_ENV != 'test')) {
75
+ let url = isHttp ? file : joinPath(this.basePath, file)
76
+ // @ts-ignore
77
+ if (TiledParserFile.isBrowser() && window.urlCache) {
78
+ // @ts-ignore
79
+ url = window.urlCache[file]
80
+ }
81
+ fetch(url, {
82
+ headers: {
83
+ 'Content-Type': 'application/xml'
84
+ }
85
+ })
86
+ .then(response => response.text())
87
+ .then(loadContent)
88
+ .catch(err => cb(null, err))
89
+ }
90
+ }
91
+
92
+ parseFile(cb: Function, options: ParseOptions = {}) {
93
+ const { getOnlyBasename } = options
94
+ const basename = path => path.substring(path.lastIndexOf('/') + 1)
95
+ if (getOnlyBasename) {
96
+ if (TiledParserFile.typeOfFile(this.file).isPath) {
97
+ this.file = basename(this.file)
98
+ }
99
+ }
100
+ this._parseFile<TiledMap>(this.file, 'map', (map, err) => {
101
+ let hasError = false
102
+ if (err) return cb(null, err)
103
+ if (map.tilesets) {
104
+ const parseTileset: TiledTileset[] = []
105
+ const finish = () => {
106
+ loadAll++
107
+ if (loadAll == map.tilesets.length && !hasError) {
108
+ map.tilesets = parseTileset
109
+ cb(map)
110
+ }
111
+ }
112
+ let loadAll = 0
113
+ for (let i=0; i < map.tilesets.length ; i++) {
114
+ const tileset = map.tilesets[i]
115
+
116
+ // Check if tileset already contains XML child data (tiles, image, etc.)
117
+ if (!tileset.source || tileset.tiles || tileset.image || tileset.tile) {
118
+ // Tileset already contains data, no need to load from external source
119
+ parseTileset[i] = tileset
120
+ finish()
121
+ continue
122
+ }
123
+
124
+ if (getOnlyBasename) {
125
+ if (TiledParserFile.typeOfFile(tileset.source).isPath) {
126
+ tileset.source = basename(tileset.source)
127
+ }
128
+ }
129
+
130
+ this._parseFile<TiledTileset>(tileset.source, 'tileset', (result, err) => {
131
+ if (err) {
132
+ hasError = true
133
+ return cb(null, err)
134
+ }
135
+ parseTileset[i] = {
136
+ ...result,
137
+ firstgid: tileset.firstgid
138
+ }
139
+ finish()
140
+ })
141
+ }
142
+
143
+ }
144
+ })
145
+ }
146
+
147
+ parseFilePromise(options: ParseOptions = {}): Promise<TiledMap> {
148
+ return new Promise((resolve, reject) => {
149
+ this.parseFile((ret, err) => {
150
+ if (ret) resolve(ret)
151
+ else reject(err)
152
+ }, options)
153
+ })
154
+ }
155
+ }