@rpgjs/tiledmap 5.0.0-alpha.9 → 5.0.0-beta.10

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.
@@ -0,0 +1,93 @@
1
+ import { MapClass } from "./index2.js";
2
+ import { applyTiledMapCompat } from "./index3.js";
3
+ //#region src/physics.ts
4
+ var TILED_HITBOX_ID_PREFIX = "__tiled_collision__:";
5
+ var START_POSITION_NAME = "start";
6
+ function prepareTiledPhysicsData(mapData, map) {
7
+ if (!mapData?.parsedMap) return;
8
+ const tiledMap = new MapClass(mapData.parsedMap);
9
+ map.tiled = tiledMap;
10
+ applyTiledMapCompat(map, mapData.parsedMap);
11
+ const tiledHitboxes = collectBlockedTileHitboxes(tiledMap);
12
+ mapData.hitboxes = mergeTiledHitboxes(mapData.hitboxes, tiledHitboxes);
13
+ mapData.width = tiledMap.widthPx;
14
+ mapData.height = tiledMap.heightPx;
15
+ mapData.positions = mergeTiledPositions(mapData.positions, collectTiledPointPositions(mapData.parsedMap.objects));
16
+ }
17
+ function applyTiledPointEvents(mapData) {
18
+ const objects = mapData?.parsedMap?.objects;
19
+ if (!Array.isArray(objects) || !Array.isArray(mapData?.events)) return;
20
+ for (const obj of objects) {
21
+ if (!obj?.point) continue;
22
+ mapData.events = mapData.events.map((eventEntry) => {
23
+ if (obj.name && eventEntry?.x === void 0 && eventEntry?.y === void 0 && getEventName(eventEntry) === obj.name) return {
24
+ ...eventEntry && typeof eventEntry === "object" && "event" in eventEntry ? eventEntry : { event: eventEntry },
25
+ x: obj.x,
26
+ y: obj.y
27
+ };
28
+ return eventEntry;
29
+ }).filter((eventEntry) => eventEntry !== null);
30
+ }
31
+ }
32
+ function collectTiledPointPositions(objects) {
33
+ if (!Array.isArray(objects)) return {};
34
+ const positions = {};
35
+ for (const obj of objects) {
36
+ if (!obj?.point || typeof obj.x !== "number" || typeof obj.y !== "number") continue;
37
+ if (typeof obj.name === "string" && obj.name.length > 0) positions[obj.name] = {
38
+ x: obj.x,
39
+ y: obj.y
40
+ };
41
+ if (!positions[START_POSITION_NAME] && (obj.class === START_POSITION_NAME || obj.type === START_POSITION_NAME)) positions[START_POSITION_NAME] = {
42
+ x: obj.x,
43
+ y: obj.y
44
+ };
45
+ }
46
+ return positions;
47
+ }
48
+ function mergeTiledPositions(existingPositions, tiledPositions) {
49
+ return {
50
+ ...existingPositions && typeof existingPositions === "object" ? existingPositions : {},
51
+ ...tiledPositions
52
+ };
53
+ }
54
+ function getEventName(eventEntry) {
55
+ const event = eventEntry?.event ?? eventEntry;
56
+ if (typeof event === "function") {
57
+ const staticEventName = event._name;
58
+ const prototypeEventName = event.prototype?._name;
59
+ const staticName = event.name;
60
+ const prototypeName = event.prototype?.name;
61
+ if (typeof prototypeEventName === "string") return prototypeEventName;
62
+ if (typeof staticEventName === "string") return staticEventName;
63
+ if (typeof prototypeName === "string") return prototypeName;
64
+ if (typeof staticName === "string") return staticName;
65
+ }
66
+ if (typeof event?.name === "string") return event.name;
67
+ }
68
+ function collectBlockedTileHitboxes(tiledMap) {
69
+ const hitboxes = [];
70
+ const mapWidth = tiledMap.width;
71
+ const mapHeight = tiledMap.height;
72
+ const tileWidth = tiledMap.tilewidth;
73
+ const tileHeight = tiledMap.tileheight;
74
+ for (let y = 0; y < mapHeight; y++) for (let x = 0; x < mapWidth; x++) if (tiledMap.getTileByPosition(x * tileWidth, y * tileHeight, [0, 0], { populateTiles: true }).hasCollision) hitboxes.push({
75
+ id: createTiledHitboxId(x, y),
76
+ x: x * tileWidth,
77
+ y: y * tileHeight,
78
+ width: tileWidth,
79
+ height: tileHeight
80
+ });
81
+ return hitboxes;
82
+ }
83
+ function mergeTiledHitboxes(existingHitboxes, tiledHitboxes) {
84
+ return [...Array.isArray(existingHitboxes) ? existingHitboxes.filter((hitbox) => !isGeneratedTiledHitbox(hitbox)) : [], ...tiledHitboxes];
85
+ }
86
+ function isGeneratedTiledHitbox(hitbox) {
87
+ return typeof hitbox?.id === "string" && hitbox.id.startsWith(TILED_HITBOX_ID_PREFIX);
88
+ }
89
+ function createTiledHitboxId(x, y) {
90
+ return `${TILED_HITBOX_ID_PREFIX}${x},${y}`;
91
+ }
92
+ //#endregion
93
+ export { applyTiledPointEvents, prepareTiledPhysicsData };
@@ -0,0 +1,15 @@
1
+ import { applyTiledPointEvents, prepareTiledPhysicsData } from "./index4.js";
2
+ import { defineModule } from "@rpgjs/common";
3
+ //#region src/server.ts
4
+ var server_default = defineModule({ map: {
5
+ onBeforeUpdate(mapData, map) {
6
+ prepareTiledPhysicsData(mapData, map);
7
+ applyTiledPointEvents(mapData);
8
+ return map;
9
+ },
10
+ onPhysicsInit(map, context) {
11
+ prepareTiledPhysicsData(context?.mapData, map);
12
+ }
13
+ } });
14
+ //#endregion
15
+ export { server_default as default };
@@ -0,0 +1,7 @@
1
+ import { MapClass } from '@canvasengine/tiled';
2
+ type AnyMap = {
3
+ tiled?: MapClass;
4
+ };
5
+ export declare function prepareTiledPhysicsData(mapData: any, map: AnyMap): void;
6
+ export declare function applyTiledPointEvents(mapData: any): void;
7
+ export {};
@@ -0,0 +1,54 @@
1
+ import { RpgMap, RpgServer } from '@rpgjs/server';
2
+ import { MapClass } from '@canvasengine/tiled';
3
+ declare module "@rpgjs/server" {
4
+ interface RpgMap {
5
+ /**
6
+ * CanvasEngine Tiled map instance attached by `@rpgjs/tiledmap`.
7
+ */
8
+ tiled?: MapClass;
9
+ /**
10
+ * Tiled layers from the parsed map.
11
+ */
12
+ layers?: any[];
13
+ /**
14
+ * Height used by tile depth calculations. Defaults to the Tiled tile height.
15
+ */
16
+ zTileHeight?: number;
17
+ /**
18
+ * v4 compatibility helper. Returns a Tiled layer by its name.
19
+ */
20
+ getLayerByName?(name: string): any;
21
+ /**
22
+ * v4 compatibility helper. Returns the one-dimensional tile index for tile coordinates.
23
+ */
24
+ getTileIndex?(x: number, y: number): number;
25
+ /**
26
+ * v4 compatibility helper. Returns the pixel origin of a tile coordinate.
27
+ */
28
+ getTileOriginPosition?(x: number, y: number): {
29
+ x: number;
30
+ y: number;
31
+ };
32
+ /**
33
+ * v4 compatibility helper. Returns Tiled tile information from pixel coordinates.
34
+ */
35
+ getTileByPosition?(x: number, y: number, z?: number): any;
36
+ /**
37
+ * v4 compatibility helper. Returns Tiled tile information from a one-dimensional index.
38
+ */
39
+ getTileByIndex?(tileIndex: number): any;
40
+ /**
41
+ * v4 compatibility helper. Updates a tile in a Tiled tile layer.
42
+ */
43
+ setTile?(x: number, y: number, layer: string | number, tileInfo: any): any;
44
+ /**
45
+ * v4 compatibility helper. Updates the parsed Tiled tileset list.
46
+ */
47
+ updateTileset?(tileset: any): any;
48
+ }
49
+ }
50
+ export interface RpgTiledMap extends RpgMap {
51
+ tiled: MapClass;
52
+ }
53
+ declare const _default: RpgServer;
54
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/tiledmap",
3
- "version": "5.0.0-alpha.9",
3
+ "version": "5.0.0-beta.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -22,24 +22,24 @@
22
22
  "license": "MIT",
23
23
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
24
24
  "peerDependencies": {
25
- "@canvasengine/presets": "2.0.0-beta.25",
26
- "@rpgjs/client": "5.0.0-alpha.9",
27
- "@rpgjs/common": "5.0.0-alpha.9",
28
- "@rpgjs/server": "5.0.0-alpha.9",
29
- "@rpgjs/vite": "5.0.0-alpha.9",
30
- "canvasengine": "2.0.0-beta.25"
25
+ "@canvasengine/presets": "^2.0.0-rc.1",
26
+ "@rpgjs/client": "5.0.0-beta.10",
27
+ "@rpgjs/common": "5.0.0-beta.10",
28
+ "@rpgjs/server": "5.0.0-beta.10",
29
+ "@rpgjs/vite": "5.0.0-beta.10",
30
+ "canvasengine": "^2.0.0-rc.1"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
35
  "devDependencies": {
36
- "@canvasengine/compiler": "2.0.0-beta.21",
37
- "vite": "^6.2.5",
38
- "vite-plugin-dts": "^4.5.3"
36
+ "@canvasengine/compiler": "^2.0.0-beta.58",
37
+ "vite": "^8.0.10",
38
+ "vite-plugin-dts": "^4.5.4"
39
39
  },
40
40
  "type": "module",
41
41
  "dependencies": {
42
- "@canvasengine/tiled": "2.0.0-beta.25"
42
+ "@canvasengine/tiled": "^2.0.0-beta.58"
43
43
  },
44
44
  "scripts": {
45
45
  "dev": "vite build --watch",
package/src/client.ts CHANGED
@@ -1,6 +1,12 @@
1
- import { RpgClient, RpgClientEngine } from "@rpgjs/client";
1
+ import { RpgClient } from "@rpgjs/client";
2
2
  import { defineModule } from "@rpgjs/common";
3
+ import { prepareTiledPhysicsData } from "./physics";
3
4
 
4
5
  export default defineModule<RpgClient>({
5
- effects: []
6
- })
6
+ componentAnimations: [],
7
+ sceneMap: {
8
+ onPhysicsInit(map: any, context: { mapData: any }) {
9
+ prepareTiledPhysicsData(context?.mapData, map);
10
+ },
11
+ },
12
+ });
package/src/compat.ts ADDED
@@ -0,0 +1,221 @@
1
+ import { MapClass } from "@canvasengine/tiled";
2
+
3
+ type AnyMap = {
4
+ tiled?: MapClass;
5
+ [key: string]: any;
6
+ };
7
+
8
+ type ParsedTiledMap = {
9
+ width?: number;
10
+ height?: number;
11
+ tilewidth?: number;
12
+ tileheight?: number;
13
+ layers?: any[];
14
+ tilesets?: any[];
15
+ [key: string]: any;
16
+ };
17
+
18
+ type TileLayer = {
19
+ name?: string;
20
+ data?: any[];
21
+ [key: string]: any;
22
+ };
23
+
24
+ const COMPAT_MARKER = "__rpgjsTiledMapCompat";
25
+
26
+ /**
27
+ * Adds v4-style map helpers backed by CanvasEngine Tiled.
28
+ */
29
+ export function applyTiledMapCompat(map: AnyMap, parsedMap: ParsedTiledMap): void {
30
+ if (!map?.tiled) {
31
+ return;
32
+ }
33
+
34
+ const state = (map as any)[COMPAT_MARKER] ?? { parsedMap };
35
+ state.parsedMap = parsedMap;
36
+
37
+ if (!(map as any)[COMPAT_MARKER]) {
38
+ Object.defineProperty(map, COMPAT_MARKER, {
39
+ value: state,
40
+ configurable: true,
41
+ });
42
+ }
43
+
44
+ defineGetter(map, "layers", () => getLayers(map, state.parsedMap));
45
+ defineGetter(map, "zTileHeight", () => getNumber(map.tiled, state.parsedMap, "zTileHeight", "tileheight"));
46
+
47
+ defineMethod(map, "getLayerByName", (name: string) => {
48
+ const tiled = map.tiled as any;
49
+ if (typeof tiled?.getLayerByName === "function") {
50
+ return tiled.getLayerByName(name);
51
+ }
52
+ return getLayers(map, state.parsedMap).find((layer: any) => layer?.name === name);
53
+ });
54
+
55
+ defineMethod(map, "getTileIndex", (x: number, y: number) => {
56
+ const tiled = map.tiled as any;
57
+ if (typeof tiled?.getTileIndex === "function") {
58
+ return tiled.getTileIndex(x, y);
59
+ }
60
+ return y * getNumber(map.tiled, state.parsedMap, "width") + x;
61
+ });
62
+
63
+ defineMethod(map, "getTileOriginPosition", (x: number, y: number) => {
64
+ const tiled = map.tiled as any;
65
+ if (typeof tiled?.getTileOriginPosition === "function") {
66
+ return tiled.getTileOriginPosition(x, y);
67
+ }
68
+ return {
69
+ x: x * getNumber(map.tiled, state.parsedMap, "tilewidth"),
70
+ y: y * getNumber(map.tiled, state.parsedMap, "tileheight"),
71
+ };
72
+ });
73
+
74
+ defineMethod(map, "getTileByPosition", (x: number, y: number, z?: number) => {
75
+ const tiled = map.tiled as any;
76
+ if (typeof tiled?.getTileByPosition === "function") {
77
+ const layerRange = typeof z === "number" ? [z, z] : undefined;
78
+ return tiled.getTileByPosition(x, y, layerRange, { populateTiles: true });
79
+ }
80
+ const tileX = Math.floor(x / getNumber(map.tiled, state.parsedMap, "tilewidth"));
81
+ const tileY = Math.floor(y / getNumber(map.tiled, state.parsedMap, "tileheight"));
82
+ return map.getTileByIndex(map.getTileIndex(tileX, tileY));
83
+ });
84
+
85
+ defineMethod(map, "getTileByIndex", (tileIndex: number) => {
86
+ const tiled = map.tiled as any;
87
+ if (typeof tiled?.getTileByIndex === "function") {
88
+ return tiled.getTileByIndex(tileIndex);
89
+ }
90
+ return getTileByIndexFromLayers(getLayers(map, state.parsedMap), tileIndex);
91
+ });
92
+
93
+ defineMethod(map, "setTile", (x: number, y: number, layer: string | number, tileInfo: any) => {
94
+ const tiled = map.tiled as any;
95
+ if (typeof tiled?.setTile === "function") {
96
+ return tiled.setTile(x, y, layer, tileInfo);
97
+ }
98
+
99
+ const targetLayer = getTargetLayer(getLayers(map, state.parsedMap), layer);
100
+ if (!targetLayer?.data) {
101
+ return undefined;
102
+ }
103
+
104
+ const tileIndex = map.getTileIndex(x, y);
105
+ targetLayer.data[tileIndex] = getTileGid(tileInfo);
106
+ return tileInfo;
107
+ });
108
+
109
+ defineMethod(map, "updateTileset", (tileset: any) => {
110
+ const tiled = map.tiled as any;
111
+ if (typeof tiled?.updateTileset === "function") {
112
+ return tiled.updateTileset(tileset);
113
+ }
114
+
115
+ const nextTilesets = updateTilesets(getTilesets(map, state.parsedMap), tileset);
116
+ tiled.tilesets = nextTilesets;
117
+ state.parsedMap.tilesets = nextTilesets;
118
+ return tileset;
119
+ });
120
+ }
121
+
122
+ function defineGetter(target: AnyMap, name: string, get: () => any): void {
123
+ Object.defineProperty(target, name, {
124
+ configurable: true,
125
+ enumerable: true,
126
+ get,
127
+ });
128
+ }
129
+
130
+ function defineMethod(target: AnyMap, name: string, value: (...args: any[]) => any): void {
131
+ if (typeof target[name] === "function") {
132
+ return;
133
+ }
134
+
135
+ Object.defineProperty(target, name, {
136
+ configurable: true,
137
+ enumerable: false,
138
+ value,
139
+ });
140
+ }
141
+
142
+ function getLayers(map: AnyMap, parsedMap: ParsedTiledMap): any[] {
143
+ const tiled = map.tiled as any;
144
+ return Array.isArray(tiled?.layers) ? tiled.layers : Array.isArray(parsedMap?.layers) ? parsedMap.layers : [];
145
+ }
146
+
147
+ function getTilesets(map: AnyMap, parsedMap: ParsedTiledMap): any[] {
148
+ const tiled = map.tiled as any;
149
+ return Array.isArray(tiled?.tilesets)
150
+ ? tiled.tilesets
151
+ : Array.isArray(parsedMap?.tilesets)
152
+ ? parsedMap.tilesets
153
+ : [];
154
+ }
155
+
156
+ function getNumber(tiled: any, parsedMap: ParsedTiledMap, property: string, fallbackProperty = property): number {
157
+ const value = typeof tiled?.[property] === "number" ? tiled[property] : parsedMap?.[property];
158
+ if (typeof value === "number") {
159
+ return value;
160
+ }
161
+ const fallback = typeof tiled?.[fallbackProperty] === "number" ? tiled[fallbackProperty] : parsedMap?.[fallbackProperty];
162
+ return typeof fallback === "number" ? fallback : 0;
163
+ }
164
+
165
+ function getTileByIndexFromLayers(layers: any[], tileIndex: number): any {
166
+ for (let i = layers.length - 1; i >= 0; i--) {
167
+ const layer = layers[i] as TileLayer;
168
+ const gid = layer?.data?.[tileIndex];
169
+ if (gid) {
170
+ return {
171
+ id: gid,
172
+ gid,
173
+ index: tileIndex,
174
+ layer,
175
+ };
176
+ }
177
+ }
178
+ return undefined;
179
+ }
180
+
181
+ function getTargetLayer(layers: any[], layer: string | number): TileLayer | undefined {
182
+ if (typeof layer === "number") {
183
+ return layers[layer];
184
+ }
185
+ return layers.find((currentLayer: any) => currentLayer?.name === layer);
186
+ }
187
+
188
+ function getTileGid(tileInfo: any): any {
189
+ if (tileInfo && typeof tileInfo === "object") {
190
+ return tileInfo.gid ?? tileInfo.id ?? tileInfo.tileId ?? 0;
191
+ }
192
+ return tileInfo;
193
+ }
194
+
195
+ function updateTilesets(currentTilesets: any[], tileset: any): any[] {
196
+ if (Array.isArray(tileset)) {
197
+ return tileset;
198
+ }
199
+
200
+ const nextTilesets = [...currentTilesets];
201
+ const index = nextTilesets.findIndex((currentTileset) => isSameTileset(currentTileset, tileset));
202
+ if (index >= 0) {
203
+ nextTilesets[index] = {
204
+ ...nextTilesets[index],
205
+ ...tileset,
206
+ };
207
+ } else {
208
+ nextTilesets.push(tileset);
209
+ }
210
+ return nextTilesets;
211
+ }
212
+
213
+ function isSameTileset(a: any, b: any): boolean {
214
+ return (
215
+ a &&
216
+ b &&
217
+ ((a.name && a.name === b.name) ||
218
+ (a.source && a.source === b.source) ||
219
+ (a.firstgid !== undefined && a.firstgid === b.firstgid))
220
+ );
221
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import { createModule } from "@rpgjs/common";
4
4
  import { provideLoadMap } from "@rpgjs/client";
5
5
  import { TiledParser } from "@canvasengine/tiled";
6
6
  import Tiled from "./tiled.ce";
7
+ import { prepareTiledPhysicsData } from "./physics";
7
8
 
8
9
  export function provideTiledMap(options: {
9
10
  basePath: string;
@@ -35,7 +36,7 @@ export function provideTiledMap(options: {
35
36
  }
36
37
  parsedMap.tilesets = tilesets;
37
38
 
38
- const obj = {
39
+ const obj: any = {
39
40
  data: mapData,
40
41
  component: Tiled,
41
42
  parsedMap,
@@ -45,6 +46,9 @@ export function provideTiledMap(options: {
45
46
  },
46
47
  };
47
48
 
49
+ // Populate dimensions and static hitboxes before the first loadPhysic() call.
50
+ prepareTiledPhysicsData(obj, obj);
51
+
48
52
  if (options.onLoadMap) {
49
53
  await options.onLoadMap(map);
50
54
  }