@rpgjs/server 5.0.0-alpha.25 → 5.0.0-alpha.27
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/dist/Player/MoveManager.d.ts +62 -1
- package/dist/Player/Player.d.ts +33 -22
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1827 -365
- package/dist/index.js.map +1 -1
- package/dist/rooms/BaseRoom.d.ts +95 -0
- package/dist/rooms/lobby.d.ts +4 -1
- package/dist/rooms/map.d.ts +17 -75
- package/package.json +10 -10
- package/src/Player/ItemManager.ts +50 -15
- package/src/Player/MoveManager.ts +654 -112
- package/src/Player/Player.ts +179 -136
- package/src/index.ts +2 -1
- package/src/module.ts +13 -0
- package/src/rooms/BaseRoom.ts +120 -0
- package/src/rooms/lobby.ts +11 -1
- package/src/rooms/map.ts +70 -146
- package/tests/change-map.spec.ts +2 -2
- package/tests/event.spec.ts +80 -0
- package/tests/item.spec.ts +455 -441
- package/tests/move.spec.ts +601 -0
- package/tests/random-move.spec.ts +65 -0
- package/tests/world-maps.spec.ts +43 -81
package/src/rooms/map.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Action, MockConnection, Request, Room, RoomOnJoin } from "@signe/room";
|
|
2
|
-
import { Hooks, IceMovement, ModulesToken, ProjectileMovement, ProjectileType, RpgCommonMap, Direction, RpgCommonPlayer, RpgShape } from "@rpgjs/common";
|
|
1
|
+
import { Action, MockConnection, Request, Room, RoomMethods, RoomOnJoin } from "@signe/room";
|
|
2
|
+
import { Hooks, IceMovement, ModulesToken, ProjectileMovement, ProjectileType, RpgCommonMap, Direction, RpgCommonPlayer, RpgShape, findModules } from "@rpgjs/common";
|
|
3
3
|
import { WorldMapsManager, type WorldMapConfig } from "@rpgjs/common";
|
|
4
4
|
import { RpgPlayer, RpgEvent } from "../Player/Player";
|
|
5
5
|
import { generateShortUUID, sync, type, users } from "@signe/sync";
|
|
@@ -13,6 +13,7 @@ import { COEFFICIENT_ELEMENTS, DAMAGE_CRITICAL, DAMAGE_PHYSIC, DAMAGE_SKILL } fr
|
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { EntityState } from "@rpgjs/physic";
|
|
15
15
|
import { MapOptions } from "../decorators/map";
|
|
16
|
+
import { BaseRoom } from "./BaseRoom";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Interface for input controls configuration
|
|
@@ -113,7 +114,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
113
114
|
* ```
|
|
114
115
|
*/
|
|
115
116
|
@users(RpgPlayer) players = signal({});
|
|
116
|
-
|
|
117
|
+
|
|
117
118
|
/**
|
|
118
119
|
* Synchronized signal containing all events (NPCs, objects) on the map
|
|
119
120
|
*
|
|
@@ -130,7 +131,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
130
131
|
* ```
|
|
131
132
|
*/
|
|
132
133
|
@sync(RpgPlayer) events = signal({});
|
|
133
|
-
|
|
134
|
+
|
|
134
135
|
/**
|
|
135
136
|
* Signal containing the map's database of items, classes, and other game data
|
|
136
137
|
*
|
|
@@ -148,7 +149,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
148
149
|
* ```
|
|
149
150
|
*/
|
|
150
151
|
database = signal({});
|
|
151
|
-
|
|
152
|
+
|
|
152
153
|
/**
|
|
153
154
|
* Array of map configurations - can contain MapOptions objects or instances of map classes
|
|
154
155
|
*
|
|
@@ -156,7 +157,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
156
157
|
* It's populated when the map is loaded via `updateMap()`.
|
|
157
158
|
*/
|
|
158
159
|
maps: (MapOptions | any)[] = []
|
|
159
|
-
|
|
160
|
+
|
|
160
161
|
/**
|
|
161
162
|
* Array of sound IDs to play when players join the map
|
|
162
163
|
*
|
|
@@ -170,7 +171,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
170
171
|
* ```
|
|
171
172
|
*/
|
|
172
173
|
sounds: string[] = []
|
|
173
|
-
|
|
174
|
+
|
|
174
175
|
/**
|
|
175
176
|
* BehaviorSubject that completes when the map data is ready
|
|
176
177
|
*
|
|
@@ -186,7 +187,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
186
187
|
* ```
|
|
187
188
|
*/
|
|
188
189
|
dataIsReady$ = new BehaviorSubject<void>(undefined);
|
|
189
|
-
|
|
190
|
+
|
|
190
191
|
/**
|
|
191
192
|
* Global configuration object for the map
|
|
192
193
|
*
|
|
@@ -194,7 +195,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
194
195
|
* It's populated from the map data when `updateMap()` is called.
|
|
195
196
|
*/
|
|
196
197
|
globalConfig: any = {}
|
|
197
|
-
|
|
198
|
+
|
|
198
199
|
/**
|
|
199
200
|
* Damage formulas configuration for the map
|
|
200
201
|
*
|
|
@@ -212,12 +213,24 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
212
213
|
/** Enable/disable automatic tick processing (useful for unit tests) */
|
|
213
214
|
private _autoTickEnabled: boolean = true;
|
|
214
215
|
|
|
215
|
-
|
|
216
|
+
autoSync: boolean = true;
|
|
217
|
+
|
|
218
|
+
constructor(room) {
|
|
216
219
|
super();
|
|
217
220
|
this.hooks.callHooks("server-map-onStart", this).subscribe();
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
+
const isTest = room.env.TEST === 'true' ? true : false;
|
|
222
|
+
if (isTest) {
|
|
223
|
+
this.autoSync = false;
|
|
224
|
+
this.setAutoTick(false);
|
|
225
|
+
this.autoTickEnabled = false;
|
|
226
|
+
this.throttleSync = 0;
|
|
227
|
+
this.throttleStorage = 0;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this.throttleSync = this.isStandalone ? 1 : 50
|
|
231
|
+
this.throttleStorage = this.isStandalone ? 1 : 50
|
|
232
|
+
};
|
|
233
|
+
this.sessionExpiryTime = 1000 * 60 * 5;
|
|
221
234
|
this.setupCollisionDetection();
|
|
222
235
|
if (this._autoTickEnabled) {
|
|
223
236
|
this.loop();
|
|
@@ -291,7 +304,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
291
304
|
// One of the entities is a shape
|
|
292
305
|
const shape = shapeA || shapeB;
|
|
293
306
|
const otherEntity = shapeA ? entityB : entityA;
|
|
294
|
-
|
|
307
|
+
|
|
295
308
|
if (shape) {
|
|
296
309
|
const shapeKey = `${otherEntity.uuid}-${shape.name}`;
|
|
297
310
|
if (!activeShapeCollisions.has(shapeKey)) {
|
|
@@ -327,7 +340,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
327
340
|
if (event) {
|
|
328
341
|
// Mark this collision as processed
|
|
329
342
|
activeCollisions.add(collisionKey);
|
|
330
|
-
|
|
343
|
+
|
|
331
344
|
// Trigger the onPlayerTouch hook on the event
|
|
332
345
|
event.execMethod('onPlayerTouch', [player]);
|
|
333
346
|
}
|
|
@@ -350,7 +363,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
350
363
|
// One of the entities is a shape
|
|
351
364
|
const shape = shapeA || shapeB;
|
|
352
365
|
const otherEntity = shapeA ? entityB : entityA;
|
|
353
|
-
|
|
366
|
+
|
|
354
367
|
if (shape) {
|
|
355
368
|
const shapeKey = `${otherEntity.uuid}-${shape.name}`;
|
|
356
369
|
if (activeShapeCollisions.has(shapeKey)) {
|
|
@@ -467,7 +480,11 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
467
480
|
* ```
|
|
468
481
|
*/
|
|
469
482
|
onJoin(player: RpgPlayer, conn: MockConnection) {
|
|
470
|
-
player.
|
|
483
|
+
if (player.setMap) {
|
|
484
|
+
player.setMap(this);
|
|
485
|
+
} else {
|
|
486
|
+
player.map = this;
|
|
487
|
+
}
|
|
471
488
|
player.context = context;
|
|
472
489
|
player.conn = conn;
|
|
473
490
|
player._onInit()
|
|
@@ -477,17 +494,17 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
477
494
|
if ((this as any).stopAllSoundsBeforeJoin) {
|
|
478
495
|
player.stopAllSounds();
|
|
479
496
|
}
|
|
480
|
-
|
|
481
|
-
this.sounds.forEach(sound => player.playSound(sound,{ loop: true }));
|
|
482
|
-
|
|
497
|
+
|
|
498
|
+
this.sounds.forEach(sound => player.playSound(sound, { loop: true }));
|
|
499
|
+
|
|
483
500
|
// Execute global map hooks (from RpgServer.map)
|
|
484
501
|
await lastValueFrom(this.hooks.callHooks("server-map-onJoin", player, this));
|
|
485
|
-
|
|
502
|
+
|
|
486
503
|
// // Execute map-specific hooks (from @MapData or MapOptions)
|
|
487
504
|
if (typeof (this as any)._onJoin === 'function') {
|
|
488
505
|
await (this as any)._onJoin(player);
|
|
489
506
|
}
|
|
490
|
-
|
|
507
|
+
|
|
491
508
|
// Execute player hooks
|
|
492
509
|
await lastValueFrom(this.hooks.callHooks("server-player-onJoinMap", player, this));
|
|
493
510
|
})
|
|
@@ -520,12 +537,12 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
520
537
|
async onLeave(player: RpgPlayer, conn: MockConnection) {
|
|
521
538
|
// Execute global map hooks (from RpgServer.map)
|
|
522
539
|
await lastValueFrom(this.hooks.callHooks("server-map-onLeave", player, this));
|
|
523
|
-
|
|
540
|
+
|
|
524
541
|
// Execute map-specific hooks (from @MapData or MapOptions)
|
|
525
542
|
if (typeof (this as any)._onLeave === 'function') {
|
|
526
543
|
await (this as any)._onLeave(player);
|
|
527
544
|
}
|
|
528
|
-
|
|
545
|
+
|
|
529
546
|
// Execute player hooks
|
|
530
547
|
await lastValueFrom(this.hooks.callHooks("server-player-onLeaveMap", player, this));
|
|
531
548
|
player.pendingInputs = [];
|
|
@@ -549,89 +566,6 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
549
566
|
return inject<Hooks>(context, ModulesToken);
|
|
550
567
|
}
|
|
551
568
|
|
|
552
|
-
/**
|
|
553
|
-
* Get the width of the map in pixels
|
|
554
|
-
*
|
|
555
|
-
* @returns The width of the map in pixels, or 0 if not loaded
|
|
556
|
-
*
|
|
557
|
-
* @example
|
|
558
|
-
* ```ts
|
|
559
|
-
* const width = map.widthPx;
|
|
560
|
-
* console.log(`Map width: ${width}px`);
|
|
561
|
-
* ```
|
|
562
|
-
*/
|
|
563
|
-
get widthPx(): number {
|
|
564
|
-
return this.data()?.width ?? 0
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Get the height of the map in pixels
|
|
569
|
-
*
|
|
570
|
-
* @returns The height of the map in pixels, or 0 if not loaded
|
|
571
|
-
*
|
|
572
|
-
* @example
|
|
573
|
-
* ```ts
|
|
574
|
-
* const height = map.heightPx;
|
|
575
|
-
* console.log(`Map height: ${height}px`);
|
|
576
|
-
* ```
|
|
577
|
-
*/
|
|
578
|
-
get heightPx(): number {
|
|
579
|
-
return this.data()?.height ?? 0
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
/**
|
|
583
|
-
* Get the unique identifier of the map
|
|
584
|
-
*
|
|
585
|
-
* @returns The map ID, or empty string if not loaded
|
|
586
|
-
*
|
|
587
|
-
* @example
|
|
588
|
-
* ```ts
|
|
589
|
-
* const mapId = map.id;
|
|
590
|
-
* console.log(`Current map: ${mapId}`);
|
|
591
|
-
* ```
|
|
592
|
-
*/
|
|
593
|
-
get id(): string {
|
|
594
|
-
return this.data()?.id ?? ''
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Get the X position of this map in the world coordinate system
|
|
599
|
-
*
|
|
600
|
-
* This is used when maps are part of a larger world map. The world position
|
|
601
|
-
* indicates where this map is located relative to other maps.
|
|
602
|
-
*
|
|
603
|
-
* @returns The X position in world coordinates, or 0 if not in a world
|
|
604
|
-
*
|
|
605
|
-
* @example
|
|
606
|
-
* ```ts
|
|
607
|
-
* const worldX = map.worldX;
|
|
608
|
-
* console.log(`Map is at world position (${worldX}, ${map.worldY})`);
|
|
609
|
-
* ```
|
|
610
|
-
*/
|
|
611
|
-
get worldX(): number {
|
|
612
|
-
const worldMaps = this.getWorldMapsManager?.();
|
|
613
|
-
return worldMaps?.getMapInfo(this.id)?.worldX ?? 0
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Get the Y position of this map in the world coordinate system
|
|
618
|
-
*
|
|
619
|
-
* This is used when maps are part of a larger world map. The world position
|
|
620
|
-
* indicates where this map is located relative to other maps.
|
|
621
|
-
*
|
|
622
|
-
* @returns The Y position in world coordinates, or 0 if not in a world
|
|
623
|
-
*
|
|
624
|
-
* @example
|
|
625
|
-
* ```ts
|
|
626
|
-
* const worldY = map.worldY;
|
|
627
|
-
* console.log(`Map is at world position (${map.worldX}, ${worldY})`);
|
|
628
|
-
* ```
|
|
629
|
-
*/
|
|
630
|
-
get worldY(): number {
|
|
631
|
-
const worldMaps = this.getWorldMapsManager?.();
|
|
632
|
-
return worldMaps?.getMapInfo(this.id)?.worldY ?? 0
|
|
633
|
-
}
|
|
634
|
-
|
|
635
569
|
/**
|
|
636
570
|
* Handle GUI interaction from a player
|
|
637
571
|
*
|
|
@@ -649,7 +583,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
649
583
|
*/
|
|
650
584
|
@Action('gui.interaction')
|
|
651
585
|
guiInteraction(player: RpgPlayer, value) {
|
|
652
|
-
|
|
586
|
+
this.hooks.callHooks("server-player-guiInteraction", player, value);
|
|
653
587
|
player.syncChanges();
|
|
654
588
|
}
|
|
655
589
|
|
|
@@ -791,6 +725,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
791
725
|
}
|
|
792
726
|
await lastValueFrom(this.hooks.callHooks("server-maps-load", this))
|
|
793
727
|
await lastValueFrom(this.hooks.callHooks("server-worldMaps-load", this))
|
|
728
|
+
await lastValueFrom(this.hooks.callHooks("server-databaseHooks-load", this))
|
|
794
729
|
|
|
795
730
|
map.events = map.events ?? []
|
|
796
731
|
|
|
@@ -811,7 +746,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
811
746
|
else {
|
|
812
747
|
this.sounds = map.sounds ?? []
|
|
813
748
|
}
|
|
814
|
-
|
|
749
|
+
|
|
815
750
|
// Attach map-specific hooks from MapOptions or @MapData
|
|
816
751
|
if (mapFound?.onLoad) {
|
|
817
752
|
(this as any)._onLoad = mapFound.onLoad;
|
|
@@ -836,15 +771,15 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
836
771
|
}
|
|
837
772
|
|
|
838
773
|
this.dataIsReady$.complete()
|
|
839
|
-
|
|
774
|
+
|
|
840
775
|
// Execute global map hooks (from RpgServer.map)
|
|
841
776
|
await lastValueFrom(this.hooks.callHooks("server-map-onLoad", this))
|
|
842
|
-
|
|
777
|
+
|
|
843
778
|
// Execute map-specific hooks (from @MapData or MapOptions)
|
|
844
779
|
if (typeof (this as any)._onLoad === 'function') {
|
|
845
780
|
await (this as any)._onLoad();
|
|
846
781
|
}
|
|
847
|
-
|
|
782
|
+
|
|
848
783
|
// TODO: Update map
|
|
849
784
|
}
|
|
850
785
|
|
|
@@ -1079,7 +1014,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1079
1014
|
if (this._inputLoopSubscription) {
|
|
1080
1015
|
this._inputLoopSubscription.unsubscribe();
|
|
1081
1016
|
}
|
|
1082
|
-
|
|
1017
|
+
|
|
1083
1018
|
this._inputLoopSubscription = this.tick$.pipe(
|
|
1084
1019
|
throttleTime(50) // Throttle to 50ms for input processing
|
|
1085
1020
|
).subscribe(async ({ timestamp }) => {
|
|
@@ -1232,8 +1167,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1232
1167
|
/**
|
|
1233
1168
|
* Add data to the map's database
|
|
1234
1169
|
*
|
|
1235
|
-
* This method
|
|
1236
|
-
* By default, if an ID already exists, the operation is ignored to prevent overwriting existing data.
|
|
1170
|
+
* This method delegates to BaseRoom's implementation to avoid code duplication.
|
|
1237
1171
|
*
|
|
1238
1172
|
* @param id - Unique identifier for the data
|
|
1239
1173
|
* @param data - The data to store (can be a class, object, or any value)
|
|
@@ -1257,23 +1191,13 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1257
1191
|
* ```
|
|
1258
1192
|
*/
|
|
1259
1193
|
addInDatabase(id: string, data: any, options?: { force?: boolean }): boolean {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
// Check if ID already exists
|
|
1263
|
-
if (database[id] !== undefined && !options?.force) {
|
|
1264
|
-
// Ignore the addition if ID exists and force is not enabled
|
|
1265
|
-
return false;
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// Add or overwrite the data
|
|
1269
|
-
database[id] = data;
|
|
1270
|
-
return true;
|
|
1194
|
+
return BaseRoom.prototype.addInDatabase.call(this, id, data, options);
|
|
1271
1195
|
}
|
|
1272
1196
|
|
|
1273
1197
|
/**
|
|
1274
1198
|
* Remove data from the map's database
|
|
1275
1199
|
*
|
|
1276
|
-
* This method
|
|
1200
|
+
* This method delegates to BaseRoom's implementation to avoid code duplication.
|
|
1277
1201
|
*
|
|
1278
1202
|
* @param id - Unique identifier of the data to remove
|
|
1279
1203
|
* @returns true if data was removed, false if ID didn't exist
|
|
@@ -1291,16 +1215,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1291
1215
|
* ```
|
|
1292
1216
|
*/
|
|
1293
1217
|
removeInDatabase(id: string): boolean {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
// Check if ID exists
|
|
1297
|
-
if (database[id] === undefined) {
|
|
1298
|
-
return false;
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
// Remove the data
|
|
1302
|
-
delete database[id];
|
|
1303
|
-
return true;
|
|
1218
|
+
return BaseRoom.prototype.removeInDatabase.call(this, id);
|
|
1304
1219
|
}
|
|
1305
1220
|
|
|
1306
1221
|
/**
|
|
@@ -1406,9 +1321,8 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1406
1321
|
eventInstance.map = this;
|
|
1407
1322
|
eventInstance.context = context;
|
|
1408
1323
|
|
|
1409
|
-
eventInstance.
|
|
1410
|
-
|
|
1411
|
-
|
|
1324
|
+
await eventInstance.teleport({ x, y });
|
|
1325
|
+
|
|
1412
1326
|
this.events()[id] = eventInstance;
|
|
1413
1327
|
|
|
1414
1328
|
await eventInstance.execMethod('onInit')
|
|
@@ -1708,6 +1622,20 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1708
1622
|
}
|
|
1709
1623
|
}
|
|
1710
1624
|
|
|
1625
|
+
/**
|
|
1626
|
+
* Apply sync to the client
|
|
1627
|
+
*
|
|
1628
|
+
* This method applies sync to the client by calling the `$applySync()` method.
|
|
1629
|
+
*
|
|
1630
|
+
* @example
|
|
1631
|
+
* ```ts
|
|
1632
|
+
* map.applySyncToClient();
|
|
1633
|
+
* ```
|
|
1634
|
+
*/
|
|
1635
|
+
applySyncToClient() {
|
|
1636
|
+
this.$applySync();
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1711
1639
|
/**
|
|
1712
1640
|
* Create a shape dynamically on the map
|
|
1713
1641
|
*
|
|
@@ -1784,7 +1712,7 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
1784
1712
|
properties?: Record<string, any>;
|
|
1785
1713
|
}): RpgShape {
|
|
1786
1714
|
const { x, y, width, height } = obj;
|
|
1787
|
-
|
|
1715
|
+
|
|
1788
1716
|
// Validate required parameters
|
|
1789
1717
|
if (typeof x !== 'number' || typeof y !== 'number') {
|
|
1790
1718
|
throw new Error('Shape x and y must be numbers');
|
|
@@ -2110,8 +2038,4 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
2110
2038
|
}
|
|
2111
2039
|
}
|
|
2112
2040
|
|
|
2113
|
-
export interface RpgMap {
|
|
2114
|
-
$send: (conn: MockConnection, data: any) => void;
|
|
2115
|
-
$broadcast: (data: any) => void;
|
|
2116
|
-
$sessionTransfer: (userOrPublicId: any | string, targetRoomId: string) => void;
|
|
2117
|
-
}
|
|
2041
|
+
export interface RpgMap extends RoomMethods { }
|
package/tests/change-map.spec.ts
CHANGED
|
@@ -67,6 +67,6 @@ test('Player can change map', async () => {
|
|
|
67
67
|
expect(newMap).toBeDefined()
|
|
68
68
|
expect(newMap?.id).toBe('map2')
|
|
69
69
|
|
|
70
|
-
expect(player.x()).toBe(200
|
|
71
|
-
expect(player.y()).toBe(200
|
|
70
|
+
expect(player.x()).toBe(200)
|
|
71
|
+
expect(player.y()).toBe(200)
|
|
72
72
|
})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { beforeEach, test, expect, afterEach } from 'vitest'
|
|
2
|
+
import { testing, TestingFixture } from '@rpgjs/testing'
|
|
3
|
+
import { defineModule, createModule } from '@rpgjs/common'
|
|
4
|
+
import { RpgPlayer, RpgServer, Move } from '../src'
|
|
5
|
+
import { RpgClient } from '../../client/src'
|
|
6
|
+
|
|
7
|
+
const Event = () => {
|
|
8
|
+
return {
|
|
9
|
+
name: "EV-1",
|
|
10
|
+
onInit() {
|
|
11
|
+
this.setGraphic("hero");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Define server module with two maps
|
|
17
|
+
const serverModule = defineModule<RpgServer>({
|
|
18
|
+
maps: [
|
|
19
|
+
{
|
|
20
|
+
id: 'map1',
|
|
21
|
+
events: [{ event: Event(), x: 100, y: 150 }]
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
player: {
|
|
25
|
+
async onConnected(player) {
|
|
26
|
+
await player.changeMap('map1', { x: 100, y: 126 })
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Define client module
|
|
32
|
+
const clientModule = defineModule<RpgClient>({
|
|
33
|
+
// Client-side logic
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
let player: RpgPlayer
|
|
37
|
+
let client: any
|
|
38
|
+
let fixture: TestingFixture
|
|
39
|
+
|
|
40
|
+
beforeEach(async () => {
|
|
41
|
+
const myModule = createModule('TestModule', [{
|
|
42
|
+
server: serverModule,
|
|
43
|
+
client: clientModule
|
|
44
|
+
}])
|
|
45
|
+
|
|
46
|
+
fixture = await testing(myModule)
|
|
47
|
+
client = await fixture.createClient()
|
|
48
|
+
player = client.player
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
fixture.clear()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('Player to touch event', async () => {
|
|
56
|
+
player = await client.waitForMapChange('map1')
|
|
57
|
+
const map = player.getCurrentMap()
|
|
58
|
+
const event = map?.getEvents()[0]
|
|
59
|
+
expect(event).toBeDefined()
|
|
60
|
+
expect(event?.name()).toBe("EV-1")
|
|
61
|
+
expect(event?.x()).toBe(100)
|
|
62
|
+
expect(event?.y()).toBe(150)
|
|
63
|
+
await fixture.waitUntil(
|
|
64
|
+
player.moveRoutes([
|
|
65
|
+
Move.tileDown(2),
|
|
66
|
+
], {
|
|
67
|
+
onStuck: () => false
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
expect(event?.x()).toBe(100)
|
|
71
|
+
expect(event?.y()).toBe(150)
|
|
72
|
+
await fixture.waitUntil(
|
|
73
|
+
event!.moveRoutes([
|
|
74
|
+
Move.down()
|
|
75
|
+
])
|
|
76
|
+
)
|
|
77
|
+
expect(event?.x()).toBe(100)
|
|
78
|
+
expect(event?.y()).toBe(150 + event!.speed())
|
|
79
|
+
|
|
80
|
+
})
|