@rpgjs/server 5.0.0-alpha.21 → 5.0.0-alpha.23

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/index.js CHANGED
@@ -19913,206 +19913,49 @@ function WithMoveManager(Base) {
19913
19913
  baseProto.showComponentAnimation.call(this, id, params);
19914
19914
  }
19915
19915
  }
19916
- /**
19917
- * The player passes through the other players (or vice versa). But the player does not go through the events.
19918
- *
19919
- * ```ts
19920
- * player.throughOtherPlayer = true
19921
- * ```
19922
- *
19923
- * @title Go through to other player
19924
- * @prop {boolean} player.throughOtherPlayer
19925
- * @default true
19926
- * @memberof MoveManager
19927
- * */
19928
19916
  set throughOtherPlayer(value) {
19929
19917
  this._throughOtherPlayer.set(value);
19930
19918
  }
19931
19919
  get throughOtherPlayer() {
19932
19920
  return this._throughOtherPlayer();
19933
19921
  }
19934
- /**
19935
- * The player goes through the event or the other players (or vice versa)
19936
- *
19937
- * ```ts
19938
- * player.through = true
19939
- * ```
19940
- *
19941
- * @title Go through the player
19942
- * @prop {boolean} player.through
19943
- * @default false
19944
- * @memberof MoveManager
19945
- * */
19946
19922
  set through(value) {
19947
19923
  this._through.set(value);
19948
19924
  }
19949
19925
  get through() {
19950
19926
  return this._through();
19951
19927
  }
19952
- /**
19953
- * The frequency allows to put a stop time between each movement in the array of the moveRoutes() method.
19954
- * The value represents a dwell time in milliseconds. The higher the value, the slower the frequency.
19955
- *
19956
- * ```ts
19957
- * player.frequency = 400
19958
- * ```
19959
- *
19960
- * You can use Frequency enum
19961
- *
19962
- * ```ts
19963
- * import { Frequency } from '@rpgjs/server'
19964
- * player.frequency = Frequency.Low
19965
- * ```
19966
- *
19967
- * @title Change Frequency
19968
- * @prop {number} player.speed
19969
- * @enum {number}
19970
- *
19971
- * Frequency.Lowest | 600
19972
- * Frequency.Lower | 400
19973
- * Frequency.Low | 200
19974
- * Frequency.High | 100
19975
- * Frequency.Higher | 50
19976
- * Frequency.Highest | 25
19977
- * Frequency.None | 0
19978
- * @default 0
19979
- * @memberof MoveManager
19980
- * */
19981
19928
  set frequency(value) {
19982
19929
  this._frequency.set(value);
19983
19930
  }
19984
19931
  get frequency() {
19985
19932
  return this._frequency();
19986
19933
  }
19987
- /**
19988
- * Add a custom movement strategy to this entity
19989
- *
19990
- * Allows adding any custom MovementStrategy implementation.
19991
- * Multiple strategies can be active simultaneously.
19992
- *
19993
- * @param strategy - The movement strategy to add
19994
- *
19995
- * @example
19996
- * ```ts
19997
- * // Add custom movement
19998
- * const customMove = new LinearMove(5, 0, 1000);
19999
- * player.addMovement(customMove);
20000
- *
20001
- * // Add multiple movements
20002
- * player.addMovement(new Dash(8, { x: 1, y: 0 }, 200));
20003
- * player.addMovement(new Oscillate({ x: 0, y: 1 }, 10, 1000));
20004
- * ```
20005
- */
20006
19934
  addMovement(strategy) {
20007
19935
  const map = this.getCurrentMap();
20008
19936
  if (!map) return;
20009
19937
  map.moveManager.add(this.id, strategy);
20010
19938
  }
20011
- /**
20012
- * Remove a specific movement strategy from this entity
20013
- *
20014
- * @param strategy - The strategy instance to remove
20015
- * @returns True if the strategy was found and removed
20016
- *
20017
- * @example
20018
- * ```ts
20019
- * const dashMove = new Dash(8, { x: 1, y: 0 }, 200);
20020
- * player.addMovement(dashMove);
20021
- *
20022
- * // Later, remove the specific movement
20023
- * const removed = player.removeMovement(dashMove);
20024
- * console.log('Movement removed:', removed);
20025
- * ```
20026
- */
20027
19939
  removeMovement(strategy) {
20028
19940
  const map = this.getCurrentMap();
20029
19941
  if (!map) return false;
20030
19942
  return map.moveManager.remove(this.id, strategy);
20031
19943
  }
20032
- /**
20033
- * Remove all active movement strategies from this entity
20034
- *
20035
- * Stops all current movements immediately.
20036
- *
20037
- * @example
20038
- * ```ts
20039
- * // Stop all movements when player dies
20040
- * player.clearMovements();
20041
- *
20042
- * // Clear movements before applying new ones
20043
- * player.clearMovements();
20044
- * player.dash({ x: 1, y: 0 });
20045
- * ```
20046
- */
20047
19944
  clearMovements() {
20048
19945
  const map = this.getCurrentMap();
20049
19946
  if (!map) return;
20050
19947
  map.moveManager.clear(this.id);
20051
19948
  }
20052
- /**
20053
- * Check if this entity has any active movement strategies
20054
- *
20055
- * @returns True if entity has active movements
20056
- *
20057
- * @example
20058
- * ```ts
20059
- * // Don't accept input while movements are active
20060
- * if (!player.hasActiveMovements()) {
20061
- * player.dash(inputDirection);
20062
- * }
20063
- *
20064
- * // Check before adding new movement
20065
- * if (player.hasActiveMovements()) {
20066
- * player.clearMovements();
20067
- * }
20068
- * ```
20069
- */
20070
19949
  hasActiveMovements() {
20071
19950
  const map = this.getCurrentMap();
20072
19951
  if (!map) return false;
20073
19952
  return map.moveManager.hasActiveStrategies(this.id);
20074
19953
  }
20075
- /**
20076
- * Get all active movement strategies for this entity
20077
- *
20078
- * @returns Array of active movement strategies
20079
- *
20080
- * @example
20081
- * ```ts
20082
- * // Check what movements are currently active
20083
- * const movements = player.getActiveMovements();
20084
- * console.log(`Player has ${movements.length} active movements`);
20085
- *
20086
- * // Find specific movement type
20087
- * const hasDash = movements.some(m => m instanceof Dash);
20088
- * ```
20089
- */
20090
19954
  getActiveMovements() {
20091
19955
  const map = this.getCurrentMap();
20092
19956
  if (!map) return [];
20093
19957
  return map.moveManager.getStrategies(this.id);
20094
19958
  }
20095
- /**
20096
- * Move toward a target player or position using AI pathfinding
20097
- *
20098
- * Uses SeekAvoid strategy for intelligent pathfinding with obstacle avoidance.
20099
- * The entity will seek toward the target while avoiding obstacles.
20100
- *
20101
- * @param target - Target player or position to move toward
20102
- *
20103
- * @example
20104
- * ```ts
20105
- * // Move toward another player
20106
- * const targetPlayer = game.getPlayer('player2');
20107
- * player.moveTo(targetPlayer);
20108
- *
20109
- * // Move toward a specific position
20110
- * player.moveTo({ x: 300, y: 200 });
20111
- *
20112
- * // Stop the movement later
20113
- * player.stopMoveTo();
20114
- * ```
20115
- */
20116
19959
  moveTo(target) {
20117
19960
  const map = this.getCurrentMap();
20118
19961
  if (!map) return;
@@ -20135,22 +19978,6 @@ function WithMoveManager(Base) {
20135
19978
  new SeekAvoid(engine, () => staticTarget, 80, 140, 80, 48)
20136
19979
  );
20137
19980
  }
20138
- /**
20139
- * Stop the current moveTo behavior
20140
- *
20141
- * Removes any active SeekAvoid strategies.
20142
- *
20143
- * @example
20144
- * ```ts
20145
- * // Start following a target
20146
- * player.moveTo(targetPlayer);
20147
- *
20148
- * // Stop following when target is reached
20149
- * if (distanceToTarget < 10) {
20150
- * player.stopMoveTo();
20151
- * }
20152
- * ```
20153
- */
20154
19981
  stopMoveTo() {
20155
19982
  const map = this.getCurrentMap();
20156
19983
  if (!map) return;
@@ -20161,147 +19988,21 @@ function WithMoveManager(Base) {
20161
19988
  }
20162
19989
  });
20163
19990
  }
20164
- /**
20165
- * Perform a dash movement in the specified direction
20166
- *
20167
- * Applies high-speed movement for a short duration.
20168
- *
20169
- * @param direction - Normalized direction vector
20170
- * @param speed - Movement speed (default: 8)
20171
- * @param duration - Duration in milliseconds (default: 200)
20172
- *
20173
- * @example
20174
- * ```ts
20175
- * // Dash right
20176
- * player.dash({ x: 1, y: 0 });
20177
- *
20178
- * // Dash diagonally with custom speed and duration
20179
- * player.dash({ x: 0.7, y: 0.7 }, 12, 300);
20180
- *
20181
- * // Dash in input direction
20182
- * player.dash(inputDirection, 10, 150);
20183
- * ```
20184
- */
20185
19991
  dash(direction, speed = 8, duration = 200) {
20186
19992
  this.addMovement(new Dash(speed, direction, duration));
20187
19993
  }
20188
- /**
20189
- * Apply knockback effect in the specified direction
20190
- *
20191
- * Creates a push effect that gradually decreases over time.
20192
- *
20193
- * @param direction - Normalized direction vector
20194
- * @param force - Initial knockback force (default: 5)
20195
- * @param duration - Duration in milliseconds (default: 300)
20196
- *
20197
- * @example
20198
- * ```ts
20199
- * // Knockback from explosion
20200
- * const explosionDir = { x: -1, y: 0 };
20201
- * player.knockback(explosionDir, 8, 400);
20202
- *
20203
- * // Light knockback from attack
20204
- * player.knockback(attackDirection, 3, 200);
20205
- * ```
20206
- */
20207
19994
  knockback(direction, force = 5, duration = 300) {
20208
19995
  this.addMovement(new Knockback(direction, force, duration));
20209
19996
  }
20210
- /**
20211
- * Follow a sequence of waypoints
20212
- *
20213
- * Entity will move through each waypoint in order.
20214
- *
20215
- * @param waypoints - Array of x,y positions to follow
20216
- * @param speed - Movement speed (default: 2)
20217
- * @param loop - Whether to loop back to start (default: false)
20218
- *
20219
- * @example
20220
- * ```ts
20221
- * // Create a patrol route
20222
- * const patrolPoints = [
20223
- * { x: 100, y: 100 },
20224
- * { x: 300, y: 100 },
20225
- * { x: 300, y: 300 },
20226
- * { x: 100, y: 300 }
20227
- * ];
20228
- * player.followPath(patrolPoints, 3, true);
20229
- *
20230
- * // One-time path to destination
20231
- * player.followPath([{ x: 500, y: 200 }], 4);
20232
- * ```
20233
- */
20234
19997
  followPath(waypoints, speed = 2, loop = false) {
20235
19998
  this.addMovement(new PathFollow(waypoints, speed, loop));
20236
19999
  }
20237
- /**
20238
- * Apply oscillating movement pattern
20239
- *
20240
- * Entity moves back and forth along the specified axis.
20241
- *
20242
- * @param direction - Primary oscillation axis (normalized)
20243
- * @param amplitude - Maximum distance from center (default: 50)
20244
- * @param period - Time for complete cycle in ms (default: 2000)
20245
- *
20246
- * @example
20247
- * ```ts
20248
- * // Horizontal oscillation
20249
- * player.oscillate({ x: 1, y: 0 }, 100, 3000);
20250
- *
20251
- * // Vertical oscillation
20252
- * player.oscillate({ x: 0, y: 1 }, 30, 1500);
20253
- *
20254
- * // Diagonal oscillation
20255
- * player.oscillate({ x: 0.7, y: 0.7 }, 75, 2500);
20256
- * ```
20257
- */
20258
20000
  oscillate(direction, amplitude = 50, period = 2e3) {
20259
20001
  this.addMovement(new Oscillate(direction, amplitude, period));
20260
20002
  }
20261
- /**
20262
- * Apply ice movement physics
20263
- *
20264
- * Creates slippery movement with gradual acceleration and inertia.
20265
- * Perfect for ice terrains or slippery surfaces.
20266
- *
20267
- * @param direction - Target movement direction
20268
- * @param maxSpeed - Maximum speed when fully accelerated (default: 4)
20269
- *
20270
- * @example
20271
- * ```ts
20272
- * // Apply ice physics when on ice terrain
20273
- * if (onIceTerrain) {
20274
- * player.applyIceMovement(inputDirection, 5);
20275
- * }
20276
- *
20277
- * // Update direction when input changes
20278
- * iceMovement.setTargetDirection(newDirection);
20279
- * ```
20280
- */
20281
20003
  applyIceMovement(direction, maxSpeed = 4) {
20282
20004
  this.addMovement(new IceMovement(direction, maxSpeed));
20283
20005
  }
20284
- /**
20285
- * Shoot a projectile in the specified direction
20286
- *
20287
- * Creates projectile movement with various trajectory types.
20288
- *
20289
- * @param type - Type of projectile trajectory
20290
- * @param direction - Normalized direction vector
20291
- * @param speed - Projectile speed (default: 200)
20292
- *
20293
- * @example
20294
- * ```ts
20295
- * // Shoot arrow
20296
- * player.shootProjectile(ProjectileType.Straight, { x: 1, y: 0 }, 300);
20297
- *
20298
- * // Throw grenade with arc
20299
- * player.shootProjectile(ProjectileType.Arc, { x: 0.7, y: 0.7 }, 150);
20300
- *
20301
- * // Bouncing projectile
20302
- * player.shootProjectile(ProjectileType.Bounce, { x: 1, y: 0 }, 100);
20303
- * ```
20304
- */
20305
20006
  shootProjectile(type, direction, speed = 200) {
20306
20007
  const config = {
20307
20008
  speed,
@@ -20314,49 +20015,6 @@ function WithMoveManager(Base) {
20314
20015
  };
20315
20016
  this.addMovement(new ProjectileMovement(type, config));
20316
20017
  }
20317
- /**
20318
- * Give an itinerary to follow using movement strategies
20319
- *
20320
- * Executes a sequence of movements and actions in order. Each route can be:
20321
- * - A Direction enum value for basic movement
20322
- * - A string starting with "turn-" for direction changes
20323
- * - A function that returns directions or actions
20324
- * - A Promise for async operations
20325
- *
20326
- * The method processes routes sequentially, respecting the entity's frequency
20327
- * setting for timing between movements.
20328
- *
20329
- * @param routes - Array of movement instructions to execute
20330
- * @returns Promise that resolves when all routes are completed
20331
- *
20332
- * @example
20333
- * ```ts
20334
- * // Basic directional movements
20335
- * await player.moveRoutes([
20336
- * Direction.Right,
20337
- * Direction.Up,
20338
- * Direction.Left
20339
- * ]);
20340
- *
20341
- * // Mix of movements and turns
20342
- * await player.moveRoutes([
20343
- * Direction.Right,
20344
- * 'turn-' + Direction.Up,
20345
- * Direction.Up
20346
- * ]);
20347
- *
20348
- * // Using functions for dynamic behavior
20349
- * const customMove = (player, map) => [Direction.Right, Direction.Down];
20350
- * await player.moveRoutes([customMove]);
20351
- *
20352
- * // With async operations
20353
- * await player.moveRoutes([
20354
- * Direction.Right,
20355
- * new Promise(resolve => setTimeout(resolve, 1000)), // Wait 1 second
20356
- * Direction.Left
20357
- * ]);
20358
- * ```
20359
- */
20360
20018
  moveRoutes(routes) {
20361
20019
  let count = 0;
20362
20020
  let frequence = 0;
@@ -20468,13 +20126,6 @@ function WithMoveManager(Base) {
20468
20126
  executeNextRoute();
20469
20127
  });
20470
20128
  }
20471
- /**
20472
- * Utility method to flatten nested route arrays
20473
- *
20474
- * @private
20475
- * @param routes - Routes array that may contain nested arrays
20476
- * @returns Flattened array of routes
20477
- */
20478
20129
  flattenRoutes(routes) {
20479
20130
  return routes.reduce((acc, item) => {
20480
20131
  if (Array.isArray(item)) {
@@ -20483,43 +20134,6 @@ function WithMoveManager(Base) {
20483
20134
  return acc.concat(item);
20484
20135
  }, []);
20485
20136
  }
20486
- /**
20487
- * Give a path that repeats itself in a loop to a character
20488
- *
20489
- * Creates an infinite movement pattern that continues until manually stopped.
20490
- * The routes will repeat in a continuous loop, making it perfect for patrol
20491
- * patterns, ambient movements, or any repetitive behavior.
20492
- *
20493
- * You can stop the movement at any time with `breakRoutes()` and replay it
20494
- * with `replayRoutes()`.
20495
- *
20496
- * @param routes - Array of movement instructions to repeat infinitely
20497
- *
20498
- * @example
20499
- * ```ts
20500
- * // Create an infinite random movement pattern
20501
- * player.infiniteMoveRoute([Move.random()]);
20502
- *
20503
- * // Create a patrol route
20504
- * player.infiniteMoveRoute([
20505
- * Direction.Right,
20506
- * Direction.Right,
20507
- * Direction.Down,
20508
- * Direction.Left,
20509
- * Direction.Left,
20510
- * Direction.Up
20511
- * ]);
20512
- *
20513
- * // Mix movements and rotations
20514
- * player.infiniteMoveRoute([
20515
- * Move.turnRight(),
20516
- * Direction.Right,
20517
- * Move.wait(1),
20518
- * Move.turnLeft(),
20519
- * Direction.Left
20520
- * ]);
20521
- * ```
20522
- */
20523
20137
  infiniteMoveRoute(routes) {
20524
20138
  this._infiniteRoutes = routes;
20525
20139
  this._isInfiniteRouteActive = true;
@@ -20538,29 +20152,6 @@ function WithMoveManager(Base) {
20538
20152
  };
20539
20153
  executeInfiniteRoute();
20540
20154
  }
20541
- /**
20542
- * Stop an infinite movement
20543
- *
20544
- * Works only for infinite movements created with `infiniteMoveRoute()`.
20545
- * This method stops the current route execution and prevents the next
20546
- * iteration from starting.
20547
- *
20548
- * @param force - Forces the stop of the infinite movement immediately
20549
- *
20550
- * @example
20551
- * ```ts
20552
- * // Start infinite movement
20553
- * player.infiniteMoveRoute([Move.random()]);
20554
- *
20555
- * // Stop it when player enters combat
20556
- * if (inCombat) {
20557
- * player.breakRoutes(true);
20558
- * }
20559
- *
20560
- * // Gentle stop (completes current route first)
20561
- * player.breakRoutes();
20562
- * ```
20563
- */
20564
20155
  breakRoutes(force = false) {
20565
20156
  this._isInfiniteRouteActive = false;
20566
20157
  if (force) {
@@ -20571,32 +20162,6 @@ function WithMoveManager(Base) {
20571
20162
  this._finishRoute = null;
20572
20163
  }
20573
20164
  }
20574
- /**
20575
- * Replay an infinite movement
20576
- *
20577
- * Works only for infinite movements that were previously created with
20578
- * `infiniteMoveRoute()`. If the route was stopped with `breakRoutes()`,
20579
- * you can restart it with this method using the same route configuration.
20580
- *
20581
- * @example
20582
- * ```ts
20583
- * // Create infinite movement
20584
- * player.infiniteMoveRoute([Move.random()]);
20585
- *
20586
- * // Stop it temporarily
20587
- * player.breakRoutes(true);
20588
- *
20589
- * // Resume the same movement pattern
20590
- * player.replayRoutes();
20591
- *
20592
- * // Stop and start with different conditions
20593
- * if (playerNearby) {
20594
- * player.breakRoutes();
20595
- * } else {
20596
- * player.replayRoutes();
20597
- * }
20598
- * ```
20599
- */
20600
20165
  replayRoutes() {
20601
20166
  if (this._infiniteRoutes && !this._isInfiniteRouteActive) {
20602
20167
  this.infiniteMoveRoute(this._infiniteRoutes);
@@ -20903,168 +20468,21 @@ function WithVariableManager(Base) {
20903
20468
  super(...arguments);
20904
20469
  this.variables = /* @__PURE__ */ new Map();
20905
20470
  }
20906
- /**
20907
- * Assign a variable to the player
20908
- *
20909
- * Stores a key-value pair in the player's variable map. This is useful for
20910
- * tracking game state, quest progress, flags, and other player-specific data.
20911
- * The variable system provides a flexible way to store any type of data
20912
- * associated with the player that persists throughout the game session.
20913
- *
20914
- * @param key - The variable identifier (string key to reference the variable)
20915
- * @param val - The value to store (can be any type: boolean, number, string, object, array)
20916
- * @returns void
20917
- *
20918
- * @example
20919
- * ```ts
20920
- * // Set different types of variables
20921
- * player.setVariable('CHEST_OPENED', true);
20922
- * player.setVariable('playerLevel', 5);
20923
- * player.setVariable('questProgress', { step: 1, completed: false });
20924
- * player.setVariable('inventory', ['sword', 'potion', 'key']);
20925
- * player.setVariable('lastSaveTime', new Date().toISOString());
20926
- * ```
20927
- */
20928
20471
  setVariable(key, val) {
20929
20472
  this.variables.set(key, val);
20930
20473
  }
20931
- /**
20932
- * Get a variable value
20933
- *
20934
- * Retrieves the value associated with the given key from the player's variables.
20935
- * Returns undefined if the variable doesn't exist. This method is type-safe
20936
- * and can be used with generic types for better TypeScript support.
20937
- *
20938
- * @param key - The variable identifier to retrieve
20939
- * @returns The stored value or undefined if not found
20940
- *
20941
- * @example
20942
- * ```ts
20943
- * // Get variables with type inference
20944
- * const hasKey = player.getVariable('CHEST_OPENED'); // boolean | undefined
20945
- * const level = player.getVariable('playerLevel'); // number | undefined
20946
- * const quest = player.getVariable('questProgress'); // object | undefined
20947
- * const missing = player.getVariable('nonexistent'); // undefined
20948
- *
20949
- * // Use with default values
20950
- * const level = player.getVariable('playerLevel') ?? 1;
20951
- * const isChestOpened = player.getVariable('CHEST_OPENED') ?? false;
20952
- * ```
20953
- */
20954
20474
  getVariable(key) {
20955
20475
  return this.variables.get(key);
20956
20476
  }
20957
- /**
20958
- * Remove a variable
20959
- *
20960
- * Deletes a variable from the player's variable map. This is useful for
20961
- * cleaning up temporary flags, resetting certain game states, or managing
20962
- * memory by removing unused variables. The method returns a boolean indicating
20963
- * whether the variable existed and was successfully removed.
20964
- *
20965
- * @param key - The variable identifier to remove
20966
- * @returns true if a variable existed and has been removed, false if the variable does not exist
20967
- *
20968
- * @example
20969
- * ```ts
20970
- * // Remove variables and check if they existed
20971
- * const removed = player.removeVariable('CHEST_OPENED'); // true if existed
20972
- * const notFound = player.removeVariable('nonexistent'); // false
20973
- *
20974
- * // Clean up temporary variables
20975
- * player.removeVariable('tempQuestFlag');
20976
- * player.removeVariable('battleTempData');
20977
- *
20978
- * // Conditional removal
20979
- * if (player.getVariable('questCompleted')) {
20980
- * player.removeVariable('questProgress');
20981
- * }
20982
- * ```
20983
- */
20984
20477
  removeVariable(key) {
20985
20478
  return this.variables.delete(key);
20986
20479
  }
20987
- /**
20988
- * Check if a variable exists
20989
- *
20990
- * Determines whether a variable with the given key exists in the player's
20991
- * variable map, regardless of its value (including falsy values like false, 0, '').
20992
- * This is useful when you need to distinguish between a variable that doesn't
20993
- * exist and one that has a falsy value.
20994
- *
20995
- * @param key - The variable identifier to check
20996
- * @returns true if the variable exists, false otherwise
20997
- *
20998
- * @example
20999
- * ```ts
21000
- * // Check variable existence
21001
- * player.setVariable('flag', false);
21002
- * player.hasVariable('flag'); // true (even though value is false)
21003
- * player.hasVariable('missing'); // false
21004
- *
21005
- * // Use in conditional logic
21006
- * if (player.hasVariable('questStarted')) {
21007
- * // Quest has been started, check progress
21008
- * const progress = player.getVariable('questProgress');
21009
- * } else {
21010
- * // Quest not started yet
21011
- * player.setVariable('questStarted', true);
21012
- * }
21013
- * ```
21014
- */
21015
20480
  hasVariable(key) {
21016
20481
  return this.variables.has(key);
21017
20482
  }
21018
- /**
21019
- * Get all variable keys
21020
- *
21021
- * Returns an array of all variable keys currently stored for this player.
21022
- * This is useful for debugging, serialization, or iterating over all variables.
21023
- * The keys are returned in insertion order.
21024
- *
21025
- * @returns Array of all variable keys
21026
- *
21027
- * @example
21028
- * ```ts
21029
- * // Get all variable keys
21030
- * const keys = player.getVariableKeys();
21031
- * console.log('Player has variables:', keys);
21032
- *
21033
- * // Iterate over all variables
21034
- * keys.forEach(key => {
21035
- * const value = player.getVariable(key);
21036
- * console.log(`${key}: ${value}`);
21037
- * });
21038
- *
21039
- * // Filter specific variable types
21040
- * const questKeys = keys.filter(key => key.startsWith('quest_'));
21041
- * ```
21042
- */
21043
20483
  getVariableKeys() {
21044
20484
  return Array.from(this.variables.keys());
21045
20485
  }
21046
- /**
21047
- * Clear all variables
21048
- *
21049
- * Removes all variables from the player's variable map. This is useful for
21050
- * resetting the player state, cleaning up before saving, or starting fresh.
21051
- * Use with caution as this operation cannot be undone.
21052
- *
21053
- * @returns void
21054
- *
21055
- * @example
21056
- * ```ts
21057
- * // Clear all variables (use with caution)
21058
- * player.clearVariables();
21059
- *
21060
- * // Clear variables conditionally
21061
- * if (gameReset) {
21062
- * player.clearVariables();
21063
- * // Re-initialize essential variables
21064
- * player.setVariable('gameStarted', true);
21065
- * }
21066
- * ```
21067
- */
21068
20486
  clearVariables() {
21069
20487
  this.variables.clear();
21070
20488
  }
@@ -21673,42 +21091,10 @@ class StateLog {
21673
21091
 
21674
21092
  function WithItemManager(Base) {
21675
21093
  return class extends Base {
21676
- /**
21677
- * Retrieves the information of an object: the number and the instance
21678
- * @title Get Item
21679
- * @method player.getItem(itemClass)
21680
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
21681
- * @returns {{ nb: number, item: instance of ItemClass }}
21682
- * @memberof ItemManager
21683
- * @example
21684
- *
21685
- * ```ts
21686
- * import Potion from 'your-database/potion'
21687
- *
21688
- * player.addItem(Potion, 5)
21689
- * const inventory = player.getItem(Potion)
21690
- * console.log(inventory) // { nb: 5, item: <instance of Potion> }
21691
- * ```
21692
- */
21693
21094
  getItem(itemClass) {
21694
21095
  const index = this._getItemIndex(itemClass);
21695
21096
  return this.items()[index];
21696
21097
  }
21697
- /**
21698
- * Check if the player has the item in his inventory.
21699
- * @title Has Item
21700
- * @method player.hasItem(itemClass)
21701
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
21702
- * @returns {boolean}
21703
- * @memberof ItemManager
21704
- * @example
21705
- *
21706
- * ```ts
21707
- * import Potion from 'your-database/potion'
21708
- *
21709
- * player.hasItem(Potion) // false
21710
- * ```
21711
- */
21712
21098
  hasItem(itemClass) {
21713
21099
  return !!this.getItem(itemClass);
21714
21100
  }
@@ -21720,62 +21106,6 @@ function WithItemManager(Base) {
21720
21106
  return isInstanceOf(it, itemClass);
21721
21107
  });
21722
21108
  }
21723
- /**
21724
- * Add an item in the player's inventory. You can give more than one by specifying `nb`
21725
- *
21726
- * Supports three ways to add items:
21727
- * 1. **String**: Pass a string ID to retrieve the item from the database (requires item to be registered in `@RpgModule` database).
21728
- * 2. **Class**: Pass an item class (e.g., `Potion`). The class will be instantiated and automatically added to the map's database if not already present.
21729
- * 3. **Object**: Pass an item object with properties and hooks directly. The object will be automatically added to the map's database if not already present.
21730
- *
21731
- * For classes and objects, if they don't exist in the database, they are automatically added using `map.addInDatabase()`.
21732
- * This allows dynamic item creation without requiring pre-registration in the module database.
21733
- *
21734
- * `onAdd()` method is called on the ItemClass or ItemObject
21735
- *
21736
- * @title Add Item
21737
- * @method player.addItem(item,nb=1)
21738
- * @param {ItemClass | ItemObject | string} item - Item class, object, or string identifier
21739
- * @param {number} [nb] Default 1
21740
- * @returns {Item} The item instance added to inventory
21741
- * @memberof ItemManager
21742
- * @example
21743
- *
21744
- * ```ts
21745
- * import Potion from 'your-database/potion'
21746
- *
21747
- * // Using string ID (retrieves from database - item must be in @RpgModule database)
21748
- * player.addItem('Potion', 5)
21749
- *
21750
- * // Using class (creates instance, auto-adds to map database if not present)
21751
- * player.addItem(Potion, 5)
21752
- *
21753
- * // Using object directly (auto-adds to map database if not present)
21754
- * player.addItem({
21755
- * id: 'custom-potion',
21756
- * name: 'Custom Potion',
21757
- * description: 'A custom potion',
21758
- * price: 150,
21759
- * hpValue: 50,
21760
- * consumable: true,
21761
- * onAdd(player) {
21762
- * console.log('Custom potion added!');
21763
- * },
21764
- * onUse(player) {
21765
- * player.hp += 50;
21766
- * }
21767
- * }, 3)
21768
- *
21769
- * // Object without ID (auto-generates ID and adds to database)
21770
- * player.addItem({
21771
- * name: 'Dynamic Item',
21772
- * price: 100,
21773
- * onUse(player) {
21774
- * console.log('Dynamic item used!');
21775
- * }
21776
- * })
21777
- * ```
21778
- */
21779
21109
  addItem(item, nb = 1) {
21780
21110
  const map = this.getCurrentMap();
21781
21111
  if (!map) {
@@ -21835,38 +21165,6 @@ function WithItemManager(Base) {
21835
21165
  this["execMethod"]("onAdd", [this], hookTarget);
21836
21166
  return instance;
21837
21167
  }
21838
- /**
21839
- * Deletes an item. Decreases the value `nb`. If the number falls to 0, then the item is removed from the inventory. The method then returns `undefined`
21840
- *
21841
- * `onRemove()` method is called on the ItemClass
21842
- *
21843
- * @title Remove Item
21844
- * @method player.removeItem(item,nb=1)
21845
- * @param {ItemClass | string} itemClass string is item id
21846
- * @param {number} [nb] Default 1
21847
- * @returns {{ nb: number, item: instance of ItemClass } | undefined}
21848
- * @throws {ItemLog} notInInventory
21849
- * If the object is not in the inventory, an exception is raised
21850
- * ```
21851
- * {
21852
- * id: ITEM_NOT_INVENTORY,
21853
- * msg: '...'
21854
- * }
21855
- * ```
21856
- * @memberof ItemManager
21857
- * @example
21858
- *
21859
- * ```ts
21860
- * import Potion from 'your-database/potion'
21861
- *
21862
- * try {
21863
- * player.removeItem(Potion, 5)
21864
- * }
21865
- * catch (err) {
21866
- * console.log(err)
21867
- * }
21868
- * ```
21869
- */
21870
21168
  removeItem(itemClass, nb = 1) {
21871
21169
  const itemIndex = this._getItemIndex(itemClass);
21872
21170
  if (itemIndex == -1) {
@@ -21883,50 +21181,6 @@ function WithItemManager(Base) {
21883
21181
  this["execMethod"]("onRemove", [this], hookTarget);
21884
21182
  return this.items()[itemIndex];
21885
21183
  }
21886
- /**
21887
- * Purchases an item and reduces the amount of gold
21888
- *
21889
- * `onAdd()` method is called on the ItemClass
21890
- *
21891
- * @title Buy Item
21892
- * @method player.buyItem(item,nb=1)
21893
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
21894
- * @param {number} [nb] Default 1
21895
- * @returns {{ nb: number, item: instance of ItemClass }}
21896
- * @throws {ItemLog} haveNotPrice
21897
- * If you have not set a price on the item
21898
- * ```
21899
- * {
21900
- * id: NOT_PRICE,
21901
- * msg: '...'
21902
- * }
21903
- * ```
21904
- * @throws {ItemLog} notEnoughGold
21905
- * If the player does not have enough money
21906
- * ```
21907
- * {
21908
- * id: NOT_ENOUGH_GOLD,
21909
- * msg: '...'
21910
- * }
21911
- * ```
21912
- * @memberof ItemManager
21913
- * @example
21914
- *
21915
- * ```ts
21916
- * import Potion from 'your-database/potion'
21917
- *
21918
- * try {
21919
- * // Using class
21920
- * player.buyItem(Potion)
21921
- *
21922
- * // Using string ID
21923
- * player.buyItem('Potion')
21924
- * }
21925
- * catch (err) {
21926
- * console.log(err)
21927
- * }
21928
- * ```
21929
- */
21930
21184
  buyItem(item, nb = 1) {
21931
21185
  let itemId;
21932
21186
  let data;
@@ -21956,58 +21210,6 @@ function WithItemManager(Base) {
21956
21210
  this._gold.update((gold) => gold - totalPrice);
21957
21211
  return this.addItem(item, nb);
21958
21212
  }
21959
- /**
21960
- * Sell an item and the player wins the amount of the item divided by 2
21961
- *
21962
- * `onRemove()` method is called on the ItemClass
21963
- *
21964
- * @title Sell Item
21965
- * @method player.sellItem(item,nb=1)
21966
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
21967
- * @param {number} [nbToSell] Default 1
21968
- * @returns {{ nb: number, item: instance of ItemClass }}
21969
- * @throws {ItemLog} haveNotPrice
21970
- * If you have not set a price on the item
21971
- * ```
21972
- * {
21973
- * id: NOT_PRICE,
21974
- * msg: '...'
21975
- * }
21976
- * ```
21977
- * @throws {ItemLog} notInInventory
21978
- * If the object is not in the inventory, an exception is raised
21979
- * ```
21980
- * {
21981
- * id: ITEM_NOT_INVENTORY,
21982
- * msg: '...'
21983
- * }
21984
- * ```
21985
- * @throws {ItemLog} tooManyToSell
21986
- * If the number of items for sale exceeds the number of actual items in the inventory
21987
- * ```
21988
- * {
21989
- * id: TOO_MANY_ITEM_TO_SELL,
21990
- * msg: '...'
21991
- * }
21992
- * ```
21993
- * @memberof ItemManager
21994
- * @example
21995
- *
21996
- * ```ts
21997
- * import Potion from 'your-database/potion'
21998
- *
21999
- * try {
22000
- * player.addItem(Potion)
22001
- * // Using class
22002
- * player.sellItem(Potion)
22003
- * // Using string ID
22004
- * player.sellItem('Potion')
22005
- * }
22006
- * catch (err) {
22007
- * console.log(err)
22008
- * }
22009
- * ```
22010
- */
22011
21213
  sellItem(itemClass, nbToSell = 1) {
22012
21214
  const itemId = isString(itemClass) ? itemClass : itemClass.name;
22013
21215
  const data = this.databaseById(itemId);
@@ -22038,100 +21240,15 @@ function WithItemManager(Base) {
22038
21240
  }
22039
21241
  return nb;
22040
21242
  }
22041
- /**
22042
- * recover the attack sum of items equipped on the player.
22043
- *
22044
- * @title Get the player's attack
22045
- * @prop {number} player.atk
22046
- * @memberof ItemManager
22047
- */
22048
21243
  get atk() {
22049
21244
  return this.getParamItem(ATK);
22050
21245
  }
22051
- /**
22052
- * recover the physic defense sum of items equipped on the player.
22053
- *
22054
- * @title Get the player's pdef
22055
- * @prop {number} player.pdef
22056
- * @memberof ItemManager
22057
- */
22058
21246
  get pdef() {
22059
21247
  return this.getParamItem(PDEF);
22060
21248
  }
22061
- /**
22062
- * recover the skill defense sum of items equipped on the player.
22063
- *
22064
- * @title Get the player's sdef
22065
- * @prop {number} player.sdef
22066
- * @memberof ItemManager
22067
- */
22068
21249
  get sdef() {
22069
21250
  return this.getParamItem(SDEF);
22070
21251
  }
22071
- /**
22072
- * Use an object. Applies effects and states. Removes the object from the inventory then
22073
- *
22074
- * `onUse()` method is called on the ItemClass (If the use has worked)
22075
- * `onRemove()` method is called on the ItemClass
22076
- *
22077
- * @title Use an Item
22078
- * @method player.useItem(item,nb=1)
22079
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
22080
- * @returns {{ nb: number, item: instance of ItemClass }}
22081
- * @throws {ItemLog} restriction
22082
- * If the player has the `Effect.CAN_NOT_ITEM` effect
22083
- * ```
22084
- * {
22085
- * id: RESTRICTION_ITEM,
22086
- * msg: '...'
22087
- * }
22088
- * ```
22089
- * @throws {ItemLog} notInInventory
22090
- * If the object is not in the inventory, an exception is raised
22091
- * ```
22092
- * {
22093
- * id: ITEM_NOT_INVENTORY,
22094
- * msg: '...'
22095
- * }
22096
- * ```
22097
- * @throws {ItemLog} notUseItem
22098
- * If the `consumable` property is on false
22099
- * ```
22100
- * {
22101
- * id: NOT_USE_ITEM,
22102
- * msg: '...'
22103
- * }
22104
- * ```
22105
- * @throws {ItemLog} chanceToUseFailed
22106
- * Chance to use the item has failed. Chances of use is defined with `ItemClass.hitRate`
22107
- * ```
22108
- * {
22109
- * id: USE_CHANCE_ITEM_FAILED,
22110
- * msg: '...'
22111
- * }
22112
- * ```
22113
- * > the item is still deleted from the inventory
22114
- *
22115
- * `onUseFailed()` method is called on the ItemClass
22116
- *
22117
- * @memberof ItemManager
22118
- * @example
22119
- *
22120
- * ```ts
22121
- * import Potion from 'your-database/potion'
22122
- *
22123
- * try {
22124
- * player.addItem(Potion)
22125
- * // Using class
22126
- * player.useItem(Potion)
22127
- * // Using string ID
22128
- * player.useItem('Potion')
22129
- * }
22130
- * catch (err) {
22131
- * console.log(err)
22132
- * }
22133
- * ```
22134
- */
22135
21252
  useItem(itemClass) {
22136
21253
  const itemId = isString(itemClass) ? itemClass : itemClass.name;
22137
21254
  const inventory = this.getItem(itemClass);
@@ -22158,58 +21275,6 @@ function WithItemManager(Base) {
22158
21275
  this.removeItem(itemClass);
22159
21276
  return inventory;
22160
21277
  }
22161
- /**
22162
- * Equips a weapon or armor on a player. Think first to add the item in the inventory with the `addItem()` method before equipping the item.
22163
- *
22164
- * `onEquip()` method is called on the ItemClass
22165
- *
22166
- * @title Equip Weapon or Armor
22167
- * @method player.equip(itemClass,equip=true)
22168
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
22169
- * @param {number} [equip] Equip the object if true or un-equipped if false
22170
- * @returns {void}
22171
- * @throws {ItemLog} notInInventory
22172
- * If the item is not in the inventory
22173
- * ```
22174
- {
22175
- id: ITEM_NOT_INVENTORY,
22176
- msg: '...'
22177
- }
22178
- ```
22179
- * @throws {ItemLog} invalidToEquiped
22180
- If the item is not by a weapon or armor
22181
- ```
22182
- {
22183
- id: INVALID_ITEM_TO_EQUIP,
22184
- msg: '...'
22185
- }
22186
- ```
22187
- * @throws {ItemLog} isAlreadyEquiped
22188
- If the item Is already equipped
22189
- ```
22190
- {
22191
- id: ITEM_ALREADY_EQUIPED,
22192
- msg: '...'
22193
- }
22194
- ```
22195
- * @memberof ItemManager
22196
- * @example
22197
- *
22198
- * ```ts
22199
- * import Sword from 'your-database/sword'
22200
- *
22201
- * try {
22202
- * player.addItem(Sword)
22203
- * // Using class
22204
- * player.equip(Sword)
22205
- * // Using string ID
22206
- * player.equip('Sword')
22207
- * }
22208
- * catch (err) {
22209
- * console.log(err)
22210
- * }
22211
- * ```
22212
- */
22213
21278
  equip(itemClass, equip = true) {
22214
21279
  const itemId = isString(itemClass) ? itemClass : itemClass.name;
22215
21280
  const inventory = this.getItem(itemClass);
@@ -22259,69 +21324,9 @@ var Effect = /* @__PURE__ */ ((Effect2) => {
22259
21324
  })(Effect || {});
22260
21325
  function WithEffectManager(Base) {
22261
21326
  return class extends Base {
22262
- /**
22263
- * Check if the player has a specific effect
22264
- *
22265
- * Determines whether the player currently has the specified effect active.
22266
- * This includes effects from states, equipment, and temporary conditions.
22267
- * The effect system provides a flexible way to apply various gameplay
22268
- * restrictions and enhancements to the player.
22269
- *
22270
- * @param effect - The effect identifier to check for
22271
- * @returns true if the player has the effect, false otherwise
22272
- *
22273
- * @example
22274
- * ```ts
22275
- * import { Effect } from '@rpgjs/database'
22276
- *
22277
- * // Check for skill restriction
22278
- * const cannotUseSkills = player.hasEffect(Effect.CAN_NOT_SKILL);
22279
- * if (cannotUseSkills) {
22280
- * console.log('Player cannot use skills right now');
22281
- * }
22282
- *
22283
- * // Check for guard effect
22284
- * const isGuarding = player.hasEffect(Effect.GUARD);
22285
- * if (isGuarding) {
22286
- * console.log('Player is in guard stance');
22287
- * }
22288
- *
22289
- * // Check for cost reduction
22290
- * const halfCost = player.hasEffect(Effect.HALF_SP_COST);
22291
- * const actualCost = skillCost / (halfCost ? 2 : 1);
22292
- * ```
22293
- */
22294
21327
  hasEffect(effect) {
22295
21328
  return this.effects.includes(effect);
22296
21329
  }
22297
- /**
22298
- * Retrieves a array of effects assigned to the player, state effects and effects of weapons and armors equipped with the player's own weapons.
22299
- *
22300
- * Gets all currently active effects on the player from multiple sources:
22301
- * - Direct effects assigned to the player
22302
- * - Effects from active states (buffs/debuffs)
22303
- * - Effects from equipped weapons and armor
22304
- * The returned array contains unique effects without duplicates.
22305
- *
22306
- * @returns Array of all active effects on the player
22307
- *
22308
- * @example
22309
- * ```ts
22310
- * // Get all active effects
22311
- * console.log(player.effects); // ['GUARD', 'HALF_SP_COST', ...]
22312
- *
22313
- * // Check multiple effects
22314
- * const effects = player.effects;
22315
- * const hasRestrictions = effects.some(effect =>
22316
- * effect.startsWith('CAN_NOT_')
22317
- * );
22318
- *
22319
- * // Count beneficial effects
22320
- * const beneficialEffects = effects.filter(effect =>
22321
- * ['GUARD', 'SUPER_GUARD', 'HALF_SP_COST'].includes(effect)
22322
- * );
22323
- * ```
22324
- */
22325
21330
  get effects() {
22326
21331
  const getEffects = (prop) => {
22327
21332
  return arrayFlat(this[prop]().map((el) => el.effects || []));
@@ -22332,39 +21337,6 @@ function WithEffectManager(Base) {
22332
21337
  ...getEffects("equipments")
22333
21338
  ]);
22334
21339
  }
22335
- /**
22336
- * Assigns effects to the player. If you give a array, it does not change the effects of the player's states and armor/weapons equipped.
22337
- *
22338
- * Sets the direct effects on the player. This only affects the player's own effects
22339
- * and does not modify effects from states or equipment. The total effects will be
22340
- * the combination of these direct effects plus any effects from states and equipment.
22341
- *
22342
- * @param val - Array of effect identifiers to assign to the player
22343
- *
22344
- * @example
22345
- * ```ts
22346
- * import { Effect } from '@rpgjs/database'
22347
- *
22348
- * // Set direct player effects
22349
- * player.effects = [Effect.CAN_NOT_SKILL];
22350
- *
22351
- * // Add multiple effects
22352
- * player.effects = [
22353
- * Effect.GUARD,
22354
- * Effect.HALF_SP_COST,
22355
- * Effect.CAN_NOT_ITEM
22356
- * ];
22357
- *
22358
- * // Clear direct effects (equipment/state effects remain)
22359
- * player.effects = [];
22360
- *
22361
- * // Temporary effect application
22362
- * const originalEffects = player.effects;
22363
- * player.effects = [...originalEffects, Effect.SUPER_GUARD];
22364
- * // Later restore
22365
- * player.effects = originalEffects;
22366
- * ```
22367
- */
22368
21340
  set effects(val) {
22369
21341
  this._effects.set(val);
22370
21342
  }
@@ -22377,101 +21349,15 @@ function WithElementManager(Base) {
22377
21349
  super(...arguments);
22378
21350
  this._elementsEfficiency = [];
22379
21351
  }
22380
- /**
22381
- * Recovers the player's elements defense on inventory. This list is generated from the `elementsDefense` property defined on the weapons or armors equipped.
22382
- * If several items have the same element, only the highest rate will be taken into account.
22383
- *
22384
- * Gets the defensive capabilities against various elements from equipped items.
22385
- * The system automatically consolidates multiple defensive items, keeping only
22386
- * the highest protection rate for each element type. This provides a comprehensive
22387
- * view of the player's elemental resistances from all equipped gear.
22388
- *
22389
- * @returns Array of element defense objects with rate and element properties
22390
- *
22391
- * @example
22392
- * ```ts
22393
- * import { Armor } from '@rpgjs/server'
22394
- *
22395
- * enum Elements {
22396
- * Fire = 'fire'
22397
- * }
22398
- *
22399
- * @Armor({
22400
- * name: 'Shield',
22401
- * elementsDefense: [{ rate: 1, element: Elements.Fire }]
22402
- * })
22403
- * class Shield {}
22404
- *
22405
- * @Armor({
22406
- * name: 'FireShield',
22407
- * elementsDefense: [{ rate: 0.5, element: Elements.Fire }]
22408
- * })
22409
- * class FireShield {}
22410
- *
22411
- * player.addItem(Shield)
22412
- * player.addItem(FireShield)
22413
- * player.equip(Shield)
22414
- * player.equip(FireShield)
22415
- *
22416
- * console.log(player.elementsDefense) // [{ rate: 1, element: 'fire' }]
22417
- *
22418
- * // Check specific element defense
22419
- * const fireDefense = player.elementsDefense.find(def => def.element === 'fire');
22420
- * if (fireDefense) {
22421
- * console.log(`Fire defense rate: ${fireDefense.rate}`);
22422
- * }
22423
- * ```
22424
- */
22425
21352
  get elementsDefense() {
22426
21353
  return this.getFeature("elementsDefense", "element");
22427
21354
  }
22428
- /**
22429
- * Set or retrieves all the elements where the player is vulnerable or not.
22430
- *
22431
- * Manages the player's elemental efficiency modifiers, which determine how
22432
- * effective different elements are against this player. Values greater than 1
22433
- * indicate vulnerability, while values less than 1 indicate resistance.
22434
- * This combines both class-based efficiency and player-specific modifiers.
22435
- *
22436
- * @returns Array of element efficiency objects with rate and element properties
22437
- *
22438
- * @example
22439
- * ```ts
22440
- * import { Class } from '@rpgjs/server'
22441
- *
22442
- * enum Elements {
22443
- * Fire = 'fire',
22444
- * Ice = 'ice'
22445
- * }
22446
- *
22447
- * @Class({
22448
- * name: 'Fighter',
22449
- * elementsEfficiency: [{ rate: 1, element: Elements.Fire }]
22450
- * })
22451
- * class Hero {}
22452
- *
22453
- * player.setClass(Hero)
22454
- *
22455
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }]
22456
- *
22457
- * player.elementsEfficiency = [{ rate: 2, element: Elements.Ice }]
22458
- *
22459
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }, { rate: 2, element: 'ice' }]
22460
- *
22461
- * // Check for vulnerabilities
22462
- * const vulnerabilities = player.elementsEfficiency.filter(eff => eff.rate > 1);
22463
- * console.log('Vulnerable to:', vulnerabilities.map(v => v.element));
22464
- *
22465
- * // Check for resistances
22466
- * const resistances = player.elementsEfficiency.filter(eff => eff.rate < 1);
22467
- * console.log('Resistant to:', resistances.map(r => r.element));
22468
- * ```
22469
- */
22470
21355
  get elementsEfficiency() {
22471
21356
  if (this._class()) {
21357
+ const classData = this._class();
22472
21358
  return [
22473
21359
  ...this._elementsEfficiency,
22474
- ...this._class()?.elementsEfficiency || []
21360
+ ...classData?.elementsEfficiency || []
22475
21361
  ];
22476
21362
  }
22477
21363
  return this._elementsEfficiency;
@@ -22479,33 +21365,6 @@ function WithElementManager(Base) {
22479
21365
  set elementsEfficiency(val) {
22480
21366
  this._elementsEfficiency = val;
22481
21367
  }
22482
- /**
22483
- * Retrieves a array of elements assigned to the player and the elements of the weapons / armor equipped
22484
- *
22485
- * Gets all offensive elements available to the player from equipped weapons and armor.
22486
- * This determines what elemental damage types the player can deal in combat.
22487
- * The system automatically combines elements from all equipped items and removes duplicates.
22488
- *
22489
- * @returns Array of element objects with rate and element properties for offensive capabilities
22490
- *
22491
- * @example
22492
- * ```ts
22493
- * // Get all offensive elements
22494
- * console.log(player.elements); // [{ rate: 1.5, element: 'fire' }, { rate: 1.2, element: 'ice' }]
22495
- *
22496
- * // Check if player can deal fire damage
22497
- * const hasFireElement = player.elements.some(el => el.element === 'fire');
22498
- * if (hasFireElement) {
22499
- * console.log('Player can deal fire damage');
22500
- * }
22501
- *
22502
- * // Get strongest element
22503
- * const strongestElement = player.elements.reduce((max, current) =>
22504
- * current.rate > max.rate ? current : max
22505
- * );
22506
- * console.log(`Strongest element: ${strongestElement.element} (${strongestElement.rate})`);
22507
- * ```
22508
- */
22509
21368
  get elements() {
22510
21369
  let elements = [];
22511
21370
  for (let item of this.equipments()) {
@@ -22515,40 +21374,6 @@ function WithElementManager(Base) {
22515
21374
  }
22516
21375
  return arrayUniq(elements);
22517
21376
  }
22518
- /**
22519
- * Calculate elemental damage coefficient against another player
22520
- *
22521
- * Determines the damage multiplier when this player attacks another player,
22522
- * taking into account the attacker's offensive elements, the defender's
22523
- * elemental efficiency, and elemental defense from equipment. This is used
22524
- * in the battle system to calculate elemental damage modifiers.
22525
- *
22526
- * @param otherPlayer - The target player to calculate coefficient against
22527
- * @returns Numerical coefficient to multiply base damage by
22528
- *
22529
- * @example
22530
- * ```ts
22531
- * // Calculate elemental damage coefficient
22532
- * const firePlayer = new MyPlayer();
22533
- * const icePlayer = new MyPlayer();
22534
- *
22535
- * // Fire player attacks ice player (assuming ice is weak to fire)
22536
- * const coefficient = icePlayer.coefficientElements(firePlayer);
22537
- * console.log(`Damage multiplier: ${coefficient}`); // e.g., 2.0 for double damage
22538
- *
22539
- * // Use in damage calculation
22540
- * const baseDamage = 100;
22541
- * const finalDamage = baseDamage * coefficient;
22542
- * console.log(`Final damage: ${finalDamage}`);
22543
- *
22544
- * // Check for elemental advantage
22545
- * if (coefficient > 1) {
22546
- * console.log('Attacker has elemental advantage!');
22547
- * } else if (coefficient < 1) {
22548
- * console.log('Defender resists this element');
22549
- * }
22550
- * ```
22551
- */
22552
21377
  coefficientElements(otherPlayer) {
22553
21378
  const atkPlayerElements = otherPlayer.elements;
22554
21379
  const playerElements = this.elementsEfficiency;
@@ -22589,49 +21414,10 @@ function WithSkillManager(Base) {
22589
21414
  return isInstanceOf(skill, skillClass);
22590
21415
  });
22591
21416
  }
22592
- /**
22593
- * Retrieves a learned skill. Returns null, if not found
22594
- * ```ts
22595
- * import Fire from 'your-database/skills/fire'
22596
- *
22597
- * player.getSkill(Fire)
22598
- * ```
22599
- *
22600
- * @title Get Skill
22601
- * @method player.getSkill(skillClass)
22602
- * @param {SkillClass | string} skillClass or data id
22603
- * @returns {instance of SkillClass | null}
22604
- * @memberof SkillManager
22605
- */
22606
21417
  getSkill(skillClass) {
22607
21418
  const index = this._getSkillIndex(skillClass);
22608
21419
  return this.skills()[index] ?? null;
22609
21420
  }
22610
- /**
22611
- * Learn a skill. Attributes the coefficient 1 to the parameter INT (intelligence) if cd is not present on the class.
22612
- *
22613
- * `onLearn()` method is called on the SkillClass
22614
- *
22615
- * ```ts
22616
- * import Fire from 'your-database/skills/fire'
22617
- *
22618
- * player.learnSkill(Fire)
22619
- * ```
22620
- *
22621
- * @title Learn Skill
22622
- * @method player.learnSkill(skillClass)
22623
- * @param {SkillClass | string} skillId or data id
22624
- * @throws {SkillLog} alreadyLearned
22625
- * If the player already knows the skill
22626
- * ```
22627
- {
22628
- id: SKILL_ALREADY_LEARNED,
22629
- msg: '...'
22630
- }
22631
- ```
22632
- * @returns {instance of SkillClass}
22633
- * @memberof SkillManager
22634
- */
22635
21421
  learnSkill(skillId) {
22636
21422
  if (this.getSkill(skillId)) {
22637
21423
  throw SkillLog.alreadyLearned(skillId);
@@ -22640,37 +21426,7 @@ function WithSkillManager(Base) {
22640
21426
  this.skills().push(instance);
22641
21427
  this["execMethod"]("onLearn", [this], instance);
22642
21428
  return instance;
22643
- }
22644
- /**
22645
- * Forget a skill
22646
- *
22647
- * `onForget()` method is called on the SkillClass
22648
- *
22649
- * ```ts
22650
- * import Fire from 'your-database/skills/fire'
22651
- *
22652
- * try {
22653
- * player.forgetSkill(Fire)
22654
- * }
22655
- * catch (err) {
22656
- * console.log(err)
22657
- * }
22658
- * ```
22659
- *
22660
- * @title Forget Skill
22661
- * @method player.learnSkill(skillClass)
22662
- * @param {SkillClass | string} skillId or data id
22663
- * @throws {SkillLog} notLearned
22664
- * If trying to forget a skill not learned
22665
- * ```
22666
- * {
22667
- * id: SKILL_NOT_LEARNED,
22668
- * msg: '...'
22669
- * }
22670
- * ```
22671
- * @returns {instance of SkillClass}
22672
- * @memberof SkillManager
22673
- */
21429
+ }
22674
21430
  forgetSkill(skillId) {
22675
21431
  if (isString(skillId)) skillId = this.databaseById(skillId);
22676
21432
  const index = this._getSkillIndex(skillId);
@@ -22682,81 +21438,6 @@ function WithSkillManager(Base) {
22682
21438
  this["execMethod"]("onForget", [this], instance);
22683
21439
  return instance;
22684
21440
  }
22685
- /**
22686
- * Using a skill
22687
- *
22688
- * `onUse()` method is called on the SkillClass
22689
- *
22690
- * If other players are indicated then damage will be done to these other players. The method `applyDamage()` will be executed
22691
- *
22692
- * ```ts
22693
- * import Fire from 'your-database/skills/fire'
22694
- *
22695
- * try {
22696
- * player.useSkill(Fire)
22697
- * }
22698
- * catch (err) {
22699
- * console.log(err)
22700
- * }
22701
- * ```
22702
- *
22703
- * or
22704
- *
22705
- *
22706
- * * ```ts
22707
- * import Fire from 'your-database/skills/fire'
22708
- *
22709
- * try {
22710
- * player.useSkill(Fire, otherPlayer)
22711
- * }
22712
- * catch (err) {
22713
- * console.log(err)
22714
- * }
22715
- * ```
22716
- *
22717
- * @title Use Skill
22718
- * @method player.useSkill(skillClass,otherPlayer)
22719
- * @param {SkillClass | string} skillId or data id
22720
- * @param {Array<RpgPlayer> | RpgPlayer} [otherPlayer]
22721
- * @throws {SkillLog} restriction
22722
- * If the player has the `Effect.CAN_NOT_SKILL` effect
22723
- * ```
22724
- * {
22725
- * id: RESTRICTION_SKILL,
22726
- * msg: '...'
22727
- * }
22728
- * ```
22729
- * @throws {SkillLog} notLearned
22730
- * If the player tries to use an unlearned skill
22731
- * ```
22732
- * {
22733
- * id: SKILL_NOT_LEARNED,
22734
- * msg: '...'
22735
- * }
22736
- * ```
22737
- * @throws {SkillLog} notEnoughSp
22738
- * If the player does not have enough SP to use the skill
22739
- * ```
22740
- * {
22741
- * id: NOT_ENOUGH_SP,
22742
- * msg: '...'
22743
- * }
22744
- * ```
22745
- * @throws {SkillLog} chanceToUseFailed
22746
- * If the chance to use the skill has failed (defined with the `hitRate` property)
22747
- * ```
22748
- * {
22749
- * id: USE_CHANCE_SKILL_FAILED,
22750
- * msg: '...'
22751
- * }
22752
- * ```
22753
- *
22754
- * `onUseFailed()` method is called on the SkillClass
22755
- *
22756
- * @returns {instance of SkillClass}
22757
- * @memberof SkillManager
22758
- * @todo
22759
- */
22760
21441
  useSkill(skillId, otherPlayer) {
22761
21442
  const skill = this.getSkill(skillId);
22762
21443
  if (this.hasEffect(Effect.CAN_NOT_SKILL)) {
@@ -22792,42 +21473,11 @@ function WithSkillManager(Base) {
22792
21473
 
22793
21474
  function WithBattleManager(Base) {
22794
21475
  return class extends Base {
22795
- /**
22796
- * Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
22797
- *
22798
- * If you don't set the skill parameter, it will be a physical attack.
22799
- * The attack formula is already defined but you can customize it in the server options.
22800
- * This method handles all aspects of damage calculation including critical hits,
22801
- * elemental vulnerabilities, guard effects, and applies the final damage to HP.
22802
- *
22803
- * @param attackerPlayer - The attacking player who deals the damage
22804
- * @param skill - Optional skill object for magical attacks, if not provided uses physical attack
22805
- * @returns Object containing damage details and special effects that occurred
22806
- *
22807
- * @example
22808
- * ```ts
22809
- * // Physical attack
22810
- * const result = player.applyDamage(attackerPlayer);
22811
- * console.log(`Physical damage: ${result.damage}, Critical: ${result.critical}`);
22812
- *
22813
- * // Magical attack with skill
22814
- * const fireSkill = { id: 'fire', power: 50, element: 'fire' };
22815
- * const magicResult = player.applyDamage(attackerPlayer, fireSkill);
22816
- * console.log(`Magic damage: ${magicResult.damage}, Vulnerable: ${magicResult.elementVulnerable}`);
22817
- *
22818
- * // Check for guard effects
22819
- * if (result.guard) {
22820
- * console.log('Attack was partially blocked!');
22821
- * }
22822
- * if (result.superGuard) {
22823
- * console.log('Attack was heavily reduced by super guard!');
22824
- * }
22825
- * ```
22826
- */
22827
21476
  applyDamage(attackerPlayer, skill) {
21477
+ const self = this;
22828
21478
  const getParam = (player) => {
22829
21479
  const params = {};
22830
- this.parameters.forEach((val, key) => {
21480
+ Object.keys(self.parameters).forEach((key) => {
22831
21481
  params[key] = player.param[key];
22832
21482
  });
22833
21483
  return {
@@ -22843,26 +21493,25 @@ function WithBattleManager(Base) {
22843
21493
  let superGuard = false;
22844
21494
  let elementVulnerable = false;
22845
21495
  const paramA = getParam(attackerPlayer);
22846
- const paramB = getParam(this);
22847
- console.log(paramA, paramB);
21496
+ const paramB = getParam(self);
22848
21497
  if (skill) {
22849
- fn = this.getFormulas("damageSkill");
21498
+ fn = self.getFormulas("damageSkill");
22850
21499
  if (!fn) {
22851
21500
  throw new Error("Skill Formulas not exists");
22852
21501
  }
22853
21502
  damage = fn(paramA, paramB, skill);
22854
21503
  } else {
22855
- fn = this.getFormulas("damagePhysic");
21504
+ fn = self.getFormulas("damagePhysic");
22856
21505
  if (!fn) {
22857
21506
  throw new Error("Physic Formulas not exists");
22858
21507
  }
22859
21508
  damage = fn(paramA, paramB);
22860
- const coef = this.coefficientElements(attackerPlayer);
21509
+ const coef = self.coefficientElements(attackerPlayer);
22861
21510
  if (coef >= 2) {
22862
21511
  elementVulnerable = true;
22863
21512
  }
22864
21513
  damage *= coef;
22865
- fn = this.getFormulas("damageCritical");
21514
+ fn = self.getFormulas("damageCritical");
22866
21515
  if (fn) {
22867
21516
  let newDamage = fn(damage, paramA, paramB);
22868
21517
  if (damage != newDamage) {
@@ -22871,8 +21520,8 @@ function WithBattleManager(Base) {
22871
21520
  damage = newDamage;
22872
21521
  }
22873
21522
  }
22874
- if (this.hasEffect(Effect.GUARD)) {
22875
- fn = this.getFormulas("damageGuard");
21523
+ if (self.hasEffect(Effect.GUARD)) {
21524
+ fn = self.getFormulas("damageGuard");
22876
21525
  if (fn) {
22877
21526
  let newDamage = fn(damage, paramA, paramB);
22878
21527
  if (damage != newDamage) {
@@ -22881,11 +21530,11 @@ function WithBattleManager(Base) {
22881
21530
  damage = newDamage;
22882
21531
  }
22883
21532
  }
22884
- if (this.hasEffect(Effect.SUPER_GUARD)) {
21533
+ if (self.hasEffect(Effect.SUPER_GUARD)) {
22885
21534
  damage /= 4;
22886
21535
  superGuard = true;
22887
21536
  }
22888
- this.hp -= damage;
21537
+ self.hp -= damage;
22889
21538
  return {
22890
21539
  damage,
22891
21540
  critical,
@@ -22921,87 +21570,21 @@ function WithBattleManager(Base) {
22921
21570
  * ```
22922
21571
  */
22923
21572
  getFormulas(name) {
22924
- const map = this.getCurrentMap();
22925
- return map.damageFormulas[name];
21573
+ const self = this;
21574
+ const map = self.getCurrentMap();
21575
+ return map?.damageFormulas[name];
22926
21576
  }
22927
21577
  };
22928
21578
  }
22929
21579
 
22930
21580
  function WithClassManager(Base) {
22931
21581
  return class extends Base {
22932
- /**
22933
- * Assign a class to the player
22934
- *
22935
- * Sets the player's class, which defines their combat abilities, stat growth,
22936
- * and available skills. The class system provides the foundation for character
22937
- * progression and specialization. When a class is set, it automatically triggers
22938
- * the class's onSet method for any additional initialization.
22939
- *
22940
- * @param _class - The class constructor or class ID to assign to the player
22941
- * @returns The instantiated class object
22942
- *
22943
- * @example
22944
- * ```ts
22945
- * import { Fighter } from 'my-database/classes/fighter'
22946
- *
22947
- * // Set class using constructor
22948
- * const fighterClass = player.setClass(Fighter);
22949
- * console.log('Class set:', fighterClass.name);
22950
- *
22951
- * // Set class using string ID
22952
- * player.setClass('fighter');
22953
- *
22954
- * // Class affects available skills and stats
22955
- * console.log('Available skills:', player.skills);
22956
- * console.log('Class bonuses applied to stats');
22957
- *
22958
- * // Class determines level progression
22959
- * player.level = 5;
22960
- * // Skills may be automatically learned based on class definition
22961
- * ```
22962
- */
22963
21582
  setClass(_class) {
22964
21583
  if (isString(_class)) _class = this.databaseById(_class);
22965
21584
  const classInstance = new _class();
22966
21585
  this["execMethod"]("onSet", [this], classInstance);
22967
21586
  return classInstance;
22968
21587
  }
22969
- /**
22970
- * Allows to give a set of already defined properties to the player (default equipment, or a list of skills to learn according to the level)
22971
- *
22972
- * Sets up the player as a specific actor archetype, which includes predefined
22973
- * characteristics like starting equipment, parameters, level ranges, and associated class.
22974
- * This is typically used for creating pre-configured character templates or NPCs
22975
- * with specific roles and equipment loadouts.
22976
- *
22977
- * @param actorClass - The actor constructor or actor ID to assign to the player
22978
- * @returns The instantiated actor object
22979
- *
22980
- * @example
22981
- * ```ts
22982
- * import { Hero } from 'my-database/classes/hero'
22983
- *
22984
- * // Set up player as Hero actor
22985
- * const heroActor = player.setActor(Hero);
22986
- * console.log('Actor configured:', heroActor.name);
22987
- *
22988
- * // Actor automatically sets up:
22989
- * // - Starting equipment (sword, armor, etc.)
22990
- * console.log('Starting equipment:', player.equipments());
22991
- *
22992
- * // - Parameter ranges and growth
22993
- * console.log('Level range:', player.initialLevel, '-', player.finalLevel);
22994
- *
22995
- * // - Associated class
22996
- * console.log('Assigned class:', player.class);
22997
- *
22998
- * // - Experience curve
22999
- * console.log('EXP curve:', player.expCurve);
23000
- *
23001
- * // Actor setup is comprehensive
23002
- * player.setActor('hero'); // Can also use string ID
23003
- * ```
23004
- */
23005
21588
  setActor(actorClass) {
23006
21589
  if (isString(actorClass)) actorClass = this.databaseById(actorClass);
23007
21590
  const actor = new actorClass();
@@ -23028,137 +21611,15 @@ function WithStateManager(Base) {
23028
21611
  super(...arguments);
23029
21612
  this._statesEfficiency = signal$1([]);
23030
21613
  }
23031
- /**
23032
- * Recovers the player's states defense on inventory. This list is generated from the `statesDefense` property defined on the weapons or armors equipped.
23033
- * If several items have the same element, only the highest rate will be taken into account.
23034
- *
23035
- * Gets the defensive capabilities against various states from equipped items.
23036
- * The system automatically consolidates multiple defensive items, keeping only
23037
- * the highest protection rate for each state type. This provides comprehensive
23038
- * protection against debuffs and negative status effects.
23039
- *
23040
- * @returns Array of state defense objects with rate and state properties
23041
- *
23042
- * @example
23043
- * ```ts
23044
- * import { Armor, State } from '@rpgjs/server'
23045
- *
23046
- * @State({
23047
- * name: 'Paralyze'
23048
- * })
23049
- * class Paralyze {}
23050
- *
23051
- * @Armor({
23052
- * name: 'Shield',
23053
- * statesDefense: [{ rate: 1, state: Paralyze }]
23054
- * })
23055
- * class Shield {}
23056
- *
23057
- * @Armor({
23058
- * name: 'FireShield',
23059
- * statesDefense: [{ rate: 0.5, state: Paralyze }]
23060
- * })
23061
- * class FireShield {}
23062
- *
23063
- * player.addItem(Shield)
23064
- * player.addItem(FireShield)
23065
- * player.equip(Shield)
23066
- * player.equip(FireShield)
23067
- *
23068
- * console.log(player.statesDefense) // [{ rate: 1, state: instance of Paralyze }]
23069
- *
23070
- * // Check specific state defense
23071
- * const paralyzeDefense = player.statesDefense.find(def => def.state instanceof Paralyze);
23072
- * if (paralyzeDefense) {
23073
- * console.log(`Paralyze defense rate: ${paralyzeDefense.rate}`);
23074
- * }
23075
- * ```
23076
- */
23077
21614
  get statesDefense() {
23078
21615
  return this.getFeature("statesDefense", "state");
23079
21616
  }
23080
- /**
23081
- * Set or retrieves all the states where the player is vulnerable or not.
23082
- *
23083
- * Manages the player's state efficiency modifiers, which determine how
23084
- * effective different states are against this player. Values greater than 1
23085
- * indicate vulnerability, while values less than 1 indicate resistance.
23086
- * This combines both class-based efficiency and player-specific modifiers.
23087
- *
23088
- * @returns Array of state efficiency objects with rate and state properties
23089
- *
23090
- * @example
23091
- * ```ts
23092
- * import { Class, State } from '@rpgjs/server'
23093
- *
23094
- * @State({
23095
- * name: 'Paralyze'
23096
- * })
23097
- * class Paralyze {}
23098
- *
23099
- * @State({
23100
- * name: 'Sleep'
23101
- * })
23102
- * class Sleep {}
23103
- *
23104
- * @Class({
23105
- * name: 'Fighter',
23106
- * statesEfficiency: [{ rate: 1, state: Paralyze }]
23107
- * })
23108
- * class Hero {}
23109
- *
23110
- * player.setClass(Hero)
23111
- *
23112
- * console.log(player.statesEfficiency) // [{ rate: 1, instance of Paralyze }]
23113
- *
23114
- * player.statesEfficiency = [{ rate: 2, state: Sleep }]
23115
- *
23116
- * console.log(player.statesEfficiency) // [{ rate: 1, state: instance of Paralyze }, { rate: 2, state: instance of Sleep }]
23117
- *
23118
- * // Check for vulnerabilities
23119
- * const vulnerabilities = player.statesEfficiency.filter(eff => eff.rate > 1);
23120
- * console.log('Vulnerable to states:', vulnerabilities.map(v => v.state.name));
23121
- *
23122
- * // Check for resistances
23123
- * const resistances = player.statesEfficiency.filter(eff => eff.rate < 1);
23124
- * console.log('Resistant to states:', resistances.map(r => r.state.name));
23125
- * ```
23126
- */
23127
21617
  get statesEfficiency() {
23128
21618
  return this._statesEfficiency;
23129
21619
  }
23130
21620
  set statesEfficiency(val) {
23131
21621
  this._statesEfficiency = val;
23132
21622
  }
23133
- /**
23134
- * Apply states to a player from skill or item effects
23135
- *
23136
- * Processes state application and removal based on skill or item effects.
23137
- * This method handles both adding beneficial states and removing negative ones,
23138
- * with proper chance calculation and resistance checks.
23139
- *
23140
- * @param player - The target player to apply states to
23141
- * @param states - Object containing arrays of states to add or remove
23142
- *
23143
- * @example
23144
- * ```ts
23145
- * // Apply states from a healing skill
23146
- * const healingStates = {
23147
- * addStates: [{ state: Regeneration, rate: 0.8 }],
23148
- * removeStates: [{ state: Poison, rate: 1.0 }]
23149
- * };
23150
- * player.applyStates(targetPlayer, healingStates);
23151
- *
23152
- * // Apply debuff from an enemy attack
23153
- * const debuffStates = {
23154
- * addStates: [
23155
- * { state: Paralyze, rate: 0.3 },
23156
- * { state: Slow, rate: 0.5 }
23157
- * ]
23158
- * };
23159
- * player.applyStates(targetPlayer, debuffStates);
23160
- * ```
23161
- */
23162
21623
  applyStates(player, { addStates, removeStates }) {
23163
21624
  if (addStates) {
23164
21625
  for (let { state, rate } of addStates) {
@@ -23171,40 +21632,6 @@ function WithStateManager(Base) {
23171
21632
  }
23172
21633
  }
23173
21634
  }
23174
- /**
23175
- * Get a state to the player. Returns `null` if the state is not present on the player
23176
- *
23177
- * Retrieves a specific state instance from the player's active states.
23178
- * This is useful for checking state properties, duration, or performing
23179
- * state-specific operations. Returns null if the state is not currently active.
23180
- *
23181
- * @param stateClass - The state class constructor or state ID to search for
23182
- * @returns The state instance if found, null otherwise
23183
- *
23184
- * @example
23185
- * ```ts
23186
- * import Paralyze from 'your-database/states/paralyze'
23187
- *
23188
- * // Check if player has a specific state
23189
- * const paralyzeState = player.getState(Paralyze);
23190
- * if (paralyzeState) {
23191
- * console.log('Player is paralyzed');
23192
- * console.log('Remaining duration:', paralyzeState.duration);
23193
- * }
23194
- *
23195
- * // Check using string ID
23196
- * const poisonState = player.getState('poison');
23197
- * if (poisonState) {
23198
- * console.log('Player is poisoned');
23199
- * }
23200
- *
23201
- * // Use in conditional logic
23202
- * if (player.getState(Sleep)) {
23203
- * console.log('Player cannot act while sleeping');
23204
- * return; // Skip player turn
23205
- * }
23206
- * ```
23207
- */
23208
21635
  getState(stateClass) {
23209
21636
  if (isString(stateClass)) stateClass = this.databaseById(stateClass);
23210
21637
  return this.states().find((state) => {
@@ -23214,54 +21641,6 @@ function WithStateManager(Base) {
23214
21641
  return isInstanceOf(state, stateClass);
23215
21642
  });
23216
21643
  }
23217
- /**
23218
- * Adds a state to the player. Set the chance between 0 and 1 that the state can apply
23219
- *
23220
- * Attempts to apply a state to the player with a specified success chance.
23221
- * The method considers state resistance, efficiency modifiers, and random chance
23222
- * to determine if the state is successfully applied. If successful, the state
23223
- * is added to the player's active states list.
23224
- *
23225
- * @param stateClass - The state class constructor or state ID to apply
23226
- * @param chance - Probability of successful application (0-1, default 1)
23227
- * @returns The state instance if successfully applied, null if already present
23228
- * @throws StateLog.addFailed if the chance roll fails
23229
- *
23230
- * @example
23231
- * ```ts
23232
- * import Paralyze from 'your-database/states/paralyze'
23233
- *
23234
- * try {
23235
- * // Attempt to apply paralyze with 100% chance
23236
- * const state = player.addState(Paralyze);
23237
- * if (state) {
23238
- * console.log('Paralyze applied successfully');
23239
- * }
23240
- * } catch (err) {
23241
- * console.log('Failed to apply paralyze:', err.msg);
23242
- * }
23243
- *
23244
- * // Apply with reduced chance
23245
- * try {
23246
- * player.addState(Poison, 0.3); // 30% chance
23247
- * } catch (err) {
23248
- * console.log('Poison application failed');
23249
- * }
23250
- *
23251
- * // Apply multiple states with different chances
23252
- * const debuffs = [
23253
- * { state: Slow, chance: 0.8 },
23254
- * { state: Weak, chance: 0.6 }
23255
- * ];
23256
- * debuffs.forEach(({ state, chance }) => {
23257
- * try {
23258
- * player.addState(state, chance);
23259
- * } catch (err) {
23260
- * // Handle failed applications
23261
- * }
23262
- * });
23263
- * ```
23264
- */
23265
21644
  addState(stateClass, chance = 1) {
23266
21645
  const state = this.getState(stateClass);
23267
21646
  if (isString(stateClass)) {
@@ -23278,52 +21657,6 @@ function WithStateManager(Base) {
23278
21657
  }
23279
21658
  return null;
23280
21659
  }
23281
- /**
23282
- * Remove a state to the player. Set the chance between 0 and 1 that the state can be removed
23283
- *
23284
- * Attempts to remove a state from the player with a specified success chance.
23285
- * This is useful for cure spells, items, or time-based state removal.
23286
- * The method considers removal resistance and random chance.
23287
- *
23288
- * @param stateClass - The state class constructor or state ID to remove
23289
- * @param chance - Probability of successful removal (0-1, default 1)
23290
- * @throws StateLog.removeFailed if the chance roll fails
23291
- * @throws StateLog.notApplied if the state is not currently active
23292
- *
23293
- * @example
23294
- * ```ts
23295
- * import Paralyze from 'your-database/states/paralyze'
23296
- *
23297
- * try {
23298
- * // Attempt to remove paralyze with 100% chance
23299
- * player.removeState(Paralyze);
23300
- * console.log('Paralyze removed successfully');
23301
- * } catch (err) {
23302
- * if (err.id === 'STATE_NOT_APPLIED') {
23303
- * console.log('Player was not paralyzed');
23304
- * } else {
23305
- * console.log('Failed to remove paralyze:', err.msg);
23306
- * }
23307
- * }
23308
- *
23309
- * // Remove with reduced chance (for weak cure spells)
23310
- * try {
23311
- * player.removeState(Poison, 0.7); // 70% chance
23312
- * } catch (err) {
23313
- * console.log('Cure failed');
23314
- * }
23315
- *
23316
- * // Remove all negative states (cure-all effect)
23317
- * const negativeStates = [Poison, Paralyze, Sleep, Slow];
23318
- * negativeStates.forEach(state => {
23319
- * try {
23320
- * player.removeState(state);
23321
- * } catch (err) {
23322
- * // State wasn't active, continue
23323
- * }
23324
- * });
23325
- * ```
23326
- */
23327
21660
  removeState(stateClass, chance = 1) {
23328
21661
  const index = this.states().findIndex((state) => {
23329
21662
  if (isString(stateClass)) {
@@ -23340,12 +21673,6 @@ function WithStateManager(Base) {
23340
21673
  throw StateLog.notApplied(stateClass);
23341
21674
  }
23342
21675
  }
23343
- /**
23344
- * Find state efficiency modifier for a specific state class
23345
- *
23346
- * @param stateClass - The state class to find efficiency for
23347
- * @returns The efficiency object if found, undefined otherwise
23348
- */
23349
21676
  findStateEfficiency(stateClass) {
23350
21677
  return this.statesEfficiency().find(
23351
21678
  (state) => isInstanceOf(state.state, stateClass)
@@ -23443,6 +21770,10 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
23443
21770
  get hooks() {
23444
21771
  return inject$1(this.context, ModulesToken);
23445
21772
  }
21773
+ // compatibility with v4
21774
+ get server() {
21775
+ return this.map;
21776
+ }
23446
21777
  applyFrames() {
23447
21778
  this._frames.set(this.frames);
23448
21779
  this.frames = [];
@@ -24034,6 +22365,186 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
24034
22365
  };
24035
22366
  this.emit("stopSound", data);
24036
22367
  }
22368
+ /**
22369
+ * Stop all currently playing sounds for this player
22370
+ *
22371
+ * This method stops all sounds that are currently playing for the player.
22372
+ * Useful when changing maps to prevent sound overlap.
22373
+ *
22374
+ * @example
22375
+ * ```ts
22376
+ * // Stop all sounds before changing map
22377
+ * player.stopAllSounds();
22378
+ * await player.changeMap("new-map");
22379
+ * ```
22380
+ */
22381
+ stopAllSounds() {
22382
+ const map2 = this.getCurrentMap();
22383
+ if (!map2) return;
22384
+ this.emit("stopAllSounds", {});
22385
+ }
22386
+ /**
22387
+ * Make the camera follow another player or event
22388
+ *
22389
+ * This method sends an instruction to the client to fix the viewport on another sprite.
22390
+ * The camera will follow the specified player or event, with optional smooth animation.
22391
+ *
22392
+ * ## Design
22393
+ *
22394
+ * The camera follow instruction is sent only to this player's client connection.
22395
+ * This allows each player to have their own camera target, useful for cutscenes,
22396
+ * following NPCs, or focusing on specific events.
22397
+ *
22398
+ * @param otherPlayer - The player or event that the camera should follow
22399
+ * @param options - Camera follow options
22400
+ * @param options.smoothMove - Enable smooth animation. Can be a boolean (default: true) or an object with animation parameters
22401
+ * @param options.smoothMove.time - Time duration for the animation in milliseconds (optional)
22402
+ * @param options.smoothMove.ease - Easing function name. Visit https://easings.net for available functions (optional)
22403
+ *
22404
+ * @example
22405
+ * ```ts
22406
+ * // Follow another player with default smooth animation
22407
+ * player.cameraFollow(otherPlayer, { smoothMove: true });
22408
+ *
22409
+ * // Follow an event with custom smooth animation
22410
+ * player.cameraFollow(npcEvent, {
22411
+ * smoothMove: {
22412
+ * time: 1000,
22413
+ * ease: "easeInOutQuad"
22414
+ * }
22415
+ * });
22416
+ *
22417
+ * // Follow without animation (instant)
22418
+ * player.cameraFollow(targetPlayer, { smoothMove: false });
22419
+ * ```
22420
+ */
22421
+ cameraFollow(otherPlayer, options) {
22422
+ const map2 = this.getCurrentMap();
22423
+ if (!map2) return;
22424
+ const data = {
22425
+ targetId: otherPlayer.id
22426
+ };
22427
+ if (options?.smoothMove !== void 0) {
22428
+ if (typeof options.smoothMove === "boolean") {
22429
+ data.smoothMove = options.smoothMove;
22430
+ } else {
22431
+ data.smoothMove = {
22432
+ enabled: true,
22433
+ ...options.smoothMove
22434
+ };
22435
+ }
22436
+ } else {
22437
+ data.smoothMove = true;
22438
+ }
22439
+ this.emit("cameraFollow", data);
22440
+ }
22441
+ /**
22442
+ * Trigger a flash animation on this player
22443
+ *
22444
+ * This method sends a flash animation event to the client, creating a visual
22445
+ * feedback effect on the player's sprite. The flash can be configured with
22446
+ * various options including type (alpha, tint, or both), duration, cycles, and color.
22447
+ *
22448
+ * ## Design
22449
+ *
22450
+ * The flash is sent as a broadcast event to all clients viewing this player.
22451
+ * This is useful for visual feedback when the player takes damage, receives
22452
+ * a buff, or when an important event occurs.
22453
+ *
22454
+ * @param options - Flash configuration options
22455
+ * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')
22456
+ * @param options.duration - Duration of the flash animation in milliseconds (default: 300)
22457
+ * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)
22458
+ * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)
22459
+ * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)
22460
+ *
22461
+ * @example
22462
+ * ```ts
22463
+ * // Simple flash with default settings (alpha flash)
22464
+ * player.flash();
22465
+ *
22466
+ * // Flash with red tint when taking damage
22467
+ * player.flash({ type: 'tint', tint: 0xff0000 });
22468
+ *
22469
+ * // Flash with both alpha and tint for dramatic effect
22470
+ * player.flash({
22471
+ * type: 'both',
22472
+ * alpha: 0.5,
22473
+ * tint: 0xff0000,
22474
+ * duration: 200,
22475
+ * cycles: 2
22476
+ * });
22477
+ *
22478
+ * // Quick damage flash
22479
+ * player.flash({
22480
+ * type: 'tint',
22481
+ * tint: 'red',
22482
+ * duration: 150,
22483
+ * cycles: 1
22484
+ * });
22485
+ * ```
22486
+ */
22487
+ flash(options) {
22488
+ const map2 = this.getCurrentMap();
22489
+ if (!map2) return;
22490
+ const flashOptions = {
22491
+ type: options?.type || "alpha",
22492
+ duration: options?.duration ?? 300,
22493
+ cycles: options?.cycles ?? 1,
22494
+ alpha: options?.alpha ?? 0.3,
22495
+ tint: options?.tint ?? 16777215
22496
+ };
22497
+ map2.$broadcast({
22498
+ type: "flash",
22499
+ value: {
22500
+ object: this.id,
22501
+ ...flashOptions
22502
+ }
22503
+ });
22504
+ }
22505
+ /**
22506
+ * Set the hitbox of the player for collision detection
22507
+ *
22508
+ * This method defines the hitbox used for collision detection in the physics engine.
22509
+ * The hitbox can be smaller or larger than the visual representation of the player,
22510
+ * allowing for precise collision detection.
22511
+ *
22512
+ * ## Design
22513
+ *
22514
+ * The hitbox is used by the physics engine to detect collisions with other entities,
22515
+ * static obstacles, and shapes. Changing the hitbox will immediately update the
22516
+ * collision detection without affecting the visual appearance of the player.
22517
+ *
22518
+ * @param width - Width of the hitbox in pixels
22519
+ * @param height - Height of the hitbox in pixels
22520
+ *
22521
+ * @example
22522
+ * ```ts
22523
+ * // Set a 20x20 hitbox for precise collision detection
22524
+ * player.setHitbox(20, 20);
22525
+ *
22526
+ * // Set a larger hitbox for easier collision detection
22527
+ * player.setHitbox(40, 40);
22528
+ * ```
22529
+ */
22530
+ setHitbox(width, height) {
22531
+ if (typeof width !== "number" || width <= 0) {
22532
+ throw new Error("setHitbox: width must be a positive number");
22533
+ }
22534
+ if (typeof height !== "number" || height <= 0) {
22535
+ throw new Error("setHitbox: height must be a positive number");
22536
+ }
22537
+ this.hitbox.set({
22538
+ w: width,
22539
+ h: height
22540
+ });
22541
+ const map2 = this.getCurrentMap();
22542
+ if (map2 && map2.physic) {
22543
+ const topLeftX = this.x();
22544
+ const topLeftY = this.y();
22545
+ map2.updateHitbox(this.id, topLeftX, topLeftY, width, height);
22546
+ }
22547
+ }
24037
22548
  /**
24038
22549
  * Set the sync schema for the map
24039
22550
  * @param schema - The schema to set
@@ -27587,10 +26098,72 @@ let RpgMap = class extends RpgCommonMap {
27587
26098
  super();
27588
26099
  this.players = signal$1({});
27589
26100
  this.events = signal$1({});
26101
+ /**
26102
+ * Signal containing the map's database of items, classes, and other game data
26103
+ *
26104
+ * This database can be dynamically populated using `addInDatabase()` and
26105
+ * `removeInDatabase()` methods. It's used to store game entities like items,
26106
+ * classes, skills, etc. that are specific to this map.
26107
+ *
26108
+ * @example
26109
+ * ```ts
26110
+ * // Add data to database
26111
+ * map.addInDatabase('Potion', PotionClass);
26112
+ *
26113
+ * // Access database
26114
+ * const potion = map.database()['Potion'];
26115
+ * ```
26116
+ */
27590
26117
  this.database = signal$1({});
26118
+ /**
26119
+ * Array of map configurations - can contain MapOptions objects or instances of map classes
26120
+ *
26121
+ * This array stores the configuration for this map and any related maps.
26122
+ * It's populated when the map is loaded via `updateMap()`.
26123
+ */
27591
26124
  this.maps = [];
26125
+ /**
26126
+ * Array of sound IDs to play when players join the map
26127
+ *
26128
+ * These sounds are automatically played for each player when they join the map.
26129
+ * Sounds must be defined on the client side.
26130
+ *
26131
+ * @example
26132
+ * ```ts
26133
+ * // Set sounds for the map
26134
+ * map.sounds = ['background-music', 'ambient-forest'];
26135
+ * ```
26136
+ */
26137
+ this.sounds = [];
26138
+ /**
26139
+ * BehaviorSubject that completes when the map data is ready
26140
+ *
26141
+ * This subject is used to signal when the map has finished loading all its data.
26142
+ * Players wait for this to complete before the map is fully initialized.
26143
+ *
26144
+ * @example
26145
+ * ```ts
26146
+ * // Wait for map data to be ready
26147
+ * map.dataIsReady$.subscribe(() => {
26148
+ * console.log('Map is ready!');
26149
+ * });
26150
+ * ```
26151
+ */
27592
26152
  this.dataIsReady$ = new BehaviorSubject$1(void 0);
26153
+ /**
26154
+ * Global configuration object for the map
26155
+ *
26156
+ * This object contains configuration settings that apply to the entire map.
26157
+ * It's populated from the map data when `updateMap()` is called.
26158
+ */
27593
26159
  this.globalConfig = {};
26160
+ /**
26161
+ * Damage formulas configuration for the map
26162
+ *
26163
+ * Contains formulas for calculating damage from skills, physical attacks,
26164
+ * critical hits, and element coefficients. Default formulas are merged
26165
+ * with custom formulas when the map is loaded.
26166
+ */
27594
26167
  this.damageFormulas = {};
27595
26168
  /** Internal: Map of shapes by name */
27596
26169
  this._shapes = /* @__PURE__ */ new Map();
@@ -27712,7 +26285,31 @@ let RpgMap = class extends RpgCommonMap {
27712
26285
  activeCollisions.delete(collisionKey);
27713
26286
  });
27714
26287
  }
27715
- // autoload by @signe/room
26288
+ /**
26289
+ * Intercepts and modifies packets before they are sent to clients
26290
+ *
26291
+ * This method is automatically called by @signe/room for each packet sent to clients.
26292
+ * It adds timestamp and acknowledgment information to sync packets for client-side
26293
+ * prediction reconciliation. This helps with network synchronization and reduces
26294
+ * perceived latency.
26295
+ *
26296
+ * ## Architecture
26297
+ *
26298
+ * Adds metadata to packets:
26299
+ * - `timestamp`: Current server time for client-side prediction
26300
+ * - `ack`: Acknowledgment info with last processed frame and authoritative position
26301
+ *
26302
+ * @param player - The player receiving the packet
26303
+ * @param packet - The packet data to intercept
26304
+ * @param conn - The connection object
26305
+ * @returns Modified packet with timestamp and ack info, or null if player is invalid
26306
+ *
26307
+ * @example
26308
+ * ```ts
26309
+ * // This method is called automatically by the framework
26310
+ * // You typically don't call it directly
26311
+ * ```
26312
+ */
27716
26313
  interceptorPacket(player, packet, conn) {
27717
26314
  let obj = {};
27718
26315
  if (!player) {
@@ -27741,37 +26338,174 @@ let RpgMap = class extends RpgCommonMap {
27741
26338
  }
27742
26339
  };
27743
26340
  }
26341
+ /**
26342
+ * Called when a player joins the map
26343
+ *
26344
+ * This method is automatically called by @signe/room when a player connects to the map.
26345
+ * It initializes the player's connection, sets up the map context, and waits for
26346
+ * the map data to be ready before playing sounds and triggering hooks.
26347
+ *
26348
+ * ## Architecture
26349
+ *
26350
+ * 1. Sets player's map reference and context
26351
+ * 2. Initializes the player
26352
+ * 3. Waits for map data to be ready
26353
+ * 4. Plays map sounds for the player
26354
+ * 5. Triggers `server-player-onJoinMap` hook
26355
+ *
26356
+ * @param player - The player joining the map
26357
+ * @param conn - The connection object for the player
26358
+ *
26359
+ * @example
26360
+ * ```ts
26361
+ * // This method is called automatically by the framework
26362
+ * // You can listen to the hook to perform custom logic
26363
+ * server.addHook('server-player-onJoinMap', (player, map) => {
26364
+ * console.log(`Player ${player.id} joined map ${map.id}`);
26365
+ * });
26366
+ * ```
26367
+ */
27744
26368
  onJoin(player, conn) {
27745
26369
  player.map = this;
27746
26370
  player.context = context$1;
27747
26371
  player.conn = conn;
27748
26372
  player._onInit();
27749
26373
  this.dataIsReady$.pipe(
27750
- finalize$1(() => {
27751
- this.hooks.callHooks("server-player-onJoinMap", player, this).subscribe();
26374
+ finalize$1(async () => {
26375
+ if (this.stopAllSoundsBeforeJoin) {
26376
+ player.stopAllSounds();
26377
+ }
26378
+ this.sounds.forEach((sound) => player.playSound(sound, { loop: true }));
26379
+ await lastValueFrom(this.hooks.callHooks("server-map-onJoin", player, this));
26380
+ if (typeof this._onJoin === "function") {
26381
+ await this._onJoin(player);
26382
+ }
26383
+ await lastValueFrom(this.hooks.callHooks("server-player-onJoinMap", player, this));
27752
26384
  })
27753
26385
  ).subscribe();
27754
26386
  }
27755
- onLeave(player, conn) {
27756
- this.hooks.callHooks("server-player-onLeaveMap", player, this).subscribe();
26387
+ /**
26388
+ * Called when a player leaves the map
26389
+ *
26390
+ * This method is automatically called by @signe/room when a player disconnects from the map.
26391
+ * It cleans up the player's pending inputs and triggers the appropriate hooks.
26392
+ *
26393
+ * ## Architecture
26394
+ *
26395
+ * 1. Triggers `server-player-onLeaveMap` hook
26396
+ * 2. Clears pending inputs to prevent processing after disconnection
26397
+ *
26398
+ * @param player - The player leaving the map
26399
+ * @param conn - The connection object for the player
26400
+ *
26401
+ * @example
26402
+ * ```ts
26403
+ * // This method is called automatically by the framework
26404
+ * // You can listen to the hook to perform custom cleanup
26405
+ * server.addHook('server-player-onLeaveMap', (player, map) => {
26406
+ * console.log(`Player ${player.id} left map ${map.id}`);
26407
+ * });
26408
+ * ```
26409
+ */
26410
+ async onLeave(player, conn) {
26411
+ await lastValueFrom(this.hooks.callHooks("server-map-onLeave", player, this));
26412
+ if (typeof this._onLeave === "function") {
26413
+ await this._onLeave(player);
26414
+ }
26415
+ await lastValueFrom(this.hooks.callHooks("server-player-onLeaveMap", player, this));
27757
26416
  player.pendingInputs = [];
27758
26417
  }
26418
+ /**
26419
+ * Get the hooks system for this map
26420
+ *
26421
+ * Returns the dependency-injected Hooks instance that allows you to trigger
26422
+ * and listen to various game events.
26423
+ *
26424
+ * @returns The Hooks instance for this map
26425
+ *
26426
+ * @example
26427
+ * ```ts
26428
+ * // Trigger a custom hook
26429
+ * map.hooks.callHooks('custom-event', data).subscribe();
26430
+ * ```
26431
+ */
27759
26432
  get hooks() {
27760
26433
  return inject$1(context$1, ModulesToken);
27761
26434
  }
26435
+ /**
26436
+ * Get the width of the map in pixels
26437
+ *
26438
+ * @returns The width of the map in pixels, or 0 if not loaded
26439
+ *
26440
+ * @example
26441
+ * ```ts
26442
+ * const width = map.widthPx;
26443
+ * console.log(`Map width: ${width}px`);
26444
+ * ```
26445
+ */
27762
26446
  get widthPx() {
27763
26447
  return this.data()?.width ?? 0;
27764
26448
  }
26449
+ /**
26450
+ * Get the height of the map in pixels
26451
+ *
26452
+ * @returns The height of the map in pixels, or 0 if not loaded
26453
+ *
26454
+ * @example
26455
+ * ```ts
26456
+ * const height = map.heightPx;
26457
+ * console.log(`Map height: ${height}px`);
26458
+ * ```
26459
+ */
27765
26460
  get heightPx() {
27766
26461
  return this.data()?.height ?? 0;
27767
26462
  }
26463
+ /**
26464
+ * Get the unique identifier of the map
26465
+ *
26466
+ * @returns The map ID, or empty string if not loaded
26467
+ *
26468
+ * @example
26469
+ * ```ts
26470
+ * const mapId = map.id;
26471
+ * console.log(`Current map: ${mapId}`);
26472
+ * ```
26473
+ */
27768
26474
  get id() {
27769
26475
  return this.data()?.id ?? "";
27770
26476
  }
26477
+ /**
26478
+ * Get the X position of this map in the world coordinate system
26479
+ *
26480
+ * This is used when maps are part of a larger world map. The world position
26481
+ * indicates where this map is located relative to other maps.
26482
+ *
26483
+ * @returns The X position in world coordinates, or 0 if not in a world
26484
+ *
26485
+ * @example
26486
+ * ```ts
26487
+ * const worldX = map.worldX;
26488
+ * console.log(`Map is at world position (${worldX}, ${map.worldY})`);
26489
+ * ```
26490
+ */
27771
26491
  get worldX() {
27772
26492
  const worldMaps = this.getWorldMapsManager?.();
27773
26493
  return worldMaps?.getMapInfo(this.id)?.worldX ?? 0;
27774
26494
  }
26495
+ /**
26496
+ * Get the Y position of this map in the world coordinate system
26497
+ *
26498
+ * This is used when maps are part of a larger world map. The world position
26499
+ * indicates where this map is located relative to other maps.
26500
+ *
26501
+ * @returns The Y position in world coordinates, or 0 if not in a world
26502
+ *
26503
+ * @example
26504
+ * ```ts
26505
+ * const worldY = map.worldY;
26506
+ * console.log(`Map is at world position (${map.worldX}, ${worldY})`);
26507
+ * ```
26508
+ */
27775
26509
  get worldY() {
27776
26510
  const worldMaps = this.getWorldMapsManager?.();
27777
26511
  return worldMaps?.getMapInfo(this.id)?.worldY ?? 0;
@@ -27828,6 +26562,26 @@ let RpgMap = class extends RpgCommonMap {
27828
26562
  ...map.events
27829
26563
  ];
27830
26564
  }
26565
+ if (mapFound?.sounds) {
26566
+ this.sounds = [
26567
+ ...map.sounds ?? [],
26568
+ ...mapFound.sounds
26569
+ ];
26570
+ } else {
26571
+ this.sounds = map.sounds ?? [];
26572
+ }
26573
+ if (mapFound?.onLoad) {
26574
+ this._onLoad = mapFound.onLoad;
26575
+ }
26576
+ if (mapFound?.onJoin) {
26577
+ this._onJoin = mapFound.onJoin;
26578
+ }
26579
+ if (mapFound?.onLeave) {
26580
+ this._onLeave = mapFound.onLeave;
26581
+ }
26582
+ if (mapFound?.stopAllSoundsBeforeJoin !== void 0) {
26583
+ this.stopAllSoundsBeforeJoin = mapFound.stopAllSoundsBeforeJoin;
26584
+ }
27831
26585
  }
27832
26586
  await lastValueFrom(this.hooks.callHooks("server-map-onBeforeUpdate", map, this));
27833
26587
  this.loadPhysic();
@@ -27835,6 +26589,10 @@ let RpgMap = class extends RpgCommonMap {
27835
26589
  await this.createDynamicEvent(event);
27836
26590
  }
27837
26591
  this.dataIsReady$.complete();
26592
+ await lastValueFrom(this.hooks.callHooks("server-map-onLoad", this));
26593
+ if (typeof this._onLoad === "function") {
26594
+ await this._onLoad();
26595
+ }
27838
26596
  }
27839
26597
  async updateWorld(request) {
27840
26598
  let worldId = "";
@@ -27972,6 +26730,26 @@ let RpgMap = class extends RpgCommonMap {
27972
26730
  inputs: processedInputs
27973
26731
  };
27974
26732
  }
26733
+ /**
26734
+ * Main game loop that processes player inputs
26735
+ *
26736
+ * This private method runs continuously every 50ms to process pending inputs
26737
+ * for all players on the map. It ensures inputs are processed in order and
26738
+ * prevents concurrent processing for the same player.
26739
+ *
26740
+ * ## Architecture
26741
+ *
26742
+ * - Runs every 50ms for responsive input processing
26743
+ * - Processes inputs for each player with pending inputs
26744
+ * - Uses a flag to prevent concurrent processing for the same player
26745
+ * - Calls `processInput()` to handle anti-cheat validation and movement
26746
+ *
26747
+ * @example
26748
+ * ```ts
26749
+ * // This method is called automatically in the constructor
26750
+ * // You typically don't call it directly
26751
+ * ```
26752
+ */
27975
26753
  loop() {
27976
26754
  setInterval(async () => {
27977
26755
  for (const player of this.getPlayers()) {
@@ -27988,7 +26766,21 @@ let RpgMap = class extends RpgCommonMap {
27988
26766
  }, 50);
27989
26767
  }
27990
26768
  /**
27991
- * Get a world manager by id (if multiple supported in future)
26769
+ * Get a world manager by id
26770
+ *
26771
+ * Returns the world maps manager for the given world ID. Currently, only
26772
+ * one world manager is supported per map instance.
26773
+ *
26774
+ * @param id - The world ID (currently unused, returns the single manager)
26775
+ * @returns The WorldMapsManager instance, or null if not initialized
26776
+ *
26777
+ * @example
26778
+ * ```ts
26779
+ * const worldManager = map.getWorldMaps('my-world');
26780
+ * if (worldManager) {
26781
+ * const mapInfo = worldManager.getMapInfo('map1');
26782
+ * }
26783
+ * ```
27992
26784
  */
27993
26785
  getWorldMaps(id) {
27994
26786
  if (!this.worldMapsManager) return null;
@@ -27996,6 +26788,20 @@ let RpgMap = class extends RpgCommonMap {
27996
26788
  }
27997
26789
  /**
27998
26790
  * Delete a world manager by id
26791
+ *
26792
+ * Removes the world maps manager from this map instance. Currently, only
26793
+ * one world manager is supported, so this clears the single manager.
26794
+ *
26795
+ * @param id - The world ID (currently unused)
26796
+ * @returns true if the manager was deleted, false if it didn't exist
26797
+ *
26798
+ * @example
26799
+ * ```ts
26800
+ * const deleted = map.deleteWorldMaps('my-world');
26801
+ * if (deleted) {
26802
+ * console.log('World manager removed');
26803
+ * }
26804
+ * ```
27999
26805
  */
28000
26806
  deleteWorldMaps(id) {
28001
26807
  if (!this.worldMapsManager) return false;
@@ -28004,6 +26810,26 @@ let RpgMap = class extends RpgCommonMap {
28004
26810
  }
28005
26811
  /**
28006
26812
  * Create a world manager dynamically
26813
+ *
26814
+ * Creates a new WorldMapsManager instance and configures it with the provided
26815
+ * map configurations. This is used when loading world data from Tiled or
26816
+ * other map editors.
26817
+ *
26818
+ * @param world - World configuration object
26819
+ * @param world.id - Optional world identifier
26820
+ * @param world.maps - Array of map configurations for the world
26821
+ * @returns The newly created WorldMapsManager instance
26822
+ *
26823
+ * @example
26824
+ * ```ts
26825
+ * const manager = map.createDynamicWorldMaps({
26826
+ * id: 'my-world',
26827
+ * maps: [
26828
+ * { id: 'map1', worldX: 0, worldY: 0, width: 800, height: 600 },
26829
+ * { id: 'map2', worldX: 800, worldY: 0, width: 800, height: 600 }
26830
+ * ]
26831
+ * });
26832
+ * ```
28007
26833
  */
28008
26834
  createDynamicWorldMaps(world) {
28009
26835
  const manager = new WorldMapsManager();
@@ -28013,6 +26839,22 @@ let RpgMap = class extends RpgCommonMap {
28013
26839
  }
28014
26840
  /**
28015
26841
  * Update world maps by id. Auto-create when missing.
26842
+ *
26843
+ * Updates the world maps configuration. If the world manager doesn't exist,
26844
+ * it is automatically created. This is useful for dynamically loading world
26845
+ * data or updating map positions.
26846
+ *
26847
+ * @param id - The world ID
26848
+ * @param maps - Array of map configurations to update
26849
+ * @returns Promise that resolves when the update is complete
26850
+ *
26851
+ * @example
26852
+ * ```ts
26853
+ * await map.updateWorldMaps('my-world', [
26854
+ * { id: 'map1', worldX: 0, worldY: 0, width: 800, height: 600 },
26855
+ * { id: 'map2', worldX: 800, worldY: 0, width: 800, height: 600 }
26856
+ * ]);
26857
+ * ```
28016
26858
  */
28017
26859
  async updateWorldMaps(id, maps) {
28018
26860
  let world = this.getWorldMaps(id);
@@ -28168,24 +27010,159 @@ let RpgMap = class extends RpgCommonMap {
28168
27010
  this.events()[id] = eventInstance;
28169
27011
  await eventInstance.execMethod("onInit");
28170
27012
  }
27013
+ /**
27014
+ * Get an event by its ID
27015
+ *
27016
+ * Returns the event with the specified ID, or undefined if not found.
27017
+ * The return type can be narrowed using TypeScript generics.
27018
+ *
27019
+ * @param eventId - The unique identifier of the event
27020
+ * @returns The event instance, or undefined if not found
27021
+ *
27022
+ * @example
27023
+ * ```ts
27024
+ * // Get any event
27025
+ * const event = map.getEvent('npc-1');
27026
+ *
27027
+ * // Get event with type narrowing
27028
+ * const npc = map.getEvent<MyNPC>('npc-1');
27029
+ * if (npc) {
27030
+ * npc.speak('Hello!');
27031
+ * }
27032
+ * ```
27033
+ */
28171
27034
  getEvent(eventId) {
28172
27035
  return this.events()[eventId];
28173
27036
  }
27037
+ /**
27038
+ * Get a player by their ID
27039
+ *
27040
+ * Returns the player with the specified ID, or undefined if not found.
27041
+ *
27042
+ * @param playerId - The unique identifier of the player
27043
+ * @returns The player instance, or undefined if not found
27044
+ *
27045
+ * @example
27046
+ * ```ts
27047
+ * const player = map.getPlayer('player-123');
27048
+ * if (player) {
27049
+ * console.log(`Player ${player.name} is on the map`);
27050
+ * }
27051
+ * ```
27052
+ */
28174
27053
  getPlayer(playerId) {
28175
27054
  return this.players()[playerId];
28176
27055
  }
27056
+ /**
27057
+ * Get all players currently on the map
27058
+ *
27059
+ * Returns an array of all players that are currently connected to this map.
27060
+ *
27061
+ * @returns Array of all RpgPlayer instances on the map
27062
+ *
27063
+ * @example
27064
+ * ```ts
27065
+ * const players = map.getPlayers();
27066
+ * console.log(`There are ${players.length} players on the map`);
27067
+ *
27068
+ * players.forEach(player => {
27069
+ * console.log(`- ${player.name}`);
27070
+ * });
27071
+ * ```
27072
+ */
28177
27073
  getPlayers() {
28178
27074
  return Object.values(this.players());
28179
27075
  }
27076
+ /**
27077
+ * Get all events on the map
27078
+ *
27079
+ * Returns an array of all events (NPCs, objects, etc.) that are currently
27080
+ * on this map.
27081
+ *
27082
+ * @returns Array of all RpgEvent instances on the map
27083
+ *
27084
+ * @example
27085
+ * ```ts
27086
+ * const events = map.getEvents();
27087
+ * console.log(`There are ${events.length} events on the map`);
27088
+ *
27089
+ * events.forEach(event => {
27090
+ * console.log(`- ${event.name} at (${event.x}, ${event.y})`);
27091
+ * });
27092
+ * ```
27093
+ */
28180
27094
  getEvents() {
28181
27095
  return Object.values(this.events());
28182
27096
  }
27097
+ /**
27098
+ * Get the first event that matches a condition
27099
+ *
27100
+ * Searches through all events on the map and returns the first one that
27101
+ * matches the provided callback function.
27102
+ *
27103
+ * @param cb - Callback function that returns true for the desired event
27104
+ * @returns The first matching event, or undefined if none found
27105
+ *
27106
+ * @example
27107
+ * ```ts
27108
+ * // Find an event by name
27109
+ * const npc = map.getEventBy(event => event.name === 'Merchant');
27110
+ *
27111
+ * // Find an event at a specific position
27112
+ * const chest = map.getEventBy(event =>
27113
+ * event.x === 100 && event.y === 200
27114
+ * );
27115
+ * ```
27116
+ */
28183
27117
  getEventBy(cb) {
28184
27118
  return this.getEventsBy(cb)[0];
28185
27119
  }
27120
+ /**
27121
+ * Get all events that match a condition
27122
+ *
27123
+ * Searches through all events on the map and returns all events that
27124
+ * match the provided callback function.
27125
+ *
27126
+ * @param cb - Callback function that returns true for desired events
27127
+ * @returns Array of all matching events
27128
+ *
27129
+ * @example
27130
+ * ```ts
27131
+ * // Find all NPCs
27132
+ * const npcs = map.getEventsBy(event => event.name.startsWith('NPC-'));
27133
+ *
27134
+ * // Find all events in a specific area
27135
+ * const nearbyEvents = map.getEventsBy(event =>
27136
+ * event.x >= 0 && event.x <= 100 &&
27137
+ * event.y >= 0 && event.y <= 100
27138
+ * );
27139
+ * ```
27140
+ */
28186
27141
  getEventsBy(cb) {
28187
27142
  return this.getEvents().filter(cb);
28188
27143
  }
27144
+ /**
27145
+ * Remove an event from the map
27146
+ *
27147
+ * Removes the event with the specified ID from the map. The event will
27148
+ * be removed from the synchronized events signal, causing it to disappear
27149
+ * on all clients.
27150
+ *
27151
+ * @param eventId - The unique identifier of the event to remove
27152
+ *
27153
+ * @example
27154
+ * ```ts
27155
+ * // Remove an event
27156
+ * map.removeEvent('npc-1');
27157
+ *
27158
+ * // Remove event after interaction
27159
+ * const chest = map.getEvent('chest-1');
27160
+ * if (chest) {
27161
+ * // ... do something with chest ...
27162
+ * map.removeEvent('chest-1');
27163
+ * }
27164
+ * ```
27165
+ */
28189
27166
  removeEvent(eventId) {
28190
27167
  delete this.events()[eventId];
28191
27168
  }
@@ -28267,16 +27244,44 @@ let RpgMap = class extends RpgCommonMap {
28267
27244
  animationName
28268
27245
  });
28269
27246
  }
28270
- /**
28271
- * Set the sync schema for the map
28272
- * @param schema - The schema to set
28273
- */
28274
27247
  /**
28275
27248
  * Configure runtime synchronized properties on the map
28276
- *
28277
- * Design
27249
+ *
27250
+ * This method allows you to dynamically add synchronized properties to the map
27251
+ * that will be automatically synced with clients. The schema follows the same
27252
+ * structure as module properties with `$initial`, `$syncWithClient`, and `$permanent` options.
27253
+ *
27254
+ * ## Architecture
27255
+ *
28278
27256
  * - Reads a schema object shaped like module props
28279
27257
  * - Creates typed sync signals with @signe/sync
27258
+ * - Properties are accessible as `map.propertyName`
27259
+ *
27260
+ * @param schema - Schema object defining the properties to sync
27261
+ * @param schema[key].$initial - Initial value for the property
27262
+ * @param schema[key].$syncWithClient - Whether to sync this property to clients
27263
+ * @param schema[key].$permanent - Whether to persist this property
27264
+ *
27265
+ * @example
27266
+ * ```ts
27267
+ * // Add synchronized properties to the map
27268
+ * map.setSync({
27269
+ * weather: {
27270
+ * $initial: 'sunny',
27271
+ * $syncWithClient: true,
27272
+ * $permanent: false
27273
+ * },
27274
+ * timeOfDay: {
27275
+ * $initial: 12,
27276
+ * $syncWithClient: true,
27277
+ * $permanent: false
27278
+ * }
27279
+ * });
27280
+ *
27281
+ * // Use the properties
27282
+ * map.weather.set('rainy');
27283
+ * const currentWeather = map.weather();
27284
+ * ```
28280
27285
  */
28281
27286
  setSync(schema) {
28282
27287
  for (let key in schema) {
@@ -28559,6 +27564,64 @@ let RpgMap = class extends RpgCommonMap {
28559
27564
  player.stopSound(soundId);
28560
27565
  });
28561
27566
  }
27567
+ /**
27568
+ * Shake the map for all players
27569
+ *
27570
+ * This method triggers a shake animation on the map for all players currently on the map.
27571
+ * The shake effect creates a visual feedback that can be used for earthquakes, explosions,
27572
+ * impacts, or any dramatic event that should affect the entire map visually.
27573
+ *
27574
+ * ## Architecture
27575
+ *
27576
+ * Broadcasts a shake event to all clients connected to the map. Each client receives
27577
+ * the shake configuration and triggers the shake animation on the map container using
27578
+ * Canvas Engine's shake directive.
27579
+ *
27580
+ * @param options - Optional shake configuration
27581
+ * @param options.intensity - Shake intensity in pixels (default: 10)
27582
+ * @param options.duration - Duration of the shake animation in milliseconds (default: 500)
27583
+ * @param options.frequency - Number of shake oscillations during the animation (default: 10)
27584
+ * @param options.direction - Direction of the shake - 'x', 'y', or 'both' (default: 'both')
27585
+ *
27586
+ * @example
27587
+ * ```ts
27588
+ * // Basic shake with default settings
27589
+ * map.shakeMap();
27590
+ *
27591
+ * // Intense earthquake effect
27592
+ * map.shakeMap({
27593
+ * intensity: 25,
27594
+ * duration: 1000,
27595
+ * frequency: 15,
27596
+ * direction: 'both'
27597
+ * });
27598
+ *
27599
+ * // Horizontal shake for side impact
27600
+ * map.shakeMap({
27601
+ * intensity: 15,
27602
+ * duration: 400,
27603
+ * direction: 'x'
27604
+ * });
27605
+ *
27606
+ * // Vertical shake for ground impact
27607
+ * map.shakeMap({
27608
+ * intensity: 20,
27609
+ * duration: 600,
27610
+ * direction: 'y'
27611
+ * });
27612
+ * ```
27613
+ */
27614
+ shakeMap(options) {
27615
+ this.$broadcast({
27616
+ type: "shakeMap",
27617
+ value: {
27618
+ intensity: options?.intensity ?? 10,
27619
+ duration: options?.duration ?? 500,
27620
+ frequency: options?.frequency ?? 10,
27621
+ direction: options?.direction ?? "both"
27622
+ }
27623
+ });
27624
+ }
28562
27625
  };
28563
27626
  __decorateClass$1([
28564
27627
  users$1(RpgPlayer)
@@ -28894,6 +27957,14 @@ function provideServerModules(modules) {
28894
27957
  const mainModuleServer = findModules(context, "Server");
28895
27958
  modules2 = [...mainModuleServer, ...modules2];
28896
27959
  modules2 = modules2.map((module) => {
27960
+ if (typeof module === "function") {
27961
+ const instance = new module();
27962
+ const moduleObj = {};
27963
+ for (const key in instance) {
27964
+ moduleObj[key] = instance[key];
27965
+ }
27966
+ module = moduleObj;
27967
+ }
28897
27968
  if ("server" in module) {
28898
27969
  module = module.server;
28899
27970
  }
@@ -28914,7 +27985,27 @@ function provideServerModules(modules) {
28914
27985
  maps: {
28915
27986
  load: (engine) => {
28916
27987
  maps.forEach((map) => {
28917
- engine.maps.push(map);
27988
+ let mapInstance;
27989
+ if (typeof map === "function") {
27990
+ const MapClass = map;
27991
+ mapInstance = {
27992
+ id: MapClass.prototype?.id ?? MapClass.id,
27993
+ file: MapClass.prototype?.file ?? MapClass.file,
27994
+ type: MapClass.type,
27995
+ name: MapClass.prototype?.name,
27996
+ sounds: MapClass.prototype?.sounds,
27997
+ lowMemory: MapClass.prototype?.lowMemory,
27998
+ stopAllSoundsBeforeJoin: MapClass.prototype?.stopAllSoundsBeforeJoin,
27999
+ events: MapClass.prototype?._events,
28000
+ syncSchema: MapClass.prototype?.$schema,
28001
+ onLoad: MapClass.prototype?.onLoad,
28002
+ onJoin: MapClass.prototype?.onJoin,
28003
+ onLeave: MapClass.prototype?.onLeave
28004
+ };
28005
+ } else {
28006
+ mapInstance = map;
28007
+ }
28008
+ engine.maps.push(mapInstance);
28918
28009
  });
28919
28010
  }
28920
28011
  }
@@ -28966,11 +28057,21 @@ function MapData(options) {
28966
28057
  target.prototype.id = options.id;
28967
28058
  target.prototype.sounds = options.sounds;
28968
28059
  target.prototype.lowMemory = options.lowMemory;
28060
+ target.prototype.stopAllSoundsBeforeJoin = options.stopAllSoundsBeforeJoin;
28969
28061
  target.prototype.$schema = {};
28970
28062
  if (options.syncSchema) {
28971
28063
  target.prototype.$schema = options.syncSchema;
28972
28064
  }
28973
28065
  target.prototype._events = options.events;
28066
+ if (options.onLoad) {
28067
+ target.prototype.onLoad = options.onLoad;
28068
+ }
28069
+ if (options.onJoin) {
28070
+ target.prototype.onJoin = options.onJoin;
28071
+ }
28072
+ if (options.onLeave) {
28073
+ target.prototype.onLeave = options.onLeave;
28074
+ }
28974
28075
  };
28975
28076
  }
28976
28077