@rpgjs/server 5.0.0-alpha.5 → 5.0.0-alpha.7

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
@@ -25651,6 +25651,34 @@ var PrebuiltGui = /* @__PURE__ */ ((PrebuiltGui2) => {
25651
25651
 
25652
25652
  function WithComponentManager(Base) {
25653
25653
  return class extends Base {
25654
+ /**
25655
+ * Set the graphic(s) for this player
25656
+ *
25657
+ * Allows setting either a single graphic or multiple graphics for the player.
25658
+ * When multiple graphics are provided, they are used for animation sequences.
25659
+ * The graphics system provides flexible visual representation that can be
25660
+ * dynamically changed during gameplay for different states, equipment, or animations.
25661
+ *
25662
+ * @param graphic - Single graphic name or array of graphic names for animation sequences
25663
+ * @returns void
25664
+ *
25665
+ * @example
25666
+ * ```ts
25667
+ * // Set a single graphic for static representation
25668
+ * player.setGraphic("hero");
25669
+ *
25670
+ * // Set multiple graphics for animation sequences
25671
+ * player.setGraphic(["hero_idle", "hero_walk", "hero_run"]);
25672
+ *
25673
+ * // Dynamic graphic changes based on equipment
25674
+ * if (player.hasArmor('platemail')) {
25675
+ * player.setGraphic("hero_armored");
25676
+ * }
25677
+ *
25678
+ * // Animation sequences for different actions
25679
+ * player.setGraphic(["mage_cast_1", "mage_cast_2", "mage_cast_3"]);
25680
+ * ```
25681
+ */
25654
25682
  setGraphic(graphic) {
25655
25683
  if (Array.isArray(graphic)) {
25656
25684
  this.graphics.set(graphic);
@@ -26158,7 +26186,7 @@ function WithMoveManager(Base) {
26158
26186
  return class extends Base {
26159
26187
  constructor() {
26160
26188
  super(...arguments);
26161
- // Private properties for infinite route management
26189
+ // Properties for infinite route management
26162
26190
  this._infiniteRoutes = null;
26163
26191
  this._finishRoute = null;
26164
26192
  this._isInfiniteRouteActive = false;
@@ -27255,18 +27283,6 @@ function WithGuiManager(Base) {
27255
27283
 
27256
27284
  function WithGoldManager(Base) {
27257
27285
  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
27286
  set gold(val) {
27271
27287
  if (val < 0) {
27272
27288
  val = 0;
@@ -27288,52 +27304,168 @@ function WithVariableManager(Base) {
27288
27304
  /**
27289
27305
  * Assign a variable to the player
27290
27306
  *
27307
+ * Stores a key-value pair in the player's variable map. This is useful for
27308
+ * tracking game state, quest progress, flags, and other player-specific data.
27309
+ * The variable system provides a flexible way to store any type of data
27310
+ * associated with the player that persists throughout the game session.
27311
+ *
27312
+ * @param key - The variable identifier (string key to reference the variable)
27313
+ * @param val - The value to store (can be any type: boolean, number, string, object, array)
27314
+ * @returns void
27315
+ *
27316
+ * @example
27291
27317
  * ```ts
27292
- * player.setVariable('OPEN_CHEST', true)
27318
+ * // Set different types of variables
27319
+ * player.setVariable('CHEST_OPENED', true);
27320
+ * player.setVariable('playerLevel', 5);
27321
+ * player.setVariable('questProgress', { step: 1, completed: false });
27322
+ * player.setVariable('inventory', ['sword', 'potion', 'key']);
27323
+ * player.setVariable('lastSaveTime', new Date().toISOString());
27293
27324
  * ```
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
- * */
27325
+ */
27302
27326
  setVariable(key, val) {
27303
27327
  this.variables.set(key, val);
27304
27328
  }
27305
27329
  /**
27306
- * Get a variable
27330
+ * Get a variable value
27331
+ *
27332
+ * Retrieves the value associated with the given key from the player's variables.
27333
+ * Returns undefined if the variable doesn't exist. This method is type-safe
27334
+ * and can be used with generic types for better TypeScript support.
27335
+ *
27336
+ * @param key - The variable identifier to retrieve
27337
+ * @returns The stored value or undefined if not found
27307
27338
  *
27339
+ * @example
27308
27340
  * ```ts
27309
- * const val = player.getVariable('OPEN_CHEST')
27310
- * ```
27341
+ * // Get variables with type inference
27342
+ * const hasKey = player.getVariable('CHEST_OPENED'); // boolean | undefined
27343
+ * const level = player.getVariable('playerLevel'); // number | undefined
27344
+ * const quest = player.getVariable('questProgress'); // object | undefined
27345
+ * const missing = player.getVariable('nonexistent'); // undefined
27311
27346
  *
27312
- * @title Get variable
27313
- * @method player.setVariable(key,val)
27314
- * @param {string} key
27315
- * @returns {any}
27316
- * @memberof VariableManager
27317
- * */
27347
+ * // Use with default values
27348
+ * const level = player.getVariable('playerLevel') ?? 1;
27349
+ * const isChestOpened = player.getVariable('CHEST_OPENED') ?? false;
27350
+ * ```
27351
+ */
27318
27352
  getVariable(key) {
27319
27353
  return this.variables.get(key);
27320
27354
  }
27321
27355
  /**
27322
27356
  * Remove a variable
27323
27357
  *
27358
+ * Deletes a variable from the player's variable map. This is useful for
27359
+ * cleaning up temporary flags, resetting certain game states, or managing
27360
+ * memory by removing unused variables. The method returns a boolean indicating
27361
+ * whether the variable existed and was successfully removed.
27362
+ *
27363
+ * @param key - The variable identifier to remove
27364
+ * @returns true if a variable existed and has been removed, false if the variable does not exist
27365
+ *
27366
+ * @example
27324
27367
  * ```ts
27325
- * player.removeVariable('OPEN_CHEST')
27326
- * ```
27368
+ * // Remove variables and check if they existed
27369
+ * const removed = player.removeVariable('CHEST_OPENED'); // true if existed
27370
+ * const notFound = player.removeVariable('nonexistent'); // false
27327
27371
  *
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
- * */
27372
+ * // Clean up temporary variables
27373
+ * player.removeVariable('tempQuestFlag');
27374
+ * player.removeVariable('battleTempData');
27375
+ *
27376
+ * // Conditional removal
27377
+ * if (player.getVariable('questCompleted')) {
27378
+ * player.removeVariable('questProgress');
27379
+ * }
27380
+ * ```
27381
+ */
27334
27382
  removeVariable(key) {
27335
27383
  return this.variables.delete(key);
27336
27384
  }
27385
+ /**
27386
+ * Check if a variable exists
27387
+ *
27388
+ * Determines whether a variable with the given key exists in the player's
27389
+ * variable map, regardless of its value (including falsy values like false, 0, '').
27390
+ * This is useful when you need to distinguish between a variable that doesn't
27391
+ * exist and one that has a falsy value.
27392
+ *
27393
+ * @param key - The variable identifier to check
27394
+ * @returns true if the variable exists, false otherwise
27395
+ *
27396
+ * @example
27397
+ * ```ts
27398
+ * // Check variable existence
27399
+ * player.setVariable('flag', false);
27400
+ * player.hasVariable('flag'); // true (even though value is false)
27401
+ * player.hasVariable('missing'); // false
27402
+ *
27403
+ * // Use in conditional logic
27404
+ * if (player.hasVariable('questStarted')) {
27405
+ * // Quest has been started, check progress
27406
+ * const progress = player.getVariable('questProgress');
27407
+ * } else {
27408
+ * // Quest not started yet
27409
+ * player.setVariable('questStarted', true);
27410
+ * }
27411
+ * ```
27412
+ */
27413
+ hasVariable(key) {
27414
+ return this.variables.has(key);
27415
+ }
27416
+ /**
27417
+ * Get all variable keys
27418
+ *
27419
+ * Returns an array of all variable keys currently stored for this player.
27420
+ * This is useful for debugging, serialization, or iterating over all variables.
27421
+ * The keys are returned in insertion order.
27422
+ *
27423
+ * @returns Array of all variable keys
27424
+ *
27425
+ * @example
27426
+ * ```ts
27427
+ * // Get all variable keys
27428
+ * const keys = player.getVariableKeys();
27429
+ * console.log('Player has variables:', keys);
27430
+ *
27431
+ * // Iterate over all variables
27432
+ * keys.forEach(key => {
27433
+ * const value = player.getVariable(key);
27434
+ * console.log(`${key}: ${value}`);
27435
+ * });
27436
+ *
27437
+ * // Filter specific variable types
27438
+ * const questKeys = keys.filter(key => key.startsWith('quest_'));
27439
+ * ```
27440
+ */
27441
+ getVariableKeys() {
27442
+ return Array.from(this.variables.keys());
27443
+ }
27444
+ /**
27445
+ * Clear all variables
27446
+ *
27447
+ * Removes all variables from the player's variable map. This is useful for
27448
+ * resetting the player state, cleaning up before saving, or starting fresh.
27449
+ * Use with caution as this operation cannot be undone.
27450
+ *
27451
+ * @returns void
27452
+ *
27453
+ * @example
27454
+ * ```ts
27455
+ * // Clear all variables (use with caution)
27456
+ * player.clearVariables();
27457
+ *
27458
+ * // Clear variables conditionally
27459
+ * if (gameReset) {
27460
+ * player.clearVariables();
27461
+ * // Re-initialize essential variables
27462
+ * player.setVariable('gameStarted', true);
27463
+ * }
27464
+ * ```
27465
+ */
27466
+ clearVariables() {
27467
+ this.variables.clear();
27468
+ }
27337
27469
  };
27338
27470
  }
27339
27471
 
@@ -27886,328 +28018,103 @@ class StateLog {
27886
28018
  }
27887
28019
  }
27888
28020
 
27889
- function WithStateManager(Base) {
28021
+ function WithItemManager(Base) {
27890
28022
  return class extends Base {
27891
- constructor() {
27892
- super(...arguments);
27893
- this._statesEfficiency = signal$2([]);
27894
- }
27895
28023
  /**
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.
28024
+ * Retrieves the information of an object: the number and the instance
28025
+ * @title Get Item
28026
+ * @method player.getItem(itemClass)
28027
+ * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
28028
+ * @returns {{ nb: number, item: instance of ItemClass }}
28029
+ * @memberof ItemManager
28030
+ * @example
27898
28031
  *
27899
28032
  * ```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)
28033
+ * import Potion from 'your-database/potion'
27923
28034
  *
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");
28035
+ * player.addItem(Potion, 5)
28036
+ * const inventory = player.getItem(Potion)
28037
+ * console.log(inventory) // { nb: 5, item: <instance of Potion> }
28038
+ * ```
28039
+ */
28040
+ getItem(itemClass) {
28041
+ const index = this._getItemIndex(itemClass);
28042
+ return this.items()[index];
27933
28043
  }
27934
28044
  /**
27935
- * Set or retrieves all the states where the player is vulnerable or not.
28045
+ * Check if the player has the item in his inventory.
28046
+ * @title Has Item
28047
+ * @method player.hasItem(itemClass)
28048
+ * @param {ItemClass | string} itemClass Identifier of the object if the parameter is a string
28049
+ * @returns {boolean}
28050
+ * @memberof ItemManager
28051
+ * @example
27936
28052
  *
27937
28053
  * ```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 }]
28054
+ * import Potion from 'your-database/potion'
27961
28055
  *
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;
28056
+ * player.hasItem(Potion) // false
28057
+ * ```
28058
+ */
28059
+ hasItem(itemClass) {
28060
+ return !!this.getItem(itemClass);
27973
28061
  }
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);
28062
+ _getItemIndex(itemClass) {
28063
+ return this.items().findIndex((it) => {
28064
+ if (isString(itemClass)) {
28065
+ return it.id() == itemClass;
27983
28066
  }
27984
- }
28067
+ return isInstanceOf(it, itemClass);
28068
+ });
27985
28069
  }
27986
28070
  /**
27987
- * Get a state to the player. Returns `null` if the state is not present on the player
27988
- * ```ts
27989
- * import Paralyze from 'your-database/states/paralyze'
28071
+ * Add an item in the player's inventory. You can give more than one by specifying `nb`
27990
28072
  *
27991
- * player.getState(Paralyze)
27992
- * ```
28073
+ * `onAdd()` method is called on the ItemClass
28074
+ *
28075
+ * @title Add Item
28076
+ * @method player.addItem(item,nb=1)
28077
+ * @param {ItemClass} itemClass
28078
+ * @param {number} [nb] Default 1
28079
+ * @returns {{ nb: number, item: instance of ItemClass }}
28080
+ * @memberof ItemManager
28081
+ * @example
27993
28082
  *
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
28083
+ * ```ts
28084
+ * import Potion from 'your-database/potion'
28085
+ * player.addItem(Potion, 5)
28086
+ * ```
27999
28087
  */
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;
28005
- }
28006
- return isInstanceOf(state, stateClass);
28007
- });
28088
+ addItem(itemId, nb = 1) {
28089
+ const data = this.databaseById(itemId);
28090
+ const item = this.items().find((it) => it.id() == itemId);
28091
+ let instance;
28092
+ if (item) {
28093
+ instance = item;
28094
+ instance.quantity.update((it) => it + nb);
28095
+ } else {
28096
+ instance = new Item(data);
28097
+ instance.id.set(itemId);
28098
+ this.items().push(instance);
28099
+ }
28100
+ this["execMethod"]("onAdd", [this], instance);
28101
+ return instance;
28008
28102
  }
28009
28103
  /**
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'
28104
+ * 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`
28013
28105
  *
28014
- * try {
28015
- * player.addState(Paralyze)
28016
- * }
28017
- * catch (err) {
28018
- * console.log(err)
28019
- * }
28020
- * ```
28106
+ * `onRemove()` method is called on the ItemClass
28021
28107
  *
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)
28108
+ * @title Remove Item
28109
+ * @method player.removeItem(item,nb=1)
28110
+ * @param {ItemClass | string} itemClass string is item id
28111
+ * @param {number} [nb] Default 1
28112
+ * @returns {{ nb: number, item: instance of ItemClass } | undefined}
28113
+ * @throws {ItemLog} notInInventory
28114
+ * If the object is not in the inventory, an exception is raised
28028
28115
  * ```
28029
28116
  * {
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);
28161
- });
28162
- }
28163
- /**
28164
- * Add an item in the player's inventory. You can give more than one by specifying `nb`
28165
- *
28166
- * `onAdd()` method is called on the ItemClass
28167
- *
28168
- * @title Add Item
28169
- * @method player.addItem(item,nb=1)
28170
- * @param {ItemClass} itemClass
28171
- * @param {number} [nb] Default 1
28172
- * @returns {{ nb: number, item: instance of ItemClass }}
28173
- * @memberof ItemManager
28174
- * @example
28175
- *
28176
- * ```ts
28177
- * import Potion from 'your-database/potion'
28178
- * player.addItem(Potion, 5)
28179
- * ```
28180
- */
28181
- addItem(itemId, nb = 1) {
28182
- const data = this.databaseById(itemId);
28183
- const item = this.items().find((it) => it.id() == itemId);
28184
- let instance;
28185
- if (item) {
28186
- instance = item;
28187
- instance.quantity.update((it) => it + nb);
28188
- } else {
28189
- instance = new Item(data);
28190
- instance.id.set(itemId);
28191
- this.items().push(instance);
28192
- }
28193
- this["execMethod"]("onAdd", [this], instance);
28194
- return instance;
28195
- }
28196
- /**
28197
- * 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`
28198
- *
28199
- * `onRemove()` method is called on the ItemClass
28200
- *
28201
- * @title Remove Item
28202
- * @method player.removeItem(item,nb=1)
28203
- * @param {ItemClass | string} itemClass string is item id
28204
- * @param {number} [nb] Default 1
28205
- * @returns {{ nb: number, item: instance of ItemClass } | undefined}
28206
- * @throws {ItemLog} notInInventory
28207
- * If the object is not in the inventory, an exception is raised
28208
- * ```
28209
- * {
28210
- * id: ITEM_NOT_INVENTORY,
28117
+ * id: ITEM_NOT_INVENTORY,
28211
28118
  * msg: '...'
28212
28119
  * }
28213
28120
  * ```
@@ -28582,31 +28489,68 @@ var Effect = /* @__PURE__ */ ((Effect2) => {
28582
28489
  function WithEffectManager(Base) {
28583
28490
  return class extends Base {
28584
28491
  /**
28492
+ * Check if the player has a specific effect
28493
+ *
28494
+ * Determines whether the player currently has the specified effect active.
28495
+ * This includes effects from states, equipment, and temporary conditions.
28496
+ * The effect system provides a flexible way to apply various gameplay
28497
+ * restrictions and enhancements to the player.
28498
+ *
28499
+ * @param effect - The effect identifier to check for
28500
+ * @returns true if the player has the effect, false otherwise
28501
+ *
28502
+ * @example
28585
28503
  * ```ts
28586
28504
  * import { Effect } from '@rpgjs/database'
28587
28505
  *
28588
- * const bool = player.hasEffect(Effect.CAN_NOT_SKILL)
28506
+ * // Check for skill restriction
28507
+ * const cannotUseSkills = player.hasEffect(Effect.CAN_NOT_SKILL);
28508
+ * if (cannotUseSkills) {
28509
+ * console.log('Player cannot use skills right now');
28510
+ * }
28511
+ *
28512
+ * // Check for guard effect
28513
+ * const isGuarding = player.hasEffect(Effect.GUARD);
28514
+ * if (isGuarding) {
28515
+ * console.log('Player is in guard stance');
28516
+ * }
28517
+ *
28518
+ * // Check for cost reduction
28519
+ * const halfCost = player.hasEffect(Effect.HALF_SP_COST);
28520
+ * const actualCost = skillCost / (halfCost ? 2 : 1);
28589
28521
  * ```
28590
- *
28591
- * @title Has Effect
28592
- * @method player.hasEffect(effect)
28593
- * @param {string} effect
28594
- * @returns {boolean}
28595
- * @memberof EffectManager
28596
- * */
28522
+ */
28597
28523
  hasEffect(effect) {
28598
28524
  return this.effects.includes(effect);
28599
28525
  }
28600
28526
  /**
28601
28527
  * 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
28528
  *
28529
+ * Gets all currently active effects on the player from multiple sources:
28530
+ * - Direct effects assigned to the player
28531
+ * - Effects from active states (buffs/debuffs)
28532
+ * - Effects from equipped weapons and armor
28533
+ * The returned array contains unique effects without duplicates.
28534
+ *
28535
+ * @returns Array of all active effects on the player
28536
+ *
28537
+ * @example
28603
28538
  * ```ts
28604
- * console.log(player.effects)
28539
+ * // Get all active effects
28540
+ * console.log(player.effects); // ['GUARD', 'HALF_SP_COST', ...]
28541
+ *
28542
+ * // Check multiple effects
28543
+ * const effects = player.effects;
28544
+ * const hasRestrictions = effects.some(effect =>
28545
+ * effect.startsWith('CAN_NOT_')
28546
+ * );
28547
+ *
28548
+ * // Count beneficial effects
28549
+ * const beneficialEffects = effects.filter(effect =>
28550
+ * ['GUARD', 'SUPER_GUARD', 'HALF_SP_COST'].includes(effect)
28551
+ * );
28605
28552
  * ```
28606
- * @title Get Effects
28607
- * @prop {Array<Effect>} player.effects
28608
- * @memberof EffectManager
28609
- * */
28553
+ */
28610
28554
  get effects() {
28611
28555
  const getEffects = (prop) => {
28612
28556
  return arrayFlat(this[prop]().map((el) => el.effects || []));
@@ -28620,113 +28564,243 @@ function WithEffectManager(Base) {
28620
28564
  /**
28621
28565
  * 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
28566
  *
28567
+ * Sets the direct effects on the player. This only affects the player's own effects
28568
+ * and does not modify effects from states or equipment. The total effects will be
28569
+ * the combination of these direct effects plus any effects from states and equipment.
28570
+ *
28571
+ * @param val - Array of effect identifiers to assign to the player
28572
+ *
28573
+ * @example
28623
28574
  * ```ts
28624
28575
  * import { Effect } from '@rpgjs/database'
28625
28576
  *
28626
- * player.effects = [Effect.CAN_NOT_SKILL]
28577
+ * // Set direct player effects
28578
+ * player.effects = [Effect.CAN_NOT_SKILL];
28579
+ *
28580
+ * // Add multiple effects
28581
+ * player.effects = [
28582
+ * Effect.GUARD,
28583
+ * Effect.HALF_SP_COST,
28584
+ * Effect.CAN_NOT_ITEM
28585
+ * ];
28586
+ *
28587
+ * // Clear direct effects (equipment/state effects remain)
28588
+ * player.effects = [];
28589
+ *
28590
+ * // Temporary effect application
28591
+ * const originalEffects = player.effects;
28592
+ * player.effects = [...originalEffects, Effect.SUPER_GUARD];
28593
+ * // Later restore
28594
+ * player.effects = originalEffects;
28627
28595
  * ```
28628
- * @title Set Effects
28629
- * @prop {Array<Effect>} player.effects
28630
- * @memberof EffectManager
28631
- * */
28596
+ */
28632
28597
  set effects(val) {
28633
28598
  this._effects.set(val);
28634
28599
  }
28635
28600
  };
28636
28601
  }
28637
28602
 
28638
- function WithBattleManager(Base) {
28603
+ function WithElementManager(Base) {
28639
28604
  return class extends Base {
28605
+ constructor() {
28606
+ super(...arguments);
28607
+ this._elementsEfficiency = [];
28608
+ }
28640
28609
  /**
28641
- * Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
28642
- *
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
28610
+ * Recovers the player's elements defense on inventory. This list is generated from the `elementsDefense` property defined on the weapons or armors equipped.
28611
+ * If several items have the same element, only the highest rate will be taken into account.
28645
28612
  *
28613
+ * Gets the defensive capabilities against various elements from equipped items.
28614
+ * The system automatically consolidates multiple defensive items, keeping only
28615
+ * the highest protection rate for each element type. This provides a comprehensive
28616
+ * view of the player's elemental resistances from all equipped gear.
28617
+ *
28618
+ * @returns Array of element defense objects with rate and element properties
28619
+ *
28620
+ * @example
28646
28621
  * ```ts
28647
- * player.applyDamage(attackerPlayer) // returns { damage: number }
28622
+ * import { Armor } from '@rpgjs/server'
28623
+ *
28624
+ * enum Elements {
28625
+ * Fire = 'fire'
28626
+ * }
28627
+ *
28628
+ * @Armor({
28629
+ * name: 'Shield',
28630
+ * elementsDefense: [{ rate: 1, element: Elements.Fire }]
28631
+ * })
28632
+ * class Shield {}
28633
+ *
28634
+ * @Armor({
28635
+ * name: 'FireShield',
28636
+ * elementsDefense: [{ rate: 0.5, element: Elements.Fire }]
28637
+ * })
28638
+ * class FireShield {}
28639
+ *
28640
+ * player.addItem(Shield)
28641
+ * player.addItem(FireShield)
28642
+ * player.equip(Shield)
28643
+ * player.equip(FireShield)
28644
+ *
28645
+ * console.log(player.elementsDefense) // [{ rate: 1, element: 'fire' }]
28646
+ *
28647
+ * // Check specific element defense
28648
+ * const fireDefense = player.elementsDefense.find(def => def.element === 'fire');
28649
+ * if (fireDefense) {
28650
+ * console.log(`Fire defense rate: ${fireDefense.rate}`);
28651
+ * }
28648
28652
  * ```
28653
+ */
28654
+ get elementsDefense() {
28655
+ return this.getFeature("elementsDefense", "element");
28656
+ }
28657
+ /**
28658
+ * Set or retrieves all the elements where the player is vulnerable or not.
28649
28659
  *
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;
28702
- }
28660
+ * Manages the player's elemental efficiency modifiers, which determine how
28661
+ * effective different elements are against this player. Values greater than 1
28662
+ * indicate vulnerability, while values less than 1 indicate resistance.
28663
+ * This combines both class-based efficiency and player-specific modifiers.
28664
+ *
28665
+ * @returns Array of element efficiency objects with rate and element properties
28666
+ *
28667
+ * @example
28668
+ * ```ts
28669
+ * import { Class } from '@rpgjs/server'
28670
+ *
28671
+ * enum Elements {
28672
+ * Fire = 'fire',
28673
+ * Ice = 'ice'
28674
+ * }
28675
+ *
28676
+ * @Class({
28677
+ * name: 'Fighter',
28678
+ * elementsEfficiency: [{ rate: 1, element: Elements.Fire }]
28679
+ * })
28680
+ * class Hero {}
28681
+ *
28682
+ * player.setClass(Hero)
28683
+ *
28684
+ * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }]
28685
+ *
28686
+ * player.elementsEfficiency = [{ rate: 2, element: Elements.Ice }]
28687
+ *
28688
+ * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }, { rate: 2, element: 'ice' }]
28689
+ *
28690
+ * // Check for vulnerabilities
28691
+ * const vulnerabilities = player.elementsEfficiency.filter(eff => eff.rate > 1);
28692
+ * console.log('Vulnerable to:', vulnerabilities.map(v => v.element));
28693
+ *
28694
+ * // Check for resistances
28695
+ * const resistances = player.elementsEfficiency.filter(eff => eff.rate < 1);
28696
+ * console.log('Resistant to:', resistances.map(r => r.element));
28697
+ * ```
28698
+ */
28699
+ get elementsEfficiency() {
28700
+ if (this._class()) {
28701
+ return [
28702
+ ...this._elementsEfficiency,
28703
+ ...this._class()?.elementsEfficiency || []
28704
+ ];
28703
28705
  }
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;
28706
+ return this._elementsEfficiency;
28707
+ }
28708
+ set elementsEfficiency(val) {
28709
+ this._elementsEfficiency = val;
28710
+ }
28711
+ /**
28712
+ * Retrieves a array of elements assigned to the player and the elements of the weapons / armor equipped
28713
+ *
28714
+ * Gets all offensive elements available to the player from equipped weapons and armor.
28715
+ * This determines what elemental damage types the player can deal in combat.
28716
+ * The system automatically combines elements from all equipped items and removes duplicates.
28717
+ *
28718
+ * @returns Array of element objects with rate and element properties for offensive capabilities
28719
+ *
28720
+ * @example
28721
+ * ```ts
28722
+ * // Get all offensive elements
28723
+ * console.log(player.elements); // [{ rate: 1.5, element: 'fire' }, { rate: 1.2, element: 'ice' }]
28724
+ *
28725
+ * // Check if player can deal fire damage
28726
+ * const hasFireElement = player.elements.some(el => el.element === 'fire');
28727
+ * if (hasFireElement) {
28728
+ * console.log('Player can deal fire damage');
28729
+ * }
28730
+ *
28731
+ * // Get strongest element
28732
+ * const strongestElement = player.elements.reduce((max, current) =>
28733
+ * current.rate > max.rate ? current : max
28734
+ * );
28735
+ * console.log(`Strongest element: ${strongestElement.element} (${strongestElement.rate})`);
28736
+ * ```
28737
+ */
28738
+ get elements() {
28739
+ let elements = [];
28740
+ for (let item of this.equipments()) {
28741
+ if (item.elements) {
28742
+ elements = [...elements, ...item.elements];
28712
28743
  }
28713
28744
  }
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
- };
28745
+ return arrayUniq(elements);
28726
28746
  }
28727
- getFormulas(name) {
28728
- const map = this.getCurrentMap();
28729
- return map.damageFormulas[name];
28747
+ /**
28748
+ * Calculate elemental damage coefficient against another player
28749
+ *
28750
+ * Determines the damage multiplier when this player attacks another player,
28751
+ * taking into account the attacker's offensive elements, the defender's
28752
+ * elemental efficiency, and elemental defense from equipment. This is used
28753
+ * in the battle system to calculate elemental damage modifiers.
28754
+ *
28755
+ * @param otherPlayer - The target player to calculate coefficient against
28756
+ * @returns Numerical coefficient to multiply base damage by
28757
+ *
28758
+ * @example
28759
+ * ```ts
28760
+ * // Calculate elemental damage coefficient
28761
+ * const firePlayer = new MyPlayer();
28762
+ * const icePlayer = new MyPlayer();
28763
+ *
28764
+ * // Fire player attacks ice player (assuming ice is weak to fire)
28765
+ * const coefficient = icePlayer.coefficientElements(firePlayer);
28766
+ * console.log(`Damage multiplier: ${coefficient}`); // e.g., 2.0 for double damage
28767
+ *
28768
+ * // Use in damage calculation
28769
+ * const baseDamage = 100;
28770
+ * const finalDamage = baseDamage * coefficient;
28771
+ * console.log(`Final damage: ${finalDamage}`);
28772
+ *
28773
+ * // Check for elemental advantage
28774
+ * if (coefficient > 1) {
28775
+ * console.log('Attacker has elemental advantage!');
28776
+ * } else if (coefficient < 1) {
28777
+ * console.log('Defender resists this element');
28778
+ * }
28779
+ * ```
28780
+ */
28781
+ coefficientElements(otherPlayer) {
28782
+ const atkPlayerElements = otherPlayer.elements;
28783
+ const playerElements = this.elementsEfficiency;
28784
+ let coefficient = 1;
28785
+ for (let atkElement of atkPlayerElements) {
28786
+ const elementPlayer = playerElements.find(
28787
+ (el) => el.element == atkElement.element
28788
+ );
28789
+ const elementPlayerDef = this.elementsDefense.find(
28790
+ (el) => el.element == atkElement.element
28791
+ );
28792
+ if (!elementPlayer) continue;
28793
+ const fn = this.getFormulas("coefficientElements");
28794
+ if (!fn) {
28795
+ return coefficient;
28796
+ }
28797
+ coefficient += fn(
28798
+ atkElement,
28799
+ elementPlayer,
28800
+ elementPlayerDef || { rate: 0 }
28801
+ );
28802
+ }
28803
+ return coefficient;
28730
28804
  }
28731
28805
  };
28732
28806
  }
@@ -28945,23 +29019,176 @@ function WithSkillManager(Base) {
28945
29019
  };
28946
29020
  }
28947
29021
 
29022
+ function WithBattleManager(Base) {
29023
+ return class extends Base {
29024
+ /**
29025
+ * Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
29026
+ *
29027
+ * If you don't set the skill parameter, it will be a physical attack.
29028
+ * The attack formula is already defined but you can customize it in the server options.
29029
+ * This method handles all aspects of damage calculation including critical hits,
29030
+ * elemental vulnerabilities, guard effects, and applies the final damage to HP.
29031
+ *
29032
+ * @param attackerPlayer - The attacking player who deals the damage
29033
+ * @param skill - Optional skill object for magical attacks, if not provided uses physical attack
29034
+ * @returns Object containing damage details and special effects that occurred
29035
+ *
29036
+ * @example
29037
+ * ```ts
29038
+ * // Physical attack
29039
+ * const result = player.applyDamage(attackerPlayer);
29040
+ * console.log(`Physical damage: ${result.damage}, Critical: ${result.critical}`);
29041
+ *
29042
+ * // Magical attack with skill
29043
+ * const fireSkill = { id: 'fire', power: 50, element: 'fire' };
29044
+ * const magicResult = player.applyDamage(attackerPlayer, fireSkill);
29045
+ * console.log(`Magic damage: ${magicResult.damage}, Vulnerable: ${magicResult.elementVulnerable}`);
29046
+ *
29047
+ * // Check for guard effects
29048
+ * if (result.guard) {
29049
+ * console.log('Attack was partially blocked!');
29050
+ * }
29051
+ * if (result.superGuard) {
29052
+ * console.log('Attack was heavily reduced by super guard!');
29053
+ * }
29054
+ * ```
29055
+ */
29056
+ applyDamage(attackerPlayer, skill) {
29057
+ const getParam = (player) => {
29058
+ const params = {};
29059
+ this.parameters.forEach((val, key) => {
29060
+ params[key] = player.param[key];
29061
+ });
29062
+ return {
29063
+ [ATK]: player.atk,
29064
+ [PDEF]: player.pdef,
29065
+ [SDEF]: player.sdef,
29066
+ ...params
29067
+ };
29068
+ };
29069
+ let damage = 0, fn;
29070
+ let critical = false;
29071
+ let guard = false;
29072
+ let superGuard = false;
29073
+ let elementVulnerable = false;
29074
+ const paramA = getParam(attackerPlayer);
29075
+ const paramB = getParam(this);
29076
+ console.log(paramA, paramB);
29077
+ if (skill) {
29078
+ fn = this.getFormulas("damageSkill");
29079
+ if (!fn) {
29080
+ throw new Error("Skill Formulas not exists");
29081
+ }
29082
+ damage = fn(paramA, paramB, skill);
29083
+ } else {
29084
+ fn = this.getFormulas("damagePhysic");
29085
+ if (!fn) {
29086
+ throw new Error("Physic Formulas not exists");
29087
+ }
29088
+ damage = fn(paramA, paramB);
29089
+ const coef = this.coefficientElements(attackerPlayer);
29090
+ if (coef >= 2) {
29091
+ elementVulnerable = true;
29092
+ }
29093
+ damage *= coef;
29094
+ fn = this.getFormulas("damageCritical");
29095
+ if (fn) {
29096
+ let newDamage = fn(damage, paramA, paramB);
29097
+ if (damage != newDamage) {
29098
+ critical = true;
29099
+ }
29100
+ damage = newDamage;
29101
+ }
29102
+ }
29103
+ if (this.hasEffect(Effect.GUARD)) {
29104
+ fn = this.getFormulas("damageGuard");
29105
+ if (fn) {
29106
+ let newDamage = fn(damage, paramA, paramB);
29107
+ if (damage != newDamage) {
29108
+ guard = true;
29109
+ }
29110
+ damage = newDamage;
29111
+ }
29112
+ }
29113
+ if (this.hasEffect(Effect.SUPER_GUARD)) {
29114
+ damage /= 4;
29115
+ superGuard = true;
29116
+ }
29117
+ this.hp -= damage;
29118
+ return {
29119
+ damage,
29120
+ critical,
29121
+ elementVulnerable,
29122
+ guard,
29123
+ superGuard
29124
+ };
29125
+ }
29126
+ /**
29127
+ * Get damage formulas from the current map
29128
+ *
29129
+ * Retrieves the damage calculation formulas defined in the current map's configuration.
29130
+ * These formulas are used to calculate different types of damage including physical,
29131
+ * magical, critical hits, and guard effects. The formulas provide flexibility in
29132
+ * customizing the battle system's damage calculations.
29133
+ *
29134
+ * @param name - The name of the formula to retrieve (e.g., 'damagePhysic', 'damageSkill')
29135
+ * @returns The formula function or undefined if not found
29136
+ *
29137
+ * @example
29138
+ * ```ts
29139
+ * // Get physical damage formula
29140
+ * const physicFormula = player.getFormulas('damagePhysic');
29141
+ * if (physicFormula) {
29142
+ * const damage = physicFormula(attackerParams, defenderParams);
29143
+ * }
29144
+ *
29145
+ * // Get critical damage formula
29146
+ * const criticalFormula = player.getFormulas('damageCritical');
29147
+ * if (criticalFormula) {
29148
+ * const criticalDamage = criticalFormula(baseDamage, attackerParams, defenderParams);
29149
+ * }
29150
+ * ```
29151
+ */
29152
+ getFormulas(name) {
29153
+ const map = this.getCurrentMap();
29154
+ return map.damageFormulas[name];
29155
+ }
29156
+ };
29157
+ }
29158
+
28948
29159
  function WithClassManager(Base) {
28949
29160
  return class extends Base {
28950
29161
  /**
28951
29162
  * Assign a class to the player
28952
29163
  *
29164
+ * Sets the player's class, which defines their combat abilities, stat growth,
29165
+ * and available skills. The class system provides the foundation for character
29166
+ * progression and specialization. When a class is set, it automatically triggers
29167
+ * the class's onSet method for any additional initialization.
29168
+ *
29169
+ * @param _class - The class constructor or class ID to assign to the player
29170
+ * @returns The instantiated class object
29171
+ *
29172
+ * @example
28953
29173
  * ```ts
28954
29174
  * import { Fighter } from 'my-database/classes/fighter'
28955
29175
  *
28956
- * player.setClass(Fighter)
29176
+ * // Set class using constructor
29177
+ * const fighterClass = player.setClass(Fighter);
29178
+ * console.log('Class set:', fighterClass.name);
29179
+ *
29180
+ * // Set class using string ID
29181
+ * player.setClass('fighter');
29182
+ *
29183
+ * // Class affects available skills and stats
29184
+ * console.log('Available skills:', player.skills);
29185
+ * console.log('Class bonuses applied to stats');
29186
+ *
29187
+ * // Class determines level progression
29188
+ * player.level = 5;
29189
+ * // Skills may be automatically learned based on class definition
28957
29190
  * ```
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
- * */
29191
+ */
28965
29192
  setClass(_class) {
28966
29193
  if (isString(_class)) _class = this.databaseById(_class);
28967
29194
  const classInstance = new _class();
@@ -28971,18 +29198,39 @@ function WithClassManager(Base) {
28971
29198
  /**
28972
29199
  * 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
29200
  *
29201
+ * Sets up the player as a specific actor archetype, which includes predefined
29202
+ * characteristics like starting equipment, parameters, level ranges, and associated class.
29203
+ * This is typically used for creating pre-configured character templates or NPCs
29204
+ * with specific roles and equipment loadouts.
29205
+ *
29206
+ * @param actorClass - The actor constructor or actor ID to assign to the player
29207
+ * @returns The instantiated actor object
29208
+ *
29209
+ * @example
28974
29210
  * ```ts
28975
29211
  * import { Hero } from 'my-database/classes/hero'
28976
29212
  *
28977
- * player.setActor(Hero)
29213
+ * // Set up player as Hero actor
29214
+ * const heroActor = player.setActor(Hero);
29215
+ * console.log('Actor configured:', heroActor.name);
29216
+ *
29217
+ * // Actor automatically sets up:
29218
+ * // - Starting equipment (sword, armor, etc.)
29219
+ * console.log('Starting equipment:', player.equipments());
29220
+ *
29221
+ * // - Parameter ranges and growth
29222
+ * console.log('Level range:', player.initialLevel, '-', player.finalLevel);
29223
+ *
29224
+ * // - Associated class
29225
+ * console.log('Assigned class:', player.class);
29226
+ *
29227
+ * // - Experience curve
29228
+ * console.log('EXP curve:', player.expCurve);
29229
+ *
29230
+ * // Actor setup is comprehensive
29231
+ * player.setActor('hero'); // Can also use string ID
28978
29232
  * ```
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
- * */
29233
+ */
28986
29234
  setActor(actorClass) {
28987
29235
  if (isString(actorClass)) actorClass = this.databaseById(actorClass);
28988
29236
  const actor = new actorClass();
@@ -29003,32 +29251,41 @@ function WithClassManager(Base) {
29003
29251
  };
29004
29252
  }
29005
29253
 
29006
- function WithElementManager(Base) {
29254
+ function WithStateManager(Base) {
29007
29255
  return class extends Base {
29008
29256
  constructor() {
29009
29257
  super(...arguments);
29010
- this._elementsEfficiency = [];
29258
+ this._statesEfficiency = signal$2([]);
29011
29259
  }
29012
29260
  /**
29013
- * Recovers the player's elements defense on inventory. This list is generated from the `elementsDefense` property defined on the weapons or armors equipped.
29261
+ * Recovers the player's states defense on inventory. This list is generated from the `statesDefense` property defined on the weapons or armors equipped.
29014
29262
  * If several items have the same element, only the highest rate will be taken into account.
29015
29263
  *
29264
+ * Gets the defensive capabilities against various states from equipped items.
29265
+ * The system automatically consolidates multiple defensive items, keeping only
29266
+ * the highest protection rate for each state type. This provides comprehensive
29267
+ * protection against debuffs and negative status effects.
29268
+ *
29269
+ * @returns Array of state defense objects with rate and state properties
29270
+ *
29271
+ * @example
29016
29272
  * ```ts
29017
- * import { Armor } from '@rpgjs/server'
29273
+ * import { Armor, State } from '@rpgjs/server'
29018
29274
  *
29019
- * enum Elements {
29020
- * Fire = 'fire'
29021
- * }
29275
+ * @State({
29276
+ * name: 'Paralyze'
29277
+ * })
29278
+ * class Paralyze {}
29022
29279
  *
29023
29280
  * @Armor({
29024
29281
  * name: 'Shield',
29025
- * elementsDefense: [{ rate: 1, element: Elements.Fire }]
29282
+ * statesDefense: [{ rate: 1, state: Paralyze }]
29026
29283
  * })
29027
29284
  * class Shield {}
29028
29285
  *
29029
29286
  * @Armor({
29030
29287
  * name: 'FireShield',
29031
- * elementsDefense: [{ rate: 0.5, element: Elements.Fire }]
29288
+ * statesDefense: [{ rate: 0.5, state: Paralyze }]
29032
29289
  * })
29033
29290
  * class FireShield {}
29034
29291
  *
@@ -29037,100 +29294,291 @@ function WithElementManager(Base) {
29037
29294
  * player.equip(Shield)
29038
29295
  * player.equip(FireShield)
29039
29296
  *
29040
- * console.log(player.elementsDefense) // [{ rate: 1, element: 'fire' }]
29297
+ * console.log(player.statesDefense) // [{ rate: 1, state: instance of Paralyze }]
29298
+ *
29299
+ * // Check specific state defense
29300
+ * const paralyzeDefense = player.statesDefense.find(def => def.state instanceof Paralyze);
29301
+ * if (paralyzeDefense) {
29302
+ * console.log(`Paralyze defense rate: ${paralyzeDefense.rate}`);
29303
+ * }
29041
29304
  * ```
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");
29305
+ */
29306
+ get statesDefense() {
29307
+ return this.getFeature("statesDefense", "state");
29049
29308
  }
29050
29309
  /**
29051
- * Set or retrieves all the elements where the player is vulnerable or not.
29310
+ * Set or retrieves all the states where the player is vulnerable or not.
29052
29311
  *
29312
+ * Manages the player's state efficiency modifiers, which determine how
29313
+ * effective different states are against this player. Values greater than 1
29314
+ * indicate vulnerability, while values less than 1 indicate resistance.
29315
+ * This combines both class-based efficiency and player-specific modifiers.
29316
+ *
29317
+ * @returns Array of state efficiency objects with rate and state properties
29318
+ *
29319
+ * @example
29053
29320
  * ```ts
29054
- * import { Class } from '@rpgjs/server'
29321
+ * import { Class, State } from '@rpgjs/server'
29055
29322
  *
29056
- * enum Elements {
29057
- * Fire = 'fire',
29058
- * Ice = 'ice'
29059
- * }
29323
+ * @State({
29324
+ * name: 'Paralyze'
29325
+ * })
29326
+ * class Paralyze {}
29327
+ *
29328
+ * @State({
29329
+ * name: 'Sleep'
29330
+ * })
29331
+ * class Sleep {}
29060
29332
  *
29061
29333
  * @Class({
29062
29334
  * name: 'Fighter',
29063
- * elementsEfficiency: [{ rate: 1, element: Elements.Fire }]
29335
+ * statesEfficiency: [{ rate: 1, state: Paralyze }]
29064
29336
  * })
29065
29337
  * class Hero {}
29066
29338
  *
29067
29339
  * player.setClass(Hero)
29068
29340
  *
29069
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }]
29341
+ * console.log(player.statesEfficiency) // [{ rate: 1, instance of Paralyze }]
29070
29342
  *
29071
- * player.elementsEfficiency = [{ rate: 2, element: Elements.Ice }]
29343
+ * player.statesEfficiency = [{ rate: 2, state: Sleep }]
29072
29344
  *
29073
- * console.log(player.elementsEfficiency) // [{ rate: 1, element: 'fire' }, { rate: 2, element: 'ice' }]
29345
+ * console.log(player.statesEfficiency) // [{ rate: 1, state: instance of Paralyze }, { rate: 2, state: instance of Sleep }]
29346
+ *
29347
+ * // Check for vulnerabilities
29348
+ * const vulnerabilities = player.statesEfficiency.filter(eff => eff.rate > 1);
29349
+ * console.log('Vulnerable to states:', vulnerabilities.map(v => v.state.name));
29350
+ *
29351
+ * // Check for resistances
29352
+ * const resistances = player.statesEfficiency.filter(eff => eff.rate < 1);
29353
+ * console.log('Resistant to states:', resistances.map(r => r.state.name));
29074
29354
  * ```
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;
29355
+ */
29356
+ get statesEfficiency() {
29357
+ return this._statesEfficiency;
29087
29358
  }
29088
- set elementsEfficiency(val) {
29089
- this._elementsEfficiency = val;
29359
+ set statesEfficiency(val) {
29360
+ this._statesEfficiency = val;
29090
29361
  }
29091
29362
  /**
29092
- * Retrieves a array of elements assigned to the player and the elements of the weapons / armor equipped
29363
+ * Apply states to a player from skill or item effects
29364
+ *
29365
+ * Processes state application and removal based on skill or item effects.
29366
+ * This method handles both adding beneficial states and removing negative ones,
29367
+ * with proper chance calculation and resistance checks.
29368
+ *
29369
+ * @param player - The target player to apply states to
29370
+ * @param states - Object containing arrays of states to add or remove
29371
+ *
29372
+ * @example
29373
+ * ```ts
29374
+ * // Apply states from a healing skill
29375
+ * const healingStates = {
29376
+ * addStates: [{ state: Regeneration, rate: 0.8 }],
29377
+ * removeStates: [{ state: Poison, rate: 1.0 }]
29378
+ * };
29379
+ * player.applyStates(targetPlayer, healingStates);
29380
+ *
29381
+ * // Apply debuff from an enemy attack
29382
+ * const debuffStates = {
29383
+ * addStates: [
29384
+ * { state: Paralyze, rate: 0.3 },
29385
+ * { state: Slow, rate: 0.5 }
29386
+ * ]
29387
+ * };
29388
+ * player.applyStates(targetPlayer, debuffStates);
29389
+ * ```
29390
+ */
29391
+ applyStates(player, { addStates, removeStates }) {
29392
+ if (addStates) {
29393
+ for (let { state, rate } of addStates) {
29394
+ player.addState(state, rate);
29395
+ }
29396
+ }
29397
+ if (removeStates) {
29398
+ for (let { state, rate } of removeStates) {
29399
+ player.removeState(state, rate);
29400
+ }
29401
+ }
29402
+ }
29403
+ /**
29404
+ * Get a state to the player. Returns `null` if the state is not present on the player
29405
+ *
29406
+ * Retrieves a specific state instance from the player's active states.
29407
+ * This is useful for checking state properties, duration, or performing
29408
+ * state-specific operations. Returns null if the state is not currently active.
29409
+ *
29410
+ * @param stateClass - The state class constructor or state ID to search for
29411
+ * @returns The state instance if found, null otherwise
29412
+ *
29413
+ * @example
29414
+ * ```ts
29415
+ * import Paralyze from 'your-database/states/paralyze'
29093
29416
  *
29417
+ * // Check if player has a specific state
29418
+ * const paralyzeState = player.getState(Paralyze);
29419
+ * if (paralyzeState) {
29420
+ * console.log('Player is paralyzed');
29421
+ * console.log('Remaining duration:', paralyzeState.duration);
29422
+ * }
29423
+ *
29424
+ * // Check using string ID
29425
+ * const poisonState = player.getState('poison');
29426
+ * if (poisonState) {
29427
+ * console.log('Player is poisoned');
29428
+ * }
29429
+ *
29430
+ * // Use in conditional logic
29431
+ * if (player.getState(Sleep)) {
29432
+ * console.log('Player cannot act while sleeping');
29433
+ * return; // Skip player turn
29434
+ * }
29435
+ * ```
29436
+ */
29437
+ getState(stateClass) {
29438
+ if (isString(stateClass)) stateClass = this.databaseById(stateClass);
29439
+ return this.states().find((state) => {
29440
+ if (isString(stateClass)) {
29441
+ return state.id == stateClass;
29442
+ }
29443
+ return isInstanceOf(state, stateClass);
29444
+ });
29445
+ }
29446
+ /**
29447
+ * Adds a state to the player. Set the chance between 0 and 1 that the state can apply
29448
+ *
29449
+ * Attempts to apply a state to the player with a specified success chance.
29450
+ * The method considers state resistance, efficiency modifiers, and random chance
29451
+ * to determine if the state is successfully applied. If successful, the state
29452
+ * is added to the player's active states list.
29453
+ *
29454
+ * @param stateClass - The state class constructor or state ID to apply
29455
+ * @param chance - Probability of successful application (0-1, default 1)
29456
+ * @returns The state instance if successfully applied, null if already present
29457
+ * @throws StateLog.addFailed if the chance roll fails
29458
+ *
29459
+ * @example
29094
29460
  * ```ts
29095
- * console.log(player.elements)
29461
+ * import Paralyze from 'your-database/states/paralyze'
29462
+ *
29463
+ * try {
29464
+ * // Attempt to apply paralyze with 100% chance
29465
+ * const state = player.addState(Paralyze);
29466
+ * if (state) {
29467
+ * console.log('Paralyze applied successfully');
29468
+ * }
29469
+ * } catch (err) {
29470
+ * console.log('Failed to apply paralyze:', err.msg);
29471
+ * }
29472
+ *
29473
+ * // Apply with reduced chance
29474
+ * try {
29475
+ * player.addState(Poison, 0.3); // 30% chance
29476
+ * } catch (err) {
29477
+ * console.log('Poison application failed');
29478
+ * }
29479
+ *
29480
+ * // Apply multiple states with different chances
29481
+ * const debuffs = [
29482
+ * { state: Slow, chance: 0.8 },
29483
+ * { state: Weak, chance: 0.6 }
29484
+ * ];
29485
+ * debuffs.forEach(({ state, chance }) => {
29486
+ * try {
29487
+ * player.addState(state, chance);
29488
+ * } catch (err) {
29489
+ * // Handle failed applications
29490
+ * }
29491
+ * });
29096
29492
  * ```
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];
29493
+ */
29494
+ addState(stateClass, chance = 1) {
29495
+ const state = this.getState(stateClass);
29496
+ if (isString(stateClass)) {
29497
+ stateClass = this.databaseById(stateClass);
29498
+ }
29499
+ if (!state) {
29500
+ if (Math.random() > chance) {
29501
+ throw StateLog.addFailed(stateClass);
29107
29502
  }
29503
+ const instance = new stateClass();
29504
+ this.states().push(instance);
29505
+ this.applyStates(this, instance);
29506
+ return instance;
29108
29507
  }
29109
- return arrayUniq(elements);
29508
+ return null;
29110
29509
  }
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;
29510
+ /**
29511
+ * Remove a state to the player. Set the chance between 0 and 1 that the state can be removed
29512
+ *
29513
+ * Attempts to remove a state from the player with a specified success chance.
29514
+ * This is useful for cure spells, items, or time-based state removal.
29515
+ * The method considers removal resistance and random chance.
29516
+ *
29517
+ * @param stateClass - The state class constructor or state ID to remove
29518
+ * @param chance - Probability of successful removal (0-1, default 1)
29519
+ * @throws StateLog.removeFailed if the chance roll fails
29520
+ * @throws StateLog.notApplied if the state is not currently active
29521
+ *
29522
+ * @example
29523
+ * ```ts
29524
+ * import Paralyze from 'your-database/states/paralyze'
29525
+ *
29526
+ * try {
29527
+ * // Attempt to remove paralyze with 100% chance
29528
+ * player.removeState(Paralyze);
29529
+ * console.log('Paralyze removed successfully');
29530
+ * } catch (err) {
29531
+ * if (err.id === 'STATE_NOT_APPLIED') {
29532
+ * console.log('Player was not paralyzed');
29533
+ * } else {
29534
+ * console.log('Failed to remove paralyze:', err.msg);
29535
+ * }
29536
+ * }
29537
+ *
29538
+ * // Remove with reduced chance (for weak cure spells)
29539
+ * try {
29540
+ * player.removeState(Poison, 0.7); // 70% chance
29541
+ * } catch (err) {
29542
+ * console.log('Cure failed');
29543
+ * }
29544
+ *
29545
+ * // Remove all negative states (cure-all effect)
29546
+ * const negativeStates = [Poison, Paralyze, Sleep, Slow];
29547
+ * negativeStates.forEach(state => {
29548
+ * try {
29549
+ * player.removeState(state);
29550
+ * } catch (err) {
29551
+ * // State wasn't active, continue
29552
+ * }
29553
+ * });
29554
+ * ```
29555
+ */
29556
+ removeState(stateClass, chance = 1) {
29557
+ const index = this.states().findIndex((state) => {
29558
+ if (isString(stateClass)) {
29559
+ return state.id == stateClass;
29126
29560
  }
29127
- coefficient += fn(
29128
- atkElement,
29129
- elementPlayer,
29130
- elementPlayerDef || { rate: 0 }
29131
- );
29561
+ return isInstanceOf(state, stateClass);
29562
+ });
29563
+ if (index != -1) {
29564
+ if (Math.random() > chance) {
29565
+ throw StateLog.removeFailed(stateClass);
29566
+ }
29567
+ this.states().splice(index, 1);
29568
+ } else {
29569
+ throw StateLog.notApplied(stateClass);
29132
29570
  }
29133
- return coefficient;
29571
+ }
29572
+ /**
29573
+ * Find state efficiency modifier for a specific state class
29574
+ *
29575
+ * @param stateClass - The state class to find efficiency for
29576
+ * @returns The efficiency object if found, undefined otherwise
29577
+ */
29578
+ findStateEfficiency(stateClass) {
29579
+ return this.statesEfficiency().find(
29580
+ (state) => isInstanceOf(state.state, stateClass)
29581
+ );
29134
29582
  }
29135
29583
  };
29136
29584
  }
@@ -29147,23 +29595,23 @@ var __decorateClass$2 = (decorators, target, key, kind) => {
29147
29595
  function combinePlayerMixins(mixins) {
29148
29596
  return (Base) => mixins.reduce((ExtendedClass, mixin) => mixin(ExtendedClass), Base);
29149
29597
  }
29150
- const PlayerMixins = combinePlayerMixins([
29598
+ const BasicPlayerMixins = combinePlayerMixins([
29151
29599
  WithComponentManager,
29152
29600
  WithEffectManager,
29153
29601
  WithGuiManager,
29154
29602
  WithMoveManager,
29155
29603
  WithGoldManager,
29156
- WithVariableManager,
29157
29604
  WithParameterManager,
29158
29605
  WithItemFixture,
29159
- WithStateManager,
29160
29606
  WithItemManager,
29161
- WithSkillManager,
29607
+ WithElementManager,
29608
+ WithVariableManager,
29609
+ WithStateManager,
29162
29610
  WithClassManager,
29163
- WithBattleManager,
29164
- WithElementManager
29611
+ WithSkillManager,
29612
+ WithBattleManager
29165
29613
  ]);
29166
- const _RpgPlayer = class _RpgPlayer extends PlayerMixins(RpgCommonPlayer) {
29614
+ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
29167
29615
  constructor() {
29168
29616
  super();
29169
29617
  this.map = null;