@rpgjs/tiledmap 5.0.0-alpha.40 → 5.0.0-alpha.41

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.
package/src/server.ts CHANGED
@@ -1,288 +1,44 @@
1
1
  import { RpgMap, RpgServer } from "@rpgjs/server";
2
2
  import { MapClass } from "@canvasengine/tiled";
3
3
  import { defineModule } from "@rpgjs/common";
4
+ import {
5
+ applyTiledPointEvents,
6
+ attachTiledCollisionToEntity,
7
+ detachTiledCollisionFromEntity,
8
+ prepareTiledPhysicsData,
9
+ resetTiledCollisionHandlers,
10
+ } from "./physics";
4
11
 
5
- // Extend RpgMap interface to include tiled property
6
12
  declare module "@rpgjs/server" {
7
13
  interface RpgMap {
8
14
  tiled?: MapClass;
9
15
  }
10
16
  }
11
17
 
12
- /**
13
- * Interface for an RpgMap extended with Tiled functionality
14
- *
15
- * @description This interface combines RpgMap with MapClass to enable
16
- * the use of Tiled methods on RPG maps
17
- */
18
18
  export interface RpgTiledMap extends RpgMap {
19
19
  tiled: MapClass;
20
20
  }
21
21
 
22
- /**
23
- * Tiled Module for RPGJS
24
- *
25
- * @description This module extends RPGJS maps with Tiled functionality,
26
- * allowing TMX map parsing and automatic tile-based collision detection
27
- * using the physics engine's tile grid system
28
- *
29
- * ## Features
30
- *
31
- * - **Automatic parsing**: Parses TMX files from Tiled Map Editor
32
- * - **Collision detection**: Scans all tiles to detect collisions
33
- * - **Tile grid system**: Uses physics engine tile grid to block movement on collision tiles
34
- * - **RpgMap extension**: Adds the `tiled` property to all RpgMap instances
35
- *
36
- * ## Usage
37
- *
38
- * Once this module is activated, you can use Tiled methods on your maps:
39
- *
40
- * @example
41
- * ```ts
42
- * // In a map class
43
- * class MyMap extends RpgMap {
44
- * onLoad() {
45
- * // Access Tiled functionality
46
- * const tiles = this.tiled.getTileByPosition(100, 100);
47
- *
48
- * if (tiles.hasCollision) {
49
- * console.log('This position has a collision');
50
- * }
51
- *
52
- * // Iterate through all tiles by index
53
- * for (let i = 0; i < this.tiled.width * this.tiled.height; i++) {
54
- * const tileInfo = this.tiled.getTileByIndex(i);
55
- * if (tileInfo.hasCollision) {
56
- * console.log(`Tile ${i} has collision`);
57
- * }
58
- * }
59
- *
60
- * // Get information about a specific layer
61
- * const layer = this.tiled.getLayerByName('Collision');
62
- * if (layer) {
63
- * console.log('Collision layer found:', layer);
64
- * }
65
- * }
66
- * }
67
- * ```
68
- */
69
22
  export default defineModule<RpgServer>({
70
23
  map: {
71
- /**
72
- * Hook called before map update
73
- *
74
- * @description Parses Tiled data and sets up tile-based collision detection
75
- * using the physics engine's tile grid system instead of individual hitboxes.
76
- *
77
- * This method:
78
- * 1. Parses TMX data with TiledParser
79
- * 2. Creates a MapClass instance with parsed data
80
- * 3. Attaches the Tiled instance to the RpgMap
81
- * 4. Scans all tiles to detect collisions
82
- * 5. Stores blocked tiles in a Set for use with the physics engine tile grid
83
- * 6. Configures tile dimensions for proper coordinate conversion
84
- *
85
- * The blocked tiles are used by the physics engine's `canEnterTile` hook
86
- * to prevent entities from entering collision tiles, which is more efficient
87
- * than creating individual hitboxes for each tile.
88
- *
89
- * @param mapData - Map data containing TMX information
90
- * @param map - RpgMap instance to extend
91
- * @returns The modified map instance with tiled property
92
- *
93
- * @example
94
- * ```ts
95
- * // Blocked tiles are stored as a Set with keys "x,y" (tile coordinates)
96
- * // The physics engine will automatically check these tiles when entities
97
- * // try to move, using the canEnterTile hook applied to all entities
98
- * ```
99
- */
100
24
  onBeforeUpdate<T = RpgMap>(mapData: any, map: T): T {
101
- const tiledMap = new MapClass(mapData.parsedMap);
102
-
103
- // Attach Tiled instance to the map
104
- (map as any).tiled = tiledMap;
105
-
106
- // Initialize hitboxes array (for backward compatibility, but we won't populate it)
107
- mapData.hitboxes = mapData.hitboxes || [];
108
- mapData.width = tiledMap.widthPx;
109
- mapData.height = tiledMap.heightPx;
110
-
111
- // Store tile dimensions for coordinate conversion
112
- const tileWidth = tiledMap.tilewidth;
113
- const tileHeight = tiledMap.tileheight;
114
- (map as any)._tiledTileWidth = tileWidth;
115
- (map as any)._tiledTileHeight = tileHeight;
116
-
117
- // Store blocked tiles in a Set for efficient lookup
118
- // Key format: "x,y" where x and y are tile coordinates in Tiled's coordinate system
119
- const blockedTiles = new Set<string>();
120
-
121
- // Iterate through all map tiles to detect collisions
122
- const mapWidth = tiledMap.width;
123
- const mapHeight = tiledMap.height;
124
-
125
- // Iterate through each tile on the map
126
- for (let y = 0; y < mapHeight; y++) {
127
- for (let x = 0; x < mapWidth; x++) {
128
- // Use getTileByPosition which is simpler and handles pixel coordinates directly
129
- const pixelX = x * tileWidth;
130
- const pixelY = y * tileHeight;
131
- const tileInfo = tiledMap.getTileByPosition(pixelX, pixelY, [0, 0], {
132
- populateTiles: true,
133
- });
134
-
135
- // If tile has collision, add it to the blocked tiles set
136
- if (tileInfo.hasCollision) {
137
- blockedTiles.add(`${x},${y}`);
138
- }
139
- }
140
- }
141
-
142
- // Store blocked tiles on the map instance
143
- (map as any)._blockedTiles = blockedTiles;
144
-
145
- for (let obj of mapData.parsedMap.objects) {
146
- if (obj.point) {
147
- mapData.events = mapData.events
148
- .map((e) => {
149
- if (e.name === obj.name) {
150
- return {
151
- event: e,
152
- x: obj.x,
153
- y: obj.y,
154
- };
155
- }
156
- return e;
157
- })
158
- .filter((e) => e !== null);
159
- }
160
- }
161
-
162
- // Apply tile collision to all existing entities after a short delay
163
- // to ensure physics entities are created
164
- setTimeout(() => {
165
- applyTileCollisionToEntities(map as any);
166
- }, 0);
167
-
25
+ prepareTiledPhysicsData(mapData, map as any);
26
+ applyTiledPointEvents(mapData);
168
27
  return map;
169
28
  },
170
- },
171
- player: {
172
- /**
173
- * Hook called when a player joins a map
174
- *
175
- * @description Applies tile-based collision detection to the player's physics entity
176
- * using the blocked tiles stored on the map
177
- *
178
- * @param player - The player instance
179
- * @param map - The map instance
180
- */
181
- onJoinMap(player: any, map: any) {
182
- // Apply tile collision after a short delay to ensure physics entity is created
183
- setTimeout(() => {
184
- applyTileCollisionToEntity(player, map);
185
- }, 0);
29
+ onPhysicsInit(map: any, context: { mapData: any }) {
30
+ if (!map?._blockedTiles || !map?.tiled) {
31
+ prepareTiledPhysicsData(context?.mapData, map);
32
+ }
33
+ },
34
+ onPhysicsEntityAdd(map: any, context: { owner: any }) {
35
+ attachTiledCollisionToEntity(context?.owner, map);
36
+ },
37
+ onPhysicsEntityRemove(map: any, context: { owner: any }) {
38
+ detachTiledCollisionFromEntity(context?.owner, map);
39
+ },
40
+ onPhysicsReset(map: any) {
41
+ resetTiledCollisionHandlers(map);
186
42
  },
187
43
  },
188
44
  });
189
-
190
- /**
191
- * Applies tile-based collision detection to a single entity
192
- *
193
- * @description This function sets up the `canEnterTile` hook on an entity's physics body
194
- * to prevent movement into blocked tiles. It converts tile coordinates from the physics
195
- * engine's coordinate system (based on default 32x32 tiles) to Tiled's coordinate system.
196
- *
197
- * @param owner - The owner object (player or event) that has a physics entity
198
- * @param map - The map instance containing blocked tiles
199
- *
200
- * @example
201
- * ```ts
202
- * // This is called automatically when a player joins a map or an event is created
203
- * // The function checks if the tile the entity is trying to enter is in the
204
- * // blocked tiles set, converting coordinates as needed
205
- * ```
206
- */
207
- function applyTileCollisionToEntity(owner: any, map: any) {
208
- if (!owner?.id || !map?._blockedTiles) {
209
- return;
210
- }
211
-
212
- const entity = map.physic?.getEntityByUUID(owner.id);
213
- if (!entity) {
214
- return;
215
- }
216
-
217
- const blockedTiles = map._blockedTiles as Set<string>;
218
- const tiledTileWidth = map._tiledTileWidth ?? 32;
219
- const tiledTileHeight = map._tiledTileHeight ?? 32;
220
-
221
- // Physics engine uses default 32x32 tiles, but Tiled may have different dimensions
222
- // We need to convert physics engine tile coordinates to Tiled tile coordinates
223
- const physicsTileWidth = 32; // Default physics engine tile width
224
- const physicsTileHeight = 32; // Default physics engine tile height
225
-
226
- // Apply canEnterTile hook to the entity
227
- entity.canEnterTile(({ x, y }) => {
228
- // x, y are tile coordinates from the physics engine (based on 32x32 tiles)
229
- // Convert to Tiled tile coordinates
230
- const tiledX = Math.floor((x * physicsTileWidth) / tiledTileWidth);
231
- const tiledY = Math.floor((y * physicsTileHeight) / tiledTileHeight);
232
-
233
- // Check if this tile is blocked
234
- const tileKey = `${tiledX},${tiledY}`;
235
- if (blockedTiles.has(tileKey)) {
236
- return false; // Block movement into this tile
237
- }
238
-
239
- return true; // Allow movement
240
- });
241
- }
242
-
243
- /**
244
- * Applies tile-based collision detection to all existing entities on a map
245
- *
246
- * @description This function iterates through all players and events on the map
247
- * and applies the tile collision hook to each one's physics entity. This is useful
248
- * when setting up the map for the first time or when entities already exist before
249
- * the map is loaded.
250
- *
251
- * @param map - The map instance containing blocked tiles
252
- *
253
- * @example
254
- * ```ts
255
- * // Called automatically in onBeforeUpdate to apply collision to existing entities
256
- * ```
257
- */
258
- function applyTileCollisionToEntities(map: any) {
259
- if (!map?._blockedTiles) {
260
- return;
261
- }
262
-
263
- // Apply to all players
264
- if (map.players && typeof map.players === 'function') {
265
- const players = map.players();
266
- if (players && typeof players === 'object') {
267
- for (const playerId in players) {
268
- const player = players[playerId];
269
- if (player) {
270
- applyTileCollisionToEntity(player, map);
271
- }
272
- }
273
- }
274
- }
275
-
276
- // Apply to all events
277
- if (map.events && typeof map.events === 'function') {
278
- const events = map.events();
279
- if (events && typeof events === 'object') {
280
- for (const eventId in events) {
281
- const event = events[eventId];
282
- if (event) {
283
- applyTileCollisionToEntity(event, map);
284
- }
285
- }
286
- }
287
- }
288
- }