@rpgjs/server 5.0.0-alpha.4 → 5.0.0-alpha.40

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.
Files changed (101) hide show
  1. package/dist/Gui/DialogGui.d.ts +5 -0
  2. package/dist/Gui/GameoverGui.d.ts +23 -0
  3. package/dist/Gui/Gui.d.ts +6 -0
  4. package/dist/Gui/MenuGui.d.ts +22 -3
  5. package/dist/Gui/NotificationGui.d.ts +1 -2
  6. package/dist/Gui/SaveLoadGui.d.ts +13 -0
  7. package/dist/Gui/ShopGui.d.ts +28 -3
  8. package/dist/Gui/TitleGui.d.ts +23 -0
  9. package/dist/Gui/index.d.ts +10 -1
  10. package/dist/Player/BattleManager.d.ts +34 -12
  11. package/dist/Player/ClassManager.d.ts +46 -13
  12. package/dist/Player/ComponentManager.d.ts +123 -0
  13. package/dist/Player/Components.d.ts +345 -0
  14. package/dist/Player/EffectManager.d.ts +86 -0
  15. package/dist/Player/ElementManager.d.ts +104 -0
  16. package/dist/Player/GoldManager.d.ts +22 -0
  17. package/dist/Player/GuiManager.d.ts +259 -0
  18. package/dist/Player/ItemFixture.d.ts +6 -0
  19. package/dist/Player/ItemManager.d.ts +450 -9
  20. package/dist/Player/MoveManager.d.ts +324 -69
  21. package/dist/Player/ParameterManager.d.ts +344 -14
  22. package/dist/Player/Player.d.ts +460 -8
  23. package/dist/Player/SkillManager.d.ts +197 -15
  24. package/dist/Player/StateManager.d.ts +89 -25
  25. package/dist/Player/VariableManager.d.ts +74 -0
  26. package/dist/RpgServer.d.ts +464 -64
  27. package/dist/RpgServerEngine.d.ts +2 -1
  28. package/dist/decorators/event.d.ts +46 -0
  29. package/dist/decorators/map.d.ts +287 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +21491 -20802
  32. package/dist/index.js.map +1 -1
  33. package/dist/logs/log.d.ts +2 -3
  34. package/dist/module.d.ts +43 -1
  35. package/dist/presets/index.d.ts +0 -9
  36. package/dist/rooms/BaseRoom.d.ts +132 -0
  37. package/dist/rooms/lobby.d.ts +10 -2
  38. package/dist/rooms/map.d.ts +1232 -17
  39. package/dist/services/save.d.ts +43 -0
  40. package/dist/storage/index.d.ts +1 -0
  41. package/dist/storage/localStorage.d.ts +23 -0
  42. package/package.json +14 -10
  43. package/src/Gui/DialogGui.ts +19 -4
  44. package/src/Gui/GameoverGui.ts +39 -0
  45. package/src/Gui/Gui.ts +23 -1
  46. package/src/Gui/MenuGui.ts +155 -6
  47. package/src/Gui/NotificationGui.ts +1 -2
  48. package/src/Gui/SaveLoadGui.ts +60 -0
  49. package/src/Gui/ShopGui.ts +146 -16
  50. package/src/Gui/TitleGui.ts +39 -0
  51. package/src/Gui/index.ts +15 -2
  52. package/src/Player/BattleManager.ts +91 -49
  53. package/src/Player/ClassManager.ts +118 -50
  54. package/src/Player/ComponentManager.ts +425 -19
  55. package/src/Player/Components.ts +380 -0
  56. package/src/Player/EffectManager.ts +81 -44
  57. package/src/Player/ElementManager.ts +109 -86
  58. package/src/Player/GoldManager.ts +32 -35
  59. package/src/Player/GuiManager.ts +308 -150
  60. package/src/Player/ItemFixture.ts +4 -5
  61. package/src/Player/ItemManager.ts +774 -355
  62. package/src/Player/MoveManager.ts +1544 -774
  63. package/src/Player/ParameterManager.ts +546 -104
  64. package/src/Player/Player.ts +1163 -88
  65. package/src/Player/SkillManager.ts +520 -195
  66. package/src/Player/StateManager.ts +170 -182
  67. package/src/Player/VariableManager.ts +101 -63
  68. package/src/RpgServer.ts +483 -63
  69. package/src/core/context.ts +1 -0
  70. package/src/decorators/event.ts +61 -0
  71. package/src/decorators/map.ts +327 -0
  72. package/src/index.ts +11 -1
  73. package/src/logs/log.ts +10 -3
  74. package/src/module.ts +126 -3
  75. package/src/presets/index.ts +1 -10
  76. package/src/rooms/BaseRoom.ts +232 -0
  77. package/src/rooms/lobby.ts +25 -7
  78. package/src/rooms/map.ts +2473 -194
  79. package/src/services/save.ts +147 -0
  80. package/src/storage/index.ts +1 -0
  81. package/src/storage/localStorage.ts +76 -0
  82. package/tests/battle.spec.ts +375 -0
  83. package/tests/change-map.spec.ts +72 -0
  84. package/tests/class.spec.ts +274 -0
  85. package/tests/effect.spec.ts +219 -0
  86. package/tests/element.spec.ts +221 -0
  87. package/tests/event.spec.ts +80 -0
  88. package/tests/gold.spec.ts +99 -0
  89. package/tests/item.spec.ts +609 -0
  90. package/tests/module.spec.ts +38 -0
  91. package/tests/move.spec.ts +601 -0
  92. package/tests/player-param.spec.ts +28 -0
  93. package/tests/prediction-reconciliation.spec.ts +182 -0
  94. package/tests/random-move.spec.ts +65 -0
  95. package/tests/skill.spec.ts +658 -0
  96. package/tests/state.spec.ts +467 -0
  97. package/tests/variable.spec.ts +185 -0
  98. package/tests/world-maps.spec.ts +896 -0
  99. package/vite.config.ts +16 -0
  100. package/dist/Player/Event.d.ts +0 -0
  101. package/src/Player/Event.ts +0 -0
@@ -1,7 +1,26 @@
1
- import { MockConnection, RoomOnJoin } from '@signe/room';
2
- import { Hooks, RpgCommonMap, ZoneData } from '@rpgjs/common';
1
+ import { MockConnection, RoomMethods, RoomOnJoin } from '@signe/room';
2
+ import { Hooks, RpgCommonMap, RpgShape, WorldMapsManager, WeatherState, WorldMapConfig } from '../../../common/src';
3
3
  import { RpgPlayer, RpgEvent } from '../Player/Player';
4
4
  import { BehaviorSubject } from 'rxjs';
5
+ import { MapOptions } from '../decorators/map';
6
+ import { EventMode } from '../decorators/event';
7
+ /**
8
+ * Interface for input controls configuration
9
+ *
10
+ * Defines the structure for input validation and anti-cheat controls
11
+ */
12
+ export interface Controls {
13
+ /** Maximum allowed time delta between inputs in milliseconds */
14
+ maxTimeDelta?: number;
15
+ /** Maximum allowed frame delta between inputs */
16
+ maxFrameDelta?: number;
17
+ /** Minimum time between inputs in milliseconds */
18
+ minTimeBetweenInputs?: number;
19
+ /** Whether to enable anti-cheat validation */
20
+ enableAntiCheat?: boolean;
21
+ /** Maximum number of queued inputs processed per server tick */
22
+ maxInputsPerTick?: number;
23
+ }
5
24
  /**
6
25
  * Interface representing hook methods available for map events
7
26
  *
@@ -17,11 +36,13 @@ export interface EventHooks {
17
36
  /** Called when a player touches this event */
18
37
  onPlayerTouch?: (player: RpgPlayer) => void;
19
38
  /** Called when a player enters a shape */
20
- onInShape?: (zone: ZoneData, player: RpgPlayer) => void;
39
+ onInShape?: (zone: RpgShape, player: RpgPlayer) => void;
21
40
  /** Called when a player exits a shape */
22
- onOutShape?: (zone: ZoneData, player: RpgPlayer) => void;
23
- onDetectInShape?: (player: RpgPlayer, shape: ZoneData) => void;
24
- onDetectOutShape?: (player: RpgPlayer, shape: ZoneData) => void;
41
+ onOutShape?: (zone: RpgShape, player: RpgPlayer) => void;
42
+ /** Called when a player is detected entering a shape */
43
+ onDetectInShape?: (player: RpgPlayer, shape: RpgShape) => void;
44
+ /** Called when a player is detected exiting a shape */
45
+ onDetectOutShape?: (player: RpgPlayer, shape: RpgShape) => void;
25
46
  }
26
47
  /** Type for event class constructor */
27
48
  export type EventConstructor = new () => RpgPlayer;
@@ -30,9 +51,13 @@ export type EventPosOption = {
30
51
  /** ID of the event */
31
52
  id?: string;
32
53
  /** X position of the event on the map */
33
- x: number;
54
+ x?: number;
34
55
  /** Y position of the event on the map */
35
- y: number;
56
+ y?: number;
57
+ /** Event mode override */
58
+ mode?: EventMode | "shared" | "scenario";
59
+ /** Owner player id when mode is scenario */
60
+ scenarioOwnerId?: string;
36
61
  /**
37
62
  * Event definition - can be either:
38
63
  * - A class that extends RpgPlayer
@@ -40,25 +65,657 @@ export type EventPosOption = {
40
65
  */
41
66
  event: EventConstructor | (EventHooks & Record<string, any>);
42
67
  };
68
+ type CreateDynamicEventOptions = {
69
+ mode?: EventMode | "shared" | "scenario";
70
+ scenarioOwnerId?: string;
71
+ };
72
+ interface WeatherSetOptions {
73
+ sync?: boolean;
74
+ }
43
75
  export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
76
+ /**
77
+ * Synchronized signal containing all players currently on the map
78
+ *
79
+ * This signal is automatically synchronized with clients using @signe/sync.
80
+ * Players are indexed by their unique ID.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * // Get all players
85
+ * const allPlayers = map.players();
86
+ *
87
+ * // Get a specific player
88
+ * const player = map.players()['player-id'];
89
+ * ```
90
+ */
44
91
  players: import('@signe/reactive').WritableObjectSignal<{}>;
92
+ /**
93
+ * Synchronized signal containing all events (NPCs, objects) on the map
94
+ *
95
+ * This signal is automatically synchronized with clients using @signe/sync.
96
+ * Events are indexed by their unique ID.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * // Get all events
101
+ * const allEvents = map.events();
102
+ *
103
+ * // Get a specific event
104
+ * const event = map.events()['event-id'];
105
+ * ```
106
+ */
45
107
  events: import('@signe/reactive').WritableObjectSignal<{}>;
108
+ /**
109
+ * Signal containing the map's database of items, classes, and other game data
110
+ *
111
+ * This database can be dynamically populated using `addInDatabase()` and
112
+ * `removeInDatabase()` methods. It's used to store game entities like items,
113
+ * classes, skills, etc. that are specific to this map.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * // Add data to database
118
+ * map.addInDatabase('Potion', PotionClass);
119
+ *
120
+ * // Access database
121
+ * const potion = map.database()['Potion'];
122
+ * ```
123
+ */
46
124
  database: import('@signe/reactive').WritableObjectSignal<{}>;
47
- maps: any[];
125
+ /**
126
+ * Array of map configurations - can contain MapOptions objects or instances of map classes
127
+ *
128
+ * This array stores the configuration for this map and any related maps.
129
+ * It's populated when the map is loaded via `updateMap()`.
130
+ */
131
+ maps: (MapOptions | any)[];
132
+ /**
133
+ * Array of sound IDs to play when players join the map
134
+ *
135
+ * These sounds are automatically played for each player when they join the map.
136
+ * Sounds must be defined on the client side.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * // Set sounds for the map
141
+ * map.sounds = ['background-music', 'ambient-forest'];
142
+ * ```
143
+ */
144
+ sounds: string[];
145
+ /**
146
+ * BehaviorSubject that completes when the map data is ready
147
+ *
148
+ * This subject is used to signal when the map has finished loading all its data.
149
+ * Players wait for this to complete before the map is fully initialized.
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * // Wait for map data to be ready
154
+ * map.dataIsReady$.subscribe(() => {
155
+ * console.log('Map is ready!');
156
+ * });
157
+ * ```
158
+ */
48
159
  dataIsReady$: BehaviorSubject<void>;
160
+ /**
161
+ * Global configuration object for the map
162
+ *
163
+ * This object contains configuration settings that apply to the entire map.
164
+ * It's populated from the map data when `updateMap()` is called.
165
+ */
49
166
  globalConfig: any;
167
+ /**
168
+ * Damage formulas configuration for the map
169
+ *
170
+ * Contains formulas for calculating damage from skills, physical attacks,
171
+ * critical hits, and element coefficients. Default formulas are merged
172
+ * with custom formulas when the map is loaded.
173
+ */
50
174
  damageFormulas: any;
175
+ private _weatherState;
176
+ /** Internal: Map of shapes by name */
177
+ private _shapes;
178
+ /** Internal: Map of shape entity UUIDs to RpgShape instances */
179
+ private _shapeEntities;
180
+ /** Internal: Subscription for the input processing loop */
181
+ private _inputLoopSubscription?;
182
+ /** Enable/disable automatic tick processing (useful for unit tests) */
183
+ private _autoTickEnabled;
184
+ /** Runtime templates for scenario events to instantiate per player */
185
+ private _scenarioEventTemplates;
186
+ /** Runtime registry of event mode by id */
187
+ private _eventModeById;
188
+ /** Runtime registry of scenario owner by event id */
189
+ private _eventOwnerById;
190
+ /** Runtime registry of spawned scenario event ids by player id */
191
+ private _scenarioEventIdsByPlayer;
192
+ autoSync: boolean;
193
+ constructor(room: any);
194
+ onStart(): Promise<void>;
195
+ private isPositiveNumber;
196
+ private resolveTrustedMapDimensions;
197
+ private normalizeEventMode;
198
+ private resolveEventMode;
199
+ private resolveScenarioOwnerId;
200
+ private normalizeEventObject;
201
+ private cloneEventTemplate;
202
+ private buildRuntimeEventId;
203
+ private setEventRuntimeMetadata;
204
+ private clearEventRuntimeMetadata;
205
+ private getEventModeById;
206
+ private getScenarioOwnerIdByEventId;
207
+ isEventVisibleForPlayer(eventOrId: string | RpgEvent, playerOrId: string | RpgPlayer): boolean;
208
+ private spawnScenarioEventsForPlayer;
209
+ private removeScenarioEventsForPlayer;
210
+ /**
211
+ * Setup collision detection between players, events, and shapes
212
+ *
213
+ * This method listens to physics collision events and triggers hooks:
214
+ * - `onPlayerTouch` on events when a player collides with them
215
+ * - `onInShape` on players and events when they enter a shape
216
+ * - `onOutShape` on players and events when they exit a shape
217
+ *
218
+ * ## Architecture
219
+ *
220
+ * Uses the physics engine's collision event system to detect when entities collide.
221
+ * When a collision is detected:
222
+ * - Between a player and an event: triggers `onPlayerTouch` on the event
223
+ * - Between a player/event and a shape: triggers `onInShape`/`onOutShape` hooks
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * // Event with onPlayerTouch hook
228
+ * map.createDynamicEvent({
229
+ * x: 100,
230
+ * y: 200,
231
+ * event: {
232
+ * onPlayerTouch(player) {
233
+ * console.log(`Player ${player.id} touched this event!`);
234
+ * }
235
+ * }
236
+ * });
237
+ *
238
+ * // Player with onInShape hook
239
+ * const player: RpgPlayerHooks = {
240
+ * onInShape(player: RpgPlayer, shape: RpgShape) {
241
+ * console.log('in', player.name, shape.name);
242
+ * },
243
+ * onOutShape(player: RpgPlayer, shape: RpgShape) {
244
+ * console.log('out', player.name, shape.name);
245
+ * }
246
+ * };
247
+ * ```
248
+ */
249
+ private setupCollisionDetection;
250
+ /**
251
+ * Intercepts and modifies packets before they are sent to clients
252
+ *
253
+ * This method is automatically called by @signe/room for each packet sent to clients.
254
+ * It adds timestamp and acknowledgment information to sync packets for client-side
255
+ * prediction reconciliation. This helps with network synchronization and reduces
256
+ * perceived latency.
257
+ *
258
+ * ## Architecture
259
+ *
260
+ * Adds metadata to packets:
261
+ * - `timestamp`: Current server time for client-side prediction
262
+ * - `ack`: Acknowledgment info with last processed frame and authoritative position
263
+ *
264
+ * @param player - The player receiving the packet
265
+ * @param packet - The packet data to intercept
266
+ * @param conn - The connection object
267
+ * @returns Modified packet with timestamp and ack info, or null if player is invalid
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * // This method is called automatically by the framework
272
+ * // You typically don't call it directly
273
+ * ```
274
+ */
275
+ interceptorPacket(player: RpgPlayer, packet: any, conn: MockConnection): any;
276
+ /**
277
+ * Called when a player joins the map
278
+ *
279
+ * This method is automatically called by @signe/room when a player connects to the map.
280
+ * It initializes the player's connection, sets up the map context, and waits for
281
+ * the map data to be ready before playing sounds and triggering hooks.
282
+ *
283
+ * ## Architecture
284
+ *
285
+ * 1. Sets player's map reference and context
286
+ * 2. Initializes the player
287
+ * 3. Waits for map data to be ready
288
+ * 4. Plays map sounds for the player
289
+ * 5. Triggers `server-player-onJoinMap` hook
290
+ *
291
+ * @param player - The player joining the map
292
+ * @param conn - The connection object for the player
293
+ *
294
+ * @example
295
+ * ```ts
296
+ * // This method is called automatically by the framework
297
+ * // You can listen to the hook to perform custom logic
298
+ * server.addHook('server-player-onJoinMap', (player, map) => {
299
+ * console.log(`Player ${player.id} joined map ${map.id}`);
300
+ * });
301
+ * ```
302
+ */
51
303
  onJoin(player: RpgPlayer, conn: MockConnection): void;
304
+ /**
305
+ * Called when a player leaves the map
306
+ *
307
+ * This method is automatically called by @signe/room when a player disconnects from the map.
308
+ * It cleans up the player's pending inputs and triggers the appropriate hooks.
309
+ *
310
+ * ## Architecture
311
+ *
312
+ * 1. Triggers `server-player-onLeaveMap` hook
313
+ * 2. Clears pending inputs to prevent processing after disconnection
314
+ *
315
+ * @param player - The player leaving the map
316
+ * @param conn - The connection object for the player
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * // This method is called automatically by the framework
321
+ * // You can listen to the hook to perform custom cleanup
322
+ * server.addHook('server-player-onLeaveMap', (player, map) => {
323
+ * console.log(`Player ${player.id} left map ${map.id}`);
324
+ * });
325
+ * ```
326
+ */
327
+ onLeave(player: RpgPlayer, conn: MockConnection): Promise<void>;
328
+ /**
329
+ * Get the hooks system for this map
330
+ *
331
+ * Returns the dependency-injected Hooks instance that allows you to trigger
332
+ * and listen to various game events.
333
+ *
334
+ * @returns The Hooks instance for this map
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * // Trigger a custom hook
339
+ * map.hooks.callHooks('custom-event', data).subscribe();
340
+ * ```
341
+ */
52
342
  get hooks(): Hooks;
53
- guiInteraction(player: RpgPlayer, value: any): void;
343
+ onSessionRestore(payload: {
344
+ userSnapshot: any;
345
+ user?: RpgPlayer;
346
+ }): Promise<any>;
347
+ /**
348
+ * Handle GUI interaction from a player
349
+ *
350
+ * This method is called when a player interacts with a GUI element.
351
+ * It synchronizes the player's changes to ensure the client state is up to date.
352
+ *
353
+ * @param player - The player performing the interaction
354
+ * @param value - The interaction data from the client
355
+ *
356
+ * @example
357
+ * ```ts
358
+ * // This method is called automatically when a player interacts with a GUI
359
+ * // The interaction data is sent from the client
360
+ * ```
361
+ */
362
+ guiInteraction(player: RpgPlayer, value: {
363
+ guiId: string;
364
+ name: string;
365
+ data: any;
366
+ }): Promise<void>;
367
+ /**
368
+ * Handle GUI exit from a player
369
+ *
370
+ * This method is called when a player closes or exits a GUI.
371
+ * It removes the GUI from the player's active GUIs.
372
+ *
373
+ * @param player - The player exiting the GUI
374
+ * @param guiId - The ID of the GUI being exited
375
+ * @param data - Optional data associated with the GUI exit
376
+ *
377
+ * @example
378
+ * ```ts
379
+ * // This method is called automatically when a player closes a GUI
380
+ * // The GUI is removed from the player's active GUIs
381
+ * ```
382
+ */
54
383
  guiExit(player: RpgPlayer, { guiId, data }: {
55
384
  guiId: any;
56
385
  data: any;
57
386
  }): void;
387
+ /**
388
+ * Handle action input from a player
389
+ *
390
+ * This method is called when a player performs an action (like pressing a button).
391
+ * It checks for collisions with events and triggers the appropriate hooks.
392
+ *
393
+ * ## Architecture
394
+ *
395
+ * 1. Gets all entities colliding with the player
396
+ * 2. Triggers `onAction` hook on colliding events
397
+ * 3. Triggers `onInput` hook on the player
398
+ *
399
+ * @param player - The player performing the action
400
+ * @param action - The action data (button pressed, etc.)
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * // This method is called automatically when a player presses an action button
405
+ * // Events near the player will have their onAction hook triggered
406
+ * ```
407
+ */
58
408
  onAction(player: RpgPlayer, action: any): void;
59
- onInput(player: RpgPlayer, input: any): void;
409
+ /**
410
+ * Handle movement input from a player
411
+ *
412
+ * This method is called when a player sends movement input from the client.
413
+ * It queues the input for processing by the game loop. Inputs are processed
414
+ * with frame numbers to ensure proper ordering and client-side prediction.
415
+ *
416
+ * ## Architecture
417
+ *
418
+ * - Inputs are queued in `player.pendingInputs`
419
+ * - Duplicate frames are skipped to prevent processing the same input twice
420
+ * - Inputs are processed asynchronously by the game loop
421
+ *
422
+ * @param player - The player sending the movement input
423
+ * @param input - The input data containing frame number, input direction, and timestamp
424
+ *
425
+ * @example
426
+ * ```ts
427
+ * // This method is called automatically when a player moves
428
+ * // The input is queued and processed by processInput()
429
+ * ```
430
+ */
431
+ onInput(player: RpgPlayer, input: any): Promise<void>;
432
+ onPing(player: RpgPlayer, payload: {
433
+ clientTime?: number;
434
+ clientFrame?: number;
435
+ }): void;
436
+ saveSlot(player: RpgPlayer, value: {
437
+ requestId: string;
438
+ index: number;
439
+ meta?: any;
440
+ }): Promise<void>;
441
+ loadSlot(player: RpgPlayer, value: {
442
+ requestId: string;
443
+ index: number;
444
+ }): Promise<void>;
445
+ listSaveSlots(player: RpgPlayer, value: {
446
+ requestId: string;
447
+ }): Promise<import('../../../common/src').SaveSlotList>;
448
+ /**
449
+ * Update the map configuration and data
450
+ *
451
+ * This endpoint receives map data from the client and initializes the map.
452
+ * It loads the map configuration, damage formulas, events, and physics.
453
+ *
454
+ * ## Architecture
455
+ *
456
+ * 1. Validates the request body using MapUpdateSchema
457
+ * 2. Updates map data, global config, and damage formulas
458
+ * 3. Merges events and sounds from map configuration
459
+ * 4. Triggers hooks for map loading
460
+ * 5. Loads physics engine
461
+ * 6. Creates all events on the map
462
+ * 7. Completes the dataIsReady$ subject
463
+ *
464
+ * @param request - HTTP request containing map data
465
+ * @returns Promise that resolves when the map is fully loaded
466
+ *
467
+ * @example
468
+ * ```ts
469
+ * // This endpoint is called automatically when a map is loaded
470
+ * // POST /map/update
471
+ * // Body: { id: string, width: number, height: number, config?: any, damageFormulas?: any }
472
+ * ```
473
+ */
60
474
  updateMap(request: Request): Promise<void>;
61
- addInDatabase(id: string, data: any): void;
475
+ /**
476
+ * Update (or create) a world configuration and propagate to all maps in that world
477
+ *
478
+ * This endpoint receives world map configuration data (typically from Tiled world import)
479
+ * and creates or updates the world manager. The world ID is extracted from the URL path.
480
+ *
481
+ * ## Architecture
482
+ *
483
+ * 1. Extracts world ID from URL path parameter
484
+ * 2. Normalizes input to array of WorldMapConfig
485
+ * 3. Ensures all required map properties are present (width, height, tile sizes)
486
+ * 4. Creates or updates the world manager
487
+ *
488
+ * Expected payload examples:
489
+ * - `{ id: string, maps: WorldMapConfig[] }`
490
+ * - `WorldMapConfig[]`
491
+ *
492
+ * @param request - HTTP request containing world configuration
493
+ * @returns Promise resolving to `{ ok: true }` when complete
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * // POST /world/my-world/update
498
+ * // Body: [{ id: 'map1', worldX: 0, worldY: 0, width: 800, height: 600 }]
499
+ *
500
+ * // Or with nested structure
501
+ * // Body: { id: 'my-world', maps: [{ id: 'map1', ... }] }
502
+ * ```
503
+ */
504
+ updateWorld(request: Request): Promise<any>;
505
+ /**
506
+ * Process pending inputs for a player with anti-cheat validation
507
+ *
508
+ * This method processes pending inputs for a player while performing
509
+ * anti-cheat validation to prevent time manipulation and frame skipping.
510
+ * It validates the time deltas between inputs and ensures they are within
511
+ * acceptable ranges. To preserve movement itinerary under network bursts,
512
+ * the number of inputs processed per call is capped.
513
+ *
514
+ * ## Architecture
515
+ *
516
+ * **Important**: This method only updates entity velocities - it does NOT step
517
+ * the physics engine. Physics simulation is handled centrally by the game loop
518
+ * (`tick$` -> `runFixedTicks`). This ensures:
519
+ * - Consistent physics timing (60fps fixed timestep)
520
+ * - No double-stepping when multiple inputs are processed
521
+ * - Deterministic physics regardless of input frequency
522
+ *
523
+ * @param playerId - The ID of the player to process inputs for
524
+ * @param controls - Optional anti-cheat configuration
525
+ * @returns Promise containing the player and processed input strings
526
+ *
527
+ * @example
528
+ * ```ts
529
+ * // Process inputs with default anti-cheat settings
530
+ * const result = await map.processInput('player1');
531
+ * console.log('Processed inputs:', result.inputs);
532
+ *
533
+ * // Process inputs with custom anti-cheat configuration
534
+ * const result = await map.processInput('player1', {
535
+ * maxTimeDelta: 100,
536
+ * maxFrameDelta: 5,
537
+ * minTimeBetweenInputs: 16,
538
+ * enableAntiCheat: true
539
+ * });
540
+ * ```
541
+ */
542
+ processInput(playerId: string, controls?: Controls): Promise<{
543
+ player: RpgPlayer;
544
+ inputs: string[];
545
+ }>;
546
+ /**
547
+ * Main game loop that processes player inputs
548
+ *
549
+ * This private method subscribes to tick$ and processes pending inputs
550
+ * for all players on the map with a throttle of 50ms. It ensures inputs are
551
+ * processed in order and prevents concurrent processing for the same player.
552
+ *
553
+ * ## Architecture
554
+ *
555
+ * - Subscribes to tick$ with throttleTime(50ms) for responsive input processing
556
+ * - Processes inputs for each player with pending inputs
557
+ * - Uses a flag to prevent concurrent processing for the same player
558
+ * - Calls `processInput()` to handle anti-cheat validation and movement
559
+ *
560
+ * @example
561
+ * ```ts
562
+ * // This method is called automatically in the constructor if autoTick is enabled
563
+ * // You typically don't call it directly
564
+ * ```
565
+ */
566
+ private loop;
567
+ /**
568
+ * Enable or disable automatic tick processing
569
+ *
570
+ * When disabled, the input processing loop will not run automatically.
571
+ * This is useful for unit tests where you want manual control over when
572
+ * inputs are processed.
573
+ *
574
+ * @param enabled - Whether to enable automatic tick processing (default: true)
575
+ *
576
+ * @example
577
+ * ```ts
578
+ * // Disable auto tick for testing
579
+ * map.setAutoTick(false);
580
+ *
581
+ * // Manually trigger tick processing
582
+ * await map.processInput('player1');
583
+ * ```
584
+ */
585
+ setAutoTick(enabled: boolean): void;
586
+ /**
587
+ * Get a world manager by id
588
+ *
589
+ * Returns the world maps manager for the given world ID. Currently, only
590
+ * one world manager is supported per map instance.
591
+ *
592
+ * @param id - The world ID (currently unused, returns the single manager)
593
+ * @returns The WorldMapsManager instance, or null if not initialized
594
+ *
595
+ * @example
596
+ * ```ts
597
+ * const worldManager = map.getWorldMaps('my-world');
598
+ * if (worldManager) {
599
+ * const mapInfo = worldManager.getMapInfo('map1');
600
+ * }
601
+ * ```
602
+ */
603
+ getWorldMaps(id: string): WorldMapsManager | null;
604
+ /**
605
+ * Delete a world manager by id
606
+ *
607
+ * Removes the world maps manager from this map instance. Currently, only
608
+ * one world manager is supported, so this clears the single manager.
609
+ *
610
+ * @param id - The world ID (currently unused)
611
+ * @returns true if the manager was deleted, false if it didn't exist
612
+ *
613
+ * @example
614
+ * ```ts
615
+ * const deleted = map.deleteWorldMaps('my-world');
616
+ * if (deleted) {
617
+ * console.log('World manager removed');
618
+ * }
619
+ * ```
620
+ */
621
+ deleteWorldMaps(id: string): boolean;
622
+ /**
623
+ * Create a world manager dynamically
624
+ *
625
+ * Creates a new WorldMapsManager instance and configures it with the provided
626
+ * map configurations. This is used when loading world data from Tiled or
627
+ * other map editors.
628
+ *
629
+ * @param world - World configuration object
630
+ * @param world.id - Optional world identifier
631
+ * @param world.maps - Array of map configurations for the world
632
+ * @returns The newly created WorldMapsManager instance
633
+ *
634
+ * @example
635
+ * ```ts
636
+ * const manager = map.createDynamicWorldMaps({
637
+ * id: 'my-world',
638
+ * maps: [
639
+ * { id: 'map1', worldX: 0, worldY: 0, width: 800, height: 600 },
640
+ * { id: 'map2', worldX: 800, worldY: 0, width: 800, height: 600 }
641
+ * ]
642
+ * });
643
+ * ```
644
+ */
645
+ createDynamicWorldMaps(world: {
646
+ id?: string;
647
+ maps: WorldMapConfig[];
648
+ }): WorldMapsManager;
649
+ /**
650
+ * Update world maps by id. Auto-create when missing.
651
+ *
652
+ * Updates the world maps configuration. If the world manager doesn't exist,
653
+ * it is automatically created. This is useful for dynamically loading world
654
+ * data or updating map positions.
655
+ *
656
+ * @param id - The world ID
657
+ * @param maps - Array of map configurations to update
658
+ * @returns Promise that resolves when the update is complete
659
+ *
660
+ * @example
661
+ * ```ts
662
+ * await map.updateWorldMaps('my-world', [
663
+ * { id: 'map1', worldX: 0, worldY: 0, width: 800, height: 600 },
664
+ * { id: 'map2', worldX: 800, worldY: 0, width: 800, height: 600 }
665
+ * ]);
666
+ * ```
667
+ */
668
+ updateWorldMaps(id: string, maps: WorldMapConfig[]): Promise<void>;
669
+ /**
670
+ * Add data to the map's database
671
+ *
672
+ * This method delegates to BaseRoom's implementation to avoid code duplication.
673
+ *
674
+ * @param id - Unique identifier for the data
675
+ * @param data - The data to store (can be a class, object, or any value)
676
+ * @param options - Optional configuration
677
+ * @param options.force - If true, overwrites existing data even if ID already exists (default: false)
678
+ * @returns true if data was added, false if ignored (ID already exists)
679
+ *
680
+ * @example
681
+ * ```ts
682
+ * // Add an item class to the database
683
+ * map.addInDatabase('Potion', PotionClass);
684
+ *
685
+ * // Add an item object to the database
686
+ * map.addInDatabase('custom-item', {
687
+ * name: 'Custom Item',
688
+ * price: 100
689
+ * });
690
+ *
691
+ * // Force overwrite existing data
692
+ * map.addInDatabase('Potion', UpdatedPotionClass, { force: true });
693
+ * ```
694
+ */
695
+ addInDatabase(id: string, data: any, options?: {
696
+ force?: boolean;
697
+ }): boolean;
698
+ /**
699
+ * Remove data from the map's database
700
+ *
701
+ * This method delegates to BaseRoom's implementation to avoid code duplication.
702
+ *
703
+ * @param id - Unique identifier of the data to remove
704
+ * @returns true if data was removed, false if ID didn't exist
705
+ *
706
+ * @example
707
+ * ```ts
708
+ * // Remove an item from the database
709
+ * map.removeInDatabase('Potion');
710
+ *
711
+ * // Check if removal was successful
712
+ * const removed = map.removeInDatabase('custom-item');
713
+ * if (removed) {
714
+ * console.log('Item removed successfully');
715
+ * }
716
+ * ```
717
+ */
718
+ removeInDatabase(id: string): boolean;
62
719
  /**
63
720
  * Creates a dynamic event on the map
64
721
  *
@@ -97,14 +754,572 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
97
754
  * }
98
755
  * });
99
756
  */
100
- createDynamicEvent(eventObj: EventPosOption): Promise<void>;
757
+ createDynamicEvent(eventObj: EventPosOption, options?: CreateDynamicEventOptions): Promise<string | undefined>;
758
+ /**
759
+ * Get an event by its ID
760
+ *
761
+ * Returns the event with the specified ID, or undefined if not found.
762
+ * The return type can be narrowed using TypeScript generics.
763
+ *
764
+ * @param eventId - The unique identifier of the event
765
+ * @returns The event instance, or undefined if not found
766
+ *
767
+ * @example
768
+ * ```ts
769
+ * // Get any event
770
+ * const event = map.getEvent('npc-1');
771
+ *
772
+ * // Get event with type narrowing
773
+ * const npc = map.getEvent<MyNPC>('npc-1');
774
+ * if (npc) {
775
+ * npc.speak('Hello!');
776
+ * }
777
+ * ```
778
+ */
101
779
  getEvent<T extends RpgPlayer>(eventId: string): T | undefined;
780
+ /**
781
+ * Get a player by their ID
782
+ *
783
+ * Returns the player with the specified ID, or undefined if not found.
784
+ *
785
+ * @param playerId - The unique identifier of the player
786
+ * @returns The player instance, or undefined if not found
787
+ *
788
+ * @example
789
+ * ```ts
790
+ * const player = map.getPlayer('player-123');
791
+ * if (player) {
792
+ * console.log(`Player ${player.name} is on the map`);
793
+ * }
794
+ * ```
795
+ */
102
796
  getPlayer(playerId: string): RpgPlayer | undefined;
797
+ /**
798
+ * Get all players currently on the map
799
+ *
800
+ * Returns an array of all players that are currently connected to this map.
801
+ *
802
+ * @returns Array of all RpgPlayer instances on the map
803
+ *
804
+ * @example
805
+ * ```ts
806
+ * const players = map.getPlayers();
807
+ * console.log(`There are ${players.length} players on the map`);
808
+ *
809
+ * players.forEach(player => {
810
+ * console.log(`- ${player.name}`);
811
+ * });
812
+ * ```
813
+ */
814
+ getPlayers(): RpgPlayer[];
815
+ /**
816
+ * Get all events on the map
817
+ *
818
+ * Returns an array of all events (NPCs, objects, etc.) that are currently
819
+ * on this map.
820
+ *
821
+ * @returns Array of all RpgEvent instances on the map
822
+ *
823
+ * @example
824
+ * ```ts
825
+ * const events = map.getEvents();
826
+ * console.log(`There are ${events.length} events on the map`);
827
+ *
828
+ * events.forEach(event => {
829
+ * console.log(`- ${event.name} at (${event.x}, ${event.y})`);
830
+ * });
831
+ * ```
832
+ */
103
833
  getEvents(): RpgEvent[];
834
+ getEventsForPlayer(playerOrId: string | RpgPlayer): RpgEvent[];
835
+ /**
836
+ * Get the first event that matches a condition
837
+ *
838
+ * Searches through all events on the map and returns the first one that
839
+ * matches the provided callback function.
840
+ *
841
+ * @param cb - Callback function that returns true for the desired event
842
+ * @returns The first matching event, or undefined if none found
843
+ *
844
+ * @example
845
+ * ```ts
846
+ * // Find an event by name
847
+ * const npc = map.getEventBy(event => event.name === 'Merchant');
848
+ *
849
+ * // Find an event at a specific position
850
+ * const chest = map.getEventBy(event =>
851
+ * event.x === 100 && event.y === 200
852
+ * );
853
+ * ```
854
+ */
855
+ getEventBy(cb: (event: RpgEvent) => boolean): RpgEvent | undefined;
856
+ /**
857
+ * Get all events that match a condition
858
+ *
859
+ * Searches through all events on the map and returns all events that
860
+ * match the provided callback function.
861
+ *
862
+ * @param cb - Callback function that returns true for desired events
863
+ * @returns Array of all matching events
864
+ *
865
+ * @example
866
+ * ```ts
867
+ * // Find all NPCs
868
+ * const npcs = map.getEventsBy(event => event.name.startsWith('NPC-'));
869
+ *
870
+ * // Find all events in a specific area
871
+ * const nearbyEvents = map.getEventsBy(event =>
872
+ * event.x >= 0 && event.x <= 100 &&
873
+ * event.y >= 0 && event.y <= 100
874
+ * );
875
+ * ```
876
+ */
877
+ getEventsBy(cb: (event: RpgEvent) => boolean): RpgEvent[];
878
+ /**
879
+ * Remove an event from the map
880
+ *
881
+ * Removes the event with the specified ID from the map. The event will
882
+ * be removed from the synchronized events signal, causing it to disappear
883
+ * on all clients.
884
+ *
885
+ * @param eventId - The unique identifier of the event to remove
886
+ *
887
+ * @example
888
+ * ```ts
889
+ * // Remove an event
890
+ * map.removeEvent('npc-1');
891
+ *
892
+ * // Remove event after interaction
893
+ * const chest = map.getEvent('chest-1');
894
+ * if (chest) {
895
+ * // ... do something with chest ...
896
+ * map.removeEvent('chest-1');
897
+ * }
898
+ * ```
899
+ */
104
900
  removeEvent(eventId: string): void;
105
- showAnimation(animationName: string, object: RpgPlayer): void;
901
+ /**
902
+ * Display a component animation at a specific position on the map
903
+ *
904
+ * This method broadcasts a component animation to all clients connected to the map,
905
+ * allowing temporary visual effects to be displayed at any location on the map.
906
+ * Component animations are custom Canvas Engine components that can display
907
+ * complex effects with custom logic and parameters.
908
+ *
909
+ * @param id - The ID of the component animation to display
910
+ * @param position - The x, y coordinates where to display the animation
911
+ * @param params - Parameters to pass to the component animation
912
+ *
913
+ * @example
914
+ * ```ts
915
+ * // Show explosion at specific coordinates
916
+ * map.showComponentAnimation("explosion", { x: 300, y: 400 }, {
917
+ * intensity: 2.5,
918
+ * duration: 1500
919
+ * });
920
+ *
921
+ * // Show area damage effect
922
+ * map.showComponentAnimation("area-damage", { x: player.x, y: player.y }, {
923
+ * radius: 100,
924
+ * color: "red",
925
+ * damage: 50
926
+ * });
927
+ *
928
+ * // Show treasure spawn effect
929
+ * map.showComponentAnimation("treasure-spawn", { x: 150, y: 200 }, {
930
+ * sparkle: true,
931
+ * sound: "treasure-appear"
932
+ * });
933
+ * ```
934
+ */
935
+ showComponentAnimation(id: string, position: {
936
+ x: number;
937
+ y: number;
938
+ }, params: any): void;
939
+ /**
940
+ * Display a spritesheet animation at a specific position on the map
941
+ *
942
+ * This method displays a temporary visual animation using a spritesheet at any
943
+ * location on the map. It's a convenience method that internally uses showComponentAnimation
944
+ * with the built-in 'animation' component. This is useful for spell effects, environmental
945
+ * animations, or any visual feedback that uses predefined spritesheets.
946
+ *
947
+ * @param position - The x, y coordinates where to display the animation
948
+ * @param graphic - The ID of the spritesheet to use for the animation
949
+ * @param animationName - The name of the animation within the spritesheet (default: 'default')
950
+ *
951
+ * @example
952
+ * ```ts
953
+ * // Show explosion at specific coordinates
954
+ * map.showAnimation({ x: 100, y: 200 }, "explosion");
955
+ *
956
+ * // Show spell effect at player position
957
+ * const playerPos = { x: player.x, y: player.y };
958
+ * map.showAnimation(playerPos, "spell-effects", "lightning");
959
+ *
960
+ * // Show environmental effect
961
+ * map.showAnimation({ x: 300, y: 150 }, "nature-effects", "wind-gust");
962
+ *
963
+ * // Show portal opening animation
964
+ * map.showAnimation({ x: 500, y: 400 }, "portals", "opening");
965
+ * ```
966
+ */
967
+ showAnimation(position: {
968
+ x: number;
969
+ y: number;
970
+ }, graphic: string, animationName?: string): void;
971
+ private cloneWeatherState;
972
+ /**
973
+ * Get the current map weather state.
974
+ */
975
+ getWeather(): WeatherState | null;
976
+ /**
977
+ * Set the full weather state for this map.
978
+ *
979
+ * When `sync` is true (default), all connected clients receive the new weather.
980
+ */
981
+ setWeather(next: WeatherState | null, options?: WeatherSetOptions): WeatherState | null;
982
+ /**
983
+ * Patch the current weather state.
984
+ *
985
+ * Nested `params` values are merged.
986
+ */
987
+ patchWeather(patch: Partial<WeatherState>, options?: WeatherSetOptions): WeatherState | null;
988
+ /**
989
+ * Clear weather for this map.
990
+ */
991
+ clearWeather(options?: WeatherSetOptions): void;
992
+ /**
993
+ * Configure runtime synchronized properties on the map
994
+ *
995
+ * This method allows you to dynamically add synchronized properties to the map
996
+ * that will be automatically synced with clients. The schema follows the same
997
+ * structure as module properties with `$initial`, `$syncWithClient`, and `$permanent` options.
998
+ *
999
+ * ## Architecture
1000
+ *
1001
+ * - Reads a schema object shaped like module props
1002
+ * - Creates typed sync signals with @signe/sync
1003
+ * - Properties are accessible as `map.propertyName`
1004
+ *
1005
+ * @param schema - Schema object defining the properties to sync
1006
+ * @param schema[key].$initial - Initial value for the property
1007
+ * @param schema[key].$syncWithClient - Whether to sync this property to clients
1008
+ * @param schema[key].$permanent - Whether to persist this property
1009
+ *
1010
+ * @example
1011
+ * ```ts
1012
+ * // Add synchronized properties to the map
1013
+ * map.setSync({
1014
+ * weather: {
1015
+ * $initial: 'sunny',
1016
+ * $syncWithClient: true,
1017
+ * $permanent: false
1018
+ * },
1019
+ * timeOfDay: {
1020
+ * $initial: 12,
1021
+ * $syncWithClient: true,
1022
+ * $permanent: false
1023
+ * }
1024
+ * });
1025
+ *
1026
+ * // Use the properties
1027
+ * map.weather.set('rainy');
1028
+ * const currentWeather = map.weather();
1029
+ * ```
1030
+ */
1031
+ setSync(schema: Record<string, any>): void;
1032
+ /**
1033
+ * Apply sync to the client
1034
+ *
1035
+ * This method applies sync to the client by calling the `$applySync()` method.
1036
+ *
1037
+ * @example
1038
+ * ```ts
1039
+ * map.applySyncToClient();
1040
+ * ```
1041
+ */
1042
+ applySyncToClient(): void;
1043
+ /**
1044
+ * Create a shape dynamically on the map
1045
+ *
1046
+ * This method creates a static hitbox on the map that can be used for
1047
+ * collision detection, area triggers, or visual boundaries. The shape is
1048
+ * backed by the physics engine's static entity system for accurate collision detection.
1049
+ *
1050
+ * ## Architecture
1051
+ *
1052
+ * Creates a static entity (hitbox) in the physics engine at the specified position and size.
1053
+ * The shape is stored internally and can be retrieved by name. When players or events
1054
+ * collide with this hitbox, the `onInShape` and `onOutShape` hooks are automatically
1055
+ * triggered on both the player and the event.
1056
+ *
1057
+ * @param obj - Shape configuration object
1058
+ * @param obj.x - X position of the shape (top-left corner) (required)
1059
+ * @param obj.y - Y position of the shape (top-left corner) (required)
1060
+ * @param obj.width - Width of the shape in pixels (required)
1061
+ * @param obj.height - Height of the shape in pixels (required)
1062
+ * @param obj.name - Name of the shape (optional, auto-generated if not provided)
1063
+ * @param obj.z - Z position/depth for rendering (optional)
1064
+ * @param obj.color - Color in hexadecimal format, shared with client (optional)
1065
+ * @param obj.collision - Whether the shape has collision (optional)
1066
+ * @param obj.properties - Additional custom properties (optional)
1067
+ * @returns The created RpgShape instance
1068
+ *
1069
+ * @example
1070
+ * ```ts
1071
+ * // Create a simple rectangular shape
1072
+ * const shape = map.createShape({
1073
+ * x: 100,
1074
+ * y: 200,
1075
+ * width: 50,
1076
+ * height: 50,
1077
+ * name: "spawn-zone"
1078
+ * });
1079
+ *
1080
+ * // Create a shape with visual properties
1081
+ * const triggerZone = map.createShape({
1082
+ * x: 300,
1083
+ * y: 400,
1084
+ * width: 100,
1085
+ * height: 100,
1086
+ * name: "treasure-area",
1087
+ * color: "#FFD700",
1088
+ * z: 1,
1089
+ * collision: false,
1090
+ * properties: {
1091
+ * type: "treasure",
1092
+ * value: 100
1093
+ * }
1094
+ * });
1095
+ *
1096
+ * // Player hooks will be triggered automatically
1097
+ * const player: RpgPlayerHooks = {
1098
+ * onInShape(player: RpgPlayer, shape: RpgShape) {
1099
+ * console.log('in', player.name, shape.name);
1100
+ * },
1101
+ * onOutShape(player: RpgPlayer, shape: RpgShape) {
1102
+ * console.log('out', player.name, shape.name);
1103
+ * }
1104
+ * };
1105
+ * ```
1106
+ */
1107
+ createShape(obj: {
1108
+ x: number;
1109
+ y: number;
1110
+ width: number;
1111
+ height: number;
1112
+ name?: string;
1113
+ z?: number;
1114
+ color?: string;
1115
+ collision?: boolean;
1116
+ properties?: Record<string, any>;
1117
+ }): RpgShape;
1118
+ /**
1119
+ * Delete a shape from the map
1120
+ *
1121
+ * Removes a shape by its name and cleans up the associated static hitbox entity.
1122
+ * If the shape doesn't exist, the method does nothing.
1123
+ *
1124
+ * @param name - Name of the shape to remove
1125
+ * @returns void
1126
+ *
1127
+ * @example
1128
+ * ```ts
1129
+ * // Create and then remove a shape
1130
+ * const shape = map.createShape({
1131
+ * x: 100,
1132
+ * y: 200,
1133
+ * width: 50,
1134
+ * height: 50,
1135
+ * name: "temp-zone"
1136
+ * });
1137
+ *
1138
+ * // Later, remove it
1139
+ * map.removeShape("temp-zone");
1140
+ * ```
1141
+ */
1142
+ removeShape(name: string): void;
1143
+ /**
1144
+ * Get all shapes on the map
1145
+ *
1146
+ * Returns an array of all shapes that have been created on this map,
1147
+ * regardless of whether they are static shapes or player-attached shapes.
1148
+ *
1149
+ * @returns Array of RpgShape instances
1150
+ *
1151
+ * @example
1152
+ * ```ts
1153
+ * // Create multiple shapes
1154
+ * map.createShape({ x: 0, y: 0, width: 50, height: 50, name: "zone1" });
1155
+ * map.createShape({ x: 100, y: 100, width: 50, height: 50, name: "zone2" });
1156
+ *
1157
+ * // Get all shapes
1158
+ * const allShapes = map.getShapes();
1159
+ * console.log(allShapes.length); // 2
1160
+ * ```
1161
+ */
1162
+ getShapes(): RpgShape[];
1163
+ /**
1164
+ * Get a shape by its name
1165
+ *
1166
+ * Returns a shape with the specified name, or undefined if no shape
1167
+ * with that name exists on the map.
1168
+ *
1169
+ * @param name - Name of the shape to retrieve
1170
+ * @returns The RpgShape instance, or undefined if not found
1171
+ *
1172
+ * @example
1173
+ * ```ts
1174
+ * // Create a shape with a specific name
1175
+ * map.createShape({
1176
+ * x: 100,
1177
+ * y: 200,
1178
+ * width: 50,
1179
+ * height: 50,
1180
+ * name: "spawn-point"
1181
+ * });
1182
+ *
1183
+ * // Retrieve it later
1184
+ * const spawnZone = map.getShape("spawn-point");
1185
+ * if (spawnZone) {
1186
+ * console.log(`Spawn zone at (${spawnZone.x}, ${spawnZone.y})`);
1187
+ * }
1188
+ * ```
1189
+ */
1190
+ getShape(name: string): RpgShape | undefined;
1191
+ /**
1192
+ * Play a sound for all players on the map
1193
+ *
1194
+ * This method plays a sound for all players currently on the map by iterating
1195
+ * over each player and calling `player.playSound()`. The sound must be defined
1196
+ * on the client side (in the client module configuration).
1197
+ * This is ideal for environmental sounds, battle music, or map-wide events that
1198
+ * all players should hear simultaneously.
1199
+ *
1200
+ * ## Design
1201
+ *
1202
+ * Iterates over all players on the map and calls `player.playSound()` for each one.
1203
+ * This avoids code duplication and reuses the existing player sound logic.
1204
+ * For player-specific sounds, use `player.playSound()` directly.
1205
+ *
1206
+ * @param soundId - Sound identifier, defined on the client side
1207
+ * @param options - Optional sound configuration
1208
+ * @param options.volume - Volume level (0.0 to 1.0, default: 1.0)
1209
+ * @param options.loop - Whether the sound should loop (default: false)
1210
+ *
1211
+ * @example
1212
+ * ```ts
1213
+ * // Play a sound for all players on the map
1214
+ * map.playSound("explosion");
1215
+ *
1216
+ * // Play background music for everyone with volume and loop
1217
+ * map.playSound("battle-theme", {
1218
+ * volume: 0.7,
1219
+ * loop: true
1220
+ * });
1221
+ *
1222
+ * // Play a door opening sound at low volume
1223
+ * map.playSound("door-open", { volume: 0.4 });
1224
+ * ```
1225
+ */
1226
+ playSound(soundId: string, options?: {
1227
+ volume?: number;
1228
+ loop?: boolean;
1229
+ }): void;
1230
+ /**
1231
+ * Stop a sound for all players on the map
1232
+ *
1233
+ * This method stops a sound that was previously started with `map.playSound()`
1234
+ * for all players on the map by iterating over each player and calling `player.stopSound()`.
1235
+ *
1236
+ * @param soundId - Sound identifier to stop
1237
+ *
1238
+ * @example
1239
+ * ```ts
1240
+ * // Start background music for everyone
1241
+ * map.playSound("battle-theme", { loop: true });
1242
+ *
1243
+ * // Later, stop it for everyone
1244
+ * map.stopSound("battle-theme");
1245
+ * ```
1246
+ */
1247
+ stopSound(soundId: string): void;
1248
+ /**
1249
+ * Shake the map for all players
1250
+ *
1251
+ * This method triggers a shake animation on the map for all players currently on the map.
1252
+ * The shake effect creates a visual feedback that can be used for earthquakes, explosions,
1253
+ * impacts, or any dramatic event that should affect the entire map visually.
1254
+ *
1255
+ * ## Architecture
1256
+ *
1257
+ * Broadcasts a shake event to all clients connected to the map. Each client receives
1258
+ * the shake configuration and triggers the shake animation on the map container using
1259
+ * Canvas Engine's shake directive.
1260
+ *
1261
+ * @param options - Optional shake configuration
1262
+ * @param options.intensity - Shake intensity in pixels (default: 10)
1263
+ * @param options.duration - Duration of the shake animation in milliseconds (default: 500)
1264
+ * @param options.frequency - Number of shake oscillations during the animation (default: 10)
1265
+ * @param options.direction - Direction of the shake - 'x', 'y', or 'both' (default: 'both')
1266
+ *
1267
+ * @example
1268
+ * ```ts
1269
+ * // Basic shake with default settings
1270
+ * map.shakeMap();
1271
+ *
1272
+ * // Intense earthquake effect
1273
+ * map.shakeMap({
1274
+ * intensity: 25,
1275
+ * duration: 1000,
1276
+ * frequency: 15,
1277
+ * direction: 'both'
1278
+ * });
1279
+ *
1280
+ * // Horizontal shake for side impact
1281
+ * map.shakeMap({
1282
+ * intensity: 15,
1283
+ * duration: 400,
1284
+ * direction: 'x'
1285
+ * });
1286
+ *
1287
+ * // Vertical shake for ground impact
1288
+ * map.shakeMap({
1289
+ * intensity: 20,
1290
+ * duration: 600,
1291
+ * direction: 'y'
1292
+ * });
1293
+ * ```
1294
+ */
1295
+ shakeMap(options?: {
1296
+ intensity?: number;
1297
+ duration?: number;
1298
+ frequency?: number;
1299
+ direction?: 'x' | 'y' | 'both';
1300
+ }): void;
1301
+ /**
1302
+ * Clear all server resources and reset state
1303
+ *
1304
+ * This method should be called to clean up all server-side resources when
1305
+ * shutting down or resetting the map. It stops the input processing loop
1306
+ * and ensures that all subscriptions are properly cleaned up.
1307
+ *
1308
+ * ## Design
1309
+ *
1310
+ * This method is used primarily in testing environments to ensure clean
1311
+ * state between tests. It stops the tick subscription to prevent memory leaks.
1312
+ *
1313
+ * @example
1314
+ * ```ts
1315
+ * // In test cleanup
1316
+ * afterEach(() => {
1317
+ * map.clear();
1318
+ * });
1319
+ * ```
1320
+ */
1321
+ clear(): void;
106
1322
  }
107
- export interface RpgMap {
108
- $send: (conn: MockConnection, data: any) => void;
109
- $broadcast: (data: any) => void;
1323
+ export interface RpgMap extends RoomMethods {
110
1324
  }
1325
+ export {};