@rpgjs/server 5.0.0-alpha.0 → 5.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/Player/BattleManager.d.ts +32 -22
  2. package/dist/Player/ClassManager.d.ts +31 -18
  3. package/dist/Player/ComponentManager.d.ts +60 -0
  4. package/dist/Player/EffectManager.d.ts +40 -0
  5. package/dist/Player/ElementManager.d.ts +31 -0
  6. package/dist/Player/GoldManager.d.ts +22 -0
  7. package/dist/Player/GuiManager.d.ts +176 -0
  8. package/dist/Player/ItemFixture.d.ts +6 -0
  9. package/dist/Player/ItemManager.d.ts +27 -13
  10. package/dist/Player/MoveManager.d.ts +31 -43
  11. package/dist/Player/ParameterManager.d.ts +27 -19
  12. package/dist/Player/Player.d.ts +123 -8
  13. package/dist/Player/SkillManager.d.ts +27 -19
  14. package/dist/Player/StateManager.d.ts +28 -35
  15. package/dist/Player/VariableManager.d.ts +30 -0
  16. package/dist/RpgServer.d.ts +224 -1
  17. package/dist/index.js +1097 -636
  18. package/dist/index.js.map +1 -1
  19. package/dist/rooms/map.d.ts +70 -1
  20. package/package.json +8 -8
  21. package/src/Player/BattleManager.ts +97 -38
  22. package/src/Player/ClassManager.ts +95 -35
  23. package/src/Player/ComponentManager.ts +64 -20
  24. package/src/Player/EffectManager.ts +110 -27
  25. package/src/Player/ElementManager.ts +126 -25
  26. package/src/Player/GoldManager.ts +32 -35
  27. package/src/Player/GuiManager.ts +187 -140
  28. package/src/Player/ItemFixture.ts +4 -5
  29. package/src/Player/ItemManager.ts +39 -26
  30. package/src/Player/MoveManager.ts +40 -31
  31. package/src/Player/ParameterManager.ts +35 -25
  32. package/src/Player/Player.ts +184 -39
  33. package/src/Player/SkillManager.ts +44 -23
  34. package/src/Player/StateManager.ts +210 -95
  35. package/src/Player/VariableManager.ts +180 -48
  36. package/src/RpgServer.ts +232 -1
  37. package/src/core/context.ts +1 -0
  38. package/src/rooms/map.ts +76 -8
  39. package/dist/Player/Event.d.ts +0 -0
  40. package/src/Player/Event.ts +0 -0
package/dist/index.js CHANGED
@@ -26158,7 +26158,7 @@ function WithMoveManager(Base) {
26158
26158
  return class extends Base {
26159
26159
  constructor() {
26160
26160
  super(...arguments);
26161
- // Private properties for infinite route management
26161
+ // Properties for infinite route management
26162
26162
  this._infiniteRoutes = null;
26163
26163
  this._finishRoute = null;
26164
26164
  this._isInfiniteRouteActive = false;
@@ -26962,123 +26962,16 @@ class NotificationGui extends Gui {
26962
26962
  }
26963
26963
 
26964
26964
  function WithGuiManager(Base) {
26965
- return class extends Base {
26965
+ class GuiManagerMixin extends Base {
26966
26966
  constructor() {
26967
26967
  super(...arguments);
26968
26968
  this._gui = {};
26969
26969
  }
26970
- /**
26971
- * Show a text. This is a graphical interface already built. Opens the GUI named `rpg-dialog`
26972
- *
26973
- * ```ts
26974
- * player.showText('Hello World')
26975
- * ```
26976
- *
26977
- * The method returns a promise. It is resolved when the dialog box is closed.
26978
- *
26979
- * ```ts
26980
- * await player.showText('Hello World')
26981
- * // dialog box is closed, then ...
26982
- * ```
26983
- *
26984
- * **Option: position**
26985
- *
26986
- * You can define how the dialog box is displayed:
26987
- * - top
26988
- * - middle
26989
- * - bottom
26990
- *
26991
- * (bottom by default)
26992
- *
26993
- * ```ts
26994
- * player.showText('Hello World', {
26995
- * position: 'top'
26996
- * })
26997
- * ```
26998
- *
26999
- * **Option: fullWidth**
27000
- *
27001
- * `boolean` (true by default)
27002
- *
27003
- * Indicate that the dialog box will take the full width of the screen.
27004
- *
27005
- * ```ts
27006
- * player.showText('Hello World', {
27007
- * fullWidth: true
27008
- * })
27009
- * ```
27010
- *
27011
- * **Option: autoClose**
27012
- *
27013
- * `boolean` (false by default)
27014
- *
27015
- * If false, the user will have to press Enter to close the dialog box.
27016
- *
27017
- * ```ts
27018
- * player.showText('Hello World', {
27019
- * autoClose: true
27020
- * })
27021
- * ```
27022
- *
27023
- * **Option: typewriterEffect**
27024
- *
27025
- * `boolean` (true by default)
27026
- *
27027
- * Performs a typewriter effect
27028
- *
27029
- * ```ts
27030
- * player.showText('Hello World', {
27031
- * typewriterEffect: false
27032
- * })
27033
- * ```
27034
- *
27035
- * **Option: talkWith**
27036
- *
27037
- * `RpgPlayer` (nothing by default)
27038
- *
27039
- * If you specify the event or another player, the other player will stop his or her movement and look in the player's direction.
27040
- *
27041
- * ```ts
27042
- * // Code in an event
27043
- * player.showText('Hello World', {
27044
- * talkWith: this
27045
- * })
27046
- * ```
27047
- *
27048
- * @title Show Text
27049
- * @method player.showText(text,options)
27050
- * @param {string} text
27051
- * @param {object} [options] the different options, see usage below
27052
- * @returns {Promise}
27053
- * @memberof GuiManager
27054
- */
27055
26970
  showText(msg, options = {}) {
27056
26971
  const gui = new DialogGui(this);
27057
26972
  this._gui[gui.id] = gui;
27058
26973
  return gui.openDialog(msg, options);
27059
26974
  }
27060
- /**
27061
- * Shows a dialog box with a choice. Opens the GUI named `rpg-dialog`
27062
- *
27063
- * ```ts
27064
- * const choice = await player.showChoices('What color do you prefer?', [
27065
- * { text: 'Black', value: 'black' },
27066
- * { text: 'Rather the blue', value: 'blue' },
27067
- * { text: 'I don\'t have a preference!', value: 'none' }
27068
- * ])
27069
- *
27070
- * // If the player selects the first
27071
- * console.log(choice) // { text: 'Black', value: 'black' }
27072
- * ```
27073
- *
27074
- * @title Show Choices
27075
- * @method player.showChoices(text,choices)
27076
- * @param {string} text
27077
- * @param {Array<{ text: string, value: any }>} choices
27078
- * @param {object} [options] Same options as the openDialog method
27079
- * @returns {Promise<Choice | null>}
27080
- * @memberof GuiManager
27081
- */
27082
26975
  showChoices(msg, choices, options) {
27083
26976
  return this.showText(msg, {
27084
26977
  choices,
@@ -27088,19 +26981,6 @@ function WithGuiManager(Base) {
27088
26981
  return choices[indexSelected];
27089
26982
  });
27090
26983
  }
27091
- /**
27092
- * Displays a notification . Opens the GUI named `rpg-notification`
27093
- *
27094
- * @title Displays a notification
27095
- * @method player.showNotification()
27096
- * @param {string} message - The message to display in the notification
27097
- * @param {object} options - An object containing options for the notification
27098
- * @param {number} options.time - The time to display the notification for (in ms). Default: 2000ms
27099
- * @param {string} options.icon - The icon to display in the notification. Put the identifier of the spritesheet (defined on the client side)
27100
- * @param {string} options.sound - The sound to play when the notification is shown. Set the sound ID (defined on the client side)
27101
- * @returns {void}
27102
- * @memberof GuiManager
27103
- */
27104
26984
  showNotification(message, options = {}) {
27105
26985
  const gui = new NotificationGui(this);
27106
26986
  this._gui[gui.id] = gui;
@@ -27110,14 +26990,6 @@ function WithGuiManager(Base) {
27110
26990
  };
27111
26991
  return gui.open(data);
27112
26992
  }
27113
- /**
27114
- * Calls main menu. Opens the GUI named `rpg-main-menu`
27115
- *
27116
- * @title Call Main Menu
27117
- * @method player.callMainMenu()
27118
- * @returns {void}
27119
- * @memberof GuiManager
27120
- */
27121
26993
  callMainMenu() {
27122
26994
  const gui = new MenuGui(this);
27123
26995
  this._gui[gui.id] = gui;
@@ -27250,23 +27122,12 @@ function WithGuiManager(Base) {
27250
27122
  const _players = players || this;
27251
27123
  this._attachedGui(_players, false);
27252
27124
  }
27253
- };
27125
+ }
27126
+ return GuiManagerMixin;
27254
27127
  }
27255
27128
 
27256
27129
  function WithGoldManager(Base) {
27257
27130
  return class extends Base {
27258
- /**
27259
- * You can change the game money
27260
- *
27261
- * ```ts
27262
- * player.gold += 100
27263
- * ```
27264
- *
27265
- * @title Change Gold
27266
- * @prop {number} player.gold
27267
- * @default 0
27268
- * @memberof GoldManager
27269
- * */
27270
27131
  set gold(val) {
27271
27132
  if (val < 0) {
27272
27133
  val = 0;
@@ -27288,52 +27149,168 @@ function WithVariableManager(Base) {
27288
27149
  /**
27289
27150
  * Assign a variable to the player
27290
27151
  *
27152
+ * Stores a key-value pair in the player's variable map. This is useful for
27153
+ * tracking game state, quest progress, flags, and other player-specific data.
27154
+ * The variable system provides a flexible way to store any type of data
27155
+ * associated with the player that persists throughout the game session.
27156
+ *
27157
+ * @param key - The variable identifier (string key to reference the variable)
27158
+ * @param val - The value to store (can be any type: boolean, number, string, object, array)
27159
+ * @returns void
27160
+ *
27161
+ * @example
27291
27162
  * ```ts
27292
- * player.setVariable('OPEN_CHEST', true)
27163
+ * // Set different types of variables
27164
+ * player.setVariable('CHEST_OPENED', true);
27165
+ * player.setVariable('playerLevel', 5);
27166
+ * player.setVariable('questProgress', { step: 1, completed: false });
27167
+ * player.setVariable('inventory', ['sword', 'potion', 'key']);
27168
+ * player.setVariable('lastSaveTime', new Date().toISOString());
27293
27169
  * ```
27294
- *
27295
- * @title Set variable
27296
- * @method player.setVariable(key,val)
27297
- * @param {string} key
27298
- * @param {any} val
27299
- * @returns {void}
27300
- * @memberof VariableManager
27301
- * */
27170
+ */
27302
27171
  setVariable(key, val) {
27303
27172
  this.variables.set(key, val);
27304
27173
  }
27305
27174
  /**
27306
- * Get a variable
27175
+ * Get a variable value
27176
+ *
27177
+ * Retrieves the value associated with the given key from the player's variables.
27178
+ * Returns undefined if the variable doesn't exist. This method is type-safe
27179
+ * and can be used with generic types for better TypeScript support.
27180
+ *
27181
+ * @param key - The variable identifier to retrieve
27182
+ * @returns The stored value or undefined if not found
27307
27183
  *
27184
+ * @example
27308
27185
  * ```ts
27309
- * const val = player.getVariable('OPEN_CHEST')
27310
- * ```
27186
+ * // Get variables with type inference
27187
+ * const hasKey = player.getVariable('CHEST_OPENED'); // boolean | undefined
27188
+ * const level = player.getVariable('playerLevel'); // number | undefined
27189
+ * const quest = player.getVariable('questProgress'); // object | undefined
27190
+ * const missing = player.getVariable('nonexistent'); // undefined
27311
27191
  *
27312
- * @title Get variable
27313
- * @method player.setVariable(key,val)
27314
- * @param {string} key
27315
- * @returns {any}
27316
- * @memberof VariableManager
27317
- * */
27192
+ * // Use with default values
27193
+ * const level = player.getVariable('playerLevel') ?? 1;
27194
+ * const isChestOpened = player.getVariable('CHEST_OPENED') ?? false;
27195
+ * ```
27196
+ */
27318
27197
  getVariable(key) {
27319
27198
  return this.variables.get(key);
27320
27199
  }
27321
27200
  /**
27322
27201
  * Remove a variable
27323
27202
  *
27203
+ * Deletes a variable from the player's variable map. This is useful for
27204
+ * cleaning up temporary flags, resetting certain game states, or managing
27205
+ * memory by removing unused variables. The method returns a boolean indicating
27206
+ * whether the variable existed and was successfully removed.
27207
+ *
27208
+ * @param key - The variable identifier to remove
27209
+ * @returns true if a variable existed and has been removed, false if the variable does not exist
27210
+ *
27211
+ * @example
27324
27212
  * ```ts
27325
- * player.removeVariable('OPEN_CHEST')
27326
- * ```
27213
+ * // Remove variables and check if they existed
27214
+ * const removed = player.removeVariable('CHEST_OPENED'); // true if existed
27215
+ * const notFound = player.removeVariable('nonexistent'); // false
27327
27216
  *
27328
- * @title Remove variable
27329
- * @method player.removeVariable(key)
27330
- * @param {string} key
27331
- * @returns {boolean} true if a variable existed and has been removed, or false if the variable does not exist.
27332
- * @memberof VariableManager
27333
- * */
27217
+ * // Clean up temporary variables
27218
+ * player.removeVariable('tempQuestFlag');
27219
+ * player.removeVariable('battleTempData');
27220
+ *
27221
+ * // Conditional removal
27222
+ * if (player.getVariable('questCompleted')) {
27223
+ * player.removeVariable('questProgress');
27224
+ * }
27225
+ * ```
27226
+ */
27334
27227
  removeVariable(key) {
27335
27228
  return this.variables.delete(key);
27336
27229
  }
27230
+ /**
27231
+ * Check if a variable exists
27232
+ *
27233
+ * Determines whether a variable with the given key exists in the player's
27234
+ * variable map, regardless of its value (including falsy values like false, 0, '').
27235
+ * This is useful when you need to distinguish between a variable that doesn't
27236
+ * exist and one that has a falsy value.
27237
+ *
27238
+ * @param key - The variable identifier to check
27239
+ * @returns true if the variable exists, false otherwise
27240
+ *
27241
+ * @example
27242
+ * ```ts
27243
+ * // Check variable existence
27244
+ * player.setVariable('flag', false);
27245
+ * player.hasVariable('flag'); // true (even though value is false)
27246
+ * player.hasVariable('missing'); // false
27247
+ *
27248
+ * // Use in conditional logic
27249
+ * if (player.hasVariable('questStarted')) {
27250
+ * // Quest has been started, check progress
27251
+ * const progress = player.getVariable('questProgress');
27252
+ * } else {
27253
+ * // Quest not started yet
27254
+ * player.setVariable('questStarted', true);
27255
+ * }
27256
+ * ```
27257
+ */
27258
+ hasVariable(key) {
27259
+ return this.variables.has(key);
27260
+ }
27261
+ /**
27262
+ * Get all variable keys
27263
+ *
27264
+ * Returns an array of all variable keys currently stored for this player.
27265
+ * This is useful for debugging, serialization, or iterating over all variables.
27266
+ * The keys are returned in insertion order.
27267
+ *
27268
+ * @returns Array of all variable keys
27269
+ *
27270
+ * @example
27271
+ * ```ts
27272
+ * // Get all variable keys
27273
+ * const keys = player.getVariableKeys();
27274
+ * console.log('Player has variables:', keys);
27275
+ *
27276
+ * // Iterate over all variables
27277
+ * keys.forEach(key => {
27278
+ * const value = player.getVariable(key);
27279
+ * console.log(`${key}: ${value}`);
27280
+ * });
27281
+ *
27282
+ * // Filter specific variable types
27283
+ * const questKeys = keys.filter(key => key.startsWith('quest_'));
27284
+ * ```
27285
+ */
27286
+ getVariableKeys() {
27287
+ return Array.from(this.variables.keys());
27288
+ }
27289
+ /**
27290
+ * Clear all variables
27291
+ *
27292
+ * Removes all variables from the player's variable map. This is useful for
27293
+ * resetting the player state, cleaning up before saving, or starting fresh.
27294
+ * Use with caution as this operation cannot be undone.
27295
+ *
27296
+ * @returns void
27297
+ *
27298
+ * @example
27299
+ * ```ts
27300
+ * // Clear all variables (use with caution)
27301
+ * player.clearVariables();
27302
+ *
27303
+ * // Clear variables conditionally
27304
+ * if (gameReset) {
27305
+ * player.clearVariables();
27306
+ * // Re-initialize essential variables
27307
+ * player.setVariable('gameStarted', true);
27308
+ * }
27309
+ * ```
27310
+ */
27311
+ clearVariables() {
27312
+ this.variables.clear();
27313
+ }
27337
27314
  };
27338
27315
  }
27339
27316
 
@@ -27886,278 +27863,53 @@ class StateLog {
27886
27863
  }
27887
27864
  }
27888
27865
 
27889
- function WithStateManager(Base) {
27866
+ function WithItemManager(Base) {
27890
27867
  return class extends Base {
27891
- constructor() {
27892
- super(...arguments);
27893
- this._statesEfficiency = signal$2([]);
27894
- }
27895
27868
  /**
27896
- * Recovers the player's states defense on inventory. This list is generated from the `statesDefense` property defined on the weapons or armors equipped.
27897
- * If several items have the same element, only the highest rate will be taken into account.
27869
+ * Retrieves the information of an object: the number and the instance
27870
+ * @title Get Item
27871
+ * @method player.getItem(itemClass)
27872
+ * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
27873
+ * @returns {{ nb: number, item: instance of ItemClass }}
27874
+ * @memberof ItemManager
27875
+ * @example
27898
27876
  *
27899
27877
  * ```ts
27900
- * import { Armor, State } from '@rpgjs/server'
27901
- *
27902
- * @State({
27903
- * name: 'Paralyze'
27904
- * })
27905
- * class Paralyze {}
27906
- *
27907
- * @Armor({
27908
- * name: 'Shield',
27909
- * statesDefense: [{ rate: 1, state: Paralyze }]
27910
- * })
27911
- * class Shield {}
27912
- *
27913
- * @Armor({
27914
- * name: 'FireShield',
27915
- * statesDefense: [{ rate: 0.5, state: Paralyze }]
27916
- * })
27917
- * class FireShield {}
27918
- *
27919
- * player.addItem(Shield)
27920
- * player.addItem(FireShield)
27921
- * player.equip(Shield)
27922
- * player.equip(FireShield)
27878
+ * import Potion from 'your-database/potion'
27923
27879
  *
27924
- * console.log(player.statesDefense) // [{ rate: 1, state: instance of Paralyze }]
27925
- * ```
27926
- * @title Get States Defense
27927
- * @prop {Array<{ rate: number, state: StateClass}>} player.statesDefense
27928
- * @readonly
27929
- * @memberof StateManager
27930
- * */
27931
- get statesDefense() {
27932
- return this.getFeature("statesDefense", "state");
27880
+ * player.addItem(Potion, 5)
27881
+ * const inventory = player.getItem(Potion)
27882
+ * console.log(inventory) // { nb: 5, item: <instance of Potion> }
27883
+ * ```
27884
+ */
27885
+ getItem(itemClass) {
27886
+ const index = this._getItemIndex(itemClass);
27887
+ return this.items()[index];
27933
27888
  }
27934
27889
  /**
27935
- * Set or retrieves all the states where the player is vulnerable or not.
27936
- *
27937
- * ```ts
27938
- * import { Class, State } from '@rpgjs/server'
27939
- *
27940
- * @State({
27941
- * name: 'Paralyze'
27942
- * })
27943
- * class Paralyze {}
27944
- *
27945
- * @State({
27946
- * name: 'Sleep'
27947
- * })
27948
- * class Sleep {}
27949
- *
27950
- * @Class({
27951
- * name: 'Fighter',
27952
- * statesEfficiency: [{ rate: 1, state: Paralyze }]
27953
- * })
27954
- * class Hero {}
27955
- *
27956
- * player.setClass(Hero)
27957
- *
27958
- * console.log(player.statesEfficiency) // [{ rate: 1, instance of Paralyze }]
27959
- *
27960
- * player.statesEfficiency = [{ rate: 2, state: Sleep }]
27890
+ * Check if the player has the item in his inventory.
27891
+ * @title Has Item
27892
+ * @method player.hasItem(itemClass)
27893
+ * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
27894
+ * @returns {boolean}
27895
+ * @memberof ItemManager
27896
+ * @example
27961
27897
  *
27962
- * console.log(player.statesEfficiency) // [{ rate: 1, state: instance of Paralyze }, { rate: 2, state: instance of Sleep }]
27963
- * ```
27964
- * @title Set/Get States Efficiency
27965
- * @prop {Array<{ rate: number, state: StateClass}>} player.statesEfficiency
27966
- * @memberof StateManager
27967
- * */
27968
- get statesEfficiency() {
27969
- return this._statesEfficiency;
27970
- }
27971
- set statesEfficiency(val) {
27972
- this._statesEfficiency = val;
27973
- }
27974
- applyStates(player, { addStates, removeStates }) {
27975
- if (addStates) {
27976
- for (let { state, rate } of addStates) {
27977
- player.addState(state, rate);
27978
- }
27979
- }
27980
- if (removeStates) {
27981
- for (let { state, rate } of removeStates) {
27982
- player.removeState(state, rate);
27983
- }
27984
- }
27985
- }
27986
- /**
27987
- * Get a state to the player. Returns `null` if the state is not present on the player
27988
27898
  * ```ts
27989
- * import Paralyze from 'your-database/states/paralyze'
27899
+ * import Potion from 'your-database/potion'
27990
27900
  *
27991
- * player.getState(Paralyze)
27901
+ * player.hasItem(Potion) // false
27992
27902
  * ```
27993
- *
27994
- * @title Get State
27995
- * @method player.getState(stateClass)
27996
- * @param {StateClass | string} stateClass or state id
27997
- * @returns {instance of StateClass | null}
27998
- * @memberof StateManager
27999
27903
  */
28000
- getState(stateClass) {
28001
- if (isString(stateClass)) stateClass = this.databaseById(stateClass);
28002
- return this.states().find((state) => {
28003
- if (isString(stateClass)) {
28004
- return state.id == stateClass;
27904
+ hasItem(itemClass) {
27905
+ return !!this.getItem(itemClass);
27906
+ }
27907
+ _getItemIndex(itemClass) {
27908
+ return this.items().findIndex((it) => {
27909
+ if (isString(itemClass)) {
27910
+ return it.id() == itemClass;
28005
27911
  }
28006
- return isInstanceOf(state, stateClass);
28007
- });
28008
- }
28009
- /**
28010
- * Adds a state to the player. Set the chance between 0 and 1 that the state can apply
28011
- * ```ts
28012
- * import Paralyze from 'your-database/states/paralyze'
28013
- *
28014
- * try {
28015
- * player.addState(Paralyze)
28016
- * }
28017
- * catch (err) {
28018
- * console.log(err)
28019
- * }
28020
- * ```
28021
- *
28022
- * @title Add State
28023
- * @method player.addState(stateClass,chance=1)
28024
- * @param {StateClass | string} stateClass state class or state id
28025
- * @param {number} [chance] 1 by default
28026
- * @throws {StateLog} addFailed
28027
- * If the chance to add the state has failed (defined with the `chance` param)
28028
- * ```
28029
- * {
28030
- * id: ADD_STATE_FAILED,
28031
- * msg: '...'
28032
- * }
28033
- * ```
28034
- * @returns {instance of StateClass}
28035
- * @memberof StateManager
28036
- * @todo
28037
- */
28038
- addState(stateClass, chance = 1) {
28039
- const state = this.getState(stateClass);
28040
- if (isString(stateClass)) {
28041
- stateClass = this.databaseById(stateClass);
28042
- }
28043
- if (!state) {
28044
- if (Math.random() > chance) {
28045
- throw StateLog.addFailed(stateClass);
28046
- }
28047
- const instance = new stateClass();
28048
- this.states().push(instance);
28049
- this.applyStates(this, instance);
28050
- return instance;
28051
- }
28052
- return null;
28053
- }
28054
- /**
28055
- * Remove a state to the player. Set the chance between 0 and 1 that the state can be removed
28056
- * ```ts
28057
- * import Paralyze from 'your-database/states/paralyze'
28058
- *
28059
- * try {
28060
- * player.removeState(Paralyze)
28061
- * }
28062
- * catch (err) {
28063
- * console.log(err)
28064
- * }
28065
- * ```
28066
- *
28067
- * @title Remove State
28068
- * @method player.removeState(stateClass,chance=1)
28069
- * @param {StateClass|string} stateClass class state or state id
28070
- * @param {number} [chance] 1 by default
28071
- * @throws {StateLog} removeFailed
28072
- * If the chance to remove the state has failed (defined with the `chance` param)
28073
- * ```
28074
- * {
28075
- * id: REMOVE_STATE_FAILED,
28076
- * msg: '...'
28077
- * }
28078
- * ```
28079
- * @throws {StateLog} notApplied
28080
- * If the status does not exist
28081
- * ```
28082
- * {
28083
- * id: STATE_NOT_APPLIED,
28084
- * msg: '...'
28085
- * }
28086
- * ```
28087
- * @returns {instance of StateClass}
28088
- * @memberof StateManager
28089
- */
28090
- removeState(stateClass, chance = 1) {
28091
- const index = this.states().findIndex((state) => {
28092
- if (isString(stateClass)) {
28093
- return state.id == stateClass;
28094
- }
28095
- return isInstanceOf(state, stateClass);
28096
- });
28097
- if (index != -1) {
28098
- if (Math.random() > chance) {
28099
- throw StateLog.removeFailed(stateClass);
28100
- }
28101
- this.states().splice(index, 1);
28102
- } else {
28103
- throw StateLog.notApplied(stateClass);
28104
- }
28105
- }
28106
- findStateEfficiency(stateClass) {
28107
- return this.statesEfficiency().find(
28108
- (state) => isInstanceOf(state.state, stateClass)
28109
- );
28110
- }
28111
- };
28112
- }
28113
-
28114
- function WithItemManager(Base) {
28115
- return class extends Base {
28116
- /**
28117
- * Retrieves the information of an object: the number and the instance
28118
- * @title Get Item
28119
- * @method player.getItem(itemClass)
28120
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
28121
- * @returns {{ nb: number, item: instance of ItemClass }}
28122
- * @memberof ItemManager
28123
- * @example
28124
- *
28125
- * ```ts
28126
- * import Potion from 'your-database/potion'
28127
- *
28128
- * player.addItem(Potion, 5)
28129
- * const inventory = player.getItem(Potion)
28130
- * console.log(inventory) // { nb: 5, item: <instance of Potion> }
28131
- * ```
28132
- */
28133
- getItem(itemClass) {
28134
- const index = this._getItemIndex(itemClass);
28135
- return this.items()[index];
28136
- }
28137
- /**
28138
- * Check if the player has the item in his inventory.
28139
- * @title Has Item
28140
- * @method player.hasItem(itemClass)
28141
- * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
28142
- * @returns {boolean}
28143
- * @memberof ItemManager
28144
- * @example
28145
- *
28146
- * ```ts
28147
- * import Potion from 'your-database/potion'
28148
- *
28149
- * player.hasItem(Potion) // false
28150
- * ```
28151
- */
28152
- hasItem(itemClass) {
28153
- return !!this.getItem(itemClass);
28154
- }
28155
- _getItemIndex(itemClass) {
28156
- return this.items().findIndex((it) => {
28157
- if (isString(itemClass)) {
28158
- return it.id() == itemClass;
28159
- }
28160
- return isInstanceOf(it, itemClass);
27912
+ return isInstanceOf(it, itemClass);
28161
27913
  });
28162
27914
  }
28163
27915
  /**
@@ -28582,31 +28334,68 @@ var Effect = /* @__PURE__ */ ((Effect2) => {
28582
28334
  function WithEffectManager(Base) {
28583
28335
  return class extends Base {
28584
28336
  /**
28337
+ * Check if the player has a specific effect
28338
+ *
28339
+ * Determines whether the player currently has the specified effect active.
28340
+ * This includes effects from states, equipment, and temporary conditions.
28341
+ * The effect system provides a flexible way to apply various gameplay
28342
+ * restrictions and enhancements to the player.
28343
+ *
28344
+ * @param effect - The effect identifier to check for
28345
+ * @returns true if the player has the effect, false otherwise
28346
+ *
28347
+ * @example
28585
28348
  * ```ts
28586
28349
  * import { Effect } from '@rpgjs/database'
28587
28350
  *
28588
- * const bool = player.hasEffect(Effect.CAN_NOT_SKILL)
28351
+ * // Check for skill restriction
28352
+ * const cannotUseSkills = player.hasEffect(Effect.CAN_NOT_SKILL);
28353
+ * if (cannotUseSkills) {
28354
+ * console.log('Player cannot use skills right now');
28355
+ * }
28356
+ *
28357
+ * // Check for guard effect
28358
+ * const isGuarding = player.hasEffect(Effect.GUARD);
28359
+ * if (isGuarding) {
28360
+ * console.log('Player is in guard stance');
28361
+ * }
28362
+ *
28363
+ * // Check for cost reduction
28364
+ * const halfCost = player.hasEffect(Effect.HALF_SP_COST);
28365
+ * const actualCost = skillCost / (halfCost ? 2 : 1);
28589
28366
  * ```
28590
- *
28591
- * @title Has Effect
28592
- * @method player.hasEffect(effect)
28593
- * @param {string} effect
28594
- * @returns {boolean}
28595
- * @memberof EffectManager
28596
- * */
28367
+ */
28597
28368
  hasEffect(effect) {
28598
28369
  return this.effects.includes(effect);
28599
28370
  }
28600
28371
  /**
28601
28372
  * Retrieves a array of effects assigned to the player, state effects and effects of weapons and armors equipped with the player's own weapons.
28602
28373
  *
28374
+ * Gets all currently active effects on the player from multiple sources:
28375
+ * - Direct effects assigned to the player
28376
+ * - Effects from active states (buffs/debuffs)
28377
+ * - Effects from equipped weapons and armor
28378
+ * The returned array contains unique effects without duplicates.
28379
+ *
28380
+ * @returns Array of all active effects on the player
28381
+ *
28382
+ * @example
28603
28383
  * ```ts
28604
- * console.log(player.effects)
28384
+ * // Get all active effects
28385
+ * console.log(player.effects); // ['GUARD', 'HALF_SP_COST', ...]
28386
+ *
28387
+ * // Check multiple effects
28388
+ * const effects = player.effects;
28389
+ * const hasRestrictions = effects.some(effect =>
28390
+ * effect.startsWith('CAN_NOT_')
28391
+ * );
28392
+ *
28393
+ * // Count beneficial effects
28394
+ * const beneficialEffects = effects.filter(effect =>
28395
+ * ['GUARD', 'SUPER_GUARD', 'HALF_SP_COST'].includes(effect)
28396
+ * );
28605
28397
  * ```
28606
- * @title Get Effects
28607
- * @prop {Array<Effect>} player.effects
28608
- * @memberof EffectManager
28609
- * */
28398
+ */
28610
28399
  get effects() {
28611
28400
  const getEffects = (prop) => {
28612
28401
  return arrayFlat(this[prop]().map((el) => el.effects || []));
@@ -28620,113 +28409,243 @@ function WithEffectManager(Base) {
28620
28409
  /**
28621
28410
  * 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.
28622
28411
  *
28412
+ * Sets the direct effects on the player. This only affects the player's own effects
28413
+ * and does not modify effects from states or equipment. The total effects will be
28414
+ * the combination of these direct effects plus any effects from states and equipment.
28415
+ *
28416
+ * @param val - Array of effect identifiers to assign to the player
28417
+ *
28418
+ * @example
28623
28419
  * ```ts
28624
28420
  * import { Effect } from '@rpgjs/database'
28625
28421
  *
28626
- * player.effects = [Effect.CAN_NOT_SKILL]
28422
+ * // Set direct player effects
28423
+ * player.effects = [Effect.CAN_NOT_SKILL];
28424
+ *
28425
+ * // Add multiple effects
28426
+ * player.effects = [
28427
+ * Effect.GUARD,
28428
+ * Effect.HALF_SP_COST,
28429
+ * Effect.CAN_NOT_ITEM
28430
+ * ];
28431
+ *
28432
+ * // Clear direct effects (equipment/state effects remain)
28433
+ * player.effects = [];
28434
+ *
28435
+ * // Temporary effect application
28436
+ * const originalEffects = player.effects;
28437
+ * player.effects = [...originalEffects, Effect.SUPER_GUARD];
28438
+ * // Later restore
28439
+ * player.effects = originalEffects;
28627
28440
  * ```
28628
- * @title Set Effects
28629
- * @prop {Array<Effect>} player.effects
28630
- * @memberof EffectManager
28631
- * */
28441
+ */
28632
28442
  set effects(val) {
28633
28443
  this._effects.set(val);
28634
28444
  }
28635
28445
  };
28636
28446
  }
28637
28447
 
28638
- function WithBattleManager(Base) {
28448
+ function WithElementManager(Base) {
28639
28449
  return class extends Base {
28450
+ constructor() {
28451
+ super(...arguments);
28452
+ this._elementsEfficiency = [];
28453
+ }
28640
28454
  /**
28641
- * Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
28455
+ * Recovers the player's elements defense on inventory. This list is generated from the `elementsDefense` property defined on the weapons or armors equipped.
28456
+ * If several items have the same element, only the highest rate will be taken into account.
28642
28457
  *
28643
- * If you don't set the skill parameter, it will be a physical attack.
28644
- * The attack formula is already defined but you can customize it in the server options
28458
+ * Gets the defensive capabilities against various elements from equipped items.
28459
+ * The system automatically consolidates multiple defensive items, keeping only
28460
+ * the highest protection rate for each element type. This provides a comprehensive
28461
+ * view of the player's elemental resistances from all equipped gear.
28462
+ *
28463
+ * @returns Array of element defense objects with rate and element properties
28464
+ *
28465
+ * @example
28466
+ * ```ts
28467
+ * import { Armor } from '@rpgjs/server'
28468
+ *
28469
+ * enum Elements {
28470
+ * Fire = 'fire'
28471
+ * }
28472
+ *
28473
+ * @Armor({
28474
+ * name: 'Shield',
28475
+ * elementsDefense: [{ rate: 1, element: Elements.Fire }]
28476
+ * })
28477
+ * class Shield {}
28478
+ *
28479
+ * @Armor({
28480
+ * name: 'FireShield',
28481
+ * elementsDefense: [{ rate: 0.5, element: Elements.Fire }]
28482
+ * })
28483
+ * class FireShield {}
28484
+ *
28485
+ * player.addItem(Shield)
28486
+ * player.addItem(FireShield)
28487
+ * player.equip(Shield)
28488
+ * player.equip(FireShield)
28489
+ *
28490
+ * console.log(player.elementsDefense) // [{ rate: 1, element: 'fire' }]
28491
+ *
28492
+ * // Check specific element defense
28493
+ * const fireDefense = player.elementsDefense.find(def => def.element === 'fire');
28494
+ * if (fireDefense) {
28495
+ * console.log(`Fire defense rate: ${fireDefense.rate}`);
28496
+ * }
28497
+ * ```
28498
+ */
28499
+ get elementsDefense() {
28500
+ return this.getFeature("elementsDefense", "element");
28501
+ }
28502
+ /**
28503
+ * Set or retrieves all the elements where the player is vulnerable or not.
28645
28504
  *
28505
+ * Manages the player's elemental efficiency modifiers, which determine how
28506
+ * effective different elements are against this player. Values greater than 1
28507
+ * indicate vulnerability, while values less than 1 indicate resistance.
28508
+ * This combines both class-based efficiency and player-specific modifiers.
28509
+ *
28510
+ * @returns Array of element efficiency objects with rate and element properties
28511
+ *
28512
+ * @example
28646
28513
  * ```ts
28647
- * player.applyDamage(attackerPlayer) // returns { damage: number }
28514
+ * import { Class } from '@rpgjs/server'
28515
+ *
28516
+ * enum Elements {
28517
+ * Fire = 'fire',
28518
+ * Ice = 'ice'
28519
+ * }
28520
+ *
28521
+ * @Class({
28522
+ * name: 'Fighter',
28523
+ * elementsEfficiency: [{ rate: 1, element: Elements.Fire }]
28524
+ * })
28525
+ * class Hero {}
28526
+ *
28527
+ * player.setClass(Hero)
28528
+ *
28529
+ * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }]
28530
+ *
28531
+ * player.elementsEfficiency = [{ rate: 2, element: Elements.Ice }]
28532
+ *
28533
+ * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }, { rate: 2, element: 'ice' }]
28534
+ *
28535
+ * // Check for vulnerabilities
28536
+ * const vulnerabilities = player.elementsEfficiency.filter(eff => eff.rate > 1);
28537
+ * console.log('Vulnerable to:', vulnerabilities.map(v => v.element));
28538
+ *
28539
+ * // Check for resistances
28540
+ * const resistances = player.elementsEfficiency.filter(eff => eff.rate < 1);
28541
+ * console.log('Resistant to:', resistances.map(r => r.element));
28648
28542
  * ```
28543
+ */
28544
+ get elementsEfficiency() {
28545
+ if (this._class()) {
28546
+ return [
28547
+ ...this._elementsEfficiency,
28548
+ ...this._class()?.elementsEfficiency || []
28549
+ ];
28550
+ }
28551
+ return this._elementsEfficiency;
28552
+ }
28553
+ set elementsEfficiency(val) {
28554
+ this._elementsEfficiency = val;
28555
+ }
28556
+ /**
28557
+ * Retrieves a array of elements assigned to the player and the elements of the weapons / armor equipped
28649
28558
  *
28650
- * @title Apply Damage
28651
- * @method player.applyDamage(attackerPlayer,skill)
28652
- * @param {RpgPlayer} attackerPlayer The attacking player
28653
- * @param {any} [skill]
28654
- * @returns {object}
28655
- * @memberof BattleManager
28656
- * */
28657
- applyDamage(attackerPlayer, skill) {
28658
- const getParam = (player) => {
28659
- const params = {};
28660
- this.parameters.forEach((val, key) => {
28661
- params[key] = player.param[key];
28662
- });
28663
- return {
28664
- [ATK]: player.atk,
28665
- [PDEF]: player.pdef,
28666
- [SDEF]: player.sdef,
28667
- ...params
28668
- };
28669
- };
28670
- let damage = 0, fn;
28671
- let critical = false;
28672
- let guard = false;
28673
- let superGuard = false;
28674
- let elementVulnerable = false;
28675
- const paramA = getParam(attackerPlayer);
28676
- const paramB = getParam(this);
28677
- console.log(paramA, paramB);
28678
- if (skill) {
28679
- fn = this.getFormulas("damageSkill");
28680
- if (!fn) {
28681
- throw new Error("Skill Formulas not exists");
28682
- }
28683
- damage = fn(paramA, paramB, skill);
28684
- } else {
28685
- fn = this.getFormulas("damagePhysic");
28686
- if (!fn) {
28687
- throw new Error("Physic Formulas not exists");
28688
- }
28689
- damage = fn(paramA, paramB);
28690
- const coef = this.coefficientElements(attackerPlayer);
28691
- if (coef >= 2) {
28692
- elementVulnerable = true;
28693
- }
28694
- damage *= coef;
28695
- fn = this.getFormulas("damageCritical");
28696
- if (fn) {
28697
- let newDamage = fn(damage, paramA, paramB);
28698
- if (damage != newDamage) {
28699
- critical = true;
28700
- }
28701
- damage = newDamage;
28559
+ * Gets all offensive elements available to the player from equipped weapons and armor.
28560
+ * This determines what elemental damage types the player can deal in combat.
28561
+ * The system automatically combines elements from all equipped items and removes duplicates.
28562
+ *
28563
+ * @returns Array of element objects with rate and element properties for offensive capabilities
28564
+ *
28565
+ * @example
28566
+ * ```ts
28567
+ * // Get all offensive elements
28568
+ * console.log(player.elements); // [{ rate: 1.5, element: 'fire' }, { rate: 1.2, element: 'ice' }]
28569
+ *
28570
+ * // Check if player can deal fire damage
28571
+ * const hasFireElement = player.elements.some(el => el.element === 'fire');
28572
+ * if (hasFireElement) {
28573
+ * console.log('Player can deal fire damage');
28574
+ * }
28575
+ *
28576
+ * // Get strongest element
28577
+ * const strongestElement = player.elements.reduce((max, current) =>
28578
+ * current.rate > max.rate ? current : max
28579
+ * );
28580
+ * console.log(`Strongest element: ${strongestElement.element} (${strongestElement.rate})`);
28581
+ * ```
28582
+ */
28583
+ get elements() {
28584
+ let elements = [];
28585
+ for (let item of this.equipments()) {
28586
+ if (item.elements) {
28587
+ elements = [...elements, ...item.elements];
28702
28588
  }
28703
28589
  }
28704
- if (this.hasEffect(Effect.GUARD)) {
28705
- fn = this.getFormulas("damageGuard");
28706
- if (fn) {
28707
- let newDamage = fn(damage, paramA, paramB);
28708
- if (damage != newDamage) {
28709
- guard = true;
28710
- }
28711
- damage = newDamage;
28590
+ return arrayUniq(elements);
28591
+ }
28592
+ /**
28593
+ * Calculate elemental damage coefficient against another player
28594
+ *
28595
+ * Determines the damage multiplier when this player attacks another player,
28596
+ * taking into account the attacker's offensive elements, the defender's
28597
+ * elemental efficiency, and elemental defense from equipment. This is used
28598
+ * in the battle system to calculate elemental damage modifiers.
28599
+ *
28600
+ * @param otherPlayer - The target player to calculate coefficient against
28601
+ * @returns Numerical coefficient to multiply base damage by
28602
+ *
28603
+ * @example
28604
+ * ```ts
28605
+ * // Calculate elemental damage coefficient
28606
+ * const firePlayer = new MyPlayer();
28607
+ * const icePlayer = new MyPlayer();
28608
+ *
28609
+ * // Fire player attacks ice player (assuming ice is weak to fire)
28610
+ * const coefficient = icePlayer.coefficientElements(firePlayer);
28611
+ * console.log(`Damage multiplier: ${coefficient}`); // e.g., 2.0 for double damage
28612
+ *
28613
+ * // Use in damage calculation
28614
+ * const baseDamage = 100;
28615
+ * const finalDamage = baseDamage * coefficient;
28616
+ * console.log(`Final damage: ${finalDamage}`);
28617
+ *
28618
+ * // Check for elemental advantage
28619
+ * if (coefficient > 1) {
28620
+ * console.log('Attacker has elemental advantage!');
28621
+ * } else if (coefficient < 1) {
28622
+ * console.log('Defender resists this element');
28623
+ * }
28624
+ * ```
28625
+ */
28626
+ coefficientElements(otherPlayer) {
28627
+ const atkPlayerElements = otherPlayer.elements;
28628
+ const playerElements = this.elementsEfficiency;
28629
+ let coefficient = 1;
28630
+ for (let atkElement of atkPlayerElements) {
28631
+ const elementPlayer = playerElements.find(
28632
+ (el) => el.element == atkElement.element
28633
+ );
28634
+ const elementPlayerDef = this.elementsDefense.find(
28635
+ (el) => el.element == atkElement.element
28636
+ );
28637
+ if (!elementPlayer) continue;
28638
+ const fn = this.getFormulas("coefficientElements");
28639
+ if (!fn) {
28640
+ return coefficient;
28712
28641
  }
28642
+ coefficient += fn(
28643
+ atkElement,
28644
+ elementPlayer,
28645
+ elementPlayerDef || { rate: 0 }
28646
+ );
28713
28647
  }
28714
- if (this.hasEffect(Effect.SUPER_GUARD)) {
28715
- damage /= 4;
28716
- superGuard = true;
28717
- }
28718
- this.hp -= damage;
28719
- return {
28720
- damage,
28721
- critical,
28722
- elementVulnerable,
28723
- guard,
28724
- superGuard
28725
- };
28726
- }
28727
- getFormulas(name) {
28728
- const map = this.getCurrentMap();
28729
- return map.damageFormulas[name];
28648
+ return coefficient;
28730
28649
  }
28731
28650
  };
28732
28651
  }
@@ -28945,23 +28864,176 @@ function WithSkillManager(Base) {
28945
28864
  };
28946
28865
  }
28947
28866
 
28867
+ function WithBattleManager(Base) {
28868
+ return class extends Base {
28869
+ /**
28870
+ * Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
28871
+ *
28872
+ * If you don't set the skill parameter, it will be a physical attack.
28873
+ * The attack formula is already defined but you can customize it in the server options.
28874
+ * This method handles all aspects of damage calculation including critical hits,
28875
+ * elemental vulnerabilities, guard effects, and applies the final damage to HP.
28876
+ *
28877
+ * @param attackerPlayer - The attacking player who deals the damage
28878
+ * @param skill - Optional skill object for magical attacks, if not provided uses physical attack
28879
+ * @returns Object containing damage details and special effects that occurred
28880
+ *
28881
+ * @example
28882
+ * ```ts
28883
+ * // Physical attack
28884
+ * const result = player.applyDamage(attackerPlayer);
28885
+ * console.log(`Physical damage: ${result.damage}, Critical: ${result.critical}`);
28886
+ *
28887
+ * // Magical attack with skill
28888
+ * const fireSkill = { id: 'fire', power: 50, element: 'fire' };
28889
+ * const magicResult = player.applyDamage(attackerPlayer, fireSkill);
28890
+ * console.log(`Magic damage: ${magicResult.damage}, Vulnerable: ${magicResult.elementVulnerable}`);
28891
+ *
28892
+ * // Check for guard effects
28893
+ * if (result.guard) {
28894
+ * console.log('Attack was partially blocked!');
28895
+ * }
28896
+ * if (result.superGuard) {
28897
+ * console.log('Attack was heavily reduced by super guard!');
28898
+ * }
28899
+ * ```
28900
+ */
28901
+ applyDamage(attackerPlayer, skill) {
28902
+ const getParam = (player) => {
28903
+ const params = {};
28904
+ this.parameters.forEach((val, key) => {
28905
+ params[key] = player.param[key];
28906
+ });
28907
+ return {
28908
+ [ATK]: player.atk,
28909
+ [PDEF]: player.pdef,
28910
+ [SDEF]: player.sdef,
28911
+ ...params
28912
+ };
28913
+ };
28914
+ let damage = 0, fn;
28915
+ let critical = false;
28916
+ let guard = false;
28917
+ let superGuard = false;
28918
+ let elementVulnerable = false;
28919
+ const paramA = getParam(attackerPlayer);
28920
+ const paramB = getParam(this);
28921
+ console.log(paramA, paramB);
28922
+ if (skill) {
28923
+ fn = this.getFormulas("damageSkill");
28924
+ if (!fn) {
28925
+ throw new Error("Skill Formulas not exists");
28926
+ }
28927
+ damage = fn(paramA, paramB, skill);
28928
+ } else {
28929
+ fn = this.getFormulas("damagePhysic");
28930
+ if (!fn) {
28931
+ throw new Error("Physic Formulas not exists");
28932
+ }
28933
+ damage = fn(paramA, paramB);
28934
+ const coef = this.coefficientElements(attackerPlayer);
28935
+ if (coef >= 2) {
28936
+ elementVulnerable = true;
28937
+ }
28938
+ damage *= coef;
28939
+ fn = this.getFormulas("damageCritical");
28940
+ if (fn) {
28941
+ let newDamage = fn(damage, paramA, paramB);
28942
+ if (damage != newDamage) {
28943
+ critical = true;
28944
+ }
28945
+ damage = newDamage;
28946
+ }
28947
+ }
28948
+ if (this.hasEffect(Effect.GUARD)) {
28949
+ fn = this.getFormulas("damageGuard");
28950
+ if (fn) {
28951
+ let newDamage = fn(damage, paramA, paramB);
28952
+ if (damage != newDamage) {
28953
+ guard = true;
28954
+ }
28955
+ damage = newDamage;
28956
+ }
28957
+ }
28958
+ if (this.hasEffect(Effect.SUPER_GUARD)) {
28959
+ damage /= 4;
28960
+ superGuard = true;
28961
+ }
28962
+ this.hp -= damage;
28963
+ return {
28964
+ damage,
28965
+ critical,
28966
+ elementVulnerable,
28967
+ guard,
28968
+ superGuard
28969
+ };
28970
+ }
28971
+ /**
28972
+ * Get damage formulas from the current map
28973
+ *
28974
+ * Retrieves the damage calculation formulas defined in the current map's configuration.
28975
+ * These formulas are used to calculate different types of damage including physical,
28976
+ * magical, critical hits, and guard effects. The formulas provide flexibility in
28977
+ * customizing the battle system's damage calculations.
28978
+ *
28979
+ * @param name - The name of the formula to retrieve (e.g., 'damagePhysic', 'damageSkill')
28980
+ * @returns The formula function or undefined if not found
28981
+ *
28982
+ * @example
28983
+ * ```ts
28984
+ * // Get physical damage formula
28985
+ * const physicFormula = player.getFormulas('damagePhysic');
28986
+ * if (physicFormula) {
28987
+ * const damage = physicFormula(attackerParams, defenderParams);
28988
+ * }
28989
+ *
28990
+ * // Get critical damage formula
28991
+ * const criticalFormula = player.getFormulas('damageCritical');
28992
+ * if (criticalFormula) {
28993
+ * const criticalDamage = criticalFormula(baseDamage, attackerParams, defenderParams);
28994
+ * }
28995
+ * ```
28996
+ */
28997
+ getFormulas(name) {
28998
+ const map = this.getCurrentMap();
28999
+ return map.damageFormulas[name];
29000
+ }
29001
+ };
29002
+ }
29003
+
28948
29004
  function WithClassManager(Base) {
28949
29005
  return class extends Base {
28950
29006
  /**
28951
29007
  * Assign a class to the player
28952
29008
  *
29009
+ * Sets the player's class, which defines their combat abilities, stat growth,
29010
+ * and available skills. The class system provides the foundation for character
29011
+ * progression and specialization. When a class is set, it automatically triggers
29012
+ * the class's onSet method for any additional initialization.
29013
+ *
29014
+ * @param _class - The class constructor or class ID to assign to the player
29015
+ * @returns The instantiated class object
29016
+ *
29017
+ * @example
28953
29018
  * ```ts
28954
29019
  * import { Fighter } from 'my-database/classes/fighter'
28955
29020
  *
28956
- * player.setClass(Fighter)
29021
+ * // Set class using constructor
29022
+ * const fighterClass = player.setClass(Fighter);
29023
+ * console.log('Class set:', fighterClass.name);
29024
+ *
29025
+ * // Set class using string ID
29026
+ * player.setClass('fighter');
29027
+ *
29028
+ * // Class affects available skills and stats
29029
+ * console.log('Available skills:', player.skills);
29030
+ * console.log('Class bonuses applied to stats');
29031
+ *
29032
+ * // Class determines level progression
29033
+ * player.level = 5;
29034
+ * // Skills may be automatically learned based on class definition
28957
29035
  * ```
28958
- *
28959
- * @title Set Class
28960
- * @method player.setClass(ClassClass)
28961
- * @param {ClassClass | string} class class or id
28962
- * @returns {instance of ClassClass}
28963
- * @memberof ClassManager
28964
- * */
29036
+ */
28965
29037
  setClass(_class) {
28966
29038
  if (isString(_class)) _class = this.databaseById(_class);
28967
29039
  const classInstance = new _class();
@@ -28971,18 +29043,39 @@ function WithClassManager(Base) {
28971
29043
  /**
28972
29044
  * 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)
28973
29045
  *
29046
+ * Sets up the player as a specific actor archetype, which includes predefined
29047
+ * characteristics like starting equipment, parameters, level ranges, and associated class.
29048
+ * This is typically used for creating pre-configured character templates or NPCs
29049
+ * with specific roles and equipment loadouts.
29050
+ *
29051
+ * @param actorClass - The actor constructor or actor ID to assign to the player
29052
+ * @returns The instantiated actor object
29053
+ *
29054
+ * @example
28974
29055
  * ```ts
28975
29056
  * import { Hero } from 'my-database/classes/hero'
28976
29057
  *
28977
- * player.setActor(Hero)
29058
+ * // Set up player as Hero actor
29059
+ * const heroActor = player.setActor(Hero);
29060
+ * console.log('Actor configured:', heroActor.name);
29061
+ *
29062
+ * // Actor automatically sets up:
29063
+ * // - Starting equipment (sword, armor, etc.)
29064
+ * console.log('Starting equipment:', player.equipments());
29065
+ *
29066
+ * // - Parameter ranges and growth
29067
+ * console.log('Level range:', player.initialLevel, '-', player.finalLevel);
29068
+ *
29069
+ * // - Associated class
29070
+ * console.log('Assigned class:', player.class);
29071
+ *
29072
+ * // - Experience curve
29073
+ * console.log('EXP curve:', player.expCurve);
29074
+ *
29075
+ * // Actor setup is comprehensive
29076
+ * player.setActor('hero'); // Can also use string ID
28978
29077
  * ```
28979
- *
28980
- * @title Set Actor
28981
- * @method player.setActor(ActorClass)
28982
- * @param {ActorClass | string} actorClass actor class or id
28983
- * @returns {instance of ActorClass}
28984
- * @memberof ClassManager
28985
- * */
29078
+ */
28986
29079
  setActor(actorClass) {
28987
29080
  if (isString(actorClass)) actorClass = this.databaseById(actorClass);
28988
29081
  const actor = new actorClass();
@@ -29003,32 +29096,41 @@ function WithClassManager(Base) {
29003
29096
  };
29004
29097
  }
29005
29098
 
29006
- function WithElementManager(Base) {
29099
+ function WithStateManager(Base) {
29007
29100
  return class extends Base {
29008
29101
  constructor() {
29009
29102
  super(...arguments);
29010
- this._elementsEfficiency = [];
29103
+ this._statesEfficiency = signal$2([]);
29011
29104
  }
29012
29105
  /**
29013
- * Recovers the player's elements defense on inventory. This list is generated from the `elementsDefense` property defined on the weapons or armors equipped.
29106
+ * Recovers the player's states defense on inventory. This list is generated from the `statesDefense` property defined on the weapons or armors equipped.
29014
29107
  * If several items have the same element, only the highest rate will be taken into account.
29015
29108
  *
29109
+ * Gets the defensive capabilities against various states from equipped items.
29110
+ * The system automatically consolidates multiple defensive items, keeping only
29111
+ * the highest protection rate for each state type. This provides comprehensive
29112
+ * protection against debuffs and negative status effects.
29113
+ *
29114
+ * @returns Array of state defense objects with rate and state properties
29115
+ *
29116
+ * @example
29016
29117
  * ```ts
29017
- * import { Armor } from '@rpgjs/server'
29118
+ * import { Armor, State } from '@rpgjs/server'
29018
29119
  *
29019
- * enum Elements {
29020
- * Fire = 'fire'
29021
- * }
29120
+ * @State({
29121
+ * name: 'Paralyze'
29122
+ * })
29123
+ * class Paralyze {}
29022
29124
  *
29023
29125
  * @Armor({
29024
29126
  * name: 'Shield',
29025
- * elementsDefense: [{ rate: 1, element: Elements.Fire }]
29127
+ * statesDefense: [{ rate: 1, state: Paralyze }]
29026
29128
  * })
29027
29129
  * class Shield {}
29028
29130
  *
29029
29131
  * @Armor({
29030
29132
  * name: 'FireShield',
29031
- * elementsDefense: [{ rate: 0.5, element: Elements.Fire }]
29133
+ * statesDefense: [{ rate: 0.5, state: Paralyze }]
29032
29134
  * })
29033
29135
  * class FireShield {}
29034
29136
  *
@@ -29037,100 +29139,291 @@ function WithElementManager(Base) {
29037
29139
  * player.equip(Shield)
29038
29140
  * player.equip(FireShield)
29039
29141
  *
29040
- * console.log(player.elementsDefense) // [{ rate: 1, element: 'fire' }]
29142
+ * console.log(player.statesDefense) // [{ rate: 1, state: instance of Paralyze }]
29143
+ *
29144
+ * // Check specific state defense
29145
+ * const paralyzeDefense = player.statesDefense.find(def => def.state instanceof Paralyze);
29146
+ * if (paralyzeDefense) {
29147
+ * console.log(`Paralyze defense rate: ${paralyzeDefense.rate}`);
29148
+ * }
29041
29149
  * ```
29042
- * @title Get Elements Defense
29043
- * @prop {Array<{ rate: number, element: Element}>} player.elementsDefense
29044
- * @readonly
29045
- * @memberof ElementManager
29046
- * */
29047
- get elementsDefense() {
29048
- return this.getFeature("elementsDefense", "element");
29150
+ */
29151
+ get statesDefense() {
29152
+ return this.getFeature("statesDefense", "state");
29049
29153
  }
29050
29154
  /**
29051
- * Set or retrieves all the elements where the player is vulnerable or not.
29155
+ * Set or retrieves all the states where the player is vulnerable or not.
29052
29156
  *
29157
+ * Manages the player's state efficiency modifiers, which determine how
29158
+ * effective different states are against this player. Values greater than 1
29159
+ * indicate vulnerability, while values less than 1 indicate resistance.
29160
+ * This combines both class-based efficiency and player-specific modifiers.
29161
+ *
29162
+ * @returns Array of state efficiency objects with rate and state properties
29163
+ *
29164
+ * @example
29053
29165
  * ```ts
29054
- * import { Class } from '@rpgjs/server'
29166
+ * import { Class, State } from '@rpgjs/server'
29167
+ *
29168
+ * @State({
29169
+ * name: 'Paralyze'
29170
+ * })
29171
+ * class Paralyze {}
29055
29172
  *
29056
- * enum Elements {
29057
- * Fire = 'fire',
29058
- * Ice = 'ice'
29059
- * }
29173
+ * @State({
29174
+ * name: 'Sleep'
29175
+ * })
29176
+ * class Sleep {}
29060
29177
  *
29061
29178
  * @Class({
29062
29179
  * name: 'Fighter',
29063
- * elementsEfficiency: [{ rate: 1, element: Elements.Fire }]
29180
+ * statesEfficiency: [{ rate: 1, state: Paralyze }]
29064
29181
  * })
29065
29182
  * class Hero {}
29066
29183
  *
29067
29184
  * player.setClass(Hero)
29068
29185
  *
29069
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }]
29186
+ * console.log(player.statesEfficiency) // [{ rate: 1, instance of Paralyze }]
29070
29187
  *
29071
- * player.elementsEfficiency = [{ rate: 2, element: Elements.Ice }]
29188
+ * player.statesEfficiency = [{ rate: 2, state: Sleep }]
29072
29189
  *
29073
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }, { rate: 2, element: 'ice' }]
29190
+ * console.log(player.statesEfficiency) // [{ rate: 1, state: instance of Paralyze }, { rate: 2, state: instance of Sleep }]
29191
+ *
29192
+ * // Check for vulnerabilities
29193
+ * const vulnerabilities = player.statesEfficiency.filter(eff => eff.rate > 1);
29194
+ * console.log('Vulnerable to states:', vulnerabilities.map(v => v.state.name));
29195
+ *
29196
+ * // Check for resistances
29197
+ * const resistances = player.statesEfficiency.filter(eff => eff.rate < 1);
29198
+ * console.log('Resistant to states:', resistances.map(r => r.state.name));
29074
29199
  * ```
29075
- * @title Set/Get Elements Efficiency
29076
- * @prop {Array<{ rate: number, element: Element}>} player.elementsEfficiency
29077
- * @memberof ElementManager
29078
- * */
29079
- get elementsEfficiency() {
29080
- if (this._class) {
29081
- return [
29082
- ...this._elementsEfficiency,
29083
- ...this._class()?.elementsEfficiency || []
29084
- ];
29085
- }
29086
- return this._elementsEfficiency;
29200
+ */
29201
+ get statesEfficiency() {
29202
+ return this._statesEfficiency;
29087
29203
  }
29088
- set elementsEfficiency(val) {
29089
- this._elementsEfficiency = val;
29204
+ set statesEfficiency(val) {
29205
+ this._statesEfficiency = val;
29090
29206
  }
29091
29207
  /**
29092
- * Retrieves a array of elements assigned to the player and the elements of the weapons / armor equipped
29208
+ * Apply states to a player from skill or item effects
29209
+ *
29210
+ * Processes state application and removal based on skill or item effects.
29211
+ * This method handles both adding beneficial states and removing negative ones,
29212
+ * with proper chance calculation and resistance checks.
29213
+ *
29214
+ * @param player - The target player to apply states to
29215
+ * @param states - Object containing arrays of states to add or remove
29216
+ *
29217
+ * @example
29218
+ * ```ts
29219
+ * // Apply states from a healing skill
29220
+ * const healingStates = {
29221
+ * addStates: [{ state: Regeneration, rate: 0.8 }],
29222
+ * removeStates: [{ state: Poison, rate: 1.0 }]
29223
+ * };
29224
+ * player.applyStates(targetPlayer, healingStates);
29225
+ *
29226
+ * // Apply debuff from an enemy attack
29227
+ * const debuffStates = {
29228
+ * addStates: [
29229
+ * { state: Paralyze, rate: 0.3 },
29230
+ * { state: Slow, rate: 0.5 }
29231
+ * ]
29232
+ * };
29233
+ * player.applyStates(targetPlayer, debuffStates);
29234
+ * ```
29235
+ */
29236
+ applyStates(player, { addStates, removeStates }) {
29237
+ if (addStates) {
29238
+ for (let { state, rate } of addStates) {
29239
+ player.addState(state, rate);
29240
+ }
29241
+ }
29242
+ if (removeStates) {
29243
+ for (let { state, rate } of removeStates) {
29244
+ player.removeState(state, rate);
29245
+ }
29246
+ }
29247
+ }
29248
+ /**
29249
+ * Get a state to the player. Returns `null` if the state is not present on the player
29250
+ *
29251
+ * Retrieves a specific state instance from the player's active states.
29252
+ * This is useful for checking state properties, duration, or performing
29253
+ * state-specific operations. Returns null if the state is not currently active.
29254
+ *
29255
+ * @param stateClass - The state class constructor or state ID to search for
29256
+ * @returns The state instance if found, null otherwise
29257
+ *
29258
+ * @example
29259
+ * ```ts
29260
+ * import Paralyze from 'your-database/states/paralyze'
29093
29261
  *
29262
+ * // Check if player has a specific state
29263
+ * const paralyzeState = player.getState(Paralyze);
29264
+ * if (paralyzeState) {
29265
+ * console.log('Player is paralyzed');
29266
+ * console.log('Remaining duration:', paralyzeState.duration);
29267
+ * }
29268
+ *
29269
+ * // Check using string ID
29270
+ * const poisonState = player.getState('poison');
29271
+ * if (poisonState) {
29272
+ * console.log('Player is poisoned');
29273
+ * }
29274
+ *
29275
+ * // Use in conditional logic
29276
+ * if (player.getState(Sleep)) {
29277
+ * console.log('Player cannot act while sleeping');
29278
+ * return; // Skip player turn
29279
+ * }
29280
+ * ```
29281
+ */
29282
+ getState(stateClass) {
29283
+ if (isString(stateClass)) stateClass = this.databaseById(stateClass);
29284
+ return this.states().find((state) => {
29285
+ if (isString(stateClass)) {
29286
+ return state.id == stateClass;
29287
+ }
29288
+ return isInstanceOf(state, stateClass);
29289
+ });
29290
+ }
29291
+ /**
29292
+ * Adds a state to the player. Set the chance between 0 and 1 that the state can apply
29293
+ *
29294
+ * Attempts to apply a state to the player with a specified success chance.
29295
+ * The method considers state resistance, efficiency modifiers, and random chance
29296
+ * to determine if the state is successfully applied. If successful, the state
29297
+ * is added to the player's active states list.
29298
+ *
29299
+ * @param stateClass - The state class constructor or state ID to apply
29300
+ * @param chance - Probability of successful application (0-1, default 1)
29301
+ * @returns The state instance if successfully applied, null if already present
29302
+ * @throws StateLog.addFailed if the chance roll fails
29303
+ *
29304
+ * @example
29094
29305
  * ```ts
29095
- * console.log(player.elements)
29306
+ * import Paralyze from 'your-database/states/paralyze'
29307
+ *
29308
+ * try {
29309
+ * // Attempt to apply paralyze with 100% chance
29310
+ * const state = player.addState(Paralyze);
29311
+ * if (state) {
29312
+ * console.log('Paralyze applied successfully');
29313
+ * }
29314
+ * } catch (err) {
29315
+ * console.log('Failed to apply paralyze:', err.msg);
29316
+ * }
29317
+ *
29318
+ * // Apply with reduced chance
29319
+ * try {
29320
+ * player.addState(Poison, 0.3); // 30% chance
29321
+ * } catch (err) {
29322
+ * console.log('Poison application failed');
29323
+ * }
29324
+ *
29325
+ * // Apply multiple states with different chances
29326
+ * const debuffs = [
29327
+ * { state: Slow, chance: 0.8 },
29328
+ * { state: Weak, chance: 0.6 }
29329
+ * ];
29330
+ * debuffs.forEach(({ state, chance }) => {
29331
+ * try {
29332
+ * player.addState(state, chance);
29333
+ * } catch (err) {
29334
+ * // Handle failed applications
29335
+ * }
29336
+ * });
29096
29337
  * ```
29097
- * @title Get Elements
29098
- * @prop {Array<Element>} player.elements
29099
- * @readonly
29100
- * @memberof ElementManager
29101
- * */
29102
- get elements() {
29103
- let elements = [];
29104
- for (let item of this.equipments()) {
29105
- if (item.elements) {
29106
- elements = [...elements, ...item.elements];
29338
+ */
29339
+ addState(stateClass, chance = 1) {
29340
+ const state = this.getState(stateClass);
29341
+ if (isString(stateClass)) {
29342
+ stateClass = this.databaseById(stateClass);
29343
+ }
29344
+ if (!state) {
29345
+ if (Math.random() > chance) {
29346
+ throw StateLog.addFailed(stateClass);
29107
29347
  }
29348
+ const instance = new stateClass();
29349
+ this.states().push(instance);
29350
+ this.applyStates(this, instance);
29351
+ return instance;
29108
29352
  }
29109
- return arrayUniq(elements);
29353
+ return null;
29110
29354
  }
29111
- coefficientElements(otherPlayer) {
29112
- const atkPlayerElements = otherPlayer.elements;
29113
- const playerElements = this.elementsEfficiency;
29114
- let coefficient = 1;
29115
- for (let atkElement of atkPlayerElements) {
29116
- const elementPlayer = playerElements.find(
29117
- (el) => el.element == atkElement.element
29118
- );
29119
- const elementPlayerDef = this.elementsDefense.find(
29120
- (el) => el.element == atkElement.element
29121
- );
29122
- if (!elementPlayer) continue;
29123
- const fn = this.getFormulas("coefficientElements");
29124
- if (!fn) {
29125
- return coefficient;
29355
+ /**
29356
+ * Remove a state to the player. Set the chance between 0 and 1 that the state can be removed
29357
+ *
29358
+ * Attempts to remove a state from the player with a specified success chance.
29359
+ * This is useful for cure spells, items, or time-based state removal.
29360
+ * The method considers removal resistance and random chance.
29361
+ *
29362
+ * @param stateClass - The state class constructor or state ID to remove
29363
+ * @param chance - Probability of successful removal (0-1, default 1)
29364
+ * @throws StateLog.removeFailed if the chance roll fails
29365
+ * @throws StateLog.notApplied if the state is not currently active
29366
+ *
29367
+ * @example
29368
+ * ```ts
29369
+ * import Paralyze from 'your-database/states/paralyze'
29370
+ *
29371
+ * try {
29372
+ * // Attempt to remove paralyze with 100% chance
29373
+ * player.removeState(Paralyze);
29374
+ * console.log('Paralyze removed successfully');
29375
+ * } catch (err) {
29376
+ * if (err.id === 'STATE_NOT_APPLIED') {
29377
+ * console.log('Player was not paralyzed');
29378
+ * } else {
29379
+ * console.log('Failed to remove paralyze:', err.msg);
29380
+ * }
29381
+ * }
29382
+ *
29383
+ * // Remove with reduced chance (for weak cure spells)
29384
+ * try {
29385
+ * player.removeState(Poison, 0.7); // 70% chance
29386
+ * } catch (err) {
29387
+ * console.log('Cure failed');
29388
+ * }
29389
+ *
29390
+ * // Remove all negative states (cure-all effect)
29391
+ * const negativeStates = [Poison, Paralyze, Sleep, Slow];
29392
+ * negativeStates.forEach(state => {
29393
+ * try {
29394
+ * player.removeState(state);
29395
+ * } catch (err) {
29396
+ * // State wasn't active, continue
29397
+ * }
29398
+ * });
29399
+ * ```
29400
+ */
29401
+ removeState(stateClass, chance = 1) {
29402
+ const index = this.states().findIndex((state) => {
29403
+ if (isString(stateClass)) {
29404
+ return state.id == stateClass;
29126
29405
  }
29127
- coefficient += fn(
29128
- atkElement,
29129
- elementPlayer,
29130
- elementPlayerDef || { rate: 0 }
29131
- );
29406
+ return isInstanceOf(state, stateClass);
29407
+ });
29408
+ if (index != -1) {
29409
+ if (Math.random() > chance) {
29410
+ throw StateLog.removeFailed(stateClass);
29411
+ }
29412
+ this.states().splice(index, 1);
29413
+ } else {
29414
+ throw StateLog.notApplied(stateClass);
29132
29415
  }
29133
- return coefficient;
29416
+ }
29417
+ /**
29418
+ * Find state efficiency modifier for a specific state class
29419
+ *
29420
+ * @param stateClass - The state class to find efficiency for
29421
+ * @returns The efficiency object if found, undefined otherwise
29422
+ */
29423
+ findStateEfficiency(stateClass) {
29424
+ return this.statesEfficiency().find(
29425
+ (state) => isInstanceOf(state.state, stateClass)
29426
+ );
29134
29427
  }
29135
29428
  };
29136
29429
  }
@@ -29147,23 +29440,23 @@ var __decorateClass$2 = (decorators, target, key, kind) => {
29147
29440
  function combinePlayerMixins(mixins) {
29148
29441
  return (Base) => mixins.reduce((ExtendedClass, mixin) => mixin(ExtendedClass), Base);
29149
29442
  }
29150
- const PlayerMixins = combinePlayerMixins([
29443
+ const BasicPlayerMixins = combinePlayerMixins([
29151
29444
  WithComponentManager,
29152
29445
  WithEffectManager,
29153
29446
  WithGuiManager,
29154
29447
  WithMoveManager,
29155
29448
  WithGoldManager,
29156
- WithVariableManager,
29157
29449
  WithParameterManager,
29158
29450
  WithItemFixture,
29159
- WithStateManager,
29160
29451
  WithItemManager,
29161
- WithSkillManager,
29452
+ WithElementManager,
29453
+ WithVariableManager,
29454
+ WithStateManager,
29162
29455
  WithClassManager,
29163
- WithBattleManager,
29164
- WithElementManager
29456
+ WithSkillManager,
29457
+ WithBattleManager
29165
29458
  ]);
29166
- const _RpgPlayer = class _RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
29459
+ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
29167
29460
  constructor() {
29168
29461
  super();
29169
29462
  this.map = null;
@@ -29232,7 +29525,47 @@ const _RpgPlayer = class _RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
29232
29525
  value
29233
29526
  });
29234
29527
  }
29235
- showAnimation(params) {
29528
+ /**
29529
+ * Set the current animation of the player's sprite
29530
+ *
29531
+ * This method changes the animation state of the player's current sprite.
29532
+ * It's used to trigger character animations like attack, skill, or custom movements.
29533
+ * When `nbTimes` is set to a finite number, the animation will play that many times
29534
+ * before returning to the previous animation state.
29535
+ *
29536
+ * @param animationName - The name of the animation to play (e.g., 'attack', 'skill', 'walk')
29537
+ * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)
29538
+ *
29539
+ * @example
29540
+ * ```ts
29541
+ * // Set continuous walk animation
29542
+ * player.setAnimation('walk');
29543
+ *
29544
+ * // Play attack animation 3 times then return to previous state
29545
+ * player.setAnimation('attack', 3);
29546
+ *
29547
+ * // Play skill animation once
29548
+ * player.setAnimation('skill', 1);
29549
+ *
29550
+ * // Set idle/stand animation
29551
+ * player.setAnimation('stand');
29552
+ * ```
29553
+ */
29554
+ setAnimation(animationName, nbTimes = Infinity) {
29555
+ const map = this.getCurrentMap();
29556
+ if (!map) return;
29557
+ if (nbTimes === Infinity) {
29558
+ this.animationName.set(animationName);
29559
+ } else {
29560
+ map.$broadcast({
29561
+ type: "setAnimation",
29562
+ value: {
29563
+ animationName,
29564
+ nbTimes,
29565
+ object: this.id
29566
+ }
29567
+ });
29568
+ }
29236
29569
  }
29237
29570
  /**
29238
29571
  * Run the change detection cycle. Normally, as soon as a hook is called in a class, the cycle is started. But you can start it manually
@@ -29302,11 +29635,35 @@ const _RpgPlayer = class _RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
29302
29635
  }
29303
29636
  );
29304
29637
  }
29305
- broadcastEffect(id, params) {
29638
+ /**
29639
+ * Show a temporary component animation on this player
29640
+ *
29641
+ * This method broadcasts a component animation to all clients, allowing
29642
+ * temporary visual effects like hit indicators, spell effects, or status animations
29643
+ * to be displayed on the player.
29644
+ *
29645
+ * @param id - The ID of the component animation to display
29646
+ * @param params - Parameters to pass to the component animation
29647
+ *
29648
+ * @example
29649
+ * ```ts
29650
+ * // Show a hit animation with damage text
29651
+ * player.showComponentAnimation("hit", {
29652
+ * text: "150",
29653
+ * color: "red"
29654
+ * });
29655
+ *
29656
+ * // Show a heal animation
29657
+ * player.showComponentAnimation("heal", {
29658
+ * amount: 50
29659
+ * });
29660
+ * ```
29661
+ */
29662
+ showComponentAnimation(id, params) {
29306
29663
  const map = this.getCurrentMap();
29307
29664
  if (!map) return;
29308
29665
  map.$broadcast({
29309
- type: "showEffect",
29666
+ type: "showComponentAnimation",
29310
29667
  value: {
29311
29668
  id,
29312
29669
  params,
@@ -29314,8 +29671,45 @@ const _RpgPlayer = class _RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
29314
29671
  }
29315
29672
  });
29316
29673
  }
29674
+ /**
29675
+ * Display a spritesheet animation on the player
29676
+ *
29677
+ * This method displays a temporary visual animation using a spritesheet.
29678
+ * The animation can either be displayed as an overlay on the player or replace
29679
+ * the player's current graphic temporarily. This is useful for spell effects,
29680
+ * transformations, or other visual feedback that uses predefined spritesheets.
29681
+ *
29682
+ * @param graphic - The ID of the spritesheet to use for the animation
29683
+ * @param animationName - The name of the animation within the spritesheet (default: 'default')
29684
+ * @param replaceGraphic - Whether to replace the player's sprite with the animation (default: false)
29685
+ *
29686
+ * @example
29687
+ * ```ts
29688
+ * // Show explosion animation as overlay on player
29689
+ * player.showAnimation("explosion");
29690
+ *
29691
+ * // Show specific spell effect animation
29692
+ * player.showAnimation("spell-effects", "fireball");
29693
+ *
29694
+ * // Transform player graphic temporarily with animation
29695
+ * player.showAnimation("transformation", "werewolf", true);
29696
+ *
29697
+ * // Show healing effect on player
29698
+ * player.showAnimation("healing-effects", "holy-light");
29699
+ * ```
29700
+ */
29701
+ showAnimation(graphic, animationName = "default", replaceGraphic = false) {
29702
+ if (replaceGraphic) {
29703
+ this.setAnimation(animationName, 1);
29704
+ return;
29705
+ }
29706
+ this.showComponentAnimation("animation", {
29707
+ graphic,
29708
+ animationName
29709
+ });
29710
+ }
29317
29711
  showHit(text) {
29318
- this.broadcastEffect("hit", {
29712
+ this.showComponentAnimation("hit", {
29319
29713
  text,
29320
29714
  direction: this.direction()
29321
29715
  });
@@ -29343,6 +29737,7 @@ class RpgEvent extends RpgPlayer {
29343
29737
  }
29344
29738
 
29345
29739
  const context$1 = new Context();
29740
+ context$1["side"] = "server";
29346
29741
 
29347
29742
  var __defProp$1 = Object.defineProperty;
29348
29743
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
@@ -29533,18 +29928,84 @@ let RpgMap = class extends RpgCommonMap {
29533
29928
  removeEvent(eventId) {
29534
29929
  delete this.events()[eventId];
29535
29930
  }
29536
- showAnimation(animationName, object) {
29931
+ /**
29932
+ * Display a component animation at a specific position on the map
29933
+ *
29934
+ * This method broadcasts a component animation to all clients connected to the map,
29935
+ * allowing temporary visual effects to be displayed at any location on the map.
29936
+ * Component animations are custom Canvas Engine components that can display
29937
+ * complex effects with custom logic and parameters.
29938
+ *
29939
+ * @param id - The ID of the component animation to display
29940
+ * @param position - The x, y coordinates where to display the animation
29941
+ * @param params - Parameters to pass to the component animation
29942
+ *
29943
+ * @example
29944
+ * ```ts
29945
+ * // Show explosion at specific coordinates
29946
+ * map.showComponentAnimation("explosion", { x: 300, y: 400 }, {
29947
+ * intensity: 2.5,
29948
+ * duration: 1500
29949
+ * });
29950
+ *
29951
+ * // Show area damage effect
29952
+ * map.showComponentAnimation("area-damage", { x: player.x, y: player.y }, {
29953
+ * radius: 100,
29954
+ * color: "red",
29955
+ * damage: 50
29956
+ * });
29957
+ *
29958
+ * // Show treasure spawn effect
29959
+ * map.showComponentAnimation("treasure-spawn", { x: 150, y: 200 }, {
29960
+ * sparkle: true,
29961
+ * sound: "treasure-appear"
29962
+ * });
29963
+ * ```
29964
+ */
29965
+ showComponentAnimation(id, position, params) {
29537
29966
  this.$broadcast({
29538
- type: "showEffect",
29967
+ type: "showComponentAnimation",
29539
29968
  value: {
29540
- id: "animation",
29541
- params: {
29542
- name: animationName
29543
- },
29544
- object: object.id
29969
+ id,
29970
+ params,
29971
+ position
29545
29972
  }
29546
29973
  });
29547
29974
  }
29975
+ /**
29976
+ * Display a spritesheet animation at a specific position on the map
29977
+ *
29978
+ * This method displays a temporary visual animation using a spritesheet at any
29979
+ * location on the map. It's a convenience method that internally uses showComponentAnimation
29980
+ * with the built-in 'animation' component. This is useful for spell effects, environmental
29981
+ * animations, or any visual feedback that uses predefined spritesheets.
29982
+ *
29983
+ * @param position - The x, y coordinates where to display the animation
29984
+ * @param graphic - The ID of the spritesheet to use for the animation
29985
+ * @param animationName - The name of the animation within the spritesheet (default: 'default')
29986
+ *
29987
+ * @example
29988
+ * ```ts
29989
+ * // Show explosion at specific coordinates
29990
+ * map.showAnimation({ x: 100, y: 200 }, "explosion");
29991
+ *
29992
+ * // Show spell effect at player position
29993
+ * const playerPos = { x: player.x, y: player.y };
29994
+ * map.showAnimation(playerPos, "spell-effects", "lightning");
29995
+ *
29996
+ * // Show environmental effect
29997
+ * map.showAnimation({ x: 300, y: 150 }, "nature-effects", "wind-gust");
29998
+ *
29999
+ * // Show portal opening animation
30000
+ * map.showAnimation({ x: 500, y: 400 }, "portals", "opening");
30001
+ * ```
30002
+ */
30003
+ showAnimation(position, graphic, animationName = "default") {
30004
+ this.showComponentAnimation("animation", position, {
30005
+ graphic,
30006
+ animationName
30007
+ });
30008
+ }
29548
30009
  };
29549
30010
  __decorateClass$1([
29550
30011
  users$1(RpgPlayer)