@rpgjs/tiledmap 5.0.0-beta.1 → 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.
@@ -8,6 +8,8 @@ vi.mock("@canvasengine/tiled", () => {
8
8
  tileheight: number;
9
9
  widthPx: number;
10
10
  heightPx: number;
11
+ layers: any[];
12
+ tilesets: any[];
11
13
  private blockedTiles: Set<string>;
12
14
 
13
15
  constructor(parsedMap: any) {
@@ -17,6 +19,8 @@ vi.mock("@canvasengine/tiled", () => {
17
19
  this.tileheight = parsedMap.tileheight ?? 32;
18
20
  this.widthPx = this.width * this.tilewidth;
19
21
  this.heightPx = this.height * this.tileheight;
22
+ this.layers = parsedMap.layers;
23
+ this.tilesets = parsedMap.tilesets;
20
24
  this.blockedTiles = new Set(parsedMap.blockedTiles ?? []);
21
25
  }
22
26
 
@@ -32,7 +36,7 @@ vi.mock("@canvasengine/tiled", () => {
32
36
  return { MapClass };
33
37
  });
34
38
 
35
- import { prepareTiledPhysicsData } from "./physics";
39
+ import { applyTiledPointEvents, prepareTiledPhysicsData } from "./physics";
36
40
 
37
41
  describe("prepareTiledPhysicsData", () => {
38
42
  it("adds tiled collision hitboxes without duplicating them on repeated preparation", () => {
@@ -87,4 +91,195 @@ describe("prepareTiledPhysicsData", () => {
87
91
 
88
92
  expect(mapData.hitboxes).toEqual([]);
89
93
  });
94
+
95
+ it("adds v4 map helpers backed by Tiled data", () => {
96
+ const groundLayer = { name: "Ground", data: [1, 0, 2, 3] };
97
+ const mapData = {
98
+ parsedMap: {
99
+ width: 2,
100
+ height: 2,
101
+ tilewidth: 16,
102
+ tileheight: 20,
103
+ layers: [groundLayer],
104
+ tilesets: [{ firstgid: 1, name: "base" }],
105
+ },
106
+ };
107
+ const map: any = {};
108
+
109
+ prepareTiledPhysicsData(mapData, map);
110
+
111
+ expect(map.layers).toBe(mapData.parsedMap.layers);
112
+ expect(map.zTileHeight).toBe(20);
113
+ expect(map.getLayerByName("Ground")).toBe(groundLayer);
114
+ expect(map.getTileIndex(1, 1)).toBe(3);
115
+ expect(map.getTileOriginPosition(1, 1)).toEqual({ x: 16, y: 20 });
116
+ expect(map.getTileByPosition(16, 20)).toEqual({ hasCollision: false });
117
+ expect(map.getTileByIndex(2)).toMatchObject({ gid: 2, id: 2, index: 2, layer: groundLayer });
118
+
119
+ expect(map.setTile(1, 0, "Ground", { gid: 8 })).toEqual({ gid: 8 });
120
+ expect(groundLayer.data[1]).toBe(8);
121
+
122
+ expect(map.updateTileset({ firstgid: 1, name: "base", tilecount: 4 })).toEqual({
123
+ firstgid: 1,
124
+ name: "base",
125
+ tilecount: 4,
126
+ });
127
+ expect(map.tiled.tilesets).toEqual([{ firstgid: 1, name: "base", tilecount: 4 }]);
128
+ expect(mapData.parsedMap.tilesets).toEqual([{ firstgid: 1, name: "base", tilecount: 4 }]);
129
+ });
130
+ });
131
+
132
+ describe("tiled point positions", () => {
133
+ it("extracts named point objects as map positions", () => {
134
+ const mapData = {
135
+ parsedMap: {
136
+ width: 1,
137
+ height: 1,
138
+ objects: [
139
+ { point: true, name: "start", x: 10, y: 20 },
140
+ { point: true, name: "entrance", x: 30, y: 40 },
141
+ ],
142
+ },
143
+ };
144
+
145
+ prepareTiledPhysicsData(mapData, {});
146
+
147
+ expect(mapData.positions).toEqual({
148
+ start: { x: 10, y: 20 },
149
+ entrance: { x: 30, y: 40 },
150
+ });
151
+ });
152
+
153
+ it("uses class or type as a fallback for the special start position", () => {
154
+ const mapData = {
155
+ parsedMap: {
156
+ width: 1,
157
+ height: 1,
158
+ objects: [
159
+ { point: true, class: "start", x: 10, y: 20 },
160
+ { point: true, type: "start", x: 30, y: 40 },
161
+ ],
162
+ },
163
+ };
164
+
165
+ prepareTiledPhysicsData(mapData, {});
166
+
167
+ expect(mapData.positions).toEqual({
168
+ start: { x: 10, y: 20 },
169
+ });
170
+ });
171
+
172
+ it("keeps explicitly named start before class or type fallback", () => {
173
+ const mapData = {
174
+ parsedMap: {
175
+ width: 1,
176
+ height: 1,
177
+ objects: [
178
+ { point: true, name: "start", x: 10, y: 20 },
179
+ { point: true, class: "start", x: 30, y: 40 },
180
+ ],
181
+ },
182
+ };
183
+
184
+ prepareTiledPhysicsData(mapData, {});
185
+
186
+ expect(mapData.positions).toEqual({
187
+ start: { x: 10, y: 20 },
188
+ });
189
+ });
190
+ });
191
+
192
+ describe("applyTiledPointEvents", () => {
193
+ it("places direct object events by matching their name with a tiled point", () => {
194
+ const mapData = {
195
+ parsedMap: {
196
+ objects: [
197
+ { point: true, name: "EV-1", x: 10, y: 20 },
198
+ ],
199
+ },
200
+ events: [
201
+ { name: "EV-1", onInit() {} },
202
+ ],
203
+ };
204
+
205
+ applyTiledPointEvents(mapData);
206
+
207
+ expect(mapData.events).toEqual([
208
+ {
209
+ event: { name: "EV-1", onInit: expect.any(Function) },
210
+ x: 10,
211
+ y: 20,
212
+ },
213
+ ]);
214
+ });
215
+
216
+ it("places wrapped object events by matching the wrapped event name", () => {
217
+ const mapData = {
218
+ parsedMap: {
219
+ objects: [
220
+ { point: true, name: "EV-1", x: 10, y: 20 },
221
+ ],
222
+ },
223
+ events: [
224
+ { id: "event-id", event: { name: "EV-1", onInit() {} } },
225
+ ],
226
+ };
227
+
228
+ applyTiledPointEvents(mapData);
229
+
230
+ expect(mapData.events).toEqual([
231
+ {
232
+ id: "event-id",
233
+ event: { name: "EV-1", onInit: expect.any(Function) },
234
+ x: 10,
235
+ y: 20,
236
+ },
237
+ ]);
238
+ });
239
+
240
+ it("places class events decorated with EventData metadata", () => {
241
+ class NpcEvent {}
242
+ (NpcEvent as any)._name = "EV-1";
243
+ (NpcEvent as any).prototype._name = "EV-1";
244
+
245
+ const mapData = {
246
+ parsedMap: {
247
+ objects: [
248
+ { point: true, name: "EV-1", x: 10, y: 20 },
249
+ ],
250
+ },
251
+ events: [
252
+ { event: NpcEvent },
253
+ ],
254
+ };
255
+
256
+ applyTiledPointEvents(mapData);
257
+
258
+ expect(mapData.events).toEqual([
259
+ {
260
+ event: NpcEvent,
261
+ x: 10,
262
+ y: 20,
263
+ },
264
+ ]);
265
+ });
266
+
267
+ it("does not override explicit event coordinates", () => {
268
+ const mapData = {
269
+ parsedMap: {
270
+ objects: [
271
+ { point: true, name: "EV-1", x: 10, y: 20 },
272
+ ],
273
+ },
274
+ events: [
275
+ { x: 50, y: 60, event: { name: "EV-1" } },
276
+ ],
277
+ };
278
+
279
+ applyTiledPointEvents(mapData);
280
+
281
+ expect(mapData.events).toEqual([
282
+ { x: 50, y: 60, event: { name: "EV-1" } },
283
+ ]);
284
+ });
90
285
  });
package/src/physics.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { MapClass } from "@canvasengine/tiled";
2
+ import { applyTiledMapCompat } from "./compat";
2
3
 
3
4
  type AnyMap = {
4
5
  tiled?: MapClass;
@@ -13,6 +14,7 @@ type RectHitbox = {
13
14
  };
14
15
 
15
16
  const TILED_HITBOX_ID_PREFIX = "__tiled_collision__:";
17
+ const START_POSITION_NAME = "start";
16
18
 
17
19
  export function prepareTiledPhysicsData(mapData: any, map: AnyMap): void {
18
20
  if (!mapData?.parsedMap) {
@@ -21,11 +23,13 @@ export function prepareTiledPhysicsData(mapData: any, map: AnyMap): void {
21
23
 
22
24
  const tiledMap = new MapClass(mapData.parsedMap);
23
25
  map.tiled = tiledMap;
26
+ applyTiledMapCompat(map, mapData.parsedMap);
24
27
 
25
28
  const tiledHitboxes = collectBlockedTileHitboxes(tiledMap);
26
29
  mapData.hitboxes = mergeTiledHitboxes(mapData.hitboxes, tiledHitboxes);
27
30
  mapData.width = tiledMap.widthPx;
28
31
  mapData.height = tiledMap.heightPx;
32
+ mapData.positions = mergeTiledPositions(mapData.positions, collectTiledPointPositions(mapData.parsedMap.objects));
29
33
  }
30
34
 
31
35
  export function applyTiledPointEvents(mapData: any): void {
@@ -41,9 +45,15 @@ export function applyTiledPointEvents(mapData: any): void {
41
45
 
42
46
  mapData.events = mapData.events
43
47
  .map((eventEntry: any) => {
44
- if (eventEntry?.name === obj.name) {
48
+ if (
49
+ obj.name &&
50
+ eventEntry?.x === undefined &&
51
+ eventEntry?.y === undefined &&
52
+ getEventName(eventEntry) === obj.name
53
+ ) {
54
+ const isWrappedEvent = eventEntry && typeof eventEntry === "object" && "event" in eventEntry;
45
55
  return {
46
- event: eventEntry,
56
+ ...(isWrappedEvent ? eventEntry : { event: eventEntry }),
47
57
  x: obj.x,
48
58
  y: obj.y,
49
59
  };
@@ -54,6 +64,69 @@ export function applyTiledPointEvents(mapData: any): void {
54
64
  }
55
65
  }
56
66
 
67
+ function collectTiledPointPositions(objects: any): Record<string, { x: number; y: number }> {
68
+ if (!Array.isArray(objects)) {
69
+ return {};
70
+ }
71
+
72
+ const positions: Record<string, { x: number; y: number }> = {};
73
+
74
+ for (const obj of objects) {
75
+ if (!obj?.point || typeof obj.x !== "number" || typeof obj.y !== "number") {
76
+ continue;
77
+ }
78
+
79
+ if (typeof obj.name === "string" && obj.name.length > 0) {
80
+ positions[obj.name] = { x: obj.x, y: obj.y };
81
+ }
82
+
83
+ if (
84
+ !positions[START_POSITION_NAME] &&
85
+ (obj.class === START_POSITION_NAME || obj.type === START_POSITION_NAME)
86
+ ) {
87
+ positions[START_POSITION_NAME] = { x: obj.x, y: obj.y };
88
+ }
89
+ }
90
+
91
+ return positions;
92
+ }
93
+
94
+ function mergeTiledPositions(existingPositions: any, tiledPositions: Record<string, { x: number; y: number }>) {
95
+ return {
96
+ ...(existingPositions && typeof existingPositions === "object" ? existingPositions : {}),
97
+ ...tiledPositions,
98
+ };
99
+ }
100
+
101
+ function getEventName(eventEntry: any): string | undefined {
102
+ const event = eventEntry?.event ?? eventEntry;
103
+
104
+ if (typeof event === "function") {
105
+ const staticEventName = (event as any)._name;
106
+ const prototypeEventName = (event as any).prototype?._name;
107
+ const staticName = (event as any).name;
108
+ const prototypeName = (event as any).prototype?.name;
109
+ if (typeof prototypeEventName === "string") {
110
+ return prototypeEventName;
111
+ }
112
+ if (typeof staticEventName === "string") {
113
+ return staticEventName;
114
+ }
115
+ if (typeof prototypeName === "string") {
116
+ return prototypeName;
117
+ }
118
+ if (typeof staticName === "string") {
119
+ return staticName;
120
+ }
121
+ }
122
+
123
+ if (typeof event?.name === "string") {
124
+ return event.name;
125
+ }
126
+
127
+ return undefined;
128
+ }
129
+
57
130
  function collectBlockedTileHitboxes(tiledMap: MapClass): RectHitbox[] {
58
131
  const hitboxes: RectHitbox[] = [];
59
132
  const mapWidth = tiledMap.width;
package/src/server.ts CHANGED
@@ -5,7 +5,46 @@ import { applyTiledPointEvents, prepareTiledPhysicsData } from "./physics";
5
5
 
6
6
  declare module "@rpgjs/server" {
7
7
  interface RpgMap {
8
+ /**
9
+ * CanvasEngine Tiled map instance attached by `@rpgjs/tiledmap`.
10
+ */
8
11
  tiled?: MapClass;
12
+ /**
13
+ * Tiled layers from the parsed map.
14
+ */
15
+ layers?: any[];
16
+ /**
17
+ * Height used by tile depth calculations. Defaults to the Tiled tile height.
18
+ */
19
+ zTileHeight?: number;
20
+ /**
21
+ * v4 compatibility helper. Returns a Tiled layer by its name.
22
+ */
23
+ getLayerByName?(name: string): any;
24
+ /**
25
+ * v4 compatibility helper. Returns the one-dimensional tile index for tile coordinates.
26
+ */
27
+ getTileIndex?(x: number, y: number): number;
28
+ /**
29
+ * v4 compatibility helper. Returns the pixel origin of a tile coordinate.
30
+ */
31
+ getTileOriginPosition?(x: number, y: number): { x: number; y: number };
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;
9
48
  }
10
49
  }
11
50
 
package/dist/client.d.ts DELETED
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
package/dist/server.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { RpgMap } from '@rpgjs/server';
2
- import { MapClass } from '@canvasengine/tiled';
3
- declare module "@rpgjs/server" {
4
- interface RpgMap {
5
- tiled?: MapClass;
6
- }
7
- }
8
- export interface RpgTiledMap extends RpgMap {
9
- tiled: MapClass;
10
- }
11
- declare const _default: any;
12
- export default _default;
File without changes