@rpgjs/server 5.0.0-alpha.29 → 5.0.0-alpha.30

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 (51) hide show
  1. package/dist/Gui/DialogGui.d.ts +1 -0
  2. package/dist/Gui/GameoverGui.d.ts +23 -0
  3. package/dist/Gui/Gui.d.ts +6 -0
  4. package/dist/Gui/MenuGui.d.ts +22 -3
  5. package/dist/Gui/NotificationGui.d.ts +1 -2
  6. package/dist/Gui/SaveLoadGui.d.ts +13 -0
  7. package/dist/Gui/ShopGui.d.ts +24 -3
  8. package/dist/Gui/TitleGui.d.ts +23 -0
  9. package/dist/Gui/index.d.ts +9 -1
  10. package/dist/Player/GuiManager.d.ts +86 -3
  11. package/dist/Player/Player.d.ts +24 -2
  12. package/dist/RpgServer.d.ts +7 -0
  13. package/dist/RpgServerEngine.d.ts +2 -1
  14. package/dist/index.d.ts +4 -1
  15. package/dist/index.js +1621 -336
  16. package/dist/index.js.map +1 -1
  17. package/dist/presets/index.d.ts +0 -9
  18. package/dist/rooms/BaseRoom.d.ts +37 -0
  19. package/dist/rooms/lobby.d.ts +6 -1
  20. package/dist/rooms/map.d.ts +22 -1
  21. package/dist/services/save.d.ts +43 -0
  22. package/dist/storage/index.d.ts +1 -0
  23. package/dist/storage/localStorage.d.ts +23 -0
  24. package/package.json +10 -10
  25. package/src/Gui/DialogGui.ts +12 -2
  26. package/src/Gui/GameoverGui.ts +39 -0
  27. package/src/Gui/Gui.ts +23 -1
  28. package/src/Gui/MenuGui.ts +155 -6
  29. package/src/Gui/NotificationGui.ts +1 -2
  30. package/src/Gui/SaveLoadGui.ts +60 -0
  31. package/src/Gui/ShopGui.ts +145 -16
  32. package/src/Gui/TitleGui.ts +39 -0
  33. package/src/Gui/index.ts +13 -2
  34. package/src/Player/BattleManager.ts +1 -1
  35. package/src/Player/ClassManager.ts +57 -2
  36. package/src/Player/GuiManager.ts +125 -14
  37. package/src/Player/ItemManager.ts +160 -41
  38. package/src/Player/ParameterManager.ts +1 -1
  39. package/src/Player/Player.ts +87 -12
  40. package/src/Player/SkillManager.ts +145 -66
  41. package/src/Player/StateManager.ts +70 -1
  42. package/src/Player/VariableManager.ts +10 -7
  43. package/src/RpgServer.ts +8 -0
  44. package/src/index.ts +5 -2
  45. package/src/presets/index.ts +1 -10
  46. package/src/rooms/BaseRoom.ts +112 -0
  47. package/src/rooms/lobby.ts +13 -6
  48. package/src/rooms/map.ts +31 -4
  49. package/src/services/save.ts +147 -0
  50. package/src/storage/index.ts +1 -0
  51. package/src/storage/localStorage.ts +76 -0
package/dist/index.js CHANGED
@@ -4409,8 +4409,8 @@ var z = /*#__PURE__*/Object.freeze({
4409
4409
  ZodError: ZodError
4410
4410
  });
4411
4411
 
4412
- var __defProp$8 = Object.defineProperty;
4413
- var __name$3 = (target, value) => __defProp$8(target, "name", { value, configurable: true });
4412
+ var __defProp$a = Object.defineProperty;
4413
+ var __name$3 = (target, value) => __defProp$a(target, "name", { value, configurable: true });
4414
4414
 
4415
4415
  /******************************************************************************
4416
4416
  Copyright (c) Microsoft Corporation.
@@ -6204,8 +6204,8 @@ function throttleTime(duration, scheduler, config) {
6204
6204
  return throttle$1(function () { return duration$; }, config);
6205
6205
  }
6206
6206
 
6207
- var __defProp$7 = Object.defineProperty;
6208
- var __name$2 = (target, value) => __defProp$7(target, "name", { value, configurable: true });
6207
+ var __defProp$9 = Object.defineProperty;
6208
+ var __name$2 = (target, value) => __defProp$9(target, "name", { value, configurable: true });
6209
6209
  var ArraySubject = class extends BehaviorSubject {
6210
6210
  static {
6211
6211
  __name$2(this, "ArraySubject");
@@ -6810,6 +6810,70 @@ function createStatesSnapshot(instance) {
6810
6810
  return persistObject;
6811
6811
  }
6812
6812
  __name$3(createStatesSnapshot, "createStatesSnapshot");
6813
+ var SNAPSHOT_SKIP = Symbol("snapshot-skip");
6814
+ var serializeSnapshotDeep = /* @__PURE__ */ __name$3((value, path, options, seen) => {
6815
+ if (isSignal(value)) {
6816
+ return serializeSnapshotDeep(value(), path, options, seen);
6817
+ }
6818
+ if (value instanceof Map) {
6819
+ return SNAPSHOT_SKIP;
6820
+ }
6821
+ if (options.filter && !options.filter(value, path)) {
6822
+ return SNAPSHOT_SKIP;
6823
+ }
6824
+ if (value instanceof Date) {
6825
+ return options.dateToString ? options.dateToString(value) : value.toISOString();
6826
+ }
6827
+ if (value && typeof value === "object") {
6828
+ if (seen.has(value)) {
6829
+ return SNAPSHOT_SKIP;
6830
+ }
6831
+ seen.add(value);
6832
+ if (Array.isArray(value)) {
6833
+ const result2 = [];
6834
+ value.forEach((item, index) => {
6835
+ const itemPath = path ? `${path}.${index}` : String(index);
6836
+ const serialized = serializeSnapshotDeep(item, itemPath, options, seen);
6837
+ if (serialized !== SNAPSHOT_SKIP) {
6838
+ result2.push(serialized);
6839
+ }
6840
+ });
6841
+ return result2;
6842
+ }
6843
+ const result = {};
6844
+ const idKey = isInstanceOfClass(value) ? value.constructor?._propertyMetadata?.get("id") : void 0;
6845
+ const entries = Object.entries(value).filter(([key]) => isInstanceOfClass(value) ? key.startsWith("__") || (idKey ? key === idKey : false) : true);
6846
+ for (const [key, childValue] of entries) {
6847
+ const normalizedKey = key.startsWith("__") ? key.slice(2) : key;
6848
+ const childPath = path ? `${path}.${normalizedKey}` : normalizedKey;
6849
+ const serialized = serializeSnapshotDeep(childValue, childPath, options, seen);
6850
+ if (serialized !== SNAPSHOT_SKIP) {
6851
+ result[normalizedKey] = serialized;
6852
+ }
6853
+ }
6854
+ return result;
6855
+ }
6856
+ return value;
6857
+ }, "serializeSnapshotDeep");
6858
+ function createStatesSnapshotDeep(instance, options = {}) {
6859
+ const persistObject = {};
6860
+ if (instance?.$snapshot) {
6861
+ for (const key of instance.$snapshot.keys()) {
6862
+ const signal = instance.$snapshot.get(key);
6863
+ const persist2 = signal.options.persist ?? true;
6864
+ if (!persist2) {
6865
+ continue;
6866
+ }
6867
+ const value = signal();
6868
+ const serialized = serializeSnapshotDeep(value, key, options, /* @__PURE__ */ new WeakSet());
6869
+ if (serialized !== SNAPSHOT_SKIP) {
6870
+ persistObject[key] = serialized;
6871
+ }
6872
+ }
6873
+ }
6874
+ return persistObject;
6875
+ }
6876
+ __name$3(createStatesSnapshotDeep, "createStatesSnapshotDeep");
6813
6877
  function setMetadata(target, key, value) {
6814
6878
  const meta = target.constructor._propertyMetadata;
6815
6879
  const propId = meta?.get(key);
@@ -7135,8 +7199,8 @@ function getByPath(root, path) {
7135
7199
  }
7136
7200
  __name$3(getByPath, "getByPath");
7137
7201
 
7138
- var __defProp$6 = Object.defineProperty;
7139
- var __name$1 = (target, value) => __defProp$6(target, "name", { value, configurable: true });
7202
+ var __defProp$8 = Object.defineProperty;
7203
+ var __name$1 = (target, value) => __defProp$8(target, "name", { value, configurable: true });
7140
7204
 
7141
7205
  // src/decorators.ts
7142
7206
  function Action(name, bodyValidation) {
@@ -7815,7 +7879,7 @@ var Server = class {
7815
7879
  console.error("[sessionTransfer] `users` property not defined in the room.");
7816
7880
  return null;
7817
7881
  }
7818
- const userSnapshot = createStatesSnapshot(user);
7882
+ const userSnapshot = createStatesSnapshotDeep(user);
7819
7883
  const transferData = {
7820
7884
  privateId,
7821
7885
  userSnapshot,
@@ -8070,7 +8134,7 @@ var Server = class {
8070
8134
  } else {
8071
8135
  user = isClass(classType) ? new classType() : classType(conn, ctx);
8072
8136
  signal2()[publicId] = user;
8073
- const snapshot = createStatesSnapshot(user);
8137
+ const snapshot = createStatesSnapshotDeep(user);
8074
8138
  this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);
8075
8139
  }
8076
8140
  } else {
@@ -8471,9 +8535,17 @@ var Server = class {
8471
8535
  if (signal2 && usersPropName) {
8472
8536
  const { classType } = signal2.options;
8473
8537
  const user = isClass(classType) ? new classType() : classType();
8474
- load(user, userSnapshot, true);
8538
+ const hydratedSnapshot = await awaitReturn(subRoom["onSessionRestore"]?.({
8539
+ userSnapshot,
8540
+ user,
8541
+ publicId,
8542
+ privateId,
8543
+ sessionState,
8544
+ room: this.room
8545
+ })) ?? userSnapshot;
8546
+ load(user, hydratedSnapshot, true);
8475
8547
  signal2()[publicId] = user;
8476
- await this.room.storage.put(`${usersPropName}.${publicId}`, userSnapshot);
8548
+ await this.room.storage.put(`${usersPropName}.${publicId}`, hydratedSnapshot);
8477
8549
  }
8478
8550
  }
8479
8551
  const transferToken = generateShortUUID$1();
@@ -9808,13 +9880,13 @@ function createRequireSessionGuard(storage) {
9808
9880
  }
9809
9881
  __name$1(createRequireSessionGuard, "createRequireSessionGuard");
9810
9882
 
9811
- var __defProp$5 = Object.defineProperty;
9812
- var __decorateClass$4 = (decorators, target, key, kind) => {
9883
+ var __defProp$7 = Object.defineProperty;
9884
+ var __decorateClass$6 = (decorators, target, key, kind) => {
9813
9885
  var result = void 0 ;
9814
9886
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
9815
9887
  if (decorator = decorators[i])
9816
9888
  result = (decorator(target, key, result) ) || result;
9817
- if (result) __defProp$5(target, key, result);
9889
+ if (result) __defProp$7(target, key, result);
9818
9890
  return result;
9819
9891
  };
9820
9892
  class Item {
@@ -9823,39 +9895,67 @@ class Item {
9823
9895
  this.name = signal("");
9824
9896
  this.description = signal("");
9825
9897
  this.price = signal(0);
9898
+ this.atk = signal(0);
9899
+ this.pdef = signal(0);
9900
+ this.sdef = signal(0);
9826
9901
  this.quantity = signal(1);
9827
9902
  this.onAdd = () => {
9828
9903
  };
9829
9904
  this.description.set(data?.description ?? "");
9830
9905
  this.price.set(data?.price ?? 0);
9831
9906
  this.name.set(data?.name ?? "");
9907
+ this.atk.set(data?.atk ?? 0);
9908
+ this.pdef.set(data?.pdef ?? 0);
9909
+ this.sdef.set(data?.sdef ?? 0);
9832
9910
  this.onAdd = data?.onAdd?.bind(this) ?? (() => {
9833
9911
  });
9834
9912
  }
9835
9913
  }
9836
- __decorateClass$4([
9914
+ __decorateClass$6([
9837
9915
  id()
9838
9916
  ], Item.prototype, "id");
9839
- __decorateClass$4([
9840
- sync()
9841
- ], Item.prototype, "name");
9842
- __decorateClass$4([
9843
- sync()
9844
- ], Item.prototype, "description");
9845
- __decorateClass$4([
9846
- sync()
9847
- ], Item.prototype, "price");
9848
- __decorateClass$4([
9917
+ __decorateClass$6([
9849
9918
  sync()
9850
9919
  ], Item.prototype, "quantity");
9851
9920
 
9852
- var __defProp$4 = Object.defineProperty;
9853
- var __decorateClass$3 = (decorators, target, key, kind) => {
9921
+ var __defProp$6 = Object.defineProperty;
9922
+ var __decorateClass$5 = (decorators, target, key, kind) => {
9923
+ var result = void 0 ;
9924
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
9925
+ if (decorator = decorators[i])
9926
+ result = (decorator(target, key, result) ) || result;
9927
+ if (result) __defProp$6(target, key, result);
9928
+ return result;
9929
+ };
9930
+ class Skill {
9931
+ constructor(data) {
9932
+ this.id = signal("");
9933
+ this.name = signal("");
9934
+ this.description = signal("");
9935
+ this.spCost = signal(0);
9936
+ this.hitRate = signal(0);
9937
+ this.power = signal(0);
9938
+ this.coefficient = signal({});
9939
+ this.id.set(data?.id ?? "");
9940
+ this.name.set(data?.name ?? "");
9941
+ this.description.set(data?.description ?? "");
9942
+ this.spCost.set(data?.spCost ?? 0);
9943
+ this.hitRate.set(data?.hitRate ?? 0);
9944
+ this.power.set(data?.power ?? 0);
9945
+ this.coefficient.set(data?.coefficient ?? {});
9946
+ }
9947
+ }
9948
+ __decorateClass$5([
9949
+ id()
9950
+ ], Skill.prototype, "id");
9951
+
9952
+ var __defProp$5 = Object.defineProperty;
9953
+ var __decorateClass$4 = (decorators, target, key, kind) => {
9854
9954
  var result = void 0 ;
9855
9955
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
9856
9956
  if (decorator = decorators[i])
9857
9957
  result = (decorator(target, key, result) ) || result;
9858
- if (result) __defProp$4(target, key, result);
9958
+ if (result) __defProp$5(target, key, result);
9859
9959
  return result;
9860
9960
  };
9861
9961
  var Direction = /* @__PURE__ */ ((Direction2) => {
@@ -10026,109 +10126,111 @@ class RpgCommonPlayer {
10026
10126
  return this.direction();
10027
10127
  }
10028
10128
  }
10029
- __decorateClass$3([
10129
+ __decorateClass$4([
10030
10130
  id()
10031
10131
  ], RpgCommonPlayer.prototype, "id");
10032
- __decorateClass$3([
10132
+ __decorateClass$4([
10033
10133
  sync()
10034
10134
  ], RpgCommonPlayer.prototype, "name");
10035
- __decorateClass$3([
10135
+ __decorateClass$4([
10036
10136
  sync()
10037
10137
  ], RpgCommonPlayer.prototype, "type");
10038
- __decorateClass$3([
10138
+ __decorateClass$4([
10039
10139
  sync()
10040
10140
  ], RpgCommonPlayer.prototype, "x");
10041
- __decorateClass$3([
10141
+ __decorateClass$4([
10042
10142
  sync()
10043
10143
  ], RpgCommonPlayer.prototype, "y");
10044
- __decorateClass$3([
10144
+ __decorateClass$4([
10045
10145
  sync()
10046
10146
  ], RpgCommonPlayer.prototype, "z");
10047
- __decorateClass$3([
10147
+ __decorateClass$4([
10048
10148
  sync()
10049
10149
  ], RpgCommonPlayer.prototype, "tint");
10050
- __decorateClass$3([
10150
+ __decorateClass$4([
10051
10151
  sync()
10052
10152
  ], RpgCommonPlayer.prototype, "direction");
10053
- __decorateClass$3([
10153
+ __decorateClass$4([
10054
10154
  sync()
10055
10155
  ], RpgCommonPlayer.prototype, "speed");
10056
- __decorateClass$3([
10156
+ __decorateClass$4([
10057
10157
  sync()
10058
10158
  ], RpgCommonPlayer.prototype, "graphics");
10059
- __decorateClass$3([
10060
- sync()
10159
+ __decorateClass$4([
10160
+ sync({
10161
+ persist: false
10162
+ })
10061
10163
  ], RpgCommonPlayer.prototype, "canMove");
10062
- __decorateClass$3([
10164
+ __decorateClass$4([
10063
10165
  sync()
10064
10166
  ], RpgCommonPlayer.prototype, "hitbox");
10065
- __decorateClass$3([
10167
+ __decorateClass$4([
10066
10168
  sync()
10067
10169
  ], RpgCommonPlayer.prototype, "_gold");
10068
- __decorateClass$3([
10170
+ __decorateClass$4([
10069
10171
  sync()
10070
10172
  ], RpgCommonPlayer.prototype, "animationName");
10071
- __decorateClass$3([
10173
+ __decorateClass$4([
10072
10174
  sync()
10073
10175
  ], RpgCommonPlayer.prototype, "hpSignal");
10074
- __decorateClass$3([
10176
+ __decorateClass$4([
10075
10177
  sync()
10076
10178
  ], RpgCommonPlayer.prototype, "spSignal");
10077
- __decorateClass$3([
10179
+ __decorateClass$4([
10078
10180
  sync()
10079
10181
  ], RpgCommonPlayer.prototype, "_exp");
10080
- __decorateClass$3([
10182
+ __decorateClass$4([
10081
10183
  sync()
10082
10184
  ], RpgCommonPlayer.prototype, "_level");
10083
- __decorateClass$3([
10185
+ __decorateClass$4([
10084
10186
  sync()
10085
10187
  ], RpgCommonPlayer.prototype, "_class");
10086
- __decorateClass$3([
10188
+ __decorateClass$4([
10087
10189
  sync(Item)
10088
10190
  ], RpgCommonPlayer.prototype, "items");
10089
- __decorateClass$3([
10191
+ __decorateClass$4([
10090
10192
  sync()
10091
10193
  ], RpgCommonPlayer.prototype, "equipments");
10092
- __decorateClass$3([
10194
+ __decorateClass$4([
10093
10195
  sync()
10094
10196
  ], RpgCommonPlayer.prototype, "states");
10095
- __decorateClass$3([
10096
- sync()
10197
+ __decorateClass$4([
10198
+ sync(Skill)
10097
10199
  ], RpgCommonPlayer.prototype, "skills");
10098
- __decorateClass$3([
10200
+ __decorateClass$4([
10099
10201
  sync()
10100
10202
  ], RpgCommonPlayer.prototype, "_effects");
10101
- __decorateClass$3([
10203
+ __decorateClass$4([
10102
10204
  sync()
10103
10205
  ], RpgCommonPlayer.prototype, "_through");
10104
- __decorateClass$3([
10206
+ __decorateClass$4([
10105
10207
  sync()
10106
10208
  ], RpgCommonPlayer.prototype, "_throughOtherPlayer");
10107
- __decorateClass$3([
10209
+ __decorateClass$4([
10108
10210
  sync()
10109
10211
  ], RpgCommonPlayer.prototype, "_throughEvent");
10110
- __decorateClass$3([
10212
+ __decorateClass$4([
10111
10213
  sync()
10112
10214
  ], RpgCommonPlayer.prototype, "_frequency");
10113
- __decorateClass$3([
10215
+ __decorateClass$4([
10114
10216
  sync()
10115
10217
  ], RpgCommonPlayer.prototype, "_frames");
10116
- __decorateClass$3([
10218
+ __decorateClass$4([
10117
10219
  sync()
10118
10220
  ], RpgCommonPlayer.prototype, "componentsTop");
10119
- __decorateClass$3([
10221
+ __decorateClass$4([
10120
10222
  sync()
10121
10223
  ], RpgCommonPlayer.prototype, "componentsBottom");
10122
- __decorateClass$3([
10224
+ __decorateClass$4([
10123
10225
  sync()
10124
10226
  ], RpgCommonPlayer.prototype, "componentsCenter");
10125
- __decorateClass$3([
10227
+ __decorateClass$4([
10126
10228
  sync()
10127
10229
  ], RpgCommonPlayer.prototype, "componentsLeft");
10128
- __decorateClass$3([
10230
+ __decorateClass$4([
10129
10231
  sync()
10130
10232
  ], RpgCommonPlayer.prototype, "componentsRight");
10131
- __decorateClass$3([
10233
+ __decorateClass$4([
10132
10234
  connected()
10133
10235
  ], RpgCommonPlayer.prototype, "isConnected");
10134
10236
 
@@ -17668,6 +17770,7 @@ var PrebuiltGui = /* @__PURE__ */ ((PrebuiltGui2) => {
17668
17770
  PrebuiltGui2["Save"] = "rpg-save";
17669
17771
  PrebuiltGui2["Controls"] = "rpg-controls";
17670
17772
  PrebuiltGui2["Notification"] = "rpg-notification";
17773
+ PrebuiltGui2["TitleScreen"] = "rpg-title-screen";
17671
17774
  return PrebuiltGui2;
17672
17775
  })(PrebuiltGui || {});
17673
17776
 
@@ -17869,6 +17972,16 @@ class PerlinNoise2D {
17869
17972
  }
17870
17973
  }
17871
17974
 
17975
+ const MAXHP = "maxHp";
17976
+ const MAXSP = "maxSp";
17977
+ const ATK = "atk";
17978
+ const PDEF = "pdef";
17979
+ const SDEF = "sdef";
17980
+ const STR = "str";
17981
+ const AGI = "agi";
17982
+ const INT = "int";
17983
+ const DEX = "dex";
17984
+
17872
17985
  function WithComponentManager(Base) {
17873
17986
  return class extends Base {
17874
17987
  setGraphic(graphic) {
@@ -18149,8 +18262,8 @@ function WithComponentManager(Base) {
18149
18262
  };
18150
18263
  }
18151
18264
 
18152
- var __defProp$3 = Object.defineProperty;
18153
- var __name = (target, value) => __defProp$3(target, "name", { value, configurable: true });
18265
+ var __defProp$4 = Object.defineProperty;
18266
+ var __name = (target, value) => __defProp$4(target, "name", { value, configurable: true });
18154
18267
 
18155
18268
  // src/inject.ts
18156
18269
  var DEFAULT_INSTANCE_KEY = "__default__";
@@ -18520,6 +18633,7 @@ class Gui {
18520
18633
  this._close = () => {
18521
18634
  };
18522
18635
  this._blockPlayerInput = false;
18636
+ this._events = /* @__PURE__ */ new Map();
18523
18637
  }
18524
18638
  open(data, {
18525
18639
  waitingAction = false,
@@ -18541,6 +18655,17 @@ class Gui {
18541
18655
  }
18542
18656
  });
18543
18657
  }
18658
+ on(event, callback) {
18659
+ this._events.set(event, callback);
18660
+ }
18661
+ async emit(event, data) {
18662
+ const callback = this._events.get(event);
18663
+ if (callback) {
18664
+ return await callback(data);
18665
+ } else {
18666
+ return null;
18667
+ }
18668
+ }
18544
18669
  close(data) {
18545
18670
  this.player.emit("gui.exit", this.id);
18546
18671
  if (this._blockPlayerInput) {
@@ -18548,6 +18673,13 @@ class Gui {
18548
18673
  }
18549
18674
  this._close(data);
18550
18675
  }
18676
+ update(data, { clientActionId } = {}) {
18677
+ this.player.emit("gui.update", {
18678
+ guiId: this.id,
18679
+ data,
18680
+ clientActionId
18681
+ });
18682
+ }
18551
18683
  }
18552
18684
 
18553
18685
  class AdditiveKnockback {
@@ -19751,9 +19883,17 @@ class DialogGui extends Gui {
19751
19883
  if (!options.choices) options.choices = [];
19752
19884
  if (options.autoClose == void 0) options.autoClose = false;
19753
19885
  if (!options.position) options.position = "bottom" /* Bottom */;
19754
- if (options.fullWidth == void 0) options.fullWidth = true;
19886
+ if (options.fullWidth == void 0) options.fullWidth = false;
19755
19887
  if (options.typewriterEffect == void 0) options.typewriterEffect = true;
19756
19888
  const event = options.talkWith;
19889
+ const resolveName = (target) => {
19890
+ if (!target) return void 0;
19891
+ const rawName = target.name;
19892
+ if (typeof rawName === "function") return rawName();
19893
+ if (rawName && typeof rawName.get === "function") return rawName.get();
19894
+ return rawName;
19895
+ };
19896
+ const speaker = options.speaker ?? resolveName(event);
19757
19897
  let memoryDir;
19758
19898
  if (event) {
19759
19899
  memoryDir = event.direction();
@@ -19765,6 +19905,7 @@ class DialogGui extends Gui {
19765
19905
  position: options.position,
19766
19906
  fullWidth: options.fullWidth,
19767
19907
  typewriterEffect: options.typewriterEffect,
19908
+ speaker,
19768
19909
  // remove value property. It is not useful to know this on the client side.
19769
19910
  choices: options.choices.map((choice) => ({
19770
19911
  text: choice.text
@@ -19787,20 +19928,293 @@ class DialogGui extends Gui {
19787
19928
  }
19788
19929
  }
19789
19930
 
19790
- class MenuGui extends Gui {
19931
+ class SaveLoadGui extends Gui {
19791
19932
  constructor(player) {
19792
- super(PrebuiltGui.MainMenu, player);
19933
+ super(PrebuiltGui.Save, player);
19934
+ }
19935
+ open(slots = [], options = {}) {
19936
+ const mode = options.mode || "load";
19937
+ const maxSlots = options.maxSlots ?? slots.length;
19938
+ const normalizedSlots = Array.from({ length: maxSlots }, (_, index) => slots[index] ?? null);
19939
+ const uiSlots = normalizedSlots.map((slot) => {
19940
+ if (!slot) return null;
19941
+ const { snapshot, ...data } = slot;
19942
+ return data;
19943
+ });
19944
+ const onSelect = async ({ index }) => {
19945
+ if (typeof index !== "number") return;
19946
+ if (index < 0 || index >= normalizedSlots.length) return;
19947
+ const slot = normalizedSlots[index];
19948
+ if (mode === "load") {
19949
+ const result = await this.player.load(index, { reason: "load", source: "gui" }, { changeMap: true });
19950
+ if (!result.ok) return;
19951
+ this.close(index);
19952
+ return;
19953
+ }
19954
+ if (mode === "save") {
19955
+ const result = await this.player.save(index, {}, { reason: "manual", source: "gui" });
19956
+ if (!result) return;
19957
+ const updatedSlot = {
19958
+ ...slot || {},
19959
+ ...result.meta
19960
+ };
19961
+ normalizedSlots[index] = updatedSlot;
19962
+ slots[index] = updatedSlot;
19963
+ this.close(index);
19964
+ }
19965
+ };
19966
+ this.on("save", onSelect);
19967
+ this.on("load", onSelect);
19968
+ this.on("select", onSelect);
19969
+ return super.open({ slots: uiSlots, mode }, {
19970
+ waitingAction: true,
19971
+ blockPlayerInput: true
19972
+ });
19973
+ }
19974
+ }
19975
+
19976
+ let context$1 = null;
19977
+ function inject(service, _context) {
19978
+ const c = _context ?? context$1;
19979
+ if (!c) throw new Error("Context is not set. use setInject() to set the context");
19980
+ return inject$1(c, service);
19981
+ }
19982
+ function setInject(_context) {
19983
+ context$1 = _context;
19984
+ }
19985
+ function clearInject() {
19986
+ context$1 = null;
19987
+ }
19988
+
19989
+ const SaveStorageToken = "SaveStorageToken";
19990
+ class InMemorySaveStorageStrategy {
19991
+ constructor() {
19992
+ this.slotsByPlayer = /* @__PURE__ */ new Map();
19993
+ }
19994
+ async list(player) {
19995
+ return this.stripSnapshots(this.getSlots(player));
19996
+ }
19997
+ async get(player, index) {
19998
+ const slots = this.getSlots(player);
19999
+ const slot = slots[index];
20000
+ return slot ?? null;
20001
+ }
20002
+ async save(player, index, snapshot, meta) {
20003
+ const slots = this.getSlots(player);
20004
+ const existing = slots[index];
20005
+ slots[index] = {
20006
+ ...existing ?? {},
20007
+ ...meta,
20008
+ snapshot
20009
+ };
20010
+ }
20011
+ async delete(player, index) {
20012
+ const slots = this.getSlots(player);
20013
+ slots[index] = null;
20014
+ }
20015
+ getSlots(player) {
20016
+ const key = player.id ?? "unknown";
20017
+ if (!this.slotsByPlayer.has(key)) {
20018
+ this.slotsByPlayer.set(key, []);
20019
+ }
20020
+ return this.slotsByPlayer.get(key);
20021
+ }
20022
+ stripSnapshots(slots) {
20023
+ return slots.map((slot) => {
20024
+ if (!slot) return null;
20025
+ const { snapshot, ...meta } = slot;
20026
+ return meta;
20027
+ });
20028
+ }
20029
+ }
20030
+ let cachedSaveStorage = null;
20031
+ let cachedAutoSave = null;
20032
+ const AutoSaveToken = "AutoSaveToken";
20033
+ function resolveSaveStorageStrategy() {
20034
+ if (cachedSaveStorage) return cachedSaveStorage;
20035
+ try {
20036
+ cachedSaveStorage = inject(SaveStorageToken);
20037
+ } catch {
20038
+ cachedSaveStorage = new InMemorySaveStorageStrategy();
20039
+ }
20040
+ return cachedSaveStorage;
20041
+ }
20042
+ function resolveAutoSaveStrategy() {
20043
+ if (cachedAutoSave) return cachedAutoSave;
20044
+ try {
20045
+ cachedAutoSave = inject(AutoSaveToken);
20046
+ } catch {
20047
+ cachedAutoSave = null;
20048
+ }
20049
+ cachedAutoSave ||= {
20050
+ canSave: () => true,
20051
+ canLoad: () => true,
20052
+ shouldAutoSave: () => false,
20053
+ getDefaultSlot: () => 0
20054
+ };
20055
+ return cachedAutoSave;
20056
+ }
20057
+ function resolveSaveSlot(slot, policy, player, context) {
20058
+ if (typeof slot === "number") return slot;
20059
+ const resolver = policy.getDefaultSlot;
20060
+ if (!resolver) return null;
20061
+ return resolver(player, context);
20062
+ }
20063
+ function shouldAutoSave(player, context) {
20064
+ const strategy = resolveAutoSaveStrategy();
20065
+ if (!strategy.shouldAutoSave) return false;
20066
+ return strategy.shouldAutoSave(player, context);
20067
+ }
20068
+ function buildSaveSlotMeta(player, overrides = {}) {
20069
+ const mapId = player.getCurrentMap()?.id;
20070
+ const base = {
20071
+ level: typeof player.level === "number" ? player.level : void 0,
20072
+ exp: typeof player.exp === "number" ? player.exp : void 0,
20073
+ map: typeof mapId === "string" ? mapId : void 0,
20074
+ date: (/* @__PURE__ */ new Date()).toISOString()
20075
+ };
20076
+ return { ...base, ...overrides };
20077
+ }
20078
+ function provideSaveStorage(strategy) {
20079
+ return {
20080
+ provide: SaveStorageToken,
20081
+ useValue: strategy
20082
+ };
20083
+ }
20084
+ function provideAutoSave(strategy) {
20085
+ return {
20086
+ provide: AutoSaveToken,
20087
+ useValue: strategy
20088
+ };
20089
+ }
20090
+
20091
+ class MenuGui extends Gui {
20092
+ constructor(player2) {
20093
+ super(PrebuiltGui.MainMenu, player2);
20094
+ this.menuOptions = {};
20095
+ }
20096
+ buildSaveLoad(options) {
20097
+ const autoSave = resolveAutoSaveStrategy();
20098
+ const canSave = autoSave.canSave ? autoSave.canSave(this.player, { reason: "manual", source: "menu" }) : true;
20099
+ const autoSlotIndex = options.saveAutoSlotIndex ?? autoSave.getDefaultSlot?.(this.player, { reason: "auto", source: "menu" }) ?? 0;
20100
+ return {
20101
+ mode: "save",
20102
+ canSave,
20103
+ showAutoSlot: options.saveShowAutoSlot === true,
20104
+ autoSlotIndex,
20105
+ autoSlotLabel: options.saveAutoSlotLabel
20106
+ };
20107
+ }
20108
+ buildMenuData(options) {
20109
+ const disabledSet = new Set(options.disabled || []);
20110
+ const defaultMenus = [
20111
+ { id: "items", label: "Items" },
20112
+ { id: "skills", label: "Skills" },
20113
+ { id: "equip", label: "Equip" },
20114
+ { id: "options", label: "Options" },
20115
+ { id: "save", label: "Save" },
20116
+ { id: "exit", label: "Exit" }
20117
+ ];
20118
+ const menus = (options.menus && options.menus.length ? options.menus : defaultMenus).map((menu) => ({
20119
+ ...menu,
20120
+ disabled: menu.disabled || disabledSet.has(menu.id)
20121
+ }));
20122
+ const player2 = this.player;
20123
+ const databaseById = player2.databaseById?.bind(player2);
20124
+ const equippedIds = new Set(
20125
+ (player2.equipments?.() || []).map((it) => it?.id?.() ?? it?.id ?? it?.name)
20126
+ );
20127
+ const buildStats = () => {
20128
+ const params = player2.param || {};
20129
+ const statKeys = [
20130
+ "str",
20131
+ "dex",
20132
+ "int",
20133
+ "agi",
20134
+ "maxHp",
20135
+ "maxSp"
20136
+ ];
20137
+ const stats = {};
20138
+ statKeys.forEach((key) => {
20139
+ stats[key] = params[key] ?? 0;
20140
+ });
20141
+ stats.pdef = player2.pdef ?? params.pdef ?? 0;
20142
+ stats.sdef = player2.sdef ?? params.sdef ?? 0;
20143
+ stats.atk = player2.atk ?? params.atk ?? 0;
20144
+ return stats;
20145
+ };
20146
+ const items = (player2.items?.() || []).map((item) => {
20147
+ const id = item.id();
20148
+ const data = databaseById ? databaseById(id) : {};
20149
+ const type = data?._type ?? "item";
20150
+ const consumable = data?.consumable;
20151
+ const isConsumable = consumable !== void 0 ? consumable : type === "item";
20152
+ const usable = isConsumable === false ? false : consumable === void 0 && type !== "item" ? false : true;
20153
+ return {
20154
+ id,
20155
+ name: item.name(),
20156
+ description: item.description(),
20157
+ quantity: item.quantity(),
20158
+ icon: data?.icon ?? item?.icon,
20159
+ atk: item.atk(),
20160
+ pdef: item.pdef(),
20161
+ sdef: item.sdef(),
20162
+ consumable: isConsumable,
20163
+ type,
20164
+ usable,
20165
+ equipped: equippedIds.has(id)
20166
+ };
20167
+ });
20168
+ const menuEquips = items.filter((item) => item.type === "weapon" || item.type === "armor");
20169
+ const skills = (player2.skills?.() || []).map((skill) => ({
20170
+ id: skill?.id() ?? skill?.name(),
20171
+ name: skill?.name() ?? skill?.id() ?? "Skill",
20172
+ description: skill?.description() ?? "",
20173
+ spCost: skill?.spCost() ?? 0
20174
+ }));
20175
+ const saveLoad = this.buildSaveLoad(options);
20176
+ return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats() };
19793
20177
  }
19794
- open() {
19795
- this.on("useItem", (id) => {
20178
+ refreshMenu(clientActionId) {
20179
+ const data = this.buildMenuData(this.menuOptions);
20180
+ this.update(data, { clientActionId });
20181
+ }
20182
+ open(options = {}) {
20183
+ this.menuOptions = options;
20184
+ const data = this.buildMenuData(options);
20185
+ this.on("useItem", ({ id, clientActionId }) => {
19796
20186
  try {
19797
20187
  this.player.useItem(id);
19798
20188
  this.player.syncChanges();
19799
20189
  } catch (err) {
19800
20190
  this.player.showNotification(err.msg);
20191
+ } finally {
20192
+ this.refreshMenu(clientActionId);
20193
+ }
20194
+ });
20195
+ this.on("equipItem", ({ id, equip, clientActionId }) => {
20196
+ try {
20197
+ this.player.equip(id, equip);
20198
+ this.player.syncChanges();
20199
+ } catch (err) {
20200
+ this.player.showNotification(err.msg);
20201
+ } finally {
20202
+ this.refreshMenu(clientActionId);
19801
20203
  }
19802
20204
  });
19803
- return super.open("", {
20205
+ this.on("openSave", async () => {
20206
+ this.close();
20207
+ const gui = new SaveLoadGui(this.player);
20208
+ player._gui[gui.id] = gui;
20209
+ await gui.open(options.saveSlots || [], {
20210
+ mode: "save",
20211
+ maxSlots: options.saveMaxSlots
20212
+ });
20213
+ });
20214
+ this.on("exit", () => {
20215
+ this.close("exit");
20216
+ });
20217
+ return super.open(data, {
19804
20218
  waitingAction: true,
19805
20219
  blockPlayerInput: true
19806
20220
  });
@@ -19810,33 +20224,127 @@ class MenuGui extends Gui {
19810
20224
  class ShopGui extends Gui {
19811
20225
  constructor(player) {
19812
20226
  super(PrebuiltGui.Shop, player);
19813
- }
19814
- open(items) {
19815
- items = items.map((item) => {
19816
- const it = new item();
20227
+ this.itemsInput = [];
20228
+ this.sellMultipliers = {};
20229
+ this.baseSellMultiplier = 0.5;
20230
+ }
20231
+ normalizeSellMultipliers(sell) {
20232
+ if (!sell) return {};
20233
+ if (Array.isArray(sell)) {
20234
+ return sell.reduce((acc, entry) => {
20235
+ if (entry && entry.id) acc[entry.id] = entry.multiplier ?? 0;
20236
+ return acc;
20237
+ }, {});
20238
+ }
20239
+ return { ...sell };
20240
+ }
20241
+ buildShopData() {
20242
+ const player = this.player;
20243
+ const databaseById = player.databaseById?.bind(player);
20244
+ const equippedIds = new Set(
20245
+ (player.equipments?.() || []).map((it) => it?.id?.() ?? it?.id ?? it?.name)
20246
+ );
20247
+ const playerParams = {
20248
+ ...player.param || {},
20249
+ atk: player.atk ?? 0,
20250
+ def: player.pdef ?? 0,
20251
+ pdef: player.pdef ?? 0,
20252
+ sdef: player.sdef ?? 0
20253
+ };
20254
+ const getStatValue = (data, key, fallbackKeys = []) => {
20255
+ if (data && typeof data[key] === "number") return data[key];
20256
+ for (const fallbackKey of fallbackKeys) {
20257
+ if (data && typeof data[fallbackKey] === "number") return data[fallbackKey];
20258
+ }
20259
+ const modifier = data?.paramsModifier?.[key];
20260
+ if (modifier && typeof modifier.value === "number") return modifier.value;
20261
+ for (const fallbackKey of fallbackKeys) {
20262
+ const fallbackModifier = data?.paramsModifier?.[fallbackKey];
20263
+ if (fallbackModifier && typeof fallbackModifier.value === "number") return fallbackModifier.value;
20264
+ }
20265
+ return void 0;
20266
+ };
20267
+ const buildItemData = (item, overrides = {}) => {
20268
+ const rawId = typeof item === "string" ? item : typeof item?.id === "function" ? item.id() : item?.id ?? item?.name;
20269
+ const data = databaseById(rawId);
20270
+ const itemName = typeof item?.name === "function" ? item.name() : item?.name;
20271
+ const itemDescription = typeof item?.description === "function" ? item.description() : item?.description;
20272
+ const itemPrice = typeof item?.price === "function" ? item.price() : item?.price;
20273
+ const itemIcon = typeof item?.icon === "function" ? item.icon() : item?.icon;
20274
+ const atk = getStatValue(data, "atk");
20275
+ const def = getStatValue(data, "def", ["pdef", "sdef"]);
20276
+ const intValue = getStatValue(data, "int");
20277
+ const agi = getStatValue(data, "agi");
20278
+ const stats = {
20279
+ ...atk !== void 0 ? { atk } : {},
20280
+ ...def !== void 0 ? { def } : {},
20281
+ ...intValue !== void 0 ? { int: intValue } : {},
20282
+ ...agi !== void 0 ? { agi } : {}
20283
+ };
19817
20284
  return {
19818
- price: it.price,
19819
- name: it.name,
19820
- description: it.description,
19821
- id: it.id,
19822
- type: item.type
20285
+ price: overrides.price ?? data?.price ?? itemPrice ?? 0,
20286
+ name: data?.name ?? itemName ?? rawId,
20287
+ description: data?.description ?? itemDescription ?? "",
20288
+ icon: data?.icon ?? itemIcon,
20289
+ id: rawId,
20290
+ type: data?._type ?? item.type ?? item?._type,
20291
+ stats: Object.keys(stats).length ? stats : void 0,
20292
+ equipped: rawId ? equippedIds.has(rawId) : false,
20293
+ ...overrides.quantity !== void 0 ? { quantity: overrides.quantity } : {}
19823
20294
  };
19824
- });
19825
- this.on("buyItem", ({ id, nb }) => {
20295
+ };
20296
+ const items = this.itemsInput.map((item) => buildItemData(item));
20297
+ const sellItems = (player.items?.() || []).map((item) => {
20298
+ const id = item?.id?.();
20299
+ if (!id) return null;
20300
+ const multiplier = Object.prototype.hasOwnProperty.call(this.sellMultipliers, id) ? this.sellMultipliers[id] : this.baseSellMultiplier;
20301
+ const basePrice = databaseById(id)?.price ?? (typeof item?.price === "function" ? item.price() : item?.price) ?? 0;
20302
+ const price = basePrice * multiplier;
20303
+ const quantity = item?.quantity?.();
20304
+ return buildItemData(item, { price, quantity });
20305
+ }).filter(Boolean);
20306
+ return { items, sellItems, playerParams, message: this.messageInput, face: this.faceInput };
20307
+ }
20308
+ refreshShop(clientActionId) {
20309
+ this.update(this.buildShopData(), { clientActionId });
20310
+ }
20311
+ open(itemsOrOptions) {
20312
+ const options = Array.isArray(itemsOrOptions) ? { items: itemsOrOptions } : itemsOrOptions || { items: [] };
20313
+ this.itemsInput = options.items || [];
20314
+ this.baseSellMultiplier = typeof options.sellMultiplier === "number" ? options.sellMultiplier : 0.5;
20315
+ this.sellMultipliers = this.normalizeSellMultipliers(options.sell);
20316
+ this.messageInput = options.message;
20317
+ this.faceInput = options.face;
20318
+ this.on("buyItem", ({ id, nb, clientActionId }) => {
19826
20319
  try {
19827
20320
  this.player.buyItem(id, nb);
20321
+ this.player.syncChanges();
19828
20322
  } catch (err) {
19829
20323
  console.log(err);
20324
+ } finally {
20325
+ this.refreshShop(clientActionId);
19830
20326
  }
19831
20327
  });
19832
- this.on("sellItem", ({ id, nb }) => {
20328
+ this.on("sellItem", ({ id, nb, clientActionId }) => {
19833
20329
  try {
19834
- this.player.sellItem(id, nb);
20330
+ const multiplier = Object.prototype.hasOwnProperty.call(this.sellMultipliers, id) ? this.sellMultipliers[id] : this.baseSellMultiplier;
20331
+ const basePrice = this.player.databaseById?.(id)?.price ?? (typeof inventory?.price === "function" ? inventory.price() : inventory?.price) ?? 0;
20332
+ const price = basePrice * multiplier;
20333
+ if (!basePrice || price <= 0) return;
20334
+ const inventory = this.player.getItem?.(id);
20335
+ if (!inventory) return;
20336
+ const quantity = inventory.quantity();
20337
+ if (quantity - nb < 0) return;
20338
+ this.player._gold.update((gold) => gold + price * nb);
20339
+ this.player.removeItem(id, nb);
20340
+ this.player.syncChanges();
19835
20341
  } catch (err) {
19836
20342
  console.log(err);
20343
+ } finally {
20344
+ this.refreshShop(clientActionId);
19837
20345
  }
19838
20346
  });
19839
- return super.open({ items }, {
20347
+ return super.open(this.buildShopData(), {
19840
20348
  waitingAction: true,
19841
20349
  blockPlayerInput: true
19842
20350
  });
@@ -19849,18 +20357,48 @@ class NotificationGui extends Gui {
19849
20357
  }
19850
20358
  }
19851
20359
 
19852
- function WithGuiManager(Base) {
19853
- class GuiManagerMixin extends Base {
19854
- constructor() {
19855
- super(...arguments);
19856
- this._gui = {};
19857
- }
19858
- showText(msg, options = {}) {
19859
- const gui = new DialogGui(this);
19860
- this._gui[gui.id] = gui;
19861
- return gui.openDialog(msg, options);
19862
- }
19863
- showChoices(msg, choices, options) {
20360
+ class TitleGui extends Gui {
20361
+ constructor(player) {
20362
+ super(PrebuiltGui.TitleScreen, player);
20363
+ }
20364
+ open(options = {}) {
20365
+ this.on("select", (selection) => {
20366
+ this.close(selection);
20367
+ });
20368
+ return super.open(options, {
20369
+ waitingAction: true,
20370
+ blockPlayerInput: true
20371
+ });
20372
+ }
20373
+ }
20374
+
20375
+ class GameoverGui extends Gui {
20376
+ constructor(player) {
20377
+ super(PrebuiltGui.Gameover, player);
20378
+ }
20379
+ open(options = {}) {
20380
+ this.on("select", (selection) => {
20381
+ this.close(selection);
20382
+ });
20383
+ return super.open(options, {
20384
+ waitingAction: true,
20385
+ blockPlayerInput: true
20386
+ });
20387
+ }
20388
+ }
20389
+
20390
+ function WithGuiManager(Base) {
20391
+ class GuiManagerMixin extends Base {
20392
+ constructor() {
20393
+ super(...arguments);
20394
+ this._gui = {};
20395
+ }
20396
+ showText(msg, options = {}) {
20397
+ const gui = new DialogGui(this);
20398
+ this._gui[gui.id] = gui;
20399
+ return gui.openDialog(msg, options);
20400
+ }
20401
+ showChoices(msg, choices, options) {
19864
20402
  return this.showText(msg, {
19865
20403
  choices,
19866
20404
  ...options
@@ -19870,18 +20408,35 @@ function WithGuiManager(Base) {
19870
20408
  });
19871
20409
  }
19872
20410
  showNotification(message, options = {}) {
19873
- const gui = new NotificationGui(this);
19874
- this._gui[gui.id] = gui;
19875
- const data = {
20411
+ this.emit("notification", {
19876
20412
  message,
19877
20413
  ...options
19878
- };
19879
- return gui.open(data);
20414
+ });
20415
+ return Promise.resolve(true);
19880
20416
  }
19881
- callMainMenu() {
20417
+ callMainMenu(options = {}) {
19882
20418
  const gui = new MenuGui(this);
19883
20419
  this._gui[gui.id] = gui;
19884
- return gui.open();
20420
+ return gui.open(options);
20421
+ }
20422
+ callGameover(options = {}) {
20423
+ const gui = new GameoverGui(this);
20424
+ this._gui[gui.id] = gui;
20425
+ return gui.open(options);
20426
+ }
20427
+ showSaveLoad(slots = [], options = {}) {
20428
+ const gui = new SaveLoadGui(this);
20429
+ this._gui[gui.id] = gui;
20430
+ return gui.open(slots, options).then((index) => {
20431
+ if (typeof index !== "number") return null;
20432
+ return index;
20433
+ });
20434
+ }
20435
+ showSave(slots = [], options = {}) {
20436
+ return this.showSaveLoad(slots, { ...options, mode: "save" });
20437
+ }
20438
+ showLoad(slots = [], options = {}) {
20439
+ return this.showSaveLoad(slots, { ...options, mode: "load" });
19885
20440
  }
19886
20441
  /**
19887
20442
  * Calls shop menu. Opens the GUI named `rpg-shop`
@@ -19936,6 +20491,9 @@ function WithGuiManager(Base) {
19936
20491
  this._gui[guiId] = gui;
19937
20492
  return gui;
19938
20493
  }
20494
+ getGui(guiId) {
20495
+ return this._gui[guiId];
20496
+ }
19939
20497
  /**
19940
20498
  * Closes the GUI and removes it from memory
19941
20499
  *
@@ -20032,91 +20590,30 @@ function WithVariableManager(Base) {
20032
20590
  return class extends Base {
20033
20591
  constructor() {
20034
20592
  super(...arguments);
20035
- this.variables = /* @__PURE__ */ new Map();
20593
+ this.variables = type(signal({}), "variables", { persist: true }, this);
20036
20594
  }
20037
20595
  setVariable(key, val) {
20038
- this.variables.set(key, val);
20596
+ this.variables()[key] = val;
20039
20597
  }
20040
20598
  getVariable(key) {
20041
- return this.variables.get(key);
20599
+ return this.variables()[key];
20042
20600
  }
20043
20601
  removeVariable(key) {
20044
- return this.variables.delete(key);
20602
+ delete this.variables()[key];
20603
+ return true;
20045
20604
  }
20046
20605
  hasVariable(key) {
20047
- return this.variables.has(key);
20606
+ return key in this.variables();
20048
20607
  }
20049
20608
  getVariableKeys() {
20050
- return Array.from(this.variables.keys());
20609
+ return Object.keys(this.variables());
20051
20610
  }
20052
20611
  clearVariables() {
20053
- this.variables.clear();
20612
+ this.variables.set({});
20054
20613
  }
20055
20614
  };
20056
20615
  }
20057
20616
 
20058
- const MAXHP = "maxHp";
20059
- const MAXSP = "maxSp";
20060
- const ATK = "atk";
20061
- const PDEF = "pdef";
20062
- const SDEF = "sdef";
20063
- const STR = "str";
20064
- const AGI = "agi";
20065
- const INT = "int";
20066
- const DEX = "dex";
20067
- const MAXHP_CURVE = {
20068
- start: 741,
20069
- end: 7467
20070
- };
20071
- const MAXSP_CURVE = {
20072
- start: 534,
20073
- end: 5500
20074
- };
20075
- const STR_CURVE = {
20076
- start: 67,
20077
- end: 635
20078
- };
20079
- const AGI_CURVE = {
20080
- start: 58,
20081
- end: 582
20082
- };
20083
- const INT_CURVE = {
20084
- start: 36,
20085
- end: 7318
20086
- };
20087
- const DEX_CURVE = {
20088
- start: 54,
20089
- end: 564
20090
- };
20091
- const DAMAGE_CRITICAL = function(damage, a, b) {
20092
- if (random(0, 100) < 4 * a[DEX] / b[AGI]) {
20093
- damage *= 2;
20094
- }
20095
- return damage;
20096
- };
20097
- const DAMAGE_PHYSIC = function(a, b) {
20098
- let damage = Math.round((a[ATK] - b[PDEF] / 2) * ((20 + a[STR]) / 20));
20099
- if (damage < 0) damage = 0;
20100
- return damage;
20101
- };
20102
- const DAMAGE_GUARD = function(damage) {
20103
- return damage / 2;
20104
- };
20105
- const COEFFICIENT_ELEMENTS = function(a, b, bDef) {
20106
- return (a.rate + 1) * (b.rate + 1) / (bDef.rate == 0 ? bDef.rate * 4 : 1);
20107
- };
20108
- const DAMAGE_SKILL = function(a, b, skill) {
20109
- let power = skill.power + a[ATK] * (skill.coefficient[ATK] || 0);
20110
- if (power > 0) {
20111
- power -= b[PDEF] * (skill.coefficient[PDEF] || 0) / 2;
20112
- power -= b[SDEF] * (skill.coefficient[SDEF] || 0) / 2;
20113
- power = Math.max(power, 0);
20114
- }
20115
- let rate = 20;
20116
- [STR, DEX, AGI, INT].forEach((val) => rate += a[val] * (skill.coefficient[val] || 0));
20117
- return Math.round(power * rate / 20);
20118
- };
20119
-
20120
20617
  function WithParameterManager(Base) {
20121
20618
  return class extends Base {
20122
20619
  constructor() {
@@ -20678,17 +21175,20 @@ function WithItemManager(Base) {
20678
21175
  return isInstanceOf(it, itemClass);
20679
21176
  });
20680
21177
  }
20681
- addItem(item, nb = 1) {
20682
- const map = this.getCurrentMap() || this.map;
20683
- if (!map || !map.database) {
21178
+ _getItemMap(required = true) {
21179
+ const map = this.getCurrentMap?.() || this.map;
21180
+ if (required && (!map || !map.database)) {
20684
21181
  throw new Error("Player must be on a map to add items");
20685
21182
  }
21183
+ return map;
21184
+ }
21185
+ _resolveItemInput(item, map, databaseByIdOverride) {
20686
21186
  let itemId;
20687
21187
  let data;
20688
21188
  let itemInstance = null;
20689
21189
  if (isString(item)) {
20690
21190
  itemId = item;
20691
- data = this.databaseById(itemId);
21191
+ data = databaseByIdOverride ? databaseByIdOverride(itemId) : this.databaseById(itemId);
20692
21192
  } else if (typeof item === "function" || item.prototype) {
20693
21193
  itemId = item.name;
20694
21194
  const existingData = map.database()[itemId];
@@ -20712,6 +21212,114 @@ function WithItemManager(Base) {
20712
21212
  }
20713
21213
  itemInstance = itemObj;
20714
21214
  }
21215
+ return { itemId, data, itemInstance };
21216
+ }
21217
+ _createItemInstance(itemId, data, nb, itemInstance) {
21218
+ const instance = new Item(data);
21219
+ instance.id.set(itemId);
21220
+ instance.quantity.set(nb);
21221
+ if (itemInstance) {
21222
+ instance._itemInstance = itemInstance;
21223
+ if (itemInstance.onAdd) {
21224
+ instance.onAdd = itemInstance.onAdd.bind(itemInstance);
21225
+ }
21226
+ }
21227
+ return instance;
21228
+ }
21229
+ /**
21230
+ * Create an item instance without inventory changes or hook execution.
21231
+ */
21232
+ createItemInstance(item, nb = 1) {
21233
+ const map = this._getItemMap();
21234
+ const { itemId, data, itemInstance } = this._resolveItemInput(item, map);
21235
+ const instance = this._createItemInstance(itemId, data, nb, itemInstance);
21236
+ return { itemId, data, itemInstance, instance };
21237
+ }
21238
+ /**
21239
+ * Resolve item snapshot entries into Item instances without side effects.
21240
+ */
21241
+ resolveItemsSnapshot(snapshot, mapOverride) {
21242
+ if (!snapshot || !Array.isArray(snapshot.items)) {
21243
+ return snapshot;
21244
+ }
21245
+ const map = mapOverride ?? this._getItemMap(false);
21246
+ if (!map || !map.database) {
21247
+ return snapshot;
21248
+ }
21249
+ const databaseByIdOverride = (id) => {
21250
+ const data = map.database()[id];
21251
+ if (!data) {
21252
+ throw new Error(
21253
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
21254
+ );
21255
+ }
21256
+ return data;
21257
+ };
21258
+ const items = snapshot.items.map((entry) => {
21259
+ const itemId = isString(entry) ? entry : entry?.id;
21260
+ if (!itemId) {
21261
+ return entry;
21262
+ }
21263
+ const nb = !isString(entry) && typeof entry?.nb === "number" ? entry.nb : !isString(entry) && typeof entry?.quantity === "number" ? entry.quantity : 1;
21264
+ const { data, itemInstance } = this._resolveItemInput(
21265
+ itemId,
21266
+ map,
21267
+ databaseByIdOverride
21268
+ );
21269
+ return this._createItemInstance(itemId, data, nb, itemInstance);
21270
+ });
21271
+ return { ...snapshot, items };
21272
+ }
21273
+ /**
21274
+ * Resolve equipment snapshot entries into Item instances without side effects.
21275
+ */
21276
+ resolveEquipmentsSnapshot(snapshot, mapOverride) {
21277
+ if (!snapshot || !Array.isArray(snapshot.equipments)) {
21278
+ return snapshot;
21279
+ }
21280
+ const map = mapOverride ?? this._getItemMap(false);
21281
+ if (!map || !map.database) {
21282
+ return snapshot;
21283
+ }
21284
+ const databaseByIdOverride = (id) => {
21285
+ const data = map.database()[id];
21286
+ if (!data) {
21287
+ throw new Error(
21288
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
21289
+ );
21290
+ }
21291
+ return data;
21292
+ };
21293
+ const resolvedItems = Array.isArray(snapshot.items) ? snapshot.items : [];
21294
+ const getItemId = (entry) => {
21295
+ if (isString(entry)) return entry;
21296
+ if (typeof entry?.id === "function") return entry.id();
21297
+ return entry?.id;
21298
+ };
21299
+ const equipments = snapshot.equipments.map((entry) => {
21300
+ const itemId = getItemId(entry);
21301
+ if (!itemId) {
21302
+ return entry;
21303
+ }
21304
+ const existing = resolvedItems.find((item) => {
21305
+ const existingId = getItemId(item);
21306
+ return existingId === itemId;
21307
+ });
21308
+ if (existing) {
21309
+ return existing;
21310
+ }
21311
+ const { data, itemInstance } = this._resolveItemInput(
21312
+ itemId,
21313
+ map,
21314
+ databaseByIdOverride
21315
+ );
21316
+ return this._createItemInstance(itemId, data, 1, itemInstance);
21317
+ });
21318
+ return { ...snapshot, equipments };
21319
+ }
21320
+ addItem(item, nb = 1) {
21321
+ const map = this._getItemMap();
21322
+ const { itemId, data, itemInstance } = this._resolveItemInput(item, map);
20715
21323
  const existingItem = this.items().find((it) => it.id() == itemId);
20716
21324
  let instance;
20717
21325
  if (existingItem) {
@@ -20733,15 +21341,7 @@ function WithItemManager(Base) {
20733
21341
  }
20734
21342
  }
20735
21343
  } else {
20736
- instance = new Item(data);
20737
- instance.id.set(itemId);
20738
- instance.quantity.set(nb);
20739
- if (itemInstance) {
20740
- instance._itemInstance = itemInstance;
20741
- if (itemInstance.onAdd) {
20742
- instance.onAdd = itemInstance.onAdd.bind(itemInstance);
20743
- }
20744
- }
21344
+ instance = this._createItemInstance(itemId, data, nb, itemInstance);
20745
21345
  this.items().push(instance);
20746
21346
  }
20747
21347
  const hookTarget = instance._itemInstance || instance;
@@ -20936,6 +21536,59 @@ function WithEffectManager(Base) {
20936
21536
  };
20937
21537
  }
20938
21538
 
21539
+ const MAXHP_CURVE = {
21540
+ start: 741,
21541
+ end: 7467
21542
+ };
21543
+ const MAXSP_CURVE = {
21544
+ start: 534,
21545
+ end: 5500
21546
+ };
21547
+ const STR_CURVE = {
21548
+ start: 67,
21549
+ end: 635
21550
+ };
21551
+ const AGI_CURVE = {
21552
+ start: 58,
21553
+ end: 582
21554
+ };
21555
+ const INT_CURVE = {
21556
+ start: 36,
21557
+ end: 7318
21558
+ };
21559
+ const DEX_CURVE = {
21560
+ start: 54,
21561
+ end: 564
21562
+ };
21563
+ const DAMAGE_CRITICAL = function(damage, a, b) {
21564
+ if (random(0, 100) < 4 * a[DEX] / b[AGI]) {
21565
+ damage *= 2;
21566
+ }
21567
+ return damage;
21568
+ };
21569
+ const DAMAGE_PHYSIC = function(a, b) {
21570
+ let damage = Math.round((a[ATK] - b[PDEF] / 2) * ((20 + a[STR]) / 20));
21571
+ if (damage < 0) damage = 0;
21572
+ return damage;
21573
+ };
21574
+ const DAMAGE_GUARD = function(damage) {
21575
+ return damage / 2;
21576
+ };
21577
+ const COEFFICIENT_ELEMENTS = function(a, b, bDef) {
21578
+ return (a.rate + 1) * (b.rate + 1) / (bDef.rate == 0 ? bDef.rate * 4 : 1);
21579
+ };
21580
+ const DAMAGE_SKILL = function(a, b, skill) {
21581
+ let power = skill.power + a[ATK] * (skill.coefficient[ATK] || 0);
21582
+ if (power > 0) {
21583
+ power -= b[PDEF] * (skill.coefficient[PDEF] || 0) / 2;
21584
+ power -= b[SDEF] * (skill.coefficient[SDEF] || 0) / 2;
21585
+ power = Math.max(power, 0);
21586
+ }
21587
+ let rate = 20;
21588
+ [STR, DEX, AGI, INT].forEach((val) => rate += a[val] * (skill.coefficient[val] || 0));
21589
+ return Math.round(power * rate / 20);
21590
+ };
21591
+
20939
21592
  function WithElementManager(Base) {
20940
21593
  return class extends Base {
20941
21594
  constructor() {
@@ -20996,6 +21649,105 @@ function WithElementManager(Base) {
20996
21649
 
20997
21650
  function WithSkillManager(Base) {
20998
21651
  return class extends Base {
21652
+ _getSkillMap(required = true) {
21653
+ const map = this.getCurrentMap?.() || this.map;
21654
+ if (required && (!map || !map.database)) {
21655
+ throw new Error("Player must be on a map to learn skills");
21656
+ }
21657
+ return map;
21658
+ }
21659
+ _resolveSkillInput(skillInput, map, databaseByIdOverride) {
21660
+ let skillId = "";
21661
+ let skillData;
21662
+ let skillInstance = null;
21663
+ if (isString(skillInput)) {
21664
+ skillId = skillInput;
21665
+ skillData = databaseByIdOverride ? databaseByIdOverride(skillId) : this.databaseById(skillId);
21666
+ } else if (typeof skillInput === "function") {
21667
+ const SkillClassCtor = skillInput;
21668
+ skillId = SkillClassCtor.id || SkillClassCtor.name;
21669
+ const existingData = map?.database()?.[skillId];
21670
+ if (existingData) {
21671
+ skillData = existingData;
21672
+ } else if (map) {
21673
+ map.addInDatabase(skillId, SkillClassCtor);
21674
+ skillData = SkillClassCtor;
21675
+ } else {
21676
+ skillData = SkillClassCtor;
21677
+ }
21678
+ skillInstance = new SkillClassCtor();
21679
+ skillData = { ...skillData, ...skillInstance, id: skillId };
21680
+ } else {
21681
+ const skillObj = skillInput;
21682
+ skillId = skillObj.id || `skill-${Date.now()}`;
21683
+ skillObj.id = skillId;
21684
+ const existingData = map?.database()?.[skillId];
21685
+ if (existingData) {
21686
+ skillData = { ...existingData, ...skillObj };
21687
+ if (map) {
21688
+ map.addInDatabase(skillId, skillData, { force: true });
21689
+ }
21690
+ } else if (map) {
21691
+ map.addInDatabase(skillId, skillObj);
21692
+ skillData = skillObj;
21693
+ } else {
21694
+ skillData = skillObj;
21695
+ }
21696
+ skillInstance = skillObj;
21697
+ }
21698
+ return { skillId, skillData, skillInstance };
21699
+ }
21700
+ _createSkillInstance(skillId, skillData, skillInstance) {
21701
+ const instance = new Skill(skillData);
21702
+ instance.id.set(skillId);
21703
+ if (skillInstance) {
21704
+ instance._skillInstance = skillInstance;
21705
+ }
21706
+ return instance;
21707
+ }
21708
+ /**
21709
+ * Create a skill instance without learning side effects.
21710
+ */
21711
+ createSkillInstance(skillInput) {
21712
+ const map = this._getSkillMap();
21713
+ const { skillId, skillData, skillInstance } = this._resolveSkillInput(skillInput, map);
21714
+ const instance = this._createSkillInstance(skillId, skillData, skillInstance);
21715
+ return { skillId, skillData, skillInstance, instance };
21716
+ }
21717
+ /**
21718
+ * Resolve skill snapshot entries into Skill instances without side effects.
21719
+ */
21720
+ resolveSkillsSnapshot(snapshot, mapOverride) {
21721
+ if (!snapshot || !Array.isArray(snapshot.skills)) {
21722
+ return snapshot;
21723
+ }
21724
+ const map = mapOverride ?? this._getSkillMap(false);
21725
+ if (!map || !map.database) {
21726
+ return snapshot;
21727
+ }
21728
+ const databaseByIdOverride = (id) => {
21729
+ const data = map.database()[id];
21730
+ if (!data) {
21731
+ throw new Error(
21732
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
21733
+ );
21734
+ }
21735
+ return data;
21736
+ };
21737
+ const skills = snapshot.skills.map((entry) => {
21738
+ const skillId = isString(entry) ? entry : entry?.id;
21739
+ if (!skillId) {
21740
+ return entry;
21741
+ }
21742
+ const { skillData, skillInstance } = this._resolveSkillInput(
21743
+ skillId,
21744
+ map,
21745
+ databaseByIdOverride
21746
+ );
21747
+ return this._createSkillInstance(skillId, skillData, skillInstance);
21748
+ });
21749
+ return { ...snapshot, skills };
21750
+ }
20999
21751
  /**
21000
21752
  * Find the index of a skill in the skills array
21001
21753
  *
@@ -21014,7 +21766,7 @@ function WithSkillManager(Base) {
21014
21766
  searchId = skillInput.id || "";
21015
21767
  }
21016
21768
  return this.skills().findIndex((skill) => {
21017
- const skillId = skill.id || skill.name || "";
21769
+ const skillId = skill.id() || skill.name() || "";
21018
21770
  return skillId === searchId;
21019
21771
  });
21020
21772
  }
@@ -21065,53 +21817,20 @@ function WithSkillManager(Base) {
21065
21817
  * spCost: 20,
21066
21818
  * onLearn(player) {
21067
21819
  * console.log('Learned custom skill!');
21068
- * }
21069
- * });
21070
- * ```
21071
- */
21072
- learnSkill(skillInput) {
21073
- const map = this.getCurrentMap() || this.map;
21074
- let skillId = "";
21075
- let skillData;
21076
- if (isString(skillInput)) {
21077
- skillId = skillInput;
21078
- skillData = this.databaseById(skillId);
21079
- } else if (typeof skillInput === "function") {
21080
- const SkillClassCtor = skillInput;
21081
- skillId = SkillClassCtor.id || SkillClassCtor.name;
21082
- const existingData = map?.database()?.[skillId];
21083
- if (existingData) {
21084
- skillData = existingData;
21085
- } else if (map) {
21086
- map.addInDatabase(skillId, SkillClassCtor);
21087
- skillData = SkillClassCtor;
21088
- } else {
21089
- skillData = SkillClassCtor;
21090
- }
21091
- const skillInstance = new SkillClassCtor();
21092
- skillData = { ...skillData, ...skillInstance, id: skillId };
21093
- } else {
21094
- const skillObj = skillInput;
21095
- skillId = skillObj.id || `skill-${Date.now()}`;
21096
- skillObj.id = skillId;
21097
- const existingData = map?.database()?.[skillId];
21098
- if (existingData) {
21099
- skillData = { ...existingData, ...skillObj };
21100
- if (map) {
21101
- map.addInDatabase(skillId, skillData, { force: true });
21102
- }
21103
- } else if (map) {
21104
- map.addInDatabase(skillId, skillObj);
21105
- skillData = skillObj;
21106
- } else {
21107
- skillData = skillObj;
21108
- }
21109
- }
21820
+ * }
21821
+ * });
21822
+ * ```
21823
+ */
21824
+ learnSkill(skillInput) {
21825
+ const map = this._getSkillMap();
21826
+ const { skillId, skillData, skillInstance } = this._resolveSkillInput(skillInput, map);
21110
21827
  if (this.getSkill(skillId)) {
21111
21828
  throw SkillLog.alreadyLearned(skillData);
21112
21829
  }
21113
- this.skills().push(skillData);
21114
- this["execMethod"]("onLearn", [this], skillData);
21830
+ const instance = this._createSkillInstance(skillId, skillData, skillInstance);
21831
+ this.skills().push(instance);
21832
+ const hookTarget = instance._skillInstance || instance;
21833
+ this["execMethod"]("onLearn", [this], hookTarget);
21115
21834
  return skillData;
21116
21835
  }
21117
21836
  /**
@@ -21147,7 +21866,8 @@ function WithSkillManager(Base) {
21147
21866
  }
21148
21867
  const skillData = this.skills()[index];
21149
21868
  this.skills().splice(index, 1);
21150
- this["execMethod"]("onForget", [this], skillData);
21869
+ const hookTarget = skillData?._skillInstance || skillData;
21870
+ this["execMethod"]("onForget", [this], hookTarget);
21151
21871
  return skillData;
21152
21872
  }
21153
21873
  /**
@@ -21192,7 +21912,8 @@ function WithSkillManager(Base) {
21192
21912
  this.sp -= spCost / costMultiplier;
21193
21913
  const hitRate = skill.hitRate ?? 1;
21194
21914
  if (Math.random() > hitRate) {
21195
- this["execMethod"]("onUseFailed", [this, otherPlayer], skill);
21915
+ const hookTarget2 = skill?._skillInstance || skill;
21916
+ this["execMethod"]("onUseFailed", [this, otherPlayer], hookTarget2);
21196
21917
  throw SkillLog.chanceToUseFailed(skill);
21197
21918
  }
21198
21919
  if (otherPlayer) {
@@ -21202,7 +21923,8 @@ function WithSkillManager(Base) {
21202
21923
  player.applyDamage(this, skill);
21203
21924
  }
21204
21925
  }
21205
- this["execMethod"]("onUse", [this, otherPlayer], skill);
21926
+ const hookTarget = skill?._skillInstance || skill;
21927
+ this["execMethod"]("onUse", [this, otherPlayer], hookTarget);
21206
21928
  return skill;
21207
21929
  }
21208
21930
  };
@@ -21316,9 +22038,54 @@ function WithBattleManager(Base) {
21316
22038
 
21317
22039
  function WithClassManager(Base) {
21318
22040
  return class extends Base {
22041
+ _resolveClassInput(classInput, databaseByIdOverride) {
22042
+ if (isString(classInput)) {
22043
+ return databaseByIdOverride ? databaseByIdOverride(classInput) : this.databaseById(classInput);
22044
+ }
22045
+ return classInput;
22046
+ }
22047
+ _createClassInstance(classInput) {
22048
+ const classClass = this._resolveClassInput(classInput);
22049
+ const instance = new classClass();
22050
+ return { classClass, instance };
22051
+ }
22052
+ /**
22053
+ * Create a class instance without side effects.
22054
+ */
22055
+ createClassInstance(classInput) {
22056
+ return this._createClassInstance(classInput);
22057
+ }
22058
+ /**
22059
+ * Resolve class snapshot entry into a class instance without side effects.
22060
+ */
22061
+ resolveClassSnapshot(snapshot, mapOverride) {
22062
+ if (!snapshot || snapshot._class == null) {
22063
+ return snapshot;
22064
+ }
22065
+ const map = mapOverride ?? (this.getCurrentMap?.() || this.map);
22066
+ if (!map || !map.database) {
22067
+ return snapshot;
22068
+ }
22069
+ const databaseByIdOverride = (id) => {
22070
+ const data = map.database()[id];
22071
+ if (!data) {
22072
+ throw new Error(
22073
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
22074
+ );
22075
+ }
22076
+ return data;
22077
+ };
22078
+ const classId = isString(snapshot._class) ? snapshot._class : snapshot._class?.id;
22079
+ if (!classId) {
22080
+ return snapshot;
22081
+ }
22082
+ const classClass = this._resolveClassInput(classId, databaseByIdOverride);
22083
+ const { instance } = this._createClassInstance(classClass);
22084
+ return { ...snapshot, _class: instance };
22085
+ }
21319
22086
  setClass(_class) {
21320
- if (isString(_class)) _class = this.databaseById(_class);
21321
- const classInstance = new _class();
22087
+ const { instance } = this._createClassInstance(_class);
22088
+ const classInstance = instance;
21322
22089
  this["execMethod"]("onSet", [this], classInstance);
21323
22090
  return classInstance;
21324
22091
  }
@@ -21348,6 +22115,60 @@ function WithStateManager(Base) {
21348
22115
  super(...arguments);
21349
22116
  this._statesEfficiency = signal([]);
21350
22117
  }
22118
+ _getStateMap(required = true) {
22119
+ const map = this.getCurrentMap?.() || this.map;
22120
+ if (required && (!map || !map.database)) {
22121
+ throw new Error("Player must be on a map to resolve states");
22122
+ }
22123
+ return map;
22124
+ }
22125
+ _resolveStateInput(stateInput, databaseByIdOverride) {
22126
+ if (isString(stateInput)) {
22127
+ return databaseByIdOverride ? databaseByIdOverride(stateInput) : this.databaseById(stateInput);
22128
+ }
22129
+ return stateInput;
22130
+ }
22131
+ _createStateInstance(stateClass) {
22132
+ return new stateClass();
22133
+ }
22134
+ /**
22135
+ * Create a state instance without side effects.
22136
+ */
22137
+ createStateInstance(stateInput) {
22138
+ const stateClass = this._resolveStateInput(stateInput);
22139
+ const instance = this._createStateInstance(stateClass);
22140
+ return { stateClass, instance };
22141
+ }
22142
+ /**
22143
+ * Resolve state snapshot entries into state instances without side effects.
22144
+ */
22145
+ resolveStatesSnapshot(snapshot, mapOverride) {
22146
+ if (!snapshot || !Array.isArray(snapshot.states)) {
22147
+ return snapshot;
22148
+ }
22149
+ const map = mapOverride ?? this._getStateMap(false);
22150
+ if (!map || !map.database) {
22151
+ return snapshot;
22152
+ }
22153
+ const databaseByIdOverride = (id) => {
22154
+ const data = map.database()[id];
22155
+ if (!data) {
22156
+ throw new Error(
22157
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
22158
+ );
22159
+ }
22160
+ return data;
22161
+ };
22162
+ const states = snapshot.states.map((entry) => {
22163
+ const stateId = isString(entry) ? entry : entry?.id;
22164
+ if (!stateId) {
22165
+ return entry;
22166
+ }
22167
+ const stateClass = this._resolveStateInput(stateId, databaseByIdOverride);
22168
+ return this._createStateInstance(stateClass);
22169
+ });
22170
+ return { ...snapshot, states };
22171
+ }
21351
22172
  get statesDefense() {
21352
22173
  return this.getFeature("statesDefense", "state");
21353
22174
  }
@@ -21387,7 +22208,7 @@ function WithStateManager(Base) {
21387
22208
  if (Math.random() > chance) {
21388
22209
  throw StateLog.addFailed(stateClass);
21389
22210
  }
21390
- const instance = new stateClass();
22211
+ const instance = this._createStateInstance(stateClass);
21391
22212
  this.states().push(instance);
21392
22213
  this.applyStates(this, instance);
21393
22214
  return instance;
@@ -21418,13 +22239,13 @@ function WithStateManager(Base) {
21418
22239
  };
21419
22240
  }
21420
22241
 
21421
- var __defProp$2 = Object.defineProperty;
21422
- var __decorateClass$2 = (decorators, target, key, kind) => {
22242
+ var __defProp$3 = Object.defineProperty;
22243
+ var __decorateClass$3 = (decorators, target, key, kind) => {
21423
22244
  var result = void 0 ;
21424
22245
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
21425
22246
  if (decorator = decorators[i])
21426
22247
  result = (decorator(target, key, result) ) || result;
21427
- if (result) __defProp$2(target, key, result);
22248
+ if (result) __defProp$3(target, key, result);
21428
22249
  return result;
21429
22250
  };
21430
22251
  function combinePlayerMixins(mixins) {
@@ -21700,16 +22521,71 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
21700
22521
  value
21701
22522
  });
21702
22523
  }
21703
- async save() {
21704
- const snapshot = createStatesSnapshot(this);
22524
+ snapshot() {
22525
+ return createStatesSnapshotDeep(this);
22526
+ }
22527
+ async applySnapshot(snapshot) {
22528
+ const data = typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot;
22529
+ const withItems = this.resolveItemsSnapshot?.(data) ?? data;
22530
+ const withSkills = this.resolveSkillsSnapshot?.(withItems) ?? withItems;
22531
+ const withStates = this.resolveStatesSnapshot?.(withSkills) ?? withSkills;
22532
+ const withClass = this.resolveClassSnapshot?.(withStates) ?? withStates;
22533
+ const resolvedSnapshot = this.resolveEquipmentsSnapshot?.(withClass) ?? withClass;
22534
+ load(this, resolvedSnapshot);
22535
+ if (Array.isArray(resolvedSnapshot.items)) {
22536
+ this.items.set(resolvedSnapshot.items);
22537
+ }
22538
+ if (Array.isArray(resolvedSnapshot.skills)) {
22539
+ this.skills.set(resolvedSnapshot.skills);
22540
+ }
22541
+ if (Array.isArray(resolvedSnapshot.states)) {
22542
+ this.states.set(resolvedSnapshot.states);
22543
+ }
22544
+ if (resolvedSnapshot._class != null && this._class?.set) {
22545
+ this._class.set(resolvedSnapshot._class);
22546
+ }
22547
+ if (Array.isArray(resolvedSnapshot.equipments)) {
22548
+ this.equipments.set(resolvedSnapshot.equipments);
22549
+ }
22550
+ await lastValueFrom(this.hooks.callHooks("server-player-onLoad", this, resolvedSnapshot));
22551
+ return resolvedSnapshot;
22552
+ }
22553
+ async save(slot = "auto", meta = {}, context = {}) {
22554
+ const policy = resolveAutoSaveStrategy();
22555
+ if (policy.canSave && !policy.canSave(this, context)) {
22556
+ return null;
22557
+ }
22558
+ const resolvedSlot = resolveSaveSlot(slot, policy, this, context);
22559
+ if (resolvedSlot === null) {
22560
+ return null;
22561
+ }
22562
+ const snapshot = this.snapshot();
21705
22563
  await lastValueFrom(this.hooks.callHooks("server-player-onSave", this, snapshot));
21706
- return JSON.stringify(snapshot);
22564
+ const storage = resolveSaveStorageStrategy();
22565
+ const finalMeta = buildSaveSlotMeta(this, meta);
22566
+ await storage.save(this, resolvedSlot, JSON.stringify(snapshot), finalMeta);
22567
+ return { index: resolvedSlot, meta: finalMeta };
21707
22568
  }
21708
- async load(snapshot) {
21709
- const data = JSON.parse(snapshot);
21710
- const dataLoaded = load(this, data);
21711
- await lastValueFrom(this.hooks.callHooks("server-player-onLoad", this, dataLoaded));
21712
- return dataLoaded;
22569
+ async load(slot = "auto", context = {}, options = {}) {
22570
+ const policy = resolveAutoSaveStrategy();
22571
+ if (policy.canLoad && !policy.canLoad(this, context)) {
22572
+ return { ok: false };
22573
+ }
22574
+ const resolvedSlot = resolveSaveSlot(slot, policy, this, context);
22575
+ if (resolvedSlot === null) {
22576
+ return { ok: false };
22577
+ }
22578
+ const storage = resolveSaveStorageStrategy();
22579
+ const slotData = await storage.get(this, resolvedSlot);
22580
+ if (!slotData?.snapshot) {
22581
+ return { ok: false };
22582
+ }
22583
+ await this.applySnapshot(slotData.snapshot);
22584
+ const { snapshot, ...meta } = slotData;
22585
+ if (options.changeMap !== false && meta.map) {
22586
+ await this.changeMap(meta.map);
22587
+ }
22588
+ return { ok: true, slot: meta, index: resolvedSlot };
21713
22589
  }
21714
22590
  /**
21715
22591
  * @deprecated Use setGraphicAnimation instead.
@@ -21778,6 +22654,9 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
21778
22654
  */
21779
22655
  syncChanges() {
21780
22656
  this._eventChanges();
22657
+ if (shouldAutoSave(this, { reason: "auto", source: "syncChanges" })) {
22658
+ void this.save("auto", {}, { reason: "auto", source: "syncChanges" });
22659
+ }
21781
22660
  }
21782
22661
  databaseById(id) {
21783
22662
  const map2 = this.map;
@@ -22338,7 +23217,7 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
22338
23217
  return false;
22339
23218
  }
22340
23219
  };
22341
- __decorateClass$2([
23220
+ __decorateClass$3([
22342
23221
  sync(_RpgPlayer)
22343
23222
  ], _RpgPlayer.prototype, "events");
22344
23223
  let RpgPlayer = _RpgPlayer;
@@ -22368,8 +23247,8 @@ class RpgEvent extends RpgPlayer {
22368
23247
  }
22369
23248
  }
22370
23249
 
22371
- const context$1 = new Context();
22372
- context$1["side"] = "server";
23250
+ const context = new Context();
23251
+ context["side"] = "server";
22373
23252
 
22374
23253
  /** A special constant with type `never` */
22375
23254
  function $constructor(name, initializer, params) {
@@ -22625,6 +23504,11 @@ const NUMBER_FORMAT_RANGES = {
22625
23504
  };
22626
23505
  function pick(schema, mask) {
22627
23506
  const currDef = schema._zod.def;
23507
+ const checks = currDef.checks;
23508
+ const hasChecks = checks && checks.length > 0;
23509
+ if (hasChecks) {
23510
+ throw new Error(".pick() cannot be used on object schemas containing refinements");
23511
+ }
22628
23512
  const def = mergeDefs(schema._zod.def, {
22629
23513
  get shape() {
22630
23514
  const newShape = {};
@@ -22645,6 +23529,11 @@ function pick(schema, mask) {
22645
23529
  }
22646
23530
  function omit(schema, mask) {
22647
23531
  const currDef = schema._zod.def;
23532
+ const checks = currDef.checks;
23533
+ const hasChecks = checks && checks.length > 0;
23534
+ if (hasChecks) {
23535
+ throw new Error(".omit() cannot be used on object schemas containing refinements");
23536
+ }
22648
23537
  const def = mergeDefs(schema._zod.def, {
22649
23538
  get shape() {
22650
23539
  const newShape = { ...schema._zod.def.shape };
@@ -22670,7 +23559,14 @@ function extend(schema, shape) {
22670
23559
  const checks = schema._zod.def.checks;
22671
23560
  const hasChecks = checks && checks.length > 0;
22672
23561
  if (hasChecks) {
22673
- throw new Error("Object schemas containing refinements cannot be extended. Use `.safeExtend()` instead.");
23562
+ // Only throw if new shape overlaps with existing shape
23563
+ // Use getOwnPropertyDescriptor to check key existence without accessing values
23564
+ const existingShape = schema._zod.def.shape;
23565
+ for (const key in shape) {
23566
+ if (Object.getOwnPropertyDescriptor(existingShape, key) !== undefined) {
23567
+ throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
23568
+ }
23569
+ }
22674
23570
  }
22675
23571
  const def = mergeDefs(schema._zod.def, {
22676
23572
  get shape() {
@@ -22678,7 +23574,6 @@ function extend(schema, shape) {
22678
23574
  assignProp(this, "shape", _shape); // self-caching
22679
23575
  return _shape;
22680
23576
  },
22681
- checks: [],
22682
23577
  });
22683
23578
  return clone(schema, def);
22684
23579
  }
@@ -22686,15 +23581,13 @@ function safeExtend(schema, shape) {
22686
23581
  if (!isPlainObject(shape)) {
22687
23582
  throw new Error("Invalid input to safeExtend: expected a plain object");
22688
23583
  }
22689
- const def = {
22690
- ...schema._zod.def,
23584
+ const def = mergeDefs(schema._zod.def, {
22691
23585
  get shape() {
22692
23586
  const _shape = { ...schema._zod.def.shape, ...shape };
22693
23587
  assignProp(this, "shape", _shape); // self-caching
22694
23588
  return _shape;
22695
23589
  },
22696
- checks: schema._zod.def.checks,
22697
- };
23590
+ });
22698
23591
  return clone(schema, def);
22699
23592
  }
22700
23593
  function merge(a, b) {
@@ -22712,6 +23605,12 @@ function merge(a, b) {
22712
23605
  return clone(a, def);
22713
23606
  }
22714
23607
  function partial(Class, schema, mask) {
23608
+ const currDef = schema._zod.def;
23609
+ const checks = currDef.checks;
23610
+ const hasChecks = checks && checks.length > 0;
23611
+ if (hasChecks) {
23612
+ throw new Error(".partial() cannot be used on object schemas containing refinements");
23613
+ }
22715
23614
  const def = mergeDefs(schema._zod.def, {
22716
23615
  get shape() {
22717
23616
  const oldShape = schema._zod.def.shape;
@@ -22781,7 +23680,6 @@ function required(Class, schema, mask) {
22781
23680
  assignProp(this, "shape", shape); // self-caching
22782
23681
  return shape;
22783
23682
  },
22784
- checks: [],
22785
23683
  });
22786
23684
  return clone(schema, def);
22787
23685
  }
@@ -23031,7 +23929,8 @@ const cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?:
23031
23929
  const base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
23032
23930
  const base64url = /^[A-Za-z0-9_-]*$/;
23033
23931
  // https://blog.stevenlevithan.com/archives/validate-phone-number#r4-3 (regex sans spaces)
23034
- const e164 = /^\+(?:[0-9]){6,14}[0-9]$/;
23932
+ // E.164: leading digit must be 1-9; total digits (excluding '+') between 7-15
23933
+ const e164 = /^\+[1-9]\d{6,14}$/;
23035
23934
  // const dateSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`;
23036
23935
  const dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
23037
23936
  const date$1 = /*@__PURE__*/ new RegExp(`^${dateSource}$`);
@@ -23066,7 +23965,7 @@ const string$1 = (params) => {
23066
23965
  return new RegExp(`^${regex}$`);
23067
23966
  };
23068
23967
  const integer = /^-?\d+$/;
23069
- const number$1 = /^-?\d+(?:\.\d+)?/;
23968
+ const number$1 = /^-?\d+(?:\.\d+)?$/;
23070
23969
  // regex for string with no uppercase letters
23071
23970
  const lowercase = /^[^A-Z]*$/;
23072
23971
  // regex for string with no lowercase letters
@@ -23104,7 +24003,7 @@ const $ZodCheckLessThan = /*@__PURE__*/ $constructor("$ZodCheckLessThan", (inst,
23104
24003
  payload.issues.push({
23105
24004
  origin,
23106
24005
  code: "too_big",
23107
- maximum: def.value,
24006
+ maximum: typeof def.value === "object" ? def.value.getTime() : def.value,
23108
24007
  input: payload.value,
23109
24008
  inclusive: def.inclusive,
23110
24009
  inst,
@@ -23132,7 +24031,7 @@ const $ZodCheckGreaterThan = /*@__PURE__*/ $constructor("$ZodCheckGreaterThan",
23132
24031
  payload.issues.push({
23133
24032
  origin,
23134
24033
  code: "too_small",
23135
- minimum: def.value,
24034
+ minimum: typeof def.value === "object" ? def.value.getTime() : def.value,
23136
24035
  input: payload.value,
23137
24036
  inclusive: def.inclusive,
23138
24037
  inst,
@@ -23220,6 +24119,7 @@ const $ZodCheckNumberFormat = /*@__PURE__*/ $constructor("$ZodCheckNumberFormat"
23220
24119
  note: "Integers must be within the safe integer range.",
23221
24120
  inst,
23222
24121
  origin,
24122
+ inclusive: true,
23223
24123
  continue: !def.abort,
23224
24124
  });
23225
24125
  }
@@ -23232,6 +24132,7 @@ const $ZodCheckNumberFormat = /*@__PURE__*/ $constructor("$ZodCheckNumberFormat"
23232
24132
  note: "Integers must be within the safe integer range.",
23233
24133
  inst,
23234
24134
  origin,
24135
+ inclusive: true,
23235
24136
  continue: !def.abort,
23236
24137
  });
23237
24138
  }
@@ -23255,7 +24156,9 @@ const $ZodCheckNumberFormat = /*@__PURE__*/ $constructor("$ZodCheckNumberFormat"
23255
24156
  input,
23256
24157
  code: "too_big",
23257
24158
  maximum,
24159
+ inclusive: true,
23258
24160
  inst,
24161
+ continue: !def.abort,
23259
24162
  });
23260
24163
  }
23261
24164
  };
@@ -23518,8 +24421,8 @@ class Doc {
23518
24421
 
23519
24422
  const version = {
23520
24423
  major: 4,
23521
- minor: 2,
23522
- patch: 1,
24424
+ minor: 3,
24425
+ patch: 5,
23523
24426
  };
23524
24427
 
23525
24428
  const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
@@ -23629,7 +24532,8 @@ const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
23629
24532
  return runChecks(result, checks, ctx);
23630
24533
  };
23631
24534
  }
23632
- inst["~standard"] = {
24535
+ // Lazy initialize ~standard to avoid creating objects for every schema
24536
+ defineLazy(inst, "~standard", () => ({
23633
24537
  validate: (value) => {
23634
24538
  try {
23635
24539
  const r = safeParse$1(inst, value);
@@ -23641,7 +24545,7 @@ const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
23641
24545
  },
23642
24546
  vendor: "zod",
23643
24547
  version: 1,
23644
- };
24548
+ }));
23645
24549
  });
23646
24550
  const $ZodString = /*@__PURE__*/ $constructor("$ZodString", (inst, def) => {
23647
24551
  $ZodType.init(inst, def);
@@ -24049,8 +24953,12 @@ const $ZodArray = /*@__PURE__*/ $constructor("$ZodArray", (inst, def) => {
24049
24953
  return payload; //handleArrayResultsAsync(parseResults, final);
24050
24954
  };
24051
24955
  });
24052
- function handlePropertyResult(result, final, key, input) {
24956
+ function handlePropertyResult(result, final, key, input, isOptionalOut) {
24053
24957
  if (result.issues.length) {
24958
+ // For optional-out schemas, ignore errors on absent keys
24959
+ if (isOptionalOut && !(key in input)) {
24960
+ return;
24961
+ }
24054
24962
  final.issues.push(...prefixIssues(key, result.issues));
24055
24963
  }
24056
24964
  if (result.value === undefined) {
@@ -24084,6 +24992,7 @@ function handleCatchall(proms, input, payload, ctx, def, inst) {
24084
24992
  const keySet = def.keySet;
24085
24993
  const _catchall = def.catchall._zod;
24086
24994
  const t = _catchall.def.type;
24995
+ const isOptionalOut = _catchall.optout === "optional";
24087
24996
  for (const key in input) {
24088
24997
  if (keySet.has(key))
24089
24998
  continue;
@@ -24093,10 +25002,10 @@ function handleCatchall(proms, input, payload, ctx, def, inst) {
24093
25002
  }
24094
25003
  const r = _catchall.run({ value: input[key], issues: [] }, ctx);
24095
25004
  if (r instanceof Promise) {
24096
- proms.push(r.then((r) => handlePropertyResult(r, payload, key, input)));
25005
+ proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));
24097
25006
  }
24098
25007
  else {
24099
- handlePropertyResult(r, payload, key, input);
25008
+ handlePropertyResult(r, payload, key, input, isOptionalOut);
24100
25009
  }
24101
25010
  }
24102
25011
  if (unrecognized.length) {
@@ -24164,12 +25073,13 @@ const $ZodObject = /*@__PURE__*/ $constructor("$ZodObject", (inst, def) => {
24164
25073
  const shape = value.shape;
24165
25074
  for (const key of value.keys) {
24166
25075
  const el = shape[key];
25076
+ const isOptionalOut = el._zod.optout === "optional";
24167
25077
  const r = el._zod.run({ value: input[key], issues: [] }, ctx);
24168
25078
  if (r instanceof Promise) {
24169
- proms.push(r.then((r) => handlePropertyResult(r, payload, key, input)));
25079
+ proms.push(r.then((r) => handlePropertyResult(r, payload, key, input, isOptionalOut)));
24170
25080
  }
24171
25081
  else {
24172
- handlePropertyResult(r, payload, key, input);
25082
+ handlePropertyResult(r, payload, key, input, isOptionalOut);
24173
25083
  }
24174
25084
  }
24175
25085
  if (!catchall) {
@@ -24201,8 +25111,33 @@ const $ZodObjectJIT = /*@__PURE__*/ $constructor("$ZodObjectJIT", (inst, def) =>
24201
25111
  for (const key of normalized.keys) {
24202
25112
  const id = ids[key];
24203
25113
  const k = esc(key);
25114
+ const schema = shape[key];
25115
+ const isOptionalOut = schema?._zod?.optout === "optional";
24204
25116
  doc.write(`const ${id} = ${parseStr(key)};`);
24205
- doc.write(`
25117
+ if (isOptionalOut) {
25118
+ // For optional-out schemas, ignore errors on absent keys
25119
+ doc.write(`
25120
+ if (${id}.issues.length) {
25121
+ if (${k} in input) {
25122
+ payload.issues = payload.issues.concat(${id}.issues.map(iss => ({
25123
+ ...iss,
25124
+ path: iss.path ? [${k}, ...iss.path] : [${k}]
25125
+ })));
25126
+ }
25127
+ }
25128
+
25129
+ if (${id}.value === undefined) {
25130
+ if (${k} in input) {
25131
+ newResult[${k}] = undefined;
25132
+ }
25133
+ } else {
25134
+ newResult[${k}] = ${id}.value;
25135
+ }
25136
+
25137
+ `);
25138
+ }
25139
+ else {
25140
+ doc.write(`
24206
25141
  if (${id}.issues.length) {
24207
25142
  payload.issues = payload.issues.concat(${id}.issues.map(iss => ({
24208
25143
  ...iss,
@@ -24210,7 +25145,6 @@ const $ZodObjectJIT = /*@__PURE__*/ $constructor("$ZodObjectJIT", (inst, def) =>
24210
25145
  })));
24211
25146
  }
24212
25147
 
24213
-
24214
25148
  if (${id}.value === undefined) {
24215
25149
  if (${k} in input) {
24216
25150
  newResult[${k}] = undefined;
@@ -24220,6 +25154,7 @@ const $ZodObjectJIT = /*@__PURE__*/ $constructor("$ZodObjectJIT", (inst, def) =>
24220
25154
  }
24221
25155
 
24222
25156
  `);
25157
+ }
24223
25158
  }
24224
25159
  doc.write(`payload.value = newResult;`);
24225
25160
  doc.write(`return payload;`);
@@ -24386,11 +25321,38 @@ function mergeValues(a, b) {
24386
25321
  return { valid: false, mergeErrorPath: [] };
24387
25322
  }
24388
25323
  function handleIntersectionResults(result, left, right) {
24389
- if (left.issues.length) {
24390
- result.issues.push(...left.issues);
25324
+ // Track which side(s) report each key as unrecognized
25325
+ const unrecKeys = new Map();
25326
+ let unrecIssue;
25327
+ for (const iss of left.issues) {
25328
+ if (iss.code === "unrecognized_keys") {
25329
+ unrecIssue ?? (unrecIssue = iss);
25330
+ for (const k of iss.keys) {
25331
+ if (!unrecKeys.has(k))
25332
+ unrecKeys.set(k, {});
25333
+ unrecKeys.get(k).l = true;
25334
+ }
25335
+ }
25336
+ else {
25337
+ result.issues.push(iss);
25338
+ }
24391
25339
  }
24392
- if (right.issues.length) {
24393
- result.issues.push(...right.issues);
25340
+ for (const iss of right.issues) {
25341
+ if (iss.code === "unrecognized_keys") {
25342
+ for (const k of iss.keys) {
25343
+ if (!unrecKeys.has(k))
25344
+ unrecKeys.set(k, {});
25345
+ unrecKeys.get(k).r = true;
25346
+ }
25347
+ }
25348
+ else {
25349
+ result.issues.push(iss);
25350
+ }
25351
+ }
25352
+ // Report only keys unrecognized by BOTH sides
25353
+ const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k]) => k);
25354
+ if (bothKeys.length && unrecIssue) {
25355
+ result.issues.push({ ...unrecIssue, keys: bothKeys });
24394
25356
  }
24395
25357
  if (aborted(result))
24396
25358
  return result;
@@ -24475,6 +25437,17 @@ const $ZodOptional = /*@__PURE__*/ $constructor("$ZodOptional", (inst, def) => {
24475
25437
  return def.innerType._zod.run(payload, ctx);
24476
25438
  };
24477
25439
  });
25440
+ const $ZodExactOptional = /*@__PURE__*/ $constructor("$ZodExactOptional", (inst, def) => {
25441
+ // Call parent init - inherits optin/optout = "optional"
25442
+ $ZodOptional.init(inst, def);
25443
+ // Override values/pattern to NOT add undefined
25444
+ defineLazy(inst._zod, "values", () => def.innerType._zod.values);
25445
+ defineLazy(inst._zod, "pattern", () => def.innerType._zod.pattern);
25446
+ // Override parse to just delegate (no undefined handling)
25447
+ inst._zod.parse = (payload, ctx) => {
25448
+ return def.innerType._zod.run(payload, ctx);
25449
+ };
25450
+ });
24478
25451
  const $ZodNullable = /*@__PURE__*/ $constructor("$ZodNullable", (inst, def) => {
24479
25452
  $ZodType.init(inst, def);
24480
25453
  defineLazy(inst._zod, "optin", () => def.innerType._zod.optin);
@@ -24697,9 +25670,6 @@ class $ZodRegistry {
24697
25670
  const meta = _meta[0];
24698
25671
  this._map.set(schema, meta);
24699
25672
  if (meta && typeof meta === "object" && "id" in meta) {
24700
- if (this._idmap.has(meta.id)) {
24701
- throw new Error(`ID ${meta.id} already exists in the registry`);
24702
- }
24703
25673
  this._idmap.set(meta.id, schema);
24704
25674
  }
24705
25675
  return this;
@@ -24740,12 +25710,14 @@ function registry() {
24740
25710
  (_a = globalThis).__zod_globalRegistry ?? (_a.__zod_globalRegistry = registry());
24741
25711
  const globalRegistry = globalThis.__zod_globalRegistry;
24742
25712
 
25713
+ // @__NO_SIDE_EFFECTS__
24743
25714
  function _string(Class, params) {
24744
25715
  return new Class({
24745
25716
  type: "string",
24746
25717
  ...normalizeParams(params),
24747
25718
  });
24748
25719
  }
25720
+ // @__NO_SIDE_EFFECTS__
24749
25721
  function _email(Class, params) {
24750
25722
  return new Class({
24751
25723
  type: "string",
@@ -24755,6 +25727,7 @@ function _email(Class, params) {
24755
25727
  ...normalizeParams(params),
24756
25728
  });
24757
25729
  }
25730
+ // @__NO_SIDE_EFFECTS__
24758
25731
  function _guid(Class, params) {
24759
25732
  return new Class({
24760
25733
  type: "string",
@@ -24764,6 +25737,7 @@ function _guid(Class, params) {
24764
25737
  ...normalizeParams(params),
24765
25738
  });
24766
25739
  }
25740
+ // @__NO_SIDE_EFFECTS__
24767
25741
  function _uuid(Class, params) {
24768
25742
  return new Class({
24769
25743
  type: "string",
@@ -24773,6 +25747,7 @@ function _uuid(Class, params) {
24773
25747
  ...normalizeParams(params),
24774
25748
  });
24775
25749
  }
25750
+ // @__NO_SIDE_EFFECTS__
24776
25751
  function _uuidv4(Class, params) {
24777
25752
  return new Class({
24778
25753
  type: "string",
@@ -24783,6 +25758,7 @@ function _uuidv4(Class, params) {
24783
25758
  ...normalizeParams(params),
24784
25759
  });
24785
25760
  }
25761
+ // @__NO_SIDE_EFFECTS__
24786
25762
  function _uuidv6(Class, params) {
24787
25763
  return new Class({
24788
25764
  type: "string",
@@ -24793,6 +25769,7 @@ function _uuidv6(Class, params) {
24793
25769
  ...normalizeParams(params),
24794
25770
  });
24795
25771
  }
25772
+ // @__NO_SIDE_EFFECTS__
24796
25773
  function _uuidv7(Class, params) {
24797
25774
  return new Class({
24798
25775
  type: "string",
@@ -24803,6 +25780,7 @@ function _uuidv7(Class, params) {
24803
25780
  ...normalizeParams(params),
24804
25781
  });
24805
25782
  }
25783
+ // @__NO_SIDE_EFFECTS__
24806
25784
  function _url(Class, params) {
24807
25785
  return new Class({
24808
25786
  type: "string",
@@ -24812,6 +25790,7 @@ function _url(Class, params) {
24812
25790
  ...normalizeParams(params),
24813
25791
  });
24814
25792
  }
25793
+ // @__NO_SIDE_EFFECTS__
24815
25794
  function _emoji(Class, params) {
24816
25795
  return new Class({
24817
25796
  type: "string",
@@ -24821,6 +25800,7 @@ function _emoji(Class, params) {
24821
25800
  ...normalizeParams(params),
24822
25801
  });
24823
25802
  }
25803
+ // @__NO_SIDE_EFFECTS__
24824
25804
  function _nanoid(Class, params) {
24825
25805
  return new Class({
24826
25806
  type: "string",
@@ -24830,6 +25810,7 @@ function _nanoid(Class, params) {
24830
25810
  ...normalizeParams(params),
24831
25811
  });
24832
25812
  }
25813
+ // @__NO_SIDE_EFFECTS__
24833
25814
  function _cuid(Class, params) {
24834
25815
  return new Class({
24835
25816
  type: "string",
@@ -24839,6 +25820,7 @@ function _cuid(Class, params) {
24839
25820
  ...normalizeParams(params),
24840
25821
  });
24841
25822
  }
25823
+ // @__NO_SIDE_EFFECTS__
24842
25824
  function _cuid2(Class, params) {
24843
25825
  return new Class({
24844
25826
  type: "string",
@@ -24848,6 +25830,7 @@ function _cuid2(Class, params) {
24848
25830
  ...normalizeParams(params),
24849
25831
  });
24850
25832
  }
25833
+ // @__NO_SIDE_EFFECTS__
24851
25834
  function _ulid(Class, params) {
24852
25835
  return new Class({
24853
25836
  type: "string",
@@ -24857,6 +25840,7 @@ function _ulid(Class, params) {
24857
25840
  ...normalizeParams(params),
24858
25841
  });
24859
25842
  }
25843
+ // @__NO_SIDE_EFFECTS__
24860
25844
  function _xid(Class, params) {
24861
25845
  return new Class({
24862
25846
  type: "string",
@@ -24866,6 +25850,7 @@ function _xid(Class, params) {
24866
25850
  ...normalizeParams(params),
24867
25851
  });
24868
25852
  }
25853
+ // @__NO_SIDE_EFFECTS__
24869
25854
  function _ksuid(Class, params) {
24870
25855
  return new Class({
24871
25856
  type: "string",
@@ -24875,6 +25860,7 @@ function _ksuid(Class, params) {
24875
25860
  ...normalizeParams(params),
24876
25861
  });
24877
25862
  }
25863
+ // @__NO_SIDE_EFFECTS__
24878
25864
  function _ipv4(Class, params) {
24879
25865
  return new Class({
24880
25866
  type: "string",
@@ -24884,6 +25870,7 @@ function _ipv4(Class, params) {
24884
25870
  ...normalizeParams(params),
24885
25871
  });
24886
25872
  }
25873
+ // @__NO_SIDE_EFFECTS__
24887
25874
  function _ipv6(Class, params) {
24888
25875
  return new Class({
24889
25876
  type: "string",
@@ -24893,6 +25880,7 @@ function _ipv6(Class, params) {
24893
25880
  ...normalizeParams(params),
24894
25881
  });
24895
25882
  }
25883
+ // @__NO_SIDE_EFFECTS__
24896
25884
  function _cidrv4(Class, params) {
24897
25885
  return new Class({
24898
25886
  type: "string",
@@ -24902,6 +25890,7 @@ function _cidrv4(Class, params) {
24902
25890
  ...normalizeParams(params),
24903
25891
  });
24904
25892
  }
25893
+ // @__NO_SIDE_EFFECTS__
24905
25894
  function _cidrv6(Class, params) {
24906
25895
  return new Class({
24907
25896
  type: "string",
@@ -24911,6 +25900,7 @@ function _cidrv6(Class, params) {
24911
25900
  ...normalizeParams(params),
24912
25901
  });
24913
25902
  }
25903
+ // @__NO_SIDE_EFFECTS__
24914
25904
  function _base64(Class, params) {
24915
25905
  return new Class({
24916
25906
  type: "string",
@@ -24920,6 +25910,7 @@ function _base64(Class, params) {
24920
25910
  ...normalizeParams(params),
24921
25911
  });
24922
25912
  }
25913
+ // @__NO_SIDE_EFFECTS__
24923
25914
  function _base64url(Class, params) {
24924
25915
  return new Class({
24925
25916
  type: "string",
@@ -24929,6 +25920,7 @@ function _base64url(Class, params) {
24929
25920
  ...normalizeParams(params),
24930
25921
  });
24931
25922
  }
25923
+ // @__NO_SIDE_EFFECTS__
24932
25924
  function _e164(Class, params) {
24933
25925
  return new Class({
24934
25926
  type: "string",
@@ -24938,6 +25930,7 @@ function _e164(Class, params) {
24938
25930
  ...normalizeParams(params),
24939
25931
  });
24940
25932
  }
25933
+ // @__NO_SIDE_EFFECTS__
24941
25934
  function _jwt(Class, params) {
24942
25935
  return new Class({
24943
25936
  type: "string",
@@ -24947,6 +25940,7 @@ function _jwt(Class, params) {
24947
25940
  ...normalizeParams(params),
24948
25941
  });
24949
25942
  }
25943
+ // @__NO_SIDE_EFFECTS__
24950
25944
  function _isoDateTime(Class, params) {
24951
25945
  return new Class({
24952
25946
  type: "string",
@@ -24958,6 +25952,7 @@ function _isoDateTime(Class, params) {
24958
25952
  ...normalizeParams(params),
24959
25953
  });
24960
25954
  }
25955
+ // @__NO_SIDE_EFFECTS__
24961
25956
  function _isoDate(Class, params) {
24962
25957
  return new Class({
24963
25958
  type: "string",
@@ -24966,6 +25961,7 @@ function _isoDate(Class, params) {
24966
25961
  ...normalizeParams(params),
24967
25962
  });
24968
25963
  }
25964
+ // @__NO_SIDE_EFFECTS__
24969
25965
  function _isoTime(Class, params) {
24970
25966
  return new Class({
24971
25967
  type: "string",
@@ -24975,6 +25971,7 @@ function _isoTime(Class, params) {
24975
25971
  ...normalizeParams(params),
24976
25972
  });
24977
25973
  }
25974
+ // @__NO_SIDE_EFFECTS__
24978
25975
  function _isoDuration(Class, params) {
24979
25976
  return new Class({
24980
25977
  type: "string",
@@ -24983,6 +25980,7 @@ function _isoDuration(Class, params) {
24983
25980
  ...normalizeParams(params),
24984
25981
  });
24985
25982
  }
25983
+ // @__NO_SIDE_EFFECTS__
24986
25984
  function _number(Class, params) {
24987
25985
  return new Class({
24988
25986
  type: "number",
@@ -24990,6 +25988,7 @@ function _number(Class, params) {
24990
25988
  ...normalizeParams(params),
24991
25989
  });
24992
25990
  }
25991
+ // @__NO_SIDE_EFFECTS__
24993
25992
  function _int(Class, params) {
24994
25993
  return new Class({
24995
25994
  type: "number",
@@ -24999,22 +25998,26 @@ function _int(Class, params) {
24999
25998
  ...normalizeParams(params),
25000
25999
  });
25001
26000
  }
26001
+ // @__NO_SIDE_EFFECTS__
25002
26002
  function _any(Class) {
25003
26003
  return new Class({
25004
26004
  type: "any",
25005
26005
  });
25006
26006
  }
26007
+ // @__NO_SIDE_EFFECTS__
25007
26008
  function _unknown(Class) {
25008
26009
  return new Class({
25009
26010
  type: "unknown",
25010
26011
  });
25011
26012
  }
26013
+ // @__NO_SIDE_EFFECTS__
25012
26014
  function _never(Class, params) {
25013
26015
  return new Class({
25014
26016
  type: "never",
25015
26017
  ...normalizeParams(params),
25016
26018
  });
25017
26019
  }
26020
+ // @__NO_SIDE_EFFECTS__
25018
26021
  function _lt(value, params) {
25019
26022
  return new $ZodCheckLessThan({
25020
26023
  check: "less_than",
@@ -25023,6 +26026,7 @@ function _lt(value, params) {
25023
26026
  inclusive: false,
25024
26027
  });
25025
26028
  }
26029
+ // @__NO_SIDE_EFFECTS__
25026
26030
  function _lte(value, params) {
25027
26031
  return new $ZodCheckLessThan({
25028
26032
  check: "less_than",
@@ -25031,6 +26035,7 @@ function _lte(value, params) {
25031
26035
  inclusive: true,
25032
26036
  });
25033
26037
  }
26038
+ // @__NO_SIDE_EFFECTS__
25034
26039
  function _gt(value, params) {
25035
26040
  return new $ZodCheckGreaterThan({
25036
26041
  check: "greater_than",
@@ -25039,6 +26044,7 @@ function _gt(value, params) {
25039
26044
  inclusive: false,
25040
26045
  });
25041
26046
  }
26047
+ // @__NO_SIDE_EFFECTS__
25042
26048
  function _gte(value, params) {
25043
26049
  return new $ZodCheckGreaterThan({
25044
26050
  check: "greater_than",
@@ -25047,6 +26053,7 @@ function _gte(value, params) {
25047
26053
  inclusive: true,
25048
26054
  });
25049
26055
  }
26056
+ // @__NO_SIDE_EFFECTS__
25050
26057
  function _multipleOf(value, params) {
25051
26058
  return new $ZodCheckMultipleOf({
25052
26059
  check: "multiple_of",
@@ -25054,6 +26061,7 @@ function _multipleOf(value, params) {
25054
26061
  value,
25055
26062
  });
25056
26063
  }
26064
+ // @__NO_SIDE_EFFECTS__
25057
26065
  function _maxLength(maximum, params) {
25058
26066
  const ch = new $ZodCheckMaxLength({
25059
26067
  check: "max_length",
@@ -25062,6 +26070,7 @@ function _maxLength(maximum, params) {
25062
26070
  });
25063
26071
  return ch;
25064
26072
  }
26073
+ // @__NO_SIDE_EFFECTS__
25065
26074
  function _minLength(minimum, params) {
25066
26075
  return new $ZodCheckMinLength({
25067
26076
  check: "min_length",
@@ -25069,6 +26078,7 @@ function _minLength(minimum, params) {
25069
26078
  minimum,
25070
26079
  });
25071
26080
  }
26081
+ // @__NO_SIDE_EFFECTS__
25072
26082
  function _length(length, params) {
25073
26083
  return new $ZodCheckLengthEquals({
25074
26084
  check: "length_equals",
@@ -25076,6 +26086,7 @@ function _length(length, params) {
25076
26086
  length,
25077
26087
  });
25078
26088
  }
26089
+ // @__NO_SIDE_EFFECTS__
25079
26090
  function _regex(pattern, params) {
25080
26091
  return new $ZodCheckRegex({
25081
26092
  check: "string_format",
@@ -25084,6 +26095,7 @@ function _regex(pattern, params) {
25084
26095
  pattern,
25085
26096
  });
25086
26097
  }
26098
+ // @__NO_SIDE_EFFECTS__
25087
26099
  function _lowercase(params) {
25088
26100
  return new $ZodCheckLowerCase({
25089
26101
  check: "string_format",
@@ -25091,6 +26103,7 @@ function _lowercase(params) {
25091
26103
  ...normalizeParams(params),
25092
26104
  });
25093
26105
  }
26106
+ // @__NO_SIDE_EFFECTS__
25094
26107
  function _uppercase(params) {
25095
26108
  return new $ZodCheckUpperCase({
25096
26109
  check: "string_format",
@@ -25098,6 +26111,7 @@ function _uppercase(params) {
25098
26111
  ...normalizeParams(params),
25099
26112
  });
25100
26113
  }
26114
+ // @__NO_SIDE_EFFECTS__
25101
26115
  function _includes(includes, params) {
25102
26116
  return new $ZodCheckIncludes({
25103
26117
  check: "string_format",
@@ -25106,6 +26120,7 @@ function _includes(includes, params) {
25106
26120
  includes,
25107
26121
  });
25108
26122
  }
26123
+ // @__NO_SIDE_EFFECTS__
25109
26124
  function _startsWith(prefix, params) {
25110
26125
  return new $ZodCheckStartsWith({
25111
26126
  check: "string_format",
@@ -25114,6 +26129,7 @@ function _startsWith(prefix, params) {
25114
26129
  prefix,
25115
26130
  });
25116
26131
  }
26132
+ // @__NO_SIDE_EFFECTS__
25117
26133
  function _endsWith(suffix, params) {
25118
26134
  return new $ZodCheckEndsWith({
25119
26135
  check: "string_format",
@@ -25122,6 +26138,7 @@ function _endsWith(suffix, params) {
25122
26138
  suffix,
25123
26139
  });
25124
26140
  }
26141
+ // @__NO_SIDE_EFFECTS__
25125
26142
  function _overwrite(tx) {
25126
26143
  return new $ZodCheckOverwrite({
25127
26144
  check: "overwrite",
@@ -25129,25 +26146,31 @@ function _overwrite(tx) {
25129
26146
  });
25130
26147
  }
25131
26148
  // normalize
26149
+ // @__NO_SIDE_EFFECTS__
25132
26150
  function _normalize(form) {
25133
26151
  return _overwrite((input) => input.normalize(form));
25134
26152
  }
25135
26153
  // trim
26154
+ // @__NO_SIDE_EFFECTS__
25136
26155
  function _trim() {
25137
26156
  return _overwrite((input) => input.trim());
25138
26157
  }
25139
26158
  // toLowerCase
26159
+ // @__NO_SIDE_EFFECTS__
25140
26160
  function _toLowerCase() {
25141
26161
  return _overwrite((input) => input.toLowerCase());
25142
26162
  }
25143
26163
  // toUpperCase
26164
+ // @__NO_SIDE_EFFECTS__
25144
26165
  function _toUpperCase() {
25145
26166
  return _overwrite((input) => input.toUpperCase());
25146
26167
  }
25147
26168
  // slugify
26169
+ // @__NO_SIDE_EFFECTS__
25148
26170
  function _slugify() {
25149
26171
  return _overwrite((input) => slugify(input));
25150
26172
  }
26173
+ // @__NO_SIDE_EFFECTS__
25151
26174
  function _array(Class, element, params) {
25152
26175
  return new Class({
25153
26176
  type: "array",
@@ -25159,6 +26182,7 @@ function _array(Class, element, params) {
25159
26182
  });
25160
26183
  }
25161
26184
  // same as _custom but defaults to abort:false
26185
+ // @__NO_SIDE_EFFECTS__
25162
26186
  function _refine(Class, fn, _params) {
25163
26187
  const schema = new Class({
25164
26188
  type: "custom",
@@ -25168,6 +26192,7 @@ function _refine(Class, fn, _params) {
25168
26192
  });
25169
26193
  return schema;
25170
26194
  }
26195
+ // @__NO_SIDE_EFFECTS__
25171
26196
  function _superRefine(fn) {
25172
26197
  const ch = _check((payload) => {
25173
26198
  payload.addIssue = (issue$1) => {
@@ -25190,6 +26215,7 @@ function _superRefine(fn) {
25190
26215
  });
25191
26216
  return ch;
25192
26217
  }
26218
+ // @__NO_SIDE_EFFECTS__
25193
26219
  function _check(fn, params) {
25194
26220
  const ch = new $ZodCheck({
25195
26221
  check: "custom",
@@ -25256,14 +26282,7 @@ function process$1(schema, ctx, _params = { path: [], schemaPath: [] }) {
25256
26282
  schemaPath: [..._params.schemaPath, schema],
25257
26283
  path: _params.path,
25258
26284
  };
25259
- const parent = schema._zod.parent;
25260
- if (parent) {
25261
- // schema was cloned from another schema
25262
- result.ref = parent;
25263
- process$1(parent, ctx, params);
25264
- ctx.seen.get(parent).isParent = true;
25265
- }
25266
- else if (schema._zod.processJSONSchema) {
26285
+ if (schema._zod.processJSONSchema) {
25267
26286
  schema._zod.processJSONSchema(ctx, result.schema, params);
25268
26287
  }
25269
26288
  else {
@@ -25274,6 +26293,14 @@ function process$1(schema, ctx, _params = { path: [], schemaPath: [] }) {
25274
26293
  }
25275
26294
  processor(schema, ctx, _json, params);
25276
26295
  }
26296
+ const parent = schema._zod.parent;
26297
+ if (parent) {
26298
+ // Also set ref if processor didn't (for inheritance)
26299
+ if (!result.ref)
26300
+ result.ref = parent;
26301
+ process$1(parent, ctx, params);
26302
+ ctx.seen.get(parent).isParent = true;
26303
+ }
25277
26304
  }
25278
26305
  // metadata
25279
26306
  const meta = ctx.metadataRegistry.get(schema);
@@ -25299,6 +26326,18 @@ function extractDefs(ctx, schema
25299
26326
  const root = ctx.seen.get(schema);
25300
26327
  if (!root)
25301
26328
  throw new Error("Unprocessed schema. This is a bug in Zod.");
26329
+ // Track ids to detect duplicates across different schemas
26330
+ const idToSchema = new Map();
26331
+ for (const entry of ctx.seen.entries()) {
26332
+ const id = ctx.metadataRegistry.get(entry[0])?.id;
26333
+ if (id) {
26334
+ const existing = idToSchema.get(id);
26335
+ if (existing && existing !== entry[0]) {
26336
+ throw new Error(`Duplicate schema id "${id}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);
26337
+ }
26338
+ idToSchema.set(id, entry[0]);
26339
+ }
26340
+ }
25302
26341
  // returns a ref to the schema
25303
26342
  // defId will be empty if the ref points to an external schema (or #)
25304
26343
  const makeURI = (entry) => {
@@ -25400,43 +26439,84 @@ function extractDefs(ctx, schema
25400
26439
  }
25401
26440
  }
25402
26441
  function finalize(ctx, schema) {
25403
- //
25404
- // iterate over seen map;
25405
26442
  const root = ctx.seen.get(schema);
25406
26443
  if (!root)
25407
26444
  throw new Error("Unprocessed schema. This is a bug in Zod.");
25408
- // flatten _refs
26445
+ // flatten refs - inherit properties from parent schemas
25409
26446
  const flattenRef = (zodSchema) => {
25410
26447
  const seen = ctx.seen.get(zodSchema);
26448
+ // already processed
26449
+ if (seen.ref === null)
26450
+ return;
25411
26451
  const schema = seen.def ?? seen.schema;
25412
26452
  const _cached = { ...schema };
25413
- // already seen
25414
- if (seen.ref === null) {
25415
- return;
25416
- }
25417
- // flatten ref if defined
25418
26453
  const ref = seen.ref;
25419
- seen.ref = null; // prevent recursion
26454
+ seen.ref = null; // prevent infinite recursion
25420
26455
  if (ref) {
25421
26456
  flattenRef(ref);
26457
+ const refSeen = ctx.seen.get(ref);
26458
+ const refSchema = refSeen.schema;
25422
26459
  // merge referenced schema into current
25423
- const refSchema = ctx.seen.get(ref).schema;
25424
26460
  if (refSchema.$ref && (ctx.target === "draft-07" || ctx.target === "draft-04" || ctx.target === "openapi-3.0")) {
26461
+ // older drafts can't combine $ref with other properties
25425
26462
  schema.allOf = schema.allOf ?? [];
25426
26463
  schema.allOf.push(refSchema);
25427
26464
  }
25428
26465
  else {
25429
26466
  Object.assign(schema, refSchema);
25430
- Object.assign(schema, _cached); // prevent overwriting any fields in the original schema
26467
+ }
26468
+ // restore child's own properties (child wins)
26469
+ Object.assign(schema, _cached);
26470
+ const isParentRef = zodSchema._zod.parent === ref;
26471
+ // For parent chain, child is a refinement - remove parent-only properties
26472
+ if (isParentRef) {
26473
+ for (const key in schema) {
26474
+ if (key === "$ref" || key === "allOf")
26475
+ continue;
26476
+ if (!(key in _cached)) {
26477
+ delete schema[key];
26478
+ }
26479
+ }
26480
+ }
26481
+ // When ref was extracted to $defs, remove properties that match the definition
26482
+ if (refSchema.$ref) {
26483
+ for (const key in schema) {
26484
+ if (key === "$ref" || key === "allOf")
26485
+ continue;
26486
+ if (key in refSeen.def && JSON.stringify(schema[key]) === JSON.stringify(refSeen.def[key])) {
26487
+ delete schema[key];
26488
+ }
26489
+ }
26490
+ }
26491
+ }
26492
+ // If parent was extracted (has $ref), propagate $ref to this schema
26493
+ // This handles cases like: readonly().meta({id}).describe()
26494
+ // where processor sets ref to innerType but parent should be referenced
26495
+ const parent = zodSchema._zod.parent;
26496
+ if (parent && parent !== ref) {
26497
+ // Ensure parent is processed first so its def has inherited properties
26498
+ flattenRef(parent);
26499
+ const parentSeen = ctx.seen.get(parent);
26500
+ if (parentSeen?.schema.$ref) {
26501
+ schema.$ref = parentSeen.schema.$ref;
26502
+ // De-duplicate with parent's definition
26503
+ if (parentSeen.def) {
26504
+ for (const key in schema) {
26505
+ if (key === "$ref" || key === "allOf")
26506
+ continue;
26507
+ if (key in parentSeen.def && JSON.stringify(schema[key]) === JSON.stringify(parentSeen.def[key])) {
26508
+ delete schema[key];
26509
+ }
26510
+ }
26511
+ }
25431
26512
  }
25432
26513
  }
25433
26514
  // execute overrides
25434
- if (!seen.isParent)
25435
- ctx.override({
25436
- zodSchema: zodSchema,
25437
- jsonSchema: schema,
25438
- path: seen.path ?? [],
25439
- });
26515
+ ctx.override({
26516
+ zodSchema: zodSchema,
26517
+ jsonSchema: schema,
26518
+ path: seen.path ?? [],
26519
+ });
25440
26520
  };
25441
26521
  for (const entry of [...ctx.seen.entries()].reverse()) {
25442
26522
  flattenRef(entry[0]);
@@ -25489,8 +26569,8 @@ function finalize(ctx, schema) {
25489
26569
  value: {
25490
26570
  ...schema["~standard"],
25491
26571
  jsonSchema: {
25492
- input: createStandardJSONSchemaMethod(schema, "input"),
25493
- output: createStandardJSONSchemaMethod(schema, "output"),
26572
+ input: createStandardJSONSchemaMethod(schema, "input", ctx.processors),
26573
+ output: createStandardJSONSchemaMethod(schema, "output", ctx.processors),
25494
26574
  },
25495
26575
  },
25496
26576
  enumerable: false,
@@ -25569,9 +26649,9 @@ const createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
25569
26649
  extractDefs(ctx, schema);
25570
26650
  return finalize(ctx, schema);
25571
26651
  };
25572
- const createStandardJSONSchemaMethod = (schema, io) => (params) => {
26652
+ const createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
25573
26653
  const { libraryOptions, target } = params ?? {};
25574
- const ctx = initializeContext({ ...(libraryOptions ?? {}), target, io, processors: {} });
26654
+ const ctx = initializeContext({ ...(libraryOptions ?? {}), target, io, processors });
25575
26655
  process$1(schema, ctx);
25576
26656
  extractDefs(ctx, schema);
25577
26657
  return finalize(ctx, schema);
@@ -25599,6 +26679,11 @@ const stringProcessor = (schema, ctx, _json, _params) => {
25599
26679
  json.format = formatMap[format] ?? format;
25600
26680
  if (json.format === "")
25601
26681
  delete json.format; // empty format is not valid
26682
+ // JSON Schema format: "time" requires a full time with offset or Z
26683
+ // z.iso.time() does not include timezone information, so format: "time" should never be used
26684
+ if (format === "time") {
26685
+ delete json.format;
26686
+ }
25602
26687
  }
25603
26688
  if (contentEncoding)
25604
26689
  json.contentEncoding = contentEncoding;
@@ -25956,8 +27041,11 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
25956
27041
  ...(def.checks ?? []),
25957
27042
  ...checks.map((ch) => typeof ch === "function" ? { _zod: { check: ch, def: { check: "custom" }, onattach: [] } } : ch),
25958
27043
  ],
25959
- }));
27044
+ }), {
27045
+ parent: true,
27046
+ });
25960
27047
  };
27048
+ inst.with = inst.check;
25961
27049
  inst.clone = (def, params) => clone(inst, def, params);
25962
27050
  inst.brand = () => inst;
25963
27051
  inst.register = ((reg, meta) => {
@@ -25985,6 +27073,7 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
25985
27073
  inst.overwrite = (fn) => inst.check(_overwrite(fn));
25986
27074
  // wrappers
25987
27075
  inst.optional = () => optional(inst);
27076
+ inst.exactOptional = () => exactOptional(inst);
25988
27077
  inst.nullable = () => nullable(inst);
25989
27078
  inst.nullish = () => optional(nullable(inst));
25990
27079
  inst.nonoptional = (params) => nonoptional(inst, params);
@@ -26021,6 +27110,7 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
26021
27110
  // helpers
26022
27111
  inst.isOptional = () => inst.safeParse(undefined).success;
26023
27112
  inst.isNullable = () => inst.safeParse(null).success;
27113
+ inst.apply = (fn) => fn(inst);
26024
27114
  return inst;
26025
27115
  });
26026
27116
  /** @internal */
@@ -26418,6 +27508,18 @@ function optional(innerType) {
26418
27508
  innerType: innerType,
26419
27509
  });
26420
27510
  }
27511
+ const ZodExactOptional = /*@__PURE__*/ $constructor("ZodExactOptional", (inst, def) => {
27512
+ $ZodExactOptional.init(inst, def);
27513
+ ZodType.init(inst, def);
27514
+ inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);
27515
+ inst.unwrap = () => inst._zod.def.innerType;
27516
+ });
27517
+ function exactOptional(innerType) {
27518
+ return new ZodExactOptional({
27519
+ type: "optional",
27520
+ innerType: innerType,
27521
+ });
27522
+ }
26421
27523
  const ZodNullable = /*@__PURE__*/ $constructor("ZodNullable", (inst, def) => {
26422
27524
  $ZodNullable.init(inst, def);
26423
27525
  ZodType.init(inst, def);
@@ -26528,6 +27630,16 @@ function superRefine(fn) {
26528
27630
  return _superRefine(fn);
26529
27631
  }
26530
27632
 
27633
+ var __defProp$2 = Object.defineProperty;
27634
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
27635
+ var __decorateClass$2 = (decorators, target, key, kind) => {
27636
+ var result = __getOwnPropDesc$2(target, key) ;
27637
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
27638
+ if (decorator = decorators[i])
27639
+ result = (decorator(target, key, result) ) || result;
27640
+ if (result) __defProp$2(target, key, result);
27641
+ return result;
27642
+ };
26531
27643
  class BaseRoom {
26532
27644
  constructor() {
26533
27645
  /**
@@ -26548,6 +27660,9 @@ class BaseRoom {
26548
27660
  */
26549
27661
  this.database = signal({});
26550
27662
  }
27663
+ async onStart() {
27664
+ await lastValueFrom(this.hooks.callHooks("server-databaseHooks-load", this));
27665
+ }
26551
27666
  /**
26552
27667
  * Add data to the room's database
26553
27668
  *
@@ -26619,7 +27734,101 @@ class BaseRoom {
26619
27734
  this.database.set(database);
26620
27735
  return true;
26621
27736
  }
27737
+ /**
27738
+ * Get the hooks system for this map
27739
+ *
27740
+ * Returns the dependency-injected Hooks instance that allows you to trigger
27741
+ * and listen to various game events.
27742
+ *
27743
+ * @returns The Hooks instance for this map
27744
+ *
27745
+ * @example
27746
+ * ```ts
27747
+ * // Trigger a custom hook
27748
+ * map.hooks.callHooks('custom-event', data).subscribe();
27749
+ * ```
27750
+ */
27751
+ get hooks() {
27752
+ return inject(ModulesToken, context);
27753
+ }
27754
+ /**
27755
+ * Resolve complex snapshot entries (e.g. inventory items) before load.
27756
+ */
27757
+ async onSessionRestore({ userSnapshot, user }) {
27758
+ if (!userSnapshot) {
27759
+ return userSnapshot;
27760
+ }
27761
+ let resolvedSnapshot = userSnapshot;
27762
+ if (user && typeof user.resolveItemsSnapshot === "function") {
27763
+ resolvedSnapshot = user.resolveItemsSnapshot(resolvedSnapshot, this);
27764
+ }
27765
+ if (user && typeof user.resolveSkillsSnapshot === "function") {
27766
+ resolvedSnapshot = user.resolveSkillsSnapshot(resolvedSnapshot, this);
27767
+ }
27768
+ if (user && typeof user.resolveStatesSnapshot === "function") {
27769
+ resolvedSnapshot = user.resolveStatesSnapshot(resolvedSnapshot, this);
27770
+ }
27771
+ if (user && typeof user.resolveClassSnapshot === "function") {
27772
+ resolvedSnapshot = user.resolveClassSnapshot(resolvedSnapshot, this);
27773
+ }
27774
+ if (user && typeof user.resolveEquipmentsSnapshot === "function") {
27775
+ resolvedSnapshot = user.resolveEquipmentsSnapshot(resolvedSnapshot, this);
27776
+ }
27777
+ return resolvedSnapshot;
27778
+ }
27779
+ async listSaveSlots(player, value) {
27780
+ const storage = resolveSaveStorageStrategy();
27781
+ try {
27782
+ const slots = await storage.list(player);
27783
+ player.emit("save.list.result", { requestId: value?.requestId, slots });
27784
+ return slots;
27785
+ } catch (error) {
27786
+ player.showNotification(error?.message || "save.list failed");
27787
+ return [];
27788
+ }
27789
+ }
27790
+ async saveSlot(player, value) {
27791
+ const storage = resolveSaveStorageStrategy();
27792
+ try {
27793
+ if (typeof value?.index !== "number") {
27794
+ throw new Error("save.save requires an index");
27795
+ }
27796
+ const result = await player.save(value.index, value?.meta, { reason: "manual", source: "gui" });
27797
+ if (!result) {
27798
+ throw new Error("save.save is not allowed");
27799
+ }
27800
+ const slots = await storage.list(player);
27801
+ player.emit("save.save.result", { requestId: value?.requestId, index: result.index, slots });
27802
+ } catch (error) {
27803
+ player.emit("save.error", { requestId: value?.requestId, message: error?.message || "save.save failed" });
27804
+ }
27805
+ }
27806
+ async loadSlot(player, value) {
27807
+ try {
27808
+ if (typeof value?.index !== "number") {
27809
+ throw new Error("save.load requires an index");
27810
+ }
27811
+ const result = await player.load(value.index, { reason: "load", source: "gui" }, { changeMap: true });
27812
+ player.emit("save.load.result", {
27813
+ requestId: value?.requestId,
27814
+ index: value.index,
27815
+ ok: result.ok,
27816
+ slot: result.slot
27817
+ });
27818
+ } catch (error) {
27819
+ player.emit("save.error", { requestId: value?.requestId, message: error?.message || "save.load failed" });
27820
+ }
27821
+ }
26622
27822
  }
27823
+ __decorateClass$2([
27824
+ Action("save.list")
27825
+ ], BaseRoom.prototype, "listSaveSlots");
27826
+ __decorateClass$2([
27827
+ Action("save.save")
27828
+ ], BaseRoom.prototype, "saveSlot");
27829
+ __decorateClass$2([
27830
+ Action("save.load")
27831
+ ], BaseRoom.prototype, "loadSlot");
26623
27832
 
26624
27833
  var __defProp$1 = Object.defineProperty;
26625
27834
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
@@ -26740,6 +27949,9 @@ let RpgMap = class extends RpgCommonMap {
26740
27949
  this.loop();
26741
27950
  }
26742
27951
  }
27952
+ onStart() {
27953
+ return BaseRoom.prototype.onStart.call(this);
27954
+ }
26743
27955
  /**
26744
27956
  * Setup collision detection between players, events, and shapes
26745
27957
  *
@@ -26946,7 +28158,7 @@ let RpgMap = class extends RpgCommonMap {
26946
28158
  } else {
26947
28159
  player.map = this;
26948
28160
  }
26949
- player.context = context$1;
28161
+ player.context = context;
26950
28162
  player.conn = conn;
26951
28163
  player._onInit();
26952
28164
  this.dataIsReady$.pipe(
@@ -27009,10 +28221,16 @@ let RpgMap = class extends RpgCommonMap {
27009
28221
  * ```
27010
28222
  */
27011
28223
  get hooks() {
27012
- return inject$1(context$1, ModulesToken);
28224
+ return BaseRoom.prototype.hooks;
28225
+ }
28226
+ async onSessionRestore(payload) {
28227
+ return await BaseRoom.prototype.onSessionRestore.call(this, payload);
27013
28228
  }
27014
- guiInteraction(player, value) {
27015
- this.hooks.callHooks("server-player-guiInteraction", player, value);
28229
+ async guiInteraction(player, value) {
28230
+ const gui = player.getGui(value.guiId);
28231
+ if (gui) {
28232
+ await gui.emit(value.name, value.data);
28233
+ }
27016
28234
  player.syncChanges();
27017
28235
  }
27018
28236
  guiExit(player, { guiId, data }) {
@@ -27041,6 +28259,15 @@ let RpgMap = class extends RpgCommonMap {
27041
28259
  });
27042
28260
  }
27043
28261
  }
28262
+ async saveSlot(player, value) {
28263
+ BaseRoom.prototype.saveSlot(player, value);
28264
+ }
28265
+ async loadSlot(player, value) {
28266
+ BaseRoom.prototype.loadSlot(player, value);
28267
+ }
28268
+ async listSaveSlots(player, value) {
28269
+ return await BaseRoom.prototype.listSaveSlots(player, value);
28270
+ }
27044
28271
  async updateMap(request) {
27045
28272
  const map = await request.json();
27046
28273
  this.data.set(map);
@@ -27529,7 +28756,7 @@ let RpgMap = class extends RpgCommonMap {
27529
28756
  if (event.name) eventInstance.name.set(event.name);
27530
28757
  }
27531
28758
  eventInstance.map = this;
27532
- eventInstance.context = context$1;
28759
+ eventInstance.context = context;
27533
28760
  await eventInstance.teleport({ x, y });
27534
28761
  this.events()[id] = eventInstance;
27535
28762
  await eventInstance.execMethod("onInit");
@@ -28208,6 +29435,15 @@ __decorateClass$1([
28208
29435
  __decorateClass$1([
28209
29436
  Action("move")
28210
29437
  ], RpgMap.prototype, "onInput", 1);
29438
+ __decorateClass$1([
29439
+ Action("save.save")
29440
+ ], RpgMap.prototype, "saveSlot", 1);
29441
+ __decorateClass$1([
29442
+ Action("save.load")
29443
+ ], RpgMap.prototype, "loadSlot", 1);
29444
+ __decorateClass$1([
29445
+ Action("save.list")
29446
+ ], RpgMap.prototype, "listSaveSlots", 1);
28211
29447
  __decorateClass$1([
28212
29448
  Request2({
28213
29449
  path: "/map/update",
@@ -28246,17 +29482,25 @@ let LobbyRoom = class extends BaseRoom {
28246
29482
  this.autoSync = false;
28247
29483
  }
28248
29484
  }
28249
- onJoin(player, conn) {
29485
+ async onJoin(player, conn) {
28250
29486
  player.map = this;
28251
- player.context = context$1;
29487
+ player.context = context;
28252
29488
  player.conn = conn;
28253
- const hooks = inject$1(context$1, ModulesToken);
28254
- hooks.callHooks("server-player-onConnected", player).subscribe();
29489
+ this.hooks.callHooks("server-player-onConnected", player).subscribe();
29490
+ }
29491
+ async guiInteraction(player, value) {
29492
+ const id = value.data.id;
29493
+ if (id === "start") {
29494
+ this.hooks.callHooks("server-player-onStart", player).subscribe();
29495
+ }
28255
29496
  }
28256
29497
  };
28257
29498
  __decorateClass([
28258
29499
  users(RpgPlayer)
28259
29500
  ], LobbyRoom.prototype, "players", 2);
29501
+ __decorateClass([
29502
+ Action("gui.interaction")
29503
+ ], LobbyRoom.prototype, "guiInteraction", 1);
28260
29504
  LobbyRoom = __decorateClass([
28261
29505
  Room({
28262
29506
  path: "lobby-{id}"
@@ -28270,19 +29514,6 @@ class RpgServerEngine extends Server {
28270
29514
  }
28271
29515
  }
28272
29516
 
28273
- let context = null;
28274
- function inject(service, _context) {
28275
- const c = _context ?? context;
28276
- if (!c) throw new Error("Context is not set. use setInject() to set the context");
28277
- return inject$1(c, service);
28278
- }
28279
- function setInject(_context) {
28280
- context = _context;
28281
- }
28282
- function clearInject() {
28283
- context = null;
28284
- }
28285
-
28286
29517
  function createServer(options) {
28287
29518
  return class extends RpgServerEngine {
28288
29519
  constructor() {
@@ -28290,8 +29521,8 @@ function createServer(options) {
28290
29521
  this.config = options;
28291
29522
  }
28292
29523
  async onStart() {
28293
- setInject(context$1);
28294
- await injector(context$1, options.providers);
29524
+ setInject(context);
29525
+ await injector(context, options.providers);
28295
29526
  return super.onStart();
28296
29527
  }
28297
29528
  };
@@ -28616,6 +29847,60 @@ function provideServerModules(modules) {
28616
29847
  });
28617
29848
  }
28618
29849
 
29850
+ class LocalStorageSaveStorageStrategy {
29851
+ constructor(options = {}) {
29852
+ this.key = options.key ?? "rpgjs-save-slots";
29853
+ }
29854
+ async list(_player) {
29855
+ return this.stripSnapshots(this.readSlots());
29856
+ }
29857
+ async get(_player, index) {
29858
+ const slots = this.readSlots();
29859
+ return slots[index] ?? null;
29860
+ }
29861
+ async save(_player, index, snapshot, meta) {
29862
+ const slots = this.readSlots();
29863
+ const existing = slots[index];
29864
+ slots[index] = {
29865
+ ...existing ?? {},
29866
+ ...meta,
29867
+ snapshot
29868
+ };
29869
+ this.writeSlots(slots);
29870
+ }
29871
+ async delete(_player, index) {
29872
+ const slots = this.readSlots();
29873
+ slots[index] = null;
29874
+ this.writeSlots(slots);
29875
+ }
29876
+ readSlots() {
29877
+ if (typeof localStorage === "undefined") {
29878
+ return [];
29879
+ }
29880
+ const raw = localStorage.getItem(this.key);
29881
+ if (!raw) return [];
29882
+ try {
29883
+ const parsed = JSON.parse(raw);
29884
+ return Array.isArray(parsed) ? parsed : [];
29885
+ } catch {
29886
+ return [];
29887
+ }
29888
+ }
29889
+ writeSlots(slots) {
29890
+ if (typeof localStorage === "undefined") {
29891
+ return;
29892
+ }
29893
+ localStorage.setItem(this.key, JSON.stringify(slots));
29894
+ }
29895
+ stripSnapshots(slots) {
29896
+ return slots.map((slot) => {
29897
+ if (!slot) return null;
29898
+ const { snapshot, ...meta } = slot;
29899
+ return meta;
29900
+ });
29901
+ }
29902
+ }
29903
+
28619
29904
  var EventMode = /* @__PURE__ */ ((EventMode2) => {
28620
29905
  EventMode2["Shared"] = "shared";
28621
29906
  EventMode2["Scenario"] = "scenario";
@@ -28661,5 +29946,5 @@ function MapData(options) {
28661
29946
  };
28662
29947
  }
28663
29948
 
28664
- export { AGI, AGI_CURVE, ATK, ArraySubject, COEFFICIENT_ELEMENTS, Components, DAMAGE_CRITICAL, DAMAGE_GUARD, DAMAGE_PHYSIC, DAMAGE_SKILL, DEX, DEX_CURVE, DialogGui, DialogPosition, EventData, EventMode, Frequency, Gui, INT, INT_CURVE, MAXHP, MAXHP_CURVE, MAXSP, MAXSP_CURVE, MapData, MenuGui, Move, NotificationGui, ObjectSubject, PDEF, RpgEvent, RpgMap, RpgModule, RpgPlayer, RpgServerEngine, RpgShape, SDEF, STR, STR_CURVE, ShopGui, Speed, WithMoveManager, clearInject, computed, context, createServer, effect, inject, isArraySubject, isComputed, isObjectSubject, isSignal, linkedSignal, provideServerModules, setInject, signal, untracked };
29949
+ export { AGI, AGI_CURVE, ATK, ArraySubject, AutoSaveToken, COEFFICIENT_ELEMENTS, Components, DAMAGE_CRITICAL, DAMAGE_GUARD, DAMAGE_PHYSIC, DAMAGE_SKILL, DEX, DEX_CURVE, DialogGui, DialogPosition, EventData, EventMode, Frequency, GameoverGui, Gui, INT, INT_CURVE, InMemorySaveStorageStrategy, LocalStorageSaveStorageStrategy, MAXHP, MAXHP_CURVE, MAXSP, MAXSP_CURVE, MapData, MenuGui, Move, NotificationGui, ObjectSubject, PDEF, RpgEvent, RpgMap, RpgModule, RpgPlayer, RpgServerEngine, RpgShape, SDEF, STR, STR_CURVE, SaveLoadGui, SaveStorageToken, ShopGui, Speed, TitleGui, WithMoveManager, buildSaveSlotMeta, clearInject, computed, context$1 as context, createServer, effect, inject, isArraySubject, isComputed, isObjectSubject, isSignal, linkedSignal, provideAutoSave, provideSaveStorage, provideServerModules, resolveAutoSaveStrategy, resolveSaveSlot, resolveSaveStorageStrategy, setInject, shouldAutoSave, signal, untracked };
28665
29950
  //# sourceMappingURL=index.js.map