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

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,17 @@
1
+ import { MapClass } from '@canvasengine/tiled';
2
+ type AnyMap = {
3
+ tiled?: MapClass;
4
+ physic?: {
5
+ getEntityByUUID(id: string): any;
6
+ };
7
+ _blockedTiles?: Set<string>;
8
+ _tiledTileWidth?: number;
9
+ _tiledTileHeight?: number;
10
+ _tiledCollisionUnsubscribers?: Map<string, () => void>;
11
+ };
12
+ export declare function prepareTiledPhysicsData(mapData: any, map: AnyMap): void;
13
+ export declare function applyTiledPointEvents(mapData: any): void;
14
+ export declare function attachTiledCollisionToEntity(owner: any, map: AnyMap): void;
15
+ export declare function detachTiledCollisionFromEntity(owner: any, map: AnyMap): void;
16
+ export declare function resetTiledCollisionHandlers(map: AnyMap): void;
17
+ export {};
@@ -1,149 +1,28 @@
1
- import { MapClass } from "./index3.js";
2
1
  import { defineModule } from "@rpgjs/common";
2
+ import { resetTiledCollisionHandlers, detachTiledCollisionFromEntity, attachTiledCollisionToEntity, prepareTiledPhysicsData, applyTiledPointEvents } from "./index4.js";
3
3
  const server = defineModule({
4
4
  map: {
5
- /**
6
- * Hook called before map update
7
- *
8
- * @description Parses Tiled data and sets up tile-based collision detection
9
- * using the physics engine's tile grid system instead of individual hitboxes.
10
- *
11
- * This method:
12
- * 1. Parses TMX data with TiledParser
13
- * 2. Creates a MapClass instance with parsed data
14
- * 3. Attaches the Tiled instance to the RpgMap
15
- * 4. Scans all tiles to detect collisions
16
- * 5. Stores blocked tiles in a Set for use with the physics engine tile grid
17
- * 6. Configures tile dimensions for proper coordinate conversion
18
- *
19
- * The blocked tiles are used by the physics engine's `canEnterTile` hook
20
- * to prevent entities from entering collision tiles, which is more efficient
21
- * than creating individual hitboxes for each tile.
22
- *
23
- * @param mapData - Map data containing TMX information
24
- * @param map - RpgMap instance to extend
25
- * @returns The modified map instance with tiled property
26
- *
27
- * @example
28
- * ```ts
29
- * // Blocked tiles are stored as a Set with keys "x,y" (tile coordinates)
30
- * // The physics engine will automatically check these tiles when entities
31
- * // try to move, using the canEnterTile hook applied to all entities
32
- * ```
33
- */
34
5
  onBeforeUpdate(mapData, map) {
35
- const tiledMap = new MapClass(mapData.parsedMap);
36
- map.tiled = tiledMap;
37
- mapData.hitboxes = mapData.hitboxes || [];
38
- mapData.width = tiledMap.widthPx;
39
- mapData.height = tiledMap.heightPx;
40
- const tileWidth = tiledMap.tilewidth;
41
- const tileHeight = tiledMap.tileheight;
42
- map._tiledTileWidth = tileWidth;
43
- map._tiledTileHeight = tileHeight;
44
- const blockedTiles = /* @__PURE__ */ new Set();
45
- const mapWidth = tiledMap.width;
46
- const mapHeight = tiledMap.height;
47
- for (let y = 0; y < mapHeight; y++) {
48
- for (let x = 0; x < mapWidth; x++) {
49
- const pixelX = x * tileWidth;
50
- const pixelY = y * tileHeight;
51
- const tileInfo = tiledMap.getTileByPosition(pixelX, pixelY, [0, 0], {
52
- populateTiles: true
53
- });
54
- if (tileInfo.hasCollision) {
55
- blockedTiles.add(`${x},${y}`);
56
- }
57
- }
58
- }
59
- map._blockedTiles = blockedTiles;
60
- for (let obj of mapData.parsedMap.objects) {
61
- if (obj.point) {
62
- mapData.events = mapData.events.map((e) => {
63
- if (e.name === obj.name) {
64
- return {
65
- event: e,
66
- x: obj.x,
67
- y: obj.y
68
- };
69
- }
70
- return e;
71
- }).filter((e) => e !== null);
72
- }
73
- }
74
- setTimeout(() => {
75
- applyTileCollisionToEntities(map);
76
- }, 0);
6
+ prepareTiledPhysicsData(mapData, map);
7
+ applyTiledPointEvents(mapData);
77
8
  return map;
78
- }
79
- },
80
- player: {
81
- /**
82
- * Hook called when a player joins a map
83
- *
84
- * @description Applies tile-based collision detection to the player's physics entity
85
- * using the blocked tiles stored on the map
86
- *
87
- * @param player - The player instance
88
- * @param map - The map instance
89
- */
90
- onJoinMap(player, map) {
91
- setTimeout(() => {
92
- applyTileCollisionToEntity(player, map);
93
- }, 0);
94
- }
95
- }
96
- });
97
- function applyTileCollisionToEntity(owner, map) {
98
- if (!owner?.id || !map?._blockedTiles) {
99
- return;
100
- }
101
- const entity = map.physic?.getEntityByUUID(owner.id);
102
- if (!entity) {
103
- return;
104
- }
105
- const blockedTiles = map._blockedTiles;
106
- const tiledTileWidth = map._tiledTileWidth ?? 32;
107
- const tiledTileHeight = map._tiledTileHeight ?? 32;
108
- const physicsTileWidth = 32;
109
- const physicsTileHeight = 32;
110
- entity.canEnterTile(({ x, y }) => {
111
- const tiledX = Math.floor(x * physicsTileWidth / tiledTileWidth);
112
- const tiledY = Math.floor(y * physicsTileHeight / tiledTileHeight);
113
- const tileKey = `${tiledX},${tiledY}`;
114
- if (blockedTiles.has(tileKey)) {
115
- return false;
116
- }
117
- return true;
118
- });
119
- }
120
- function applyTileCollisionToEntities(map) {
121
- if (!map?._blockedTiles) {
122
- return;
123
- }
124
- if (map.players && typeof map.players === "function") {
125
- const players = map.players();
126
- if (players && typeof players === "object") {
127
- for (const playerId in players) {
128
- const player = players[playerId];
129
- if (player) {
130
- applyTileCollisionToEntity(player, map);
131
- }
9
+ },
10
+ onPhysicsInit(map, context) {
11
+ if (!map?._blockedTiles || !map?.tiled) {
12
+ prepareTiledPhysicsData(context?.mapData, map);
132
13
  }
14
+ },
15
+ onPhysicsEntityAdd(map, context) {
16
+ attachTiledCollisionToEntity(context?.owner, map);
17
+ },
18
+ onPhysicsEntityRemove(map, context) {
19
+ detachTiledCollisionFromEntity(context?.owner, map);
20
+ },
21
+ onPhysicsReset(map) {
22
+ resetTiledCollisionHandlers(map);
133
23
  }
134
24
  }
135
- if (map.events && typeof map.events === "function") {
136
- const events = map.events();
137
- if (events && typeof events === "object") {
138
- for (const eventId in events) {
139
- const event = events[eventId];
140
- if (event) {
141
- applyTileCollisionToEntity(event, map);
142
- }
143
- }
144
- }
145
- }
146
- }
25
+ });
147
26
  export {
148
27
  server as default
149
28
  };
@@ -0,0 +1,117 @@
1
+ import { MapClass } from "./index3.js";
2
+ function prepareTiledPhysicsData(mapData, map) {
3
+ if (!mapData?.parsedMap) {
4
+ return;
5
+ }
6
+ const tiledMap = new MapClass(mapData.parsedMap);
7
+ map.tiled = tiledMap;
8
+ mapData.hitboxes = mapData.hitboxes || [];
9
+ mapData.width = tiledMap.widthPx;
10
+ mapData.height = tiledMap.heightPx;
11
+ map._tiledTileWidth = tiledMap.tilewidth;
12
+ map._tiledTileHeight = tiledMap.tileheight;
13
+ map._blockedTiles = collectBlockedTiles(tiledMap);
14
+ }
15
+ function applyTiledPointEvents(mapData) {
16
+ const objects = mapData?.parsedMap?.objects;
17
+ if (!Array.isArray(objects) || !Array.isArray(mapData?.events)) {
18
+ return;
19
+ }
20
+ for (const obj of objects) {
21
+ if (!obj?.point) {
22
+ continue;
23
+ }
24
+ mapData.events = mapData.events.map((eventEntry) => {
25
+ if (eventEntry?.name === obj.name) {
26
+ return {
27
+ event: eventEntry,
28
+ x: obj.x,
29
+ y: obj.y
30
+ };
31
+ }
32
+ return eventEntry;
33
+ }).filter((eventEntry) => eventEntry !== null);
34
+ }
35
+ }
36
+ function attachTiledCollisionToEntity(owner, map) {
37
+ if (!owner?.id || !map?._blockedTiles) {
38
+ return;
39
+ }
40
+ const entity = map.physic?.getEntityByUUID(owner.id);
41
+ if (!entity || typeof entity.canEnterTile !== "function") {
42
+ return;
43
+ }
44
+ const unsubscribers = ensureUnsubscribers(map);
45
+ const previousUnsubscribe = unsubscribers.get(owner.id);
46
+ if (previousUnsubscribe) {
47
+ previousUnsubscribe();
48
+ unsubscribers.delete(owner.id);
49
+ }
50
+ const blockedTiles = map._blockedTiles;
51
+ const tiledTileWidth = map._tiledTileWidth ?? 32;
52
+ const tiledTileHeight = map._tiledTileHeight ?? 32;
53
+ const physicsTileWidth = 32;
54
+ const physicsTileHeight = 32;
55
+ const unsubscribe = entity.canEnterTile(({ x, y }) => {
56
+ const tiledX = Math.floor(x * physicsTileWidth / tiledTileWidth);
57
+ const tiledY = Math.floor(y * physicsTileHeight / tiledTileHeight);
58
+ return !blockedTiles.has(`${tiledX},${tiledY}`);
59
+ });
60
+ unsubscribers.set(owner.id, unsubscribe);
61
+ }
62
+ function detachTiledCollisionFromEntity(owner, map) {
63
+ if (!owner?.id) {
64
+ return;
65
+ }
66
+ const unsubscribers = map._tiledCollisionUnsubscribers;
67
+ if (!unsubscribers) {
68
+ return;
69
+ }
70
+ const unsubscribe = unsubscribers.get(owner.id);
71
+ if (!unsubscribe) {
72
+ return;
73
+ }
74
+ unsubscribe();
75
+ unsubscribers.delete(owner.id);
76
+ }
77
+ function resetTiledCollisionHandlers(map) {
78
+ const unsubscribers = map._tiledCollisionUnsubscribers;
79
+ if (unsubscribers) {
80
+ for (const unsubscribe of unsubscribers.values()) {
81
+ unsubscribe();
82
+ }
83
+ unsubscribers.clear();
84
+ }
85
+ map._blockedTiles = void 0;
86
+ map._tiledTileWidth = void 0;
87
+ map._tiledTileHeight = void 0;
88
+ }
89
+ function collectBlockedTiles(tiledMap) {
90
+ const blockedTiles = /* @__PURE__ */ new Set();
91
+ const mapWidth = tiledMap.width;
92
+ const mapHeight = tiledMap.height;
93
+ const tileWidth = tiledMap.tilewidth;
94
+ const tileHeight = tiledMap.tileheight;
95
+ for (let y = 0; y < mapHeight; y++) {
96
+ for (let x = 0; x < mapWidth; x++) {
97
+ const tileInfo = tiledMap.getTileByPosition(x * tileWidth, y * tileHeight, [0, 0], {
98
+ populateTiles: true
99
+ });
100
+ if (tileInfo.hasCollision) {
101
+ blockedTiles.add(`${x},${y}`);
102
+ }
103
+ }
104
+ }
105
+ return blockedTiles;
106
+ }
107
+ function ensureUnsubscribers(map) {
108
+ map._tiledCollisionUnsubscribers = map._tiledCollisionUnsubscribers || /* @__PURE__ */ new Map();
109
+ return map._tiledCollisionUnsubscribers;
110
+ }
111
+ export {
112
+ applyTiledPointEvents,
113
+ attachTiledCollisionToEntity,
114
+ detachTiledCollisionFromEntity,
115
+ prepareTiledPhysicsData,
116
+ resetTiledCollisionHandlers
117
+ };
package/dist/server.d.ts CHANGED
@@ -5,61 +5,8 @@ declare module "@rpgjs/server" {
5
5
  tiled?: MapClass;
6
6
  }
7
7
  }
8
- /**
9
- * Interface for an RpgMap extended with Tiled functionality
10
- *
11
- * @description This interface combines RpgMap with MapClass to enable
12
- * the use of Tiled methods on RPG maps
13
- */
14
8
  export interface RpgTiledMap extends RpgMap {
15
9
  tiled: MapClass;
16
10
  }
17
- /**
18
- * Tiled Module for RPGJS
19
- *
20
- * @description This module extends RPGJS maps with Tiled functionality,
21
- * allowing TMX map parsing and automatic tile-based collision detection
22
- * using the physics engine's tile grid system
23
- *
24
- * ## Features
25
- *
26
- * - **Automatic parsing**: Parses TMX files from Tiled Map Editor
27
- * - **Collision detection**: Scans all tiles to detect collisions
28
- * - **Tile grid system**: Uses physics engine tile grid to block movement on collision tiles
29
- * - **RpgMap extension**: Adds the `tiled` property to all RpgMap instances
30
- *
31
- * ## Usage
32
- *
33
- * Once this module is activated, you can use Tiled methods on your maps:
34
- *
35
- * @example
36
- * ```ts
37
- * // In a map class
38
- * class MyMap extends RpgMap {
39
- * onLoad() {
40
- * // Access Tiled functionality
41
- * const tiles = this.tiled.getTileByPosition(100, 100);
42
- *
43
- * if (tiles.hasCollision) {
44
- * console.log('This position has a collision');
45
- * }
46
- *
47
- * // Iterate through all tiles by index
48
- * for (let i = 0; i < this.tiled.width * this.tiled.height; i++) {
49
- * const tileInfo = this.tiled.getTileByIndex(i);
50
- * if (tileInfo.hasCollision) {
51
- * console.log(`Tile ${i} has collision`);
52
- * }
53
- * }
54
- *
55
- * // Get information about a specific layer
56
- * const layer = this.tiled.getLayerByName('Collision');
57
- * if (layer) {
58
- * console.log('Collision layer found:', layer);
59
- * }
60
- * }
61
- * }
62
- * ```
63
- */
64
11
  declare const _default: any;
65
12
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/tiledmap",
3
- "version": "5.0.0-alpha.40",
3
+ "version": "5.0.0-alpha.42",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -23,10 +23,10 @@
23
23
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
24
24
  "peerDependencies": {
25
25
  "@canvasengine/presets": "*",
26
- "@rpgjs/client": "5.0.0-alpha.40",
27
- "@rpgjs/common": "5.0.0-alpha.40",
28
- "@rpgjs/server": "5.0.0-alpha.40",
29
- "@rpgjs/vite": "5.0.0-alpha.40",
26
+ "@rpgjs/client": "5.0.0-alpha.42",
27
+ "@rpgjs/common": "5.0.0-alpha.42",
28
+ "@rpgjs/server": "5.0.0-alpha.42",
29
+ "@rpgjs/vite": "5.0.0-alpha.42",
30
30
  "canvasengine": "*"
31
31
  },
32
32
  "publishConfig": {
package/src/client.ts CHANGED
@@ -1,6 +1,26 @@
1
- import { RpgClient, RpgClientEngine } from "@rpgjs/client";
1
+ import { RpgClient } from "@rpgjs/client";
2
2
  import { defineModule } from "@rpgjs/common";
3
+ import {
4
+ attachTiledCollisionToEntity,
5
+ detachTiledCollisionFromEntity,
6
+ prepareTiledPhysicsData,
7
+ resetTiledCollisionHandlers,
8
+ } from "./physics";
3
9
 
4
10
  export default defineModule<RpgClient>({
5
- componentAnimations: []
6
- })
11
+ componentAnimations: [],
12
+ sceneMap: {
13
+ onPhysicsInit(map: any, context: { mapData: any }) {
14
+ prepareTiledPhysicsData(context?.mapData, map);
15
+ },
16
+ onPhysicsEntityAdd(map: any, context: { owner: any }) {
17
+ attachTiledCollisionToEntity(context?.owner, map);
18
+ },
19
+ onPhysicsEntityRemove(map: any, context: { owner: any }) {
20
+ detachTiledCollisionFromEntity(context?.owner, map);
21
+ },
22
+ onPhysicsReset(map: any) {
23
+ resetTiledCollisionHandlers(map);
24
+ },
25
+ },
26
+ });
package/src/physics.ts ADDED
@@ -0,0 +1,143 @@
1
+ import { MapClass } from "@canvasengine/tiled";
2
+
3
+ type AnyMap = {
4
+ tiled?: MapClass;
5
+ physic?: {
6
+ getEntityByUUID(id: string): any;
7
+ };
8
+ _blockedTiles?: Set<string>;
9
+ _tiledTileWidth?: number;
10
+ _tiledTileHeight?: number;
11
+ _tiledCollisionUnsubscribers?: Map<string, () => void>;
12
+ };
13
+
14
+ export function prepareTiledPhysicsData(mapData: any, map: AnyMap): void {
15
+ if (!mapData?.parsedMap) {
16
+ return;
17
+ }
18
+
19
+ const tiledMap = new MapClass(mapData.parsedMap);
20
+ map.tiled = tiledMap;
21
+
22
+ mapData.hitboxes = mapData.hitboxes || [];
23
+ mapData.width = tiledMap.widthPx;
24
+ mapData.height = tiledMap.heightPx;
25
+
26
+ map._tiledTileWidth = tiledMap.tilewidth;
27
+ map._tiledTileHeight = tiledMap.tileheight;
28
+ map._blockedTiles = collectBlockedTiles(tiledMap);
29
+ }
30
+
31
+ export function applyTiledPointEvents(mapData: any): void {
32
+ const objects = mapData?.parsedMap?.objects;
33
+ if (!Array.isArray(objects) || !Array.isArray(mapData?.events)) {
34
+ return;
35
+ }
36
+
37
+ for (const obj of objects) {
38
+ if (!obj?.point) {
39
+ continue;
40
+ }
41
+
42
+ mapData.events = mapData.events
43
+ .map((eventEntry: any) => {
44
+ if (eventEntry?.name === obj.name) {
45
+ return {
46
+ event: eventEntry,
47
+ x: obj.x,
48
+ y: obj.y,
49
+ };
50
+ }
51
+ return eventEntry;
52
+ })
53
+ .filter((eventEntry: any) => eventEntry !== null);
54
+ }
55
+ }
56
+
57
+ export function attachTiledCollisionToEntity(owner: any, map: AnyMap): void {
58
+ if (!owner?.id || !map?._blockedTiles) {
59
+ return;
60
+ }
61
+
62
+ const entity = map.physic?.getEntityByUUID(owner.id);
63
+ if (!entity || typeof entity.canEnterTile !== "function") {
64
+ return;
65
+ }
66
+
67
+ const unsubscribers = ensureUnsubscribers(map);
68
+ const previousUnsubscribe = unsubscribers.get(owner.id);
69
+ if (previousUnsubscribe) {
70
+ previousUnsubscribe();
71
+ unsubscribers.delete(owner.id);
72
+ }
73
+
74
+ const blockedTiles = map._blockedTiles;
75
+ const tiledTileWidth = map._tiledTileWidth ?? 32;
76
+ const tiledTileHeight = map._tiledTileHeight ?? 32;
77
+ const physicsTileWidth = 32;
78
+ const physicsTileHeight = 32;
79
+
80
+ const unsubscribe = entity.canEnterTile(({ x, y }) => {
81
+ const tiledX = Math.floor((x * physicsTileWidth) / tiledTileWidth);
82
+ const tiledY = Math.floor((y * physicsTileHeight) / tiledTileHeight);
83
+ return !blockedTiles.has(`${tiledX},${tiledY}`);
84
+ });
85
+
86
+ unsubscribers.set(owner.id, unsubscribe);
87
+ }
88
+
89
+ export function detachTiledCollisionFromEntity(owner: any, map: AnyMap): void {
90
+ if (!owner?.id) {
91
+ return;
92
+ }
93
+ const unsubscribers = map._tiledCollisionUnsubscribers;
94
+ if (!unsubscribers) {
95
+ return;
96
+ }
97
+ const unsubscribe = unsubscribers.get(owner.id);
98
+ if (!unsubscribe) {
99
+ return;
100
+ }
101
+ unsubscribe();
102
+ unsubscribers.delete(owner.id);
103
+ }
104
+
105
+ export function resetTiledCollisionHandlers(map: AnyMap): void {
106
+ const unsubscribers = map._tiledCollisionUnsubscribers;
107
+ if (unsubscribers) {
108
+ for (const unsubscribe of unsubscribers.values()) {
109
+ unsubscribe();
110
+ }
111
+ unsubscribers.clear();
112
+ }
113
+
114
+ map._blockedTiles = undefined;
115
+ map._tiledTileWidth = undefined;
116
+ map._tiledTileHeight = undefined;
117
+ }
118
+
119
+ function collectBlockedTiles(tiledMap: MapClass): Set<string> {
120
+ const blockedTiles = new Set<string>();
121
+ const mapWidth = tiledMap.width;
122
+ const mapHeight = tiledMap.height;
123
+ const tileWidth = tiledMap.tilewidth;
124
+ const tileHeight = tiledMap.tileheight;
125
+
126
+ for (let y = 0; y < mapHeight; y++) {
127
+ for (let x = 0; x < mapWidth; x++) {
128
+ const tileInfo = tiledMap.getTileByPosition(x * tileWidth, y * tileHeight, [0, 0], {
129
+ populateTiles: true,
130
+ });
131
+ if (tileInfo.hasCollision) {
132
+ blockedTiles.add(`${x},${y}`);
133
+ }
134
+ }
135
+ }
136
+
137
+ return blockedTiles;
138
+ }
139
+
140
+ function ensureUnsubscribers(map: AnyMap): Map<string, () => void> {
141
+ map._tiledCollisionUnsubscribers = map._tiledCollisionUnsubscribers || new Map();
142
+ return map._tiledCollisionUnsubscribers;
143
+ }