@rpgjs/server 5.0.0-beta.3 → 5.0.0-beta.5

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.
@@ -3,9 +3,19 @@ import { RpgPlayer } from './Player';
3
3
  /**
4
4
  * Type for skill class constructor
5
5
  */
6
- type SkillClass = {
6
+ export type SkillClass = {
7
7
  new (...args: any[]): any;
8
8
  };
9
+ export type SkillChangeAction = "learn" | "forget";
10
+ export interface SkillChangeOptions {
11
+ source?: "manual" | "level" | "class" | "studio" | string;
12
+ level?: number;
13
+ }
14
+ export interface SkillChangePayload extends SkillChangeOptions {
15
+ action: SkillChangeAction;
16
+ skill: SkillClass | SkillObject | string;
17
+ skillId: string;
18
+ }
9
19
  /**
10
20
  * Interface defining the hooks that can be implemented on skill classes or objects
11
21
  *
@@ -180,7 +190,7 @@ export interface ISkillManager {
180
190
  * @returns The learned skill data
181
191
  * @throws SkillLog.alreadyLearned if the player already knows the skill
182
192
  */
183
- learnSkill(skillInput: SkillClass | SkillObject | string): any;
193
+ learnSkill(skillInput: SkillClass | SkillObject | string, options?: SkillChangeOptions): any;
184
194
  /**
185
195
  * Forget a skill
186
196
  *
@@ -188,7 +198,7 @@ export interface ISkillManager {
188
198
  * @returns The forgotten skill data
189
199
  * @throws SkillLog.notLearned if trying to forget a skill not learned
190
200
  */
191
- forgetSkill(skillInput: SkillClass | SkillObject | string): any;
201
+ forgetSkill(skillInput: SkillClass | SkillObject | string, options?: SkillChangeOptions): any;
192
202
  /**
193
203
  * Use a skill
194
204
  *
@@ -202,4 +212,3 @@ export interface ISkillManager {
202
212
  */
203
213
  useSkill(skillInput: SkillClass | SkillObject | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any;
204
214
  }
205
- export {};
@@ -3,6 +3,7 @@ import { RpgPlayer, RpgEvent } from './Player/Player';
3
3
  import { RpgMap } from './rooms/map';
4
4
  import { RpgServerEngine } from './RpgServerEngine';
5
5
  import { WorldMapConfig, RpgShape, MapPhysicsInitContext, MapPhysicsEntityContext } from '../../common/src';
6
+ import { SkillChangePayload } from './Player/SkillManager';
6
7
  type RpgClassMap<T> = new () => T;
7
8
  type RpgClassEvent<T> = RpgEvent;
8
9
  type MatchMakerOption = any;
@@ -196,6 +197,13 @@ export interface RpgPlayerHooks {
196
197
  */
197
198
  onLevelUp?: (player: RpgPlayer, nbLevel: number) => any;
198
199
  /**
200
+ * When a player learns or forgets a skill
201
+ *
202
+ * @prop { (player: RpgPlayer, payload: SkillChangePayload) => any } [onSkillChange]
203
+ * @memberof RpgPlayerHooks
204
+ */
205
+ onSkillChange?: (player: RpgPlayer, payload: SkillChangePayload) => any;
206
+ /**
199
207
  * When the player's HP drops to 0
200
208
  *
201
209
  * @prop { (player: RpgPlayer) => any } [onDead]
package/dist/index.js CHANGED
@@ -7826,6 +7826,13 @@ function provideAutoSave(strategy) {
7826
7826
  }
7827
7827
  //#endregion
7828
7828
  //#region src/Gui/MenuGui.ts
7829
+ function readReactiveValue(value, context) {
7830
+ return typeof value === "function" ? value.call(context) : value;
7831
+ }
7832
+ function readField(source, key, fallback) {
7833
+ const value = source?.[key];
7834
+ return readReactiveValue(value, source) ?? fallback;
7835
+ }
7829
7836
  var MenuGui = class extends Gui {
7830
7837
  constructor(player) {
7831
7838
  super(PrebuiltGui.MainMenu, player);
@@ -7882,7 +7889,7 @@ var MenuGui = class extends Gui {
7882
7889
  }));
7883
7890
  const player = this.player;
7884
7891
  const databaseById = player.databaseById?.bind(player);
7885
- const equippedIds = new Set((player.equipments?.() || []).map((it) => it?.id?.() ?? it?.id ?? it?.name));
7892
+ const equippedIds = new Set((player.equipments?.() || []).map((it) => readField(it, "id", readField(it, "name"))));
7886
7893
  const buildStats = () => {
7887
7894
  const params = player.param || {};
7888
7895
  const statKeys = [
@@ -7895,29 +7902,29 @@ var MenuGui = class extends Gui {
7895
7902
  ];
7896
7903
  const stats = {};
7897
7904
  statKeys.forEach((key) => {
7898
- stats[key] = params[key] ?? 0;
7905
+ stats[key] = readReactiveValue(params[key]) ?? 0;
7899
7906
  });
7900
- stats.pdef = player.pdef ?? params.pdef ?? 0;
7901
- stats.sdef = player.sdef ?? params.sdef ?? 0;
7902
- stats.atk = player.atk ?? params.atk ?? 0;
7907
+ stats.pdef = readReactiveValue(player.pdef ?? params.pdef) ?? 0;
7908
+ stats.sdef = readReactiveValue(player.sdef ?? params.sdef) ?? 0;
7909
+ stats.atk = readReactiveValue(player.atk ?? params.atk) ?? 0;
7903
7910
  return stats;
7904
7911
  };
7905
7912
  const items = (player.items?.() || []).map((item) => {
7906
- const id = item.id();
7913
+ const id = readField(item, "id");
7907
7914
  const data = databaseById ? databaseById(id) : {};
7908
- const type = data?._type ?? "item";
7909
- const consumable = data?.consumable;
7915
+ const type = readField(data, "_type", "item");
7916
+ const consumable = readField(data, "consumable");
7910
7917
  const isConsumable = consumable !== void 0 ? consumable : type === "item";
7911
7918
  const usable = isConsumable === false ? false : consumable === void 0 && type !== "item" ? false : true;
7912
7919
  return {
7913
7920
  id,
7914
- name: item.name(),
7915
- description: item.description(),
7916
- quantity: item.quantity(),
7917
- icon: data?.icon ?? item?.icon,
7918
- atk: item.atk(),
7919
- pdef: item.pdef(),
7920
- sdef: item.sdef(),
7921
+ name: readField(item, "name"),
7922
+ description: readField(item, "description"),
7923
+ quantity: readField(item, "quantity"),
7924
+ icon: readField(data, "icon", readField(item, "icon")),
7925
+ atk: readField(item, "atk"),
7926
+ pdef: readField(item, "pdef"),
7927
+ sdef: readField(item, "sdef"),
7921
7928
  consumable: isConsumable,
7922
7929
  type,
7923
7930
  usable,
@@ -7929,14 +7936,14 @@ var MenuGui = class extends Gui {
7929
7936
  items,
7930
7937
  equips: items.filter((item) => item.type === "weapon" || item.type === "armor"),
7931
7938
  skills: (player.skills?.() || []).map((skill) => ({
7932
- id: skill?.id() ?? skill?.name(),
7933
- name: skill?.name() ?? skill?.id() ?? "Skill",
7934
- description: skill?.description() ?? "",
7935
- spCost: skill?.spCost() ?? 0
7939
+ id: readField(skill, "id", readField(skill, "name")),
7940
+ name: readField(skill, "name", readField(skill, "id", "Skill")),
7941
+ description: readField(skill, "description", ""),
7942
+ spCost: readField(skill, "spCost", 0)
7936
7943
  })),
7937
7944
  saveLoad: this.buildSaveLoad(options),
7938
7945
  playerStats: buildStats(),
7939
- expForNextlevel: player.expForNextlevel
7946
+ expForNextlevel: readReactiveValue(player.expForNextlevel)
7940
7947
  };
7941
7948
  }
7942
7949
  refreshMenu(clientActionId) {
@@ -8708,7 +8715,10 @@ function WithParameterManager(Base) {
8708
8715
  if (this.finalLevel && val > this.finalLevel) val = this.finalLevel;
8709
8716
  const currentClass = this._class && this._class();
8710
8717
  if (currentClass && "skillsToLearn" in currentClass && Array.isArray(currentClass.skillsToLearn)) {
8711
- for (let i = this._level(); i <= val; i++) for (let skill of currentClass.skillsToLearn) if (skill.level == i) this["learnSkill"](skill.skill);
8718
+ for (let i = this._level(); i <= val; i++) for (let skill of currentClass.skillsToLearn) if (skill.level == i) this["learnSkill"](skill.skill, {
8719
+ source: skill.source ?? "level",
8720
+ level: i
8721
+ });
8712
8722
  }
8713
8723
  const hasNewLevel = val - lastLevel;
8714
8724
  if (hasNewLevel > 0) this["execMethod"]("onLevelUp", [hasNewLevel]);
@@ -9763,7 +9773,7 @@ function WithSkillManager(Base) {
9763
9773
  * });
9764
9774
  * ```
9765
9775
  */
9766
- learnSkill(skillInput) {
9776
+ learnSkill(skillInput, options = {}) {
9767
9777
  const map = this._getSkillMap();
9768
9778
  const { skillId, skillData, skillInstance } = this._resolveSkillInput(skillInput, map);
9769
9779
  if (this._getLearnedSkillEntry(skillId)) throw SkillLog.alreadyLearned(skillData);
@@ -9771,6 +9781,13 @@ function WithSkillManager(Base) {
9771
9781
  this.skills().push(instance);
9772
9782
  const hookTarget = instance._skillInstance || instance;
9773
9783
  this["execMethod"]("onLearn", [this], hookTarget);
9784
+ this["execMethod"]("onSkillChange", [{
9785
+ action: "learn",
9786
+ skill: skillData,
9787
+ skillId,
9788
+ source: options.source ?? "manual",
9789
+ level: options.level
9790
+ }]);
9774
9791
  return skillData;
9775
9792
  }
9776
9793
  /**
@@ -9789,7 +9806,7 @@ function WithSkillManager(Base) {
9789
9806
  * player.forgetSkill(FireSkill);
9790
9807
  * ```
9791
9808
  */
9792
- forgetSkill(skillInput) {
9809
+ forgetSkill(skillInput, options = {}) {
9793
9810
  const index = this._getSkillIndex(skillInput);
9794
9811
  if (index === -1) {
9795
9812
  let skillData = skillInput;
@@ -9812,6 +9829,13 @@ function WithSkillManager(Base) {
9812
9829
  this.skills().splice(index, 1);
9813
9830
  const hookTarget = skillEntry?._skillInstance || skillEntry?._skillData || skillData;
9814
9831
  this["execMethod"]("onForget", [this], hookTarget);
9832
+ this["execMethod"]("onSkillChange", [{
9833
+ action: "forget",
9834
+ skill: skillData,
9835
+ skillId: skillData?.id ?? String(skillInput),
9836
+ source: options.source ?? "manual",
9837
+ level: options.level
9838
+ }]);
9815
9839
  return skillData;
9816
9840
  }
9817
9841
  /**
@@ -10730,7 +10754,9 @@ var RpgPlayer = class extends BasicPlayerMixins(RpgCommonPlayer) {
10730
10754
  animationName,
10731
10755
  graphic,
10732
10756
  nbTimes: finalNbTimes,
10733
- object: this.id
10757
+ object: this.id,
10758
+ restoreAnimationName: this.animationName(),
10759
+ restoreGraphics: [...this.graphics()]
10734
10760
  }
10735
10761
  });
10736
10762
  }
@@ -15746,6 +15772,12 @@ var RpgMap = class RpgMap extends RpgCommonMap {
15746
15772
  * ```
15747
15773
  */
15748
15774
  async onInput(player, input) {
15775
+ if (typeof player.canMove === "function" && !player.canMove()) {
15776
+ player.pendingInputs = [];
15777
+ player.lastProcessedInputTs = 0;
15778
+ this.stopMovement(player);
15779
+ return;
15780
+ }
15749
15781
  const lastAckedFrame = player._lastFramePositions?.frame ?? 0;
15750
15782
  const now = Date.now();
15751
15783
  const candidates = [];
@@ -16048,6 +16080,15 @@ var RpgMap = class RpgMap extends RpgCommonMap {
16048
16080
  inputs: []
16049
16081
  };
16050
16082
  }
16083
+ if (typeof player.canMove === "function" && !player.canMove()) {
16084
+ player.pendingInputs = [];
16085
+ player.lastProcessedInputTs = 0;
16086
+ this.stopMovement(player);
16087
+ return {
16088
+ player,
16089
+ inputs: []
16090
+ };
16091
+ }
16051
16092
  const processedInputs = [];
16052
16093
  const config = {
16053
16094
  maxTimeDelta: 1e3,
@@ -17216,7 +17257,11 @@ var LobbyRoom = class LobbyRoom extends BaseRoom {
17216
17257
  async guiInteraction(player, value) {
17217
17258
  if (value.data.id === "start") {
17218
17259
  player.initializeDefaultStats();
17219
- this.hooks.callHooks("server-player-onStart", player).subscribe();
17260
+ try {
17261
+ await lastValueFrom(this.hooks.callHooks("server-player-onStart", player));
17262
+ } catch (error) {
17263
+ console.error("[RPGJS] Error during player onStart hooks:", error);
17264
+ }
17220
17265
  }
17221
17266
  }
17222
17267
  };