@rpgjs/server 5.0.0-alpha.25 → 5.0.0-alpha.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5608,13 +5608,13 @@ function fromIterable(iterable) {
5608
5608
  }
5609
5609
  function fromAsyncIterable(asyncIterable) {
5610
5610
  return new Observable(function (subscriber) {
5611
- process$1(asyncIterable, subscriber).catch(function (err) { return subscriber.error(err); });
5611
+ process$2(asyncIterable, subscriber).catch(function (err) { return subscriber.error(err); });
5612
5612
  });
5613
5613
  }
5614
5614
  function fromReadableStreamLike(readableStream) {
5615
5615
  return fromAsyncIterable(readableStreamLikeToAsyncGenerator(readableStream));
5616
5616
  }
5617
- function process$1(asyncIterable, subscriber) {
5617
+ function process$2(asyncIterable, subscriber) {
5618
5618
  var asyncIterable_1, asyncIterable_1_1;
5619
5619
  var e_2, _a;
5620
5620
  return __awaiter(this, void 0, void 0, function () {
@@ -6065,7 +6065,7 @@ function toArray() {
6065
6065
  });
6066
6066
  }
6067
6067
 
6068
- function finalize(callback) {
6068
+ function finalize$1(callback) {
6069
6069
  return operate(function (source, subscriber) {
6070
6070
  try {
6071
6071
  source.subscribe(subscriber);
@@ -6517,7 +6517,7 @@ function computed(computeFunction, disposableFn) {
6517
6517
  }
6518
6518
  return dep.observable;
6519
6519
  });
6520
- const computedObservable = combineLatest(observables).pipe(filter(() => !init), map(() => computeFunction()), finalize(() => disposableFn?.()));
6520
+ const computedObservable = combineLatest(observables).pipe(filter(() => !init), map(() => computeFunction()), finalize$1(() => disposableFn?.()));
6521
6521
  const fn = /* @__PURE__ */ __name$2(function() {
6522
6522
  trackDependency(fn);
6523
6523
  return lastComputedValue;
@@ -7743,12 +7743,46 @@ var Server = class {
7743
7743
  load(instance, tmpObject, true);
7744
7744
  }, "loadMemory");
7745
7745
  instance.$memoryAll = {};
7746
+ instance.$autoSync = instance["autoSync"] !== false;
7747
+ instance.$pendingSync = /* @__PURE__ */ new Map();
7748
+ instance.$pendingInitialSync = /* @__PURE__ */ new Map();
7746
7749
  instance.$send = (conn, obj) => {
7747
7750
  return this.send(conn, obj, instance);
7748
7751
  };
7749
7752
  instance.$broadcast = (obj) => {
7750
7753
  return this.broadcast(obj, instance);
7751
7754
  };
7755
+ instance.$applySync = () => {
7756
+ let packet;
7757
+ if (instance.$pendingSync.size > 0) {
7758
+ if (options.getMemoryAll) {
7759
+ buildObject(instance.$pendingSync, instance.$memoryAll);
7760
+ }
7761
+ packet = buildObject(instance.$pendingSync, instance.$memoryAll);
7762
+ instance.$pendingSync.clear();
7763
+ } else {
7764
+ packet = instance.$memoryAll;
7765
+ }
7766
+ const pendingConnections = new Set(instance.$pendingInitialSync.keys());
7767
+ for (const [conn, publicId] of instance.$pendingInitialSync) {
7768
+ this.send(conn, {
7769
+ type: "sync",
7770
+ value: {
7771
+ pId: publicId,
7772
+ ...packet
7773
+ }
7774
+ }, instance);
7775
+ }
7776
+ instance.$pendingInitialSync.clear();
7777
+ for (const conn of this.room.getConnections()) {
7778
+ if (!pendingConnections.has(conn)) {
7779
+ this.send(conn, {
7780
+ type: "sync",
7781
+ value: packet
7782
+ }, instance);
7783
+ }
7784
+ }
7785
+ };
7752
7786
  instance.$sessionTransfer = async (conn, targetRoomId) => {
7753
7787
  let user;
7754
7788
  const signal2 = this.getUsersProperty(instance);
@@ -7815,6 +7849,13 @@ var Server = class {
7815
7849
  init = false;
7816
7850
  return;
7817
7851
  }
7852
+ if (!instance.$autoSync) {
7853
+ for (const [path, value] of values) {
7854
+ instance.$pendingSync.set(path, value);
7855
+ }
7856
+ values.clear();
7857
+ return;
7858
+ }
7818
7859
  const packet = buildObject(values, instance.$memoryAll);
7819
7860
  this.broadcast({
7820
7861
  type: "sync",
@@ -7839,11 +7880,12 @@ var Server = class {
7839
7880
  values.clear();
7840
7881
  }, "persistCb");
7841
7882
  syncClass(instance, {
7842
- onSync: throttle(syncCb, instance["throttleSync"] ?? 500),
7843
- onPersist: throttle(persistCb, instance["throttleStorage"] ?? 2e3)
7883
+ onSync: instance["throttleSync"] ? throttle(syncCb, instance["throttleSync"]) : syncCb,
7884
+ onPersist: instance["throttleStorage"] ? throttle(persistCb, instance["throttleStorage"]) : persistCb
7844
7885
  });
7845
7886
  await loadMemory();
7846
7887
  initPersist = false;
7888
+ init = false;
7847
7889
  return instance;
7848
7890
  }
7849
7891
  /**
@@ -8049,13 +8091,17 @@ var Server = class {
8049
8091
  publicId
8050
8092
  });
8051
8093
  await awaitReturn(subRoom["onJoin"]?.(user, conn, ctx));
8052
- this.send(conn, {
8053
- type: "sync",
8054
- value: {
8055
- pId: publicId,
8056
- ...subRoom.$memoryAll
8057
- }
8058
- }, subRoom);
8094
+ if (subRoom.$autoSync) {
8095
+ this.send(conn, {
8096
+ type: "sync",
8097
+ value: {
8098
+ pId: publicId,
8099
+ ...subRoom.$memoryAll
8100
+ }
8101
+ }, subRoom);
8102
+ } else {
8103
+ subRoom.$pendingInitialSync.set(conn, publicId);
8104
+ }
8059
8105
  }
8060
8106
  /**
8061
8107
  * @method onConnect
@@ -8340,6 +8386,9 @@ var Server = class {
8340
8386
  if (!subRoom) {
8341
8387
  return;
8342
8388
  }
8389
+ if (subRoom.$pendingInitialSync) {
8390
+ subRoom.$pendingInitialSync.delete(conn);
8391
+ }
8343
8392
  const signal2 = this.getUsersProperty(subRoom);
8344
8393
  if (!conn.state) {
8345
8394
  return;
@@ -9777,10 +9826,10 @@ class Item {
9777
9826
  this.quantity = signal(1);
9778
9827
  this.onAdd = () => {
9779
9828
  };
9780
- this.description.set(data.description);
9781
- this.price.set(data.price);
9782
- this.name.set(data.name);
9783
- this.onAdd = data.onAdd?.bind(this) ?? (() => {
9829
+ this.description.set(data?.description ?? "");
9830
+ this.price.set(data?.price ?? 0);
9831
+ this.name.set(data?.name ?? "");
9832
+ this.onAdd = data?.onAdd?.bind(this) ?? (() => {
9784
9833
  });
9785
9834
  }
9786
9835
  }
@@ -9934,6 +9983,9 @@ __decorateClass$3([
9934
9983
  __decorateClass$3([
9935
9984
  sync()
9936
9985
  ], RpgCommonPlayer.prototype, "_gold");
9986
+ __decorateClass$3([
9987
+ sync()
9988
+ ], RpgCommonPlayer.prototype, "animationName");
9937
9989
  __decorateClass$3([
9938
9990
  sync()
9939
9991
  ], RpgCommonPlayer.prototype, "hpSignal");
@@ -15236,29 +15288,6 @@ class Knockback {
15236
15288
  }
15237
15289
  }
15238
15290
 
15239
- class LinearMove {
15240
- /**
15241
- * Creates a linear movement strategy.
15242
- *
15243
- * @param velocity - Velocity to apply (units per second)
15244
- * @param duration - Optional duration in seconds (undefined for infinite)
15245
- */
15246
- constructor(velocity, duration) {
15247
- this.velocity = velocity;
15248
- this.duration = duration;
15249
- this.elapsed = 0;
15250
- }
15251
- update(body, dt) {
15252
- if (this.duration !== void 0) {
15253
- this.elapsed += dt;
15254
- }
15255
- body.setVelocity(this.velocity);
15256
- }
15257
- isFinished() {
15258
- return this.duration !== void 0 && this.elapsed >= this.duration;
15259
- }
15260
- }
15261
-
15262
15291
  class LinearRepulsion {
15263
15292
  /**
15264
15293
  * @param engine - Physics engine used for spatial queries
@@ -15777,8 +15806,17 @@ class RpgCommonMap {
15777
15806
  });
15778
15807
  this.moveManager = new MovementManager(() => this.physic);
15779
15808
  this.speedScalar = 50;
15809
+ // Default speed scalar for movement
15810
+ // World Maps properties
15811
+ this.tileWidth = 32;
15812
+ this.tileHeight = 32;
15780
15813
  this.physicsAccumulatorMs = 0;
15781
15814
  this.physicsSyncDepth = 0;
15815
+ /**
15816
+ * Whether to automatically subscribe to tick$ for physics updates
15817
+ * Set to false in test environments for manual control with nextTick()
15818
+ */
15819
+ this.autoTickEnabled = true;
15782
15820
  /**
15783
15821
  * Observable representing the game loop tick
15784
15822
  *
@@ -15831,6 +15869,88 @@ class RpgCommonMap {
15831
15869
  get isStandalone() {
15832
15870
  return typeof window !== "undefined";
15833
15871
  }
15872
+ /**
15873
+ * Get the width of the map in pixels
15874
+ *
15875
+ * @returns The width of the map in pixels, or 0 if not loaded
15876
+ *
15877
+ * @example
15878
+ * ```ts
15879
+ * const width = map.widthPx;
15880
+ * console.log(`Map width: ${width}px`);
15881
+ * ```
15882
+ */
15883
+ get widthPx() {
15884
+ return this.data()?.width ?? 0;
15885
+ }
15886
+ /**
15887
+ * Get the height of the map in pixels
15888
+ *
15889
+ * @returns The height of the map in pixels, or 0 if not loaded
15890
+ *
15891
+ * @example
15892
+ * ```ts
15893
+ * const height = map.heightPx;
15894
+ * console.log(`Map height: ${height}px`);
15895
+ * ```
15896
+ */
15897
+ get heightPx() {
15898
+ return this.data()?.height ?? 0;
15899
+ }
15900
+ /**
15901
+ * Get the unique identifier of the map
15902
+ *
15903
+ * @returns The map ID, or empty string if not loaded
15904
+ *
15905
+ * @example
15906
+ * ```ts
15907
+ * const mapId = map.id;
15908
+ * console.log(`Current map: ${mapId}`);
15909
+ * ```
15910
+ */
15911
+ get id() {
15912
+ return this.data()?.id ?? "";
15913
+ }
15914
+ /**
15915
+ * Get the X position of this map in the world coordinate system
15916
+ *
15917
+ * This is used when maps are part of a larger world map. The world position
15918
+ * indicates where this map is located relative to other maps.
15919
+ *
15920
+ * @returns The X position in world coordinates, or 0 if not in a world
15921
+ *
15922
+ * @example
15923
+ * ```ts
15924
+ * const worldX = map.worldX;
15925
+ * console.log(`Map is at world position (${worldX}, ${map.worldY})`);
15926
+ * ```
15927
+ */
15928
+ get worldX() {
15929
+ const worldMaps = this.getWorldMapsManager?.();
15930
+ if (!worldMaps) return 0;
15931
+ const mapId = this.id.startsWith("map-") ? this.id.slice(4) : this.id;
15932
+ return worldMaps.getMapInfo(mapId)?.worldX ?? 0;
15933
+ }
15934
+ /**
15935
+ * Get the Y position of this map in the world coordinate system
15936
+ *
15937
+ * This is used when maps are part of a larger world map. The world position
15938
+ * indicates where this map is located relative to other maps.
15939
+ *
15940
+ * @returns The Y position in world coordinates, or 0 if not in a world
15941
+ *
15942
+ * @example
15943
+ * ```ts
15944
+ * const worldY = map.worldY;
15945
+ * console.log(`Map is at world position (${map.worldX}, ${worldY})`);
15946
+ * ```
15947
+ */
15948
+ get worldY() {
15949
+ const worldMaps = this.getWorldMapsManager?.();
15950
+ if (!worldMaps) return 0;
15951
+ const mapId = this.id.startsWith("map-") ? this.id.slice(4) : this.id;
15952
+ return worldMaps.getMapInfo(mapId)?.worldY ?? 0;
15953
+ }
15834
15954
  /**
15835
15955
  * Clear all physics content and reset to initial state
15836
15956
  *
@@ -15930,9 +16050,11 @@ class RpgCommonMap {
15930
16050
  this.updateCharacterHitbox(event);
15931
16051
  }
15932
16052
  });
15933
- this.tickSubscription = this.tick$.subscribe(({ delta }) => {
15934
- this.runFixedTicks(delta);
15935
- });
16053
+ if (this.autoTickEnabled) {
16054
+ this.tickSubscription = this.tick$.subscribe(({ delta }) => {
16055
+ this.runFixedTicks(delta);
16056
+ });
16057
+ }
15936
16058
  }
15937
16059
  async movePlayer(player, direction) {
15938
16060
  const currentX = player.x();
@@ -15954,12 +16076,8 @@ class RpgCommonMap {
15954
16076
  nextY = currentY + speed;
15955
16077
  break;
15956
16078
  }
15957
- if (typeof player.setIntendedDirection === "function") {
15958
- player.setIntendedDirection(direction);
15959
- } else if (typeof player.changeDirection === "function") {
15960
- player.changeDirection(direction);
15961
- }
15962
- if (typeof player.autoChangeMap === "function") {
16079
+ player.changeDirection(direction);
16080
+ if (typeof player.autoChangeMap === "function" && !player.isEvent()) {
15963
16081
  const mapChanged = await player.autoChangeMap({ x: nextX, y: nextY }, direction);
15964
16082
  if (mapChanged) {
15965
16083
  this.stopMovement(player);
@@ -16043,6 +16161,46 @@ class RpgCommonMap {
16043
16161
  }
16044
16162
  return executed;
16045
16163
  }
16164
+ /**
16165
+ * Manually trigger a single game tick
16166
+ *
16167
+ * This method allows you to manually advance the game by one tick (16ms at 60fps).
16168
+ * It's primarily useful for testing where you need precise control over when
16169
+ * physics updates occur, rather than relying on the automatic tick$ subscription.
16170
+ *
16171
+ * ## Use Cases
16172
+ *
16173
+ * - **Testing**: Control exactly when physics steps occur in unit tests
16174
+ * - **Manual control**: Step through game state manually for debugging
16175
+ * - **Deterministic testing**: Ensure consistent timing in test scenarios
16176
+ *
16177
+ * ## Important
16178
+ *
16179
+ * This method should NOT be used in production code alongside the automatic `tick$`
16180
+ * subscription, as it will cause double-stepping. Use either:
16181
+ * - Automatic ticks (via `loadPhysic()` which subscribes to `tick$`)
16182
+ * - Manual ticks (via `nextTick()` without `loadPhysic()` subscription)
16183
+ *
16184
+ * @param deltaMs - Optional delta time in milliseconds (default: 16ms for 60fps)
16185
+ * @returns Number of physics ticks executed
16186
+ *
16187
+ * @example
16188
+ * ```ts
16189
+ * // In tests: manually advance game by one tick
16190
+ * map.nextTick(); // Advances by 16ms (one frame at 60fps)
16191
+ *
16192
+ * // With custom delta
16193
+ * map.nextTick(32); // Advances by 32ms (two frames at 60fps)
16194
+ *
16195
+ * // In a test loop
16196
+ * for (let i = 0; i < 60; i++) {
16197
+ * map.nextTick(); // Simulate 1 second of game time
16198
+ * }
16199
+ * ```
16200
+ */
16201
+ nextTick(deltaMs = 16) {
16202
+ return this.runFixedTicks(deltaMs);
16203
+ }
16046
16204
  /**
16047
16205
  * Force a single physics tick outside of the normal game loop
16048
16206
  *
@@ -16092,19 +16250,12 @@ class RpgCommonMap {
16092
16250
  const hitbox = typeof owner.hitbox === "function" ? owner.hitbox() : owner.hitbox;
16093
16251
  const width = hitbox?.w ?? 32;
16094
16252
  const height = hitbox?.h ?? 32;
16095
- const topLeftX = this.resolveNumeric(owner.x);
16096
- const topLeftY = this.resolveNumeric(owner.y);
16097
- const centerX = topLeftX + width / 2;
16098
- const centerY = topLeftY + height / 2;
16099
16253
  const radius = Math.max(width, height) / 2;
16100
- const speedValue = typeof owner.speed === "function" ? owner.speed() : typeof owner.speed === "number" ? owner.speed : void 0;
16101
16254
  this.addCharacter({
16102
16255
  owner,
16103
- x: centerX,
16104
- y: centerY,
16105
16256
  radius,
16106
16257
  kind,
16107
- maxSpeed: speedValue,
16258
+ maxSpeed: owner.speed(),
16108
16259
  collidesWithCharacters: !this.shouldDisableCharacterCollisions(owner),
16109
16260
  isStatic: options?.isStatic,
16110
16261
  mass: options?.mass
@@ -16476,11 +16627,24 @@ class RpgCommonMap {
16476
16627
  owner2.changeDirection(cardinalDirection);
16477
16628
  });
16478
16629
  entity.onMovementChange(({ isMoving, intensity }) => {
16630
+ const owner2 = entity.owner;
16631
+ if (!owner2) return;
16479
16632
  const LOW_INTENSITY_THRESHOLD = 10;
16633
+ const hasSetAnimation = typeof owner2.setAnimation === "function";
16634
+ const animationNameSignal = owner2.animationName;
16635
+ const ownerHasAnimationName = animationNameSignal && typeof animationNameSignal === "object" && typeof animationNameSignal.set === "function";
16480
16636
  if (isMoving && intensity > LOW_INTENSITY_THRESHOLD) {
16481
- owner.animationName.set("walk");
16637
+ if (hasSetAnimation) {
16638
+ owner2.setAnimation("walk");
16639
+ } else if (ownerHasAnimationName) {
16640
+ animationNameSignal.set("walk");
16641
+ }
16482
16642
  } else if (!isMoving) {
16483
- owner.animationName.set("stand");
16643
+ if (hasSetAnimation) {
16644
+ owner2.setAnimation("stand");
16645
+ } else if (ownerHasAnimationName) {
16646
+ animationNameSignal.set("stand");
16647
+ }
16484
16648
  }
16485
16649
  });
16486
16650
  const entityWidth = width;
@@ -16555,10 +16719,7 @@ class RpgCommonMap {
16555
16719
  moveBody(player, direction) {
16556
16720
  const entity = this.physic.getEntityByUUID(player.id);
16557
16721
  if (!entity) return false;
16558
- const speedValue = typeof player.speed === "function" ? Number(player.speed()) : 0;
16559
- if (typeof player.setIntendedDirection === "function") {
16560
- player.setIntendedDirection(direction);
16561
- }
16722
+ const speedValue = player.speed();
16562
16723
  let vx = 0, vy = 0;
16563
16724
  switch (direction) {
16564
16725
  case Direction.Left:
@@ -16610,9 +16771,6 @@ class RpgCommonMap {
16610
16771
  const entity = this.physic.getEntityByUUID(player.id);
16611
16772
  if (!entity) return false;
16612
16773
  this.moveManager.stopMovement(player.id);
16613
- if (typeof player.setIntendedDirection === "function") {
16614
- player.setIntendedDirection(null);
16615
- }
16616
16774
  player.pendingInputs = [];
16617
16775
  return true;
16618
16776
  }
@@ -16922,17 +17080,19 @@ class WorldMapsManager {
16922
17080
  if (typeof search === "number") {
16923
17081
  const src = map;
16924
17082
  return maps.filter((m) => {
16925
- const horizontallyOverlaps = Math.max(src.worldX, m.worldX) < Math.min(src.worldX + src.width, m.worldX + m.width);
16926
- const verticallyOverlaps = Math.max(src.worldY, m.worldY) < Math.min(src.worldY + src.height, m.worldY + m.height);
17083
+ const horizontallyOverlapsOrTouches = Math.max(src.worldX, m.worldX) <= Math.min(src.worldX + src.widthPx, m.worldX + m.widthPx);
17084
+ const verticallyOverlapsOrTouches = Math.max(src.worldY, m.worldY) <= Math.min(src.worldY + src.heightPx, m.worldY + m.heightPx);
17085
+ const marginLeftRight = src.tileWidth / 2;
17086
+ const marginTopDown = src.tileHeight / 2;
16927
17087
  switch (search) {
16928
17088
  case 0:
16929
- return verticallyOverlaps && m.worldY + m.height === src.worldY;
17089
+ return verticallyOverlapsOrTouches && m.worldY + m.heightPx - marginTopDown === src.worldY;
16930
17090
  case 1:
16931
- return verticallyOverlaps && m.worldY === src.worldY + src.height;
17091
+ return verticallyOverlapsOrTouches && m.worldY + marginTopDown === src.worldY + src.heightPx;
16932
17092
  case 2:
16933
- return horizontallyOverlaps && m.worldX + m.width === src.worldX;
17093
+ return horizontallyOverlapsOrTouches && m.worldX + m.widthPx - marginLeftRight === src.worldX;
16934
17094
  case 3:
16935
- return horizontallyOverlaps && m.worldX === src.worldX + src.width;
17095
+ return horizontallyOverlapsOrTouches && m.worldX + marginLeftRight === src.worldX + src.widthPx;
16936
17096
  default:
16937
17097
  return false;
16938
17098
  }
@@ -16940,7 +17100,7 @@ class WorldMapsManager {
16940
17100
  }
16941
17101
  if ("x" in search && "y" in search) {
16942
17102
  const found = maps.find(
16943
- (m) => search.x >= m.worldX && search.x < m.worldX + m.width && search.y >= m.worldY && search.y < m.worldY + m.height
17103
+ (m) => search.x >= m.worldX && search.x < m.worldX + m.widthPx && search.y >= m.worldY && search.y < m.worldY + m.heightPx
16944
17104
  );
16945
17105
  return found ? [found] : [];
16946
17106
  }
@@ -16948,9 +17108,9 @@ class WorldMapsManager {
16948
17108
  const { minX, minY, maxX, maxY } = search;
16949
17109
  return maps.filter((m) => {
16950
17110
  const aLeft = m.worldX;
16951
- const aRight = m.worldX + m.width;
17111
+ const aRight = m.worldX + m.widthPx;
16952
17112
  const aTop = m.worldY;
16953
- const aBottom = m.worldY + m.height;
17113
+ const aBottom = m.worldY + m.heightPx;
16954
17114
  const bLeft = minX;
16955
17115
  const bRight = maxX;
16956
17116
  const bTop = minY;
@@ -17193,6 +17353,204 @@ var PrebuiltGui = /* @__PURE__ */ ((PrebuiltGui2) => {
17193
17353
  return PrebuiltGui2;
17194
17354
  })(PrebuiltGui || {});
17195
17355
 
17356
+ class PerlinNoise2D {
17357
+ /**
17358
+ * Creates a new Perlin noise generator
17359
+ *
17360
+ * @param seed - Optional seed for deterministic noise generation. If not provided, uses a default seed.
17361
+ *
17362
+ * @example
17363
+ * ```ts
17364
+ * const noise = new PerlinNoise2D(12345);
17365
+ * const value = noise.get(10, 20);
17366
+ * ```
17367
+ */
17368
+ constructor(seed = 0) {
17369
+ this.permutation = this.generatePermutation(seed);
17370
+ this.p = [...this.permutation, ...this.permutation];
17371
+ }
17372
+ /**
17373
+ * Generates a permutation table based on seed
17374
+ *
17375
+ * @param seed - Seed value for permutation generation
17376
+ * @returns Array of 256 shuffled values
17377
+ */
17378
+ generatePermutation(seed) {
17379
+ const p = [];
17380
+ for (let i = 0; i < 256; i++) {
17381
+ p[i] = i;
17382
+ }
17383
+ let state = seed;
17384
+ const lcg = () => {
17385
+ state = state * 1103515245 + 12345 & 2147483647;
17386
+ return state;
17387
+ };
17388
+ for (let i = 255; i > 0; i--) {
17389
+ const j = lcg() % (i + 1);
17390
+ [p[i], p[j]] = [p[j], p[i]];
17391
+ }
17392
+ return p;
17393
+ }
17394
+ /**
17395
+ * Fade function for smooth interpolation (ease curve)
17396
+ *
17397
+ * @param t - Value between 0 and 1
17398
+ * @returns Smoothed value between 0 and 1
17399
+ */
17400
+ fade(t) {
17401
+ return t * t * t * (t * (t * 6 - 15) + 10);
17402
+ }
17403
+ /**
17404
+ * Linear interpolation
17405
+ *
17406
+ * @param a - Start value
17407
+ * @param b - End value
17408
+ * @param t - Interpolation factor (0 to 1)
17409
+ * @returns Interpolated value
17410
+ */
17411
+ lerp(a, b, t) {
17412
+ return a + t * (b - a);
17413
+ }
17414
+ /**
17415
+ * Gradient function - generates a pseudo-random gradient vector
17416
+ *
17417
+ * @param hash - Hash value from permutation table
17418
+ * @param x - X component
17419
+ * @param y - Y component
17420
+ * @returns Dot product of gradient and position
17421
+ */
17422
+ grad(hash, x, y) {
17423
+ const h = hash & 3;
17424
+ switch (h) {
17425
+ case 0:
17426
+ return x + y;
17427
+ // (1, 1)
17428
+ case 1:
17429
+ return -x + y;
17430
+ // (-1, 1)
17431
+ case 2:
17432
+ return x - y;
17433
+ // (1, -1)
17434
+ case 3:
17435
+ return -x - y;
17436
+ // (-1, -1)
17437
+ default:
17438
+ return 0;
17439
+ }
17440
+ }
17441
+ /**
17442
+ * Gets the noise value at the specified 2D coordinates
17443
+ *
17444
+ * Returns a value between approximately -1 and 1, though values near the edges
17445
+ * are less common. For practical use, you may want to clamp or normalize the result.
17446
+ *
17447
+ * @param x - X coordinate
17448
+ * @param y - Y coordinate
17449
+ * @param scale - Optional scale factor (default: 0.1). Lower values create smoother, larger patterns.
17450
+ * @returns Noise value between approximately -1 and 1
17451
+ *
17452
+ * @example
17453
+ * ```ts
17454
+ * const noise = new PerlinNoise2D();
17455
+ * const value = noise.get(10, 20); // Basic usage
17456
+ * const scaled = noise.get(10, 20, 0.05); // Smoother pattern
17457
+ * ```
17458
+ */
17459
+ get(x, y, scale = 0.1) {
17460
+ x *= scale;
17461
+ y *= scale;
17462
+ const X = Math.floor(x) & 255;
17463
+ const Y = Math.floor(y) & 255;
17464
+ x -= Math.floor(x);
17465
+ y -= Math.floor(y);
17466
+ const u = this.fade(x);
17467
+ const v = this.fade(y);
17468
+ const A = this.p[X] + Y;
17469
+ const AA = this.p[A];
17470
+ const AB = this.p[A + 1];
17471
+ const B = this.p[X + 1] + Y;
17472
+ const BA = this.p[B];
17473
+ const BB = this.p[B + 1];
17474
+ return this.lerp(
17475
+ this.lerp(
17476
+ this.grad(this.p[AA], x, y),
17477
+ this.grad(this.p[BA], x - 1, y),
17478
+ u
17479
+ ),
17480
+ this.lerp(
17481
+ this.grad(this.p[AB], x, y - 1),
17482
+ this.grad(this.p[BB], x - 1, y - 1),
17483
+ u
17484
+ ),
17485
+ v
17486
+ );
17487
+ }
17488
+ /**
17489
+ * Gets a normalized noise value between 0 and 1
17490
+ *
17491
+ * Convenience method that normalizes the noise output to a 0-1 range.
17492
+ *
17493
+ * @param x - X coordinate
17494
+ * @param y - Y coordinate
17495
+ * @param scale - Optional scale factor (default: 0.1)
17496
+ * @returns Noise value between 0 and 1
17497
+ *
17498
+ * @example
17499
+ * ```ts
17500
+ * const noise = new PerlinNoise2D();
17501
+ * const normalized = noise.getNormalized(10, 20);
17502
+ * // Returns value between 0 and 1
17503
+ * ```
17504
+ */
17505
+ getNormalized(x, y, scale = 0.1) {
17506
+ return (this.get(x, y, scale) + 1) * 0.5;
17507
+ }
17508
+ /**
17509
+ * Gets a noise value mapped to a specific range
17510
+ *
17511
+ * Maps the noise output to a custom min-max range.
17512
+ *
17513
+ * @param x - X coordinate
17514
+ * @param y - Y coordinate
17515
+ * @param min - Minimum output value
17516
+ * @param max - Maximum output value
17517
+ * @param scale - Optional scale factor (default: 0.1)
17518
+ * @returns Noise value between min and max
17519
+ *
17520
+ * @example
17521
+ * ```ts
17522
+ * const noise = new PerlinNoise2D();
17523
+ * const direction = noise.getRange(10, 20, 0, 3); // Returns 0, 1, 2, or 3
17524
+ * ```
17525
+ */
17526
+ getRange(x, y, min, max, scale = 0.1) {
17527
+ const normalized = this.getNormalized(x, y, scale);
17528
+ return min + normalized * (max - min);
17529
+ }
17530
+ /**
17531
+ * Gets an integer noise value in a specific range (inclusive)
17532
+ *
17533
+ * Useful for selecting discrete values like array indices or enum values.
17534
+ *
17535
+ * @param x - X coordinate
17536
+ * @param y - Y coordinate
17537
+ * @param min - Minimum integer value (inclusive)
17538
+ * @param max - Maximum integer value (inclusive)
17539
+ * @param scale - Optional scale factor (default: 0.1)
17540
+ * @returns Integer noise value between min and max (inclusive)
17541
+ *
17542
+ * @example
17543
+ * ```ts
17544
+ * const noise = new PerlinNoise2D();
17545
+ * const directionIndex = noise.getInt(10, 20, 0, 3); // Returns 0, 1, 2, or 3
17546
+ * ```
17547
+ */
17548
+ getInt(x, y, min, max, scale = 0.1) {
17549
+ const value = this.getRange(x, y, min, max + 1, scale);
17550
+ return Math.floor(value);
17551
+ }
17552
+ }
17553
+
17196
17554
  function WithComponentManager(Base) {
17197
17555
  return class extends Base {
17198
17556
  setGraphic(graphic) {
@@ -17900,6 +18258,63 @@ var Speed = /* @__PURE__ */ ((Speed2) => {
17900
18258
  return Speed2;
17901
18259
  })(Speed || {});
17902
18260
  class MoveList {
18261
+ static {
18262
+ // Shared Perlin noise instance for smooth random movement
18263
+ this.perlinNoise = new PerlinNoise2D();
18264
+ }
18265
+ static {
18266
+ this.randomCounter = 0;
18267
+ }
18268
+ static {
18269
+ // Instance counter for each call to ensure variation
18270
+ this.callCounter = 0;
18271
+ }
18272
+ /**
18273
+ * Gets a random direction index (0-3) using a hybrid approach for balanced randomness
18274
+ *
18275
+ * Uses a combination of hash-based pseudo-randomness and Perlin noise to ensure
18276
+ * fair distribution of directions while maintaining smooth, natural-looking movement patterns.
18277
+ * The hash function guarantees uniform distribution, while Perlin noise adds spatial/temporal coherence.
18278
+ *
18279
+ * @param player - Optional player instance for coordinate-based noise
18280
+ * @param index - Optional index for array-based calls to ensure variation
18281
+ * @returns Direction index (0-3) corresponding to Right, Left, Up, Down
18282
+ */
18283
+ getRandomDirectionIndex(player, index) {
18284
+ MoveList.callCounter++;
18285
+ let seed;
18286
+ const time = Date.now() * 1e-3;
18287
+ if (player) {
18288
+ const playerX = typeof player.x === "function" ? player.x() : player.x;
18289
+ const playerY = typeof player.y === "function" ? player.y() : player.y;
18290
+ seed = Math.floor(
18291
+ playerX * 0.1 + playerY * 0.1 + time * 1e3 + MoveList.callCounter * 17 + (index ?? 0) * 31
18292
+ );
18293
+ } else {
18294
+ MoveList.randomCounter++;
18295
+ seed = Math.floor(
18296
+ MoveList.randomCounter * 17 + time * 1e3 + MoveList.callCounter * 31 + (index ?? 0) * 47
18297
+ );
18298
+ }
18299
+ let hash1 = (seed * 1103515245 + 12345 & 2147483647) >>> 0;
18300
+ let hash2 = seed * 2654435761 >>> 0;
18301
+ let hash3 = seed ^ seed >>> 16;
18302
+ hash3 = hash3 * 2246822507 >>> 0;
18303
+ let combinedHash = (hash1 ^ hash2 ^ hash3) >>> 0;
18304
+ const hashValue = combinedHash % 1e6 / 1e6;
18305
+ const perlinX = seed * 1e-3;
18306
+ const perlinY = seed * 1.618 * 1e-3;
18307
+ const perlinValue = MoveList.perlinNoise.getNormalized(perlinX, perlinY, 0.3);
18308
+ const finalValue = hashValue * 0.9 + perlinValue * 0.1;
18309
+ const clampedValue = Math.max(0, Math.min(0.999999, finalValue));
18310
+ let directionIndex = Math.floor(clampedValue * 4);
18311
+ directionIndex = Math.max(0, Math.min(3, directionIndex));
18312
+ if (!Number.isFinite(directionIndex) || directionIndex < 0 || directionIndex > 3) {
18313
+ const fallbackIndex = Math.floor(hashValue * 4) % 4;
18314
+ return Math.max(0, Math.min(3, fallbackIndex));
18315
+ }
18316
+ return directionIndex;
18317
+ }
17903
18318
  repeatMove(direction, repeat) {
17904
18319
  if (!Number.isFinite(repeat) || repeat < 0 || repeat > 1e4) {
17905
18320
  console.warn("Invalid repeat value:", repeat, "using default value 1");
@@ -17967,12 +18382,16 @@ class MoveList {
17967
18382
  repeat = 1;
17968
18383
  }
17969
18384
  try {
17970
- return new Array(repeat).fill(null).map(() => [
17971
- Direction.Right,
17972
- Direction.Left,
17973
- Direction.Up,
17974
- Direction.Down
17975
- ][random(0, 3)]);
18385
+ MoveList.randomCounter += repeat;
18386
+ return new Array(repeat).fill(null).map((_, index) => {
18387
+ const directionIndex = this.getRandomDirectionIndex(void 0, index);
18388
+ return [
18389
+ Direction.Right,
18390
+ Direction.Left,
18391
+ Direction.Up,
18392
+ Direction.Down
18393
+ ][directionIndex];
18394
+ });
17976
18395
  } catch (error) {
17977
18396
  console.error("Error creating random array with repeat:", repeat, error);
17978
18397
  return [Direction.Down];
@@ -17998,13 +18417,23 @@ class MoveList {
17998
18417
  }
17999
18418
  repeat = Math.floor(repeat);
18000
18419
  let directions = [];
18420
+ const directionFunctions = [
18421
+ this.tileRight(),
18422
+ this.tileLeft(),
18423
+ this.tileUp(),
18424
+ this.tileDown()
18425
+ ];
18001
18426
  for (let i = 0; i < repeat; i++) {
18002
- const randFn = [
18003
- this.tileRight(),
18004
- this.tileLeft(),
18005
- this.tileUp(),
18006
- this.tileDown()
18007
- ][random(0, 3)];
18427
+ let directionIndex = this.getRandomDirectionIndex(player, i);
18428
+ if (!Number.isInteger(directionIndex) || directionIndex < 0 || directionIndex > 3) {
18429
+ console.warn("Invalid directionIndex in tileRandom:", directionIndex, "using fallback");
18430
+ directionIndex = Math.floor(Math.random() * 4) % 4;
18431
+ }
18432
+ const randFn = directionFunctions[directionIndex];
18433
+ if (typeof randFn !== "function") {
18434
+ console.warn("randFn is not a function in tileRandom, skipping iteration");
18435
+ continue;
18436
+ }
18008
18437
  try {
18009
18438
  const newDirections = randFn(player, map);
18010
18439
  if (Array.isArray(newDirections)) {
@@ -18114,12 +18543,13 @@ class MoveList {
18114
18543
  return "turn-" + Direction.Down;
18115
18544
  }
18116
18545
  turnRandom() {
18546
+ const directionIndex = this.getRandomDirectionIndex();
18117
18547
  return [
18118
18548
  this.turnRight(),
18119
18549
  this.turnLeft(),
18120
18550
  this.turnUp(),
18121
18551
  this.turnDown()
18122
- ][random(0, 3)];
18552
+ ][directionIndex];
18123
18553
  }
18124
18554
  turnAwayFromPlayer(otherPlayer) {
18125
18555
  return (player) => {
@@ -18263,115 +18693,369 @@ function WithMoveManager(Base) {
18263
18693
  };
18264
18694
  this.addMovement(new ProjectileMovement(type, config));
18265
18695
  }
18266
- moveRoutes(routes) {
18267
- let count = 0;
18268
- let frequence = 0;
18696
+ moveRoutes(routes, options) {
18269
18697
  const player = this;
18270
18698
  this.clearMovements();
18271
18699
  return new Promise(async (resolve) => {
18272
18700
  this._finishRoute = resolve;
18273
- const processedRoutes = routes.map((route) => {
18274
- if (typeof route === "function") {
18275
- const map = player.getCurrentMap();
18276
- if (!map) {
18277
- return void 0;
18701
+ const processedRoutes = await Promise.all(
18702
+ routes.map(async (route) => {
18703
+ if (typeof route === "function") {
18704
+ const map = player.getCurrentMap();
18705
+ if (!map) {
18706
+ return void 0;
18707
+ }
18708
+ return route.apply(route, [player, map]);
18278
18709
  }
18279
- return route.apply(route, [player, map]);
18280
- }
18281
- return route;
18282
- });
18283
- const flatRoutes = this.flattenRoutes(processedRoutes);
18284
- let routeIndex = 0;
18285
- const executeNextRoute = async () => {
18286
- if (!player || !player.getCurrentMap()) {
18287
- this._finishRoute = null;
18288
- resolve(false);
18289
- return;
18710
+ return route;
18711
+ })
18712
+ );
18713
+ const finalRoutes = this.flattenRoutes(processedRoutes);
18714
+ class RouteMovementStrategy {
18715
+ constructor(routes2, player2, onComplete, options2) {
18716
+ this.routeIndex = 0;
18717
+ this.currentTarget = null;
18718
+ // Center position for physics
18719
+ this.currentTargetTopLeft = null;
18720
+ // Top-left position for player.x() comparison
18721
+ this.currentDirection = { x: 0, y: 0 };
18722
+ this.finished = false;
18723
+ this.waitingForPromise = false;
18724
+ this.promiseStartTime = 0;
18725
+ this.promiseDuration = 0;
18726
+ // Frequency wait state
18727
+ this.waitingForFrequency = false;
18728
+ this.frequencyWaitStartTime = 0;
18729
+ this.ratioFrequency = 15;
18730
+ // Stuck detection state
18731
+ this.lastPosition = null;
18732
+ this.lastPositionTime = 0;
18733
+ this.stuckCheckStartTime = 0;
18734
+ this.lastDistanceToTarget = null;
18735
+ this.isCurrentlyStuck = false;
18736
+ this.stuckCheckInitialized = false;
18737
+ this.routes = routes2;
18738
+ this.player = player2;
18739
+ this.onComplete = onComplete;
18740
+ this.tileSize = player2.nbPixelInTile || 32;
18741
+ this.tolerance = 0.5;
18742
+ this.onStuck = options2?.onStuck;
18743
+ this.stuckTimeout = options2?.stuckTimeout ?? 500;
18744
+ this.stuckThreshold = options2?.stuckThreshold ?? 1;
18745
+ this.processNextRoute();
18290
18746
  }
18291
- if (count >= (player.nbPixelInTile || 32)) {
18292
- if (frequence < (player.frequency || 0)) {
18293
- frequence++;
18294
- setTimeout(executeNextRoute, 16);
18747
+ processNextRoute() {
18748
+ this.waitingForFrequency = false;
18749
+ this.frequencyWaitStartTime = 0;
18750
+ if (this.routeIndex >= this.routes.length) {
18751
+ this.finished = true;
18752
+ this.onComplete(true);
18295
18753
  return;
18296
18754
  }
18297
- }
18298
- frequence = 0;
18299
- count++;
18300
- if (routeIndex >= flatRoutes.length) {
18301
- this._finishRoute = null;
18302
- resolve(true);
18303
- return;
18304
- }
18305
- const currentRoute = flatRoutes[routeIndex];
18306
- routeIndex++;
18307
- if (currentRoute === void 0) {
18308
- executeNextRoute();
18309
- return;
18310
- }
18311
- try {
18312
- if (typeof currentRoute === "object" && "then" in currentRoute) {
18313
- await currentRoute;
18314
- executeNextRoute();
18315
- } else if (typeof currentRoute === "string" && currentRoute.startsWith("turn-")) {
18316
- const directionStr = currentRoute.replace("turn-", "");
18317
- let direction = Direction.Down;
18318
- switch (directionStr) {
18319
- case "up":
18320
- case Direction.Up:
18321
- direction = Direction.Up;
18322
- break;
18323
- case "down":
18324
- case Direction.Down:
18325
- direction = Direction.Down;
18326
- break;
18327
- case "left":
18328
- case Direction.Left:
18329
- direction = Direction.Left;
18330
- break;
18331
- case "right":
18332
- case Direction.Right:
18333
- direction = Direction.Right;
18334
- break;
18335
- }
18336
- if (player.changeDirection) {
18337
- player.changeDirection(direction);
18338
- }
18339
- executeNextRoute();
18340
- } else if (typeof currentRoute === "number") {
18341
- if (player.moveByDirection) {
18342
- await player.moveByDirection(currentRoute, 1);
18343
- } else {
18344
- let vx = 0, vy = 0;
18345
- const direction = currentRoute;
18346
- switch (direction) {
18755
+ const currentRoute = this.routes[this.routeIndex];
18756
+ this.routeIndex++;
18757
+ if (currentRoute === void 0) {
18758
+ this.processNextRoute();
18759
+ return;
18760
+ }
18761
+ try {
18762
+ if (typeof currentRoute === "object" && "then" in currentRoute) {
18763
+ this.waitingForPromise = true;
18764
+ this.promiseStartTime = Date.now();
18765
+ this.promiseDuration = 1e3;
18766
+ currentRoute.then(() => {
18767
+ this.waitingForPromise = false;
18768
+ this.processNextRoute();
18769
+ }).catch(() => {
18770
+ this.waitingForPromise = false;
18771
+ this.processNextRoute();
18772
+ });
18773
+ } else if (typeof currentRoute === "string" && currentRoute.startsWith("turn-")) {
18774
+ const directionStr = currentRoute.replace("turn-", "");
18775
+ let direction = Direction.Down;
18776
+ switch (directionStr) {
18777
+ case "up":
18778
+ case Direction.Up:
18779
+ direction = Direction.Up;
18780
+ break;
18781
+ case "down":
18782
+ case Direction.Down:
18783
+ direction = Direction.Down;
18784
+ break;
18785
+ case "left":
18786
+ case Direction.Left:
18787
+ direction = Direction.Left;
18788
+ break;
18789
+ case "right":
18790
+ case Direction.Right:
18791
+ direction = Direction.Right;
18792
+ break;
18793
+ }
18794
+ if (this.player.changeDirection) {
18795
+ this.player.changeDirection(direction);
18796
+ }
18797
+ this.processNextRoute();
18798
+ } else if (typeof currentRoute === "number" || typeof currentRoute === "string") {
18799
+ const moveDirection = currentRoute;
18800
+ const map = this.player.getCurrentMap();
18801
+ if (!map) {
18802
+ this.finished = true;
18803
+ this.onComplete(false);
18804
+ return;
18805
+ }
18806
+ const currentTopLeftX = typeof this.player.x === "function" ? this.player.x() : this.player.x;
18807
+ const currentTopLeftY = typeof this.player.y === "function" ? this.player.y() : this.player.y;
18808
+ let playerSpeed = this.player.speed();
18809
+ let distance = playerSpeed;
18810
+ const initialDistance = distance;
18811
+ const initialRouteIndex = this.routeIndex;
18812
+ while (this.routeIndex < this.routes.length) {
18813
+ const nextRoute = this.routes[this.routeIndex];
18814
+ if (nextRoute === currentRoute) {
18815
+ distance += playerSpeed;
18816
+ this.routeIndex++;
18817
+ } else {
18818
+ break;
18819
+ }
18820
+ }
18821
+ let targetTopLeftX = currentTopLeftX;
18822
+ let targetTopLeftY = currentTopLeftY;
18823
+ switch (moveDirection) {
18347
18824
  case Direction.Right:
18348
- vx = 1;
18825
+ case "right":
18826
+ targetTopLeftX = currentTopLeftX + distance;
18349
18827
  break;
18350
18828
  case Direction.Left:
18351
- vx = -1;
18829
+ case "left":
18830
+ targetTopLeftX = currentTopLeftX - distance;
18352
18831
  break;
18353
18832
  case Direction.Down:
18354
- vy = 1;
18833
+ case "down":
18834
+ targetTopLeftY = currentTopLeftY + distance;
18355
18835
  break;
18356
18836
  case Direction.Up:
18357
- vy = -1;
18837
+ case "up":
18838
+ targetTopLeftY = currentTopLeftY - distance;
18358
18839
  break;
18359
18840
  }
18360
- const speed = player.speed?.() ?? 3;
18361
- this.addMovement(new LinearMove({ x: vx * speed, y: vy * speed }, 0.1));
18362
- setTimeout(executeNextRoute, 100);
18841
+ const entity = map.physic.getEntityByUUID(this.player.id);
18842
+ if (!entity) {
18843
+ this.finished = true;
18844
+ this.onComplete(false);
18845
+ return;
18846
+ }
18847
+ const hitbox = this.player.hitbox();
18848
+ const hitboxWidth = hitbox?.w ?? 32;
18849
+ const hitboxHeight = hitbox?.h ?? 32;
18850
+ const targetX = targetTopLeftX + hitboxWidth / 2;
18851
+ const targetY = targetTopLeftY + hitboxHeight / 2;
18852
+ this.currentTarget = { x: targetX, y: targetY };
18853
+ this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY };
18854
+ this.currentDirection = { x: 0, y: 0 };
18855
+ this.lastPosition = null;
18856
+ this.isCurrentlyStuck = false;
18857
+ this.stuckCheckStartTime = 0;
18858
+ this.lastDistanceToTarget = null;
18859
+ this.stuckCheckInitialized = false;
18860
+ this.waitingForFrequency = false;
18861
+ this.frequencyWaitStartTime = 0;
18862
+ } else if (Array.isArray(currentRoute)) {
18863
+ for (let i = currentRoute.length - 1; i >= 0; i--) {
18864
+ this.routes.splice(this.routeIndex, 0, currentRoute[i]);
18865
+ }
18866
+ this.processNextRoute();
18867
+ } else {
18868
+ this.processNextRoute();
18869
+ }
18870
+ } catch (error) {
18871
+ console.warn("Error processing route:", error);
18872
+ this.processNextRoute();
18873
+ }
18874
+ }
18875
+ update(body, dt) {
18876
+ if (this.waitingForPromise) {
18877
+ body.setVelocity({ x: 0, y: 0 });
18878
+ if (Date.now() - this.promiseStartTime > this.promiseDuration) {
18879
+ this.waitingForPromise = false;
18880
+ this.processNextRoute();
18881
+ }
18882
+ return;
18883
+ }
18884
+ if (this.waitingForFrequency) {
18885
+ body.setVelocity({ x: 0, y: 0 });
18886
+ const playerFrequency = this.player.frequency;
18887
+ const frequencyMs = playerFrequency || 0;
18888
+ if (frequencyMs > 0 && Date.now() - this.frequencyWaitStartTime >= frequencyMs * this.ratioFrequency) {
18889
+ this.waitingForFrequency = false;
18890
+ this.processNextRoute();
18891
+ }
18892
+ return;
18893
+ }
18894
+ if (!this.currentTarget) {
18895
+ if (!this.finished) {
18896
+ this.processNextRoute();
18897
+ }
18898
+ if (!this.currentTarget) {
18899
+ body.setVelocity({ x: 0, y: 0 });
18900
+ this.lastPosition = null;
18901
+ this.isCurrentlyStuck = false;
18902
+ this.lastDistanceToTarget = null;
18903
+ this.stuckCheckInitialized = false;
18904
+ this.currentTargetTopLeft = null;
18905
+ return;
18906
+ }
18907
+ }
18908
+ const entity = body.getEntity?.();
18909
+ if (!entity) {
18910
+ this.finished = true;
18911
+ this.onComplete(false);
18912
+ return;
18913
+ }
18914
+ const currentPosition = { x: entity.position.x, y: entity.position.y };
18915
+ const currentTime = Date.now();
18916
+ const currentTopLeftX = this.player.x();
18917
+ const currentTopLeftY = this.player.y();
18918
+ let dx, dy, distance;
18919
+ if (this.currentTargetTopLeft) {
18920
+ dx = this.currentTargetTopLeft.x - currentTopLeftX;
18921
+ dy = this.currentTargetTopLeft.y - currentTopLeftY;
18922
+ distance = Math.hypot(dx, dy);
18923
+ if (distance <= this.tolerance) {
18924
+ this.currentTarget = null;
18925
+ this.currentTargetTopLeft = null;
18926
+ this.currentDirection = { x: 0, y: 0 };
18927
+ body.setVelocity({ x: 0, y: 0 });
18928
+ this.lastPosition = null;
18929
+ this.isCurrentlyStuck = false;
18930
+ this.lastDistanceToTarget = null;
18931
+ this.stuckCheckInitialized = false;
18932
+ if (!this.finished) {
18933
+ const playerFrequency = this.player.frequency;
18934
+ if (playerFrequency && playerFrequency > 0) {
18935
+ this.waitingForFrequency = true;
18936
+ this.frequencyWaitStartTime = Date.now();
18937
+ } else {
18938
+ this.processNextRoute();
18939
+ }
18940
+ }
18941
+ return;
18942
+ }
18943
+ } else {
18944
+ dx = this.currentTarget.x - currentPosition.x;
18945
+ dy = this.currentTarget.y - currentPosition.y;
18946
+ distance = Math.hypot(dx, dy);
18947
+ if (distance <= this.tolerance) {
18948
+ this.currentTarget = null;
18949
+ this.currentTargetTopLeft = null;
18950
+ this.currentDirection = { x: 0, y: 0 };
18951
+ body.setVelocity({ x: 0, y: 0 });
18952
+ this.lastPosition = null;
18953
+ this.isCurrentlyStuck = false;
18954
+ this.lastDistanceToTarget = null;
18955
+ this.stuckCheckInitialized = false;
18956
+ if (!this.finished) {
18957
+ const playerFrequency = player.frequency;
18958
+ if (playerFrequency && playerFrequency > 0) {
18959
+ this.waitingForFrequency = true;
18960
+ this.frequencyWaitStartTime = Date.now();
18961
+ } else {
18962
+ this.processNextRoute();
18963
+ }
18964
+ }
18363
18965
  return;
18364
18966
  }
18365
- executeNextRoute();
18967
+ }
18968
+ if (this.onStuck && this.currentTarget) {
18969
+ if (!this.stuckCheckInitialized) {
18970
+ this.lastPosition = { ...currentPosition };
18971
+ this.lastDistanceToTarget = distance;
18972
+ this.stuckCheckInitialized = true;
18973
+ this.lastPositionTime = currentTime;
18974
+ } else if (this.lastPosition && this.lastDistanceToTarget !== null) {
18975
+ const positionChanged = Math.hypot(
18976
+ currentPosition.x - this.lastPosition.x,
18977
+ currentPosition.y - this.lastPosition.y
18978
+ ) > this.stuckThreshold;
18979
+ const distanceImproved = distance < this.lastDistanceToTarget - this.stuckThreshold;
18980
+ if (!positionChanged && !distanceImproved) {
18981
+ if (!this.isCurrentlyStuck) {
18982
+ this.stuckCheckStartTime = currentTime;
18983
+ this.isCurrentlyStuck = true;
18984
+ } else {
18985
+ if (currentTime - this.stuckCheckStartTime >= this.stuckTimeout) {
18986
+ const shouldContinue = this.onStuck(
18987
+ this.player,
18988
+ this.currentTarget,
18989
+ currentPosition
18990
+ );
18991
+ if (shouldContinue === false) {
18992
+ this.finished = true;
18993
+ this.onComplete(false);
18994
+ body.setVelocity({ x: 0, y: 0 });
18995
+ return;
18996
+ }
18997
+ this.isCurrentlyStuck = false;
18998
+ this.stuckCheckStartTime = 0;
18999
+ this.lastPosition = { ...currentPosition };
19000
+ this.lastDistanceToTarget = distance;
19001
+ }
19002
+ }
19003
+ } else {
19004
+ this.isCurrentlyStuck = false;
19005
+ this.stuckCheckStartTime = 0;
19006
+ }
19007
+ this.lastPosition = { ...currentPosition };
19008
+ this.lastPositionTime = currentTime;
19009
+ this.lastDistanceToTarget = distance;
19010
+ }
19011
+ }
19012
+ const map = this.player.getCurrentMap();
19013
+ map?.speedScalar ?? 50;
19014
+ if (distance > 0) {
19015
+ this.currentDirection = { x: dx / distance, y: dy / distance };
18366
19016
  } else {
18367
- executeNextRoute();
19017
+ this.currentTarget = null;
19018
+ this.currentTargetTopLeft = null;
19019
+ this.currentDirection = { x: 0, y: 0 };
19020
+ body.setVelocity({ x: 0, y: 0 });
19021
+ if (!this.finished) {
19022
+ const playerFrequency = typeof this.player.frequency === "function" ? this.player.frequency() : this.player.frequency;
19023
+ if (playerFrequency && playerFrequency > 0) {
19024
+ this.waitingForFrequency = true;
19025
+ this.frequencyWaitStartTime = Date.now();
19026
+ } else {
19027
+ this.processNextRoute();
19028
+ }
19029
+ }
19030
+ return;
18368
19031
  }
18369
- } catch (error) {
18370
- console.warn("Error executing route:", error);
18371
- executeNextRoute();
19032
+ const absX = Math.abs(this.currentDirection.x);
19033
+ const absY = Math.abs(this.currentDirection.y);
19034
+ let cardinalDirection;
19035
+ if (absX >= absY) {
19036
+ cardinalDirection = this.currentDirection.x >= 0 ? Direction.Right : Direction.Left;
19037
+ } else {
19038
+ cardinalDirection = this.currentDirection.y >= 0 ? Direction.Down : Direction.Up;
19039
+ }
19040
+ map.movePlayer(this.player, cardinalDirection);
18372
19041
  }
18373
- };
18374
- executeNextRoute();
19042
+ isFinished() {
19043
+ return this.finished;
19044
+ }
19045
+ onFinished() {
19046
+ this.onComplete(true);
19047
+ }
19048
+ }
19049
+ const routeStrategy = new RouteMovementStrategy(
19050
+ finalRoutes,
19051
+ player,
19052
+ (success) => {
19053
+ this._finishRoute = null;
19054
+ resolve(success);
19055
+ },
19056
+ options
19057
+ );
19058
+ this.addMovement(routeStrategy);
18375
19059
  });
18376
19060
  }
18377
19061
  flattenRoutes(routes) {
@@ -19361,8 +20045,8 @@ function WithItemManager(Base) {
19361
20045
  });
19362
20046
  }
19363
20047
  addItem(item, nb = 1) {
19364
- const map = this.getCurrentMap();
19365
- if (!map) {
20048
+ const map = this.getCurrentMap() || this.map;
20049
+ if (!map || !map.database) {
19366
20050
  throw new Error("Player must be on a map to add items");
19367
20051
  }
19368
20052
  let itemId;
@@ -19371,11 +20055,6 @@ function WithItemManager(Base) {
19371
20055
  if (isString(item)) {
19372
20056
  itemId = item;
19373
20057
  data = this.databaseById(itemId);
19374
- if (!data) {
19375
- throw new Error(
19376
- `The ID=${itemId} data is not found in the database. Add the data in the property "database"`
19377
- );
19378
- }
19379
20058
  } else if (typeof item === "function" || item.prototype) {
19380
20059
  itemId = item.name;
19381
20060
  const existingData = map.database()[itemId];
@@ -19404,6 +20083,21 @@ function WithItemManager(Base) {
19404
20083
  if (existingItem) {
19405
20084
  instance = existingItem;
19406
20085
  instance.quantity.update((it) => it + nb);
20086
+ if (data.name !== void 0) {
20087
+ instance.name.set(data.name);
20088
+ }
20089
+ if (data.description !== void 0) {
20090
+ instance.description.set(data.description);
20091
+ }
20092
+ if (data.price !== void 0) {
20093
+ instance.price.set(data.price);
20094
+ }
20095
+ if (itemInstance && typeof itemInstance === "object" && !(itemInstance instanceof Function)) {
20096
+ instance._itemInstance = itemInstance;
20097
+ if (itemInstance.onAdd) {
20098
+ instance.onAdd = itemInstance.onAdd.bind(itemInstance);
20099
+ }
20100
+ }
19407
20101
  } else {
19408
20102
  instance = new Item(data);
19409
20103
  instance.id.set(itemId);
@@ -19488,7 +20182,11 @@ function WithItemManager(Base) {
19488
20182
  getParamItem(name) {
19489
20183
  let nb = 0;
19490
20184
  for (let item of this.equipments()) {
19491
- nb += item[name] || 0;
20185
+ try {
20186
+ const itemData = this.databaseById(item.id());
20187
+ nb += itemData[name] || 0;
20188
+ } catch {
20189
+ }
19492
20190
  }
19493
20191
  const modifier = this.paramsModifier?.[name];
19494
20192
  if (modifier) {
@@ -19515,19 +20213,23 @@ function WithItemManager(Base) {
19515
20213
  if (!inventory) {
19516
20214
  throw ItemLog.notInInventory(itemId);
19517
20215
  }
19518
- const item = inventory;
19519
- if (item.consumable === false) {
20216
+ const itemData = this.databaseById(itemId);
20217
+ const consumable = itemData?.consumable;
20218
+ if (consumable === false) {
19520
20219
  throw ItemLog.notUseItem(itemId);
19521
20220
  }
19522
- const hitRate = item.hitRate ?? 1;
19523
- const hookTarget = item._itemInstance || item;
20221
+ if (consumable === void 0 && itemData?._type && itemData._type !== "item") {
20222
+ throw ItemLog.notUseItem(itemId);
20223
+ }
20224
+ const hitRate = itemData?.hitRate ?? 1;
20225
+ const hookTarget = inventory._itemInstance || inventory;
19524
20226
  if (Math.random() > hitRate) {
19525
20227
  this.removeItem(itemClass);
19526
20228
  this["execMethod"]("onUseFailed", [this], hookTarget);
19527
20229
  throw ItemLog.chanceToUseFailed(itemId);
19528
20230
  }
19529
- this.applyEffect?.(item);
19530
- this.applyStates?.(this, item);
20231
+ this.applyEffect?.(itemData);
20232
+ this.applyStates?.(this, itemData);
19531
20233
  this["execMethod"]("onUse", [this], hookTarget);
19532
20234
  this.removeItem(itemClass);
19533
20235
  return inventory;
@@ -19972,7 +20674,7 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
19972
20674
  this.map = null;
19973
20675
  this.conn = null;
19974
20676
  this.touchSide = false;
19975
- // Protection against map change loops
20677
+ this._worldPositionSignals = /* @__PURE__ */ new WeakMap();
19976
20678
  /** Internal: Shapes attached to this player */
19977
20679
  this._attachedShapes = /* @__PURE__ */ new Map();
19978
20680
  /** Internal: Shapes where this player is currently located */
@@ -20021,6 +20723,56 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20021
20723
  }
20022
20724
  });
20023
20725
  }
20726
+ // Protection against map change loops
20727
+ /**
20728
+ * Computed signal for world X position
20729
+ *
20730
+ * Calculates the absolute world X position from the map's world position
20731
+ * plus the player's local X position. Returns 0 if no map is assigned.
20732
+ *
20733
+ * @example
20734
+ * ```ts
20735
+ * const worldX = player.worldX();
20736
+ * console.log(`Player is at world X: ${worldX}`);
20737
+ * ```
20738
+ */
20739
+ get worldPositionX() {
20740
+ return this._getComputedWorldPosition("x");
20741
+ }
20742
+ /**
20743
+ * Computed signal for world Y position
20744
+ *
20745
+ * Calculates the absolute world Y position from the map's world position
20746
+ * plus the player's local Y position. Returns 0 if no map is assigned.
20747
+ *
20748
+ * @example
20749
+ * ```ts
20750
+ * const worldY = player.worldY();
20751
+ * console.log(`Player is at world Y: ${worldY}`);
20752
+ * ```
20753
+ */
20754
+ get worldPositionY() {
20755
+ return this._getComputedWorldPosition("y");
20756
+ }
20757
+ _getComputedWorldPosition(axis) {
20758
+ if (!this._worldPositionSignals) {
20759
+ this._worldPositionSignals = /* @__PURE__ */ new WeakMap();
20760
+ }
20761
+ const key = axis;
20762
+ let signals = this._worldPositionSignals.get(this);
20763
+ if (!signals) {
20764
+ signals = {};
20765
+ this._worldPositionSignals.set(this, signals);
20766
+ }
20767
+ if (!signals[key]) {
20768
+ signals[key] = computed(() => {
20769
+ const map2 = this.map;
20770
+ const mapWorldPos = map2 ? map2[axis === "x" ? "worldX" : "worldY"] ?? 0 : 0;
20771
+ return mapWorldPos + this[axis]();
20772
+ });
20773
+ }
20774
+ return signals[key];
20775
+ }
20024
20776
  _onInit() {
20025
20777
  this.hooks.callHooks("server-playerProps-load", this).subscribe();
20026
20778
  }
@@ -20031,6 +20783,9 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20031
20783
  get server() {
20032
20784
  return this.map;
20033
20785
  }
20786
+ setMap(map2) {
20787
+ this.map = map2;
20788
+ }
20034
20789
  applyFrames() {
20035
20790
  this._frames.set(this.frames);
20036
20791
  this.frames = [];
@@ -20080,76 +20835,55 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20080
20835
  });
20081
20836
  return true;
20082
20837
  }
20083
- /**
20084
- * Auto change map when player touches map borders
20085
- *
20086
- * This method checks if the player touches the current map borders
20087
- * and automatically performs a change to the adjacent map if it exists.
20088
- *
20089
- * @param nextPosition - The next position of the player
20090
- * @returns Promise<boolean> - true if a map change occurred
20091
- *
20092
- * @example
20093
- * ```ts
20094
- * // Called automatically by the movement system
20095
- * const changed = await player.autoChangeMap({ x: newX, y: newY });
20096
- * if (changed) {
20097
- * console.log('Player changed map automatically');
20098
- * }
20099
- * ```
20100
- */
20101
- async autoChangeMap(nextPosition, forcedDirection) {
20838
+ async autoChangeMap(nextPosition) {
20102
20839
  const map2 = this.getCurrentMap();
20103
- if (!map2) return false;
20104
- const worldMaps = map2.getWorldMapsManager?.();
20840
+ const worldMaps = map2?.getInWorldMaps();
20105
20841
  let ret = false;
20106
20842
  if (worldMaps && map2) {
20107
- const direction = forcedDirection ?? this.getDirection();
20108
- const marginLeftRight = (map2.tileWidth ?? 32) / 2;
20109
- const marginTopDown = (map2.tileHeight ?? 32) / 2;
20110
- (map2.worldX ?? 0) + this.x();
20111
- (map2.worldY ?? 0) + this.y();
20112
- const changeMap = async (directionNumber, positionCalculator) => {
20843
+ const direction = this.getDirection();
20844
+ const marginLeftRight = map2.tileWidth / 2;
20845
+ const marginTopDown = map2.tileHeight / 2;
20846
+ const changeMap = async (adjacent, to) => {
20113
20847
  if (this.touchSide) {
20114
20848
  return false;
20115
20849
  }
20116
20850
  this.touchSide = true;
20117
- const [nextMap] = worldMaps.getAdjacentMaps(map2, directionNumber);
20118
- if (!nextMap) {
20119
- this.touchSide = false;
20120
- return false;
20121
- }
20851
+ const [nextMap] = worldMaps.getAdjacentMaps(map2, adjacent);
20852
+ if (!nextMap) return false;
20122
20853
  const id = nextMap.id;
20123
20854
  const nextMapInfo = worldMaps.getMapInfo(id);
20124
- if (!nextMapInfo) {
20125
- this.touchSide = false;
20126
- return false;
20127
- }
20128
- const newPosition = positionCalculator(nextMapInfo);
20129
- const success = await this.changeMap(id, newPosition);
20130
- setTimeout(() => {
20131
- this.touchSide = false;
20132
- }, 100);
20133
- return !!success;
20855
+ return !!await this.changeMap(id, to(nextMapInfo));
20134
20856
  };
20135
- if (nextPosition.x < marginLeftRight && direction === Direction.Left) {
20136
- ret = await changeMap(2, (nextMapInfo) => ({
20857
+ if (nextPosition.x < marginLeftRight && direction == Direction.Left) {
20858
+ ret = await changeMap({
20859
+ x: map2.worldX - 1,
20860
+ y: this.worldPositionY() + 1
20861
+ }, (nextMapInfo) => ({
20137
20862
  x: nextMapInfo.width - this.hitbox().w - marginLeftRight,
20138
- y: (map2.worldY ?? 0) - (nextMapInfo.y ?? 0) + nextPosition.y
20863
+ y: map2.worldY - nextMapInfo.y + nextPosition.y
20139
20864
  }));
20140
- } else if (nextPosition.x > map2.widthPx - this.hitbox().w - marginLeftRight && direction === Direction.Right) {
20141
- ret = await changeMap(3, (nextMapInfo) => ({
20865
+ } else if (nextPosition.x > map2.widthPx - this.hitbox().w - marginLeftRight && direction == Direction.Right) {
20866
+ ret = await changeMap({
20867
+ x: map2.worldX + map2.widthPx + 1,
20868
+ y: this.worldPositionY() + 1
20869
+ }, (nextMapInfo) => ({
20142
20870
  x: marginLeftRight,
20143
- y: (map2.worldY ?? 0) - (nextMapInfo.y ?? 0) + nextPosition.y
20871
+ y: map2.worldY - nextMapInfo.y + nextPosition.y
20144
20872
  }));
20145
- } else if (nextPosition.y < marginTopDown && direction === Direction.Up) {
20146
- ret = await changeMap(0, (nextMapInfo) => ({
20147
- x: (map2.worldX ?? 0) - (nextMapInfo.x ?? 0) + nextPosition.x,
20873
+ } else if (nextPosition.y < marginTopDown && direction == Direction.Up) {
20874
+ ret = await changeMap({
20875
+ x: this.worldPositionX() + 1,
20876
+ y: map2.worldY - 1
20877
+ }, (nextMapInfo) => ({
20878
+ x: map2.worldX - nextMapInfo.x + nextPosition.x,
20148
20879
  y: nextMapInfo.height - this.hitbox().h - marginTopDown
20149
20880
  }));
20150
- } else if (nextPosition.y > map2.heightPx - this.hitbox().h - marginTopDown && direction === Direction.Down) {
20151
- ret = await changeMap(1, (nextMapInfo) => ({
20152
- x: (map2.worldX ?? 0) - (nextMapInfo.x ?? 0) + nextPosition.x,
20881
+ } else if (nextPosition.y > map2.heightPx - this.hitbox().h - marginTopDown && direction == Direction.Down) {
20882
+ ret = await changeMap({
20883
+ x: this.worldPositionX() + 1,
20884
+ y: map2.worldY + map2.heightPx + 1
20885
+ }, (nextMapInfo) => ({
20886
+ x: map2.worldX - nextMapInfo.x + nextPosition.x,
20153
20887
  y: marginTopDown
20154
20888
  }));
20155
20889
  } else {
@@ -20160,15 +20894,19 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20160
20894
  }
20161
20895
  async teleport(positions) {
20162
20896
  if (!this.map) return false;
20163
- if (this.map.physic) {
20897
+ if (this.map && this.map.physic) {
20164
20898
  const entity = this.map.physic.getEntityByUUID(this.id);
20165
20899
  if (entity) {
20166
- this.map.physic.teleport(entity, { x: positions.x, y: positions.y });
20900
+ const hitbox = typeof this.hitbox === "function" ? this.hitbox() : this.hitbox;
20901
+ const width = hitbox?.w ?? 32;
20902
+ const height = hitbox?.h ?? 32;
20903
+ const centerX = positions.x + width / 2;
20904
+ const centerY = positions.y + height / 2;
20905
+ this.map.physic.teleport(entity, { x: centerX, y: centerY });
20167
20906
  }
20168
- } else {
20169
- this.x.set(positions.x);
20170
- this.y.set(positions.y);
20171
20907
  }
20908
+ this.x.set(positions.x);
20909
+ this.y.set(positions.y);
20172
20910
  queueMicrotask(() => {
20173
20911
  this.applyFrames();
20174
20912
  });
@@ -20250,8 +20988,8 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20250
20988
  this._eventChanges();
20251
20989
  }
20252
20990
  databaseById(id) {
20253
- const map2 = this.getCurrentMap();
20254
- if (!map2) return;
20991
+ const map2 = this.map;
20992
+ if (!map2 || !map2.database) return;
20255
20993
  const data = map2.database()[id];
20256
20994
  if (!data)
20257
20995
  throw new Error(
@@ -20804,6 +21542,9 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
20804
21542
  }, this);
20805
21543
  }
20806
21544
  }
21545
+ isEvent() {
21546
+ return false;
21547
+ }
20807
21548
  };
20808
21549
  __decorateClass$2([
20809
21550
  sync(_RpgPlayer)
@@ -20823,6 +21564,9 @@ class RpgEvent extends RpgPlayer {
20823
21564
  if (!map2) return;
20824
21565
  map2.removeEvent(this.id);
20825
21566
  }
21567
+ isEvent() {
21568
+ return true;
21569
+ }
20826
21570
  }
20827
21571
 
20828
21572
  const context$1 = new Context();
@@ -21975,8 +22719,8 @@ class Doc {
21975
22719
 
21976
22720
  const version = {
21977
22721
  major: 4,
21978
- minor: 1,
21979
- patch: 13,
22722
+ minor: 2,
22723
+ patch: 0,
21980
22724
  };
21981
22725
 
21982
22726
  const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
@@ -22046,16 +22790,6 @@ const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
22046
22790
  }
22047
22791
  return payload;
22048
22792
  };
22049
- // const handleChecksResult = (
22050
- // checkResult: ParsePayload,
22051
- // originalResult: ParsePayload,
22052
- // ctx: ParseContextInternal
22053
- // ): util.MaybeAsync<ParsePayload> => {
22054
- // // if the checks mutated the value && there are no issues, re-parse the result
22055
- // if (checkResult.value !== originalResult.value && !checkResult.issues.length)
22056
- // return inst._zod.parse(checkResult, ctx);
22057
- // return originalResult;
22058
- // };
22059
22793
  const handleCanaryResult = (canary, payload, ctx) => {
22060
22794
  // abort if the canary is aborted
22061
22795
  if (aborted(canary)) {
@@ -23666,6 +24400,656 @@ function _check(fn, params) {
23666
24400
  return ch;
23667
24401
  }
23668
24402
 
24403
+ // function initializeContext<T extends schemas.$ZodType>(inputs: JSONSchemaGeneratorParams<T>): ToJSONSchemaContext<T> {
24404
+ // return {
24405
+ // processor: inputs.processor,
24406
+ // metadataRegistry: inputs.metadata ?? globalRegistry,
24407
+ // target: inputs.target ?? "draft-2020-12",
24408
+ // unrepresentable: inputs.unrepresentable ?? "throw",
24409
+ // };
24410
+ // }
24411
+ function initializeContext(params) {
24412
+ // Normalize target: convert old non-hyphenated versions to hyphenated versions
24413
+ let target = params?.target ?? "draft-2020-12";
24414
+ if (target === "draft-4")
24415
+ target = "draft-04";
24416
+ if (target === "draft-7")
24417
+ target = "draft-07";
24418
+ return {
24419
+ processors: params.processors ?? {},
24420
+ metadataRegistry: params?.metadata ?? globalRegistry,
24421
+ target,
24422
+ unrepresentable: params?.unrepresentable ?? "throw",
24423
+ override: params?.override ?? (() => { }),
24424
+ io: params?.io ?? "output",
24425
+ counter: 0,
24426
+ seen: new Map(),
24427
+ cycles: params?.cycles ?? "ref",
24428
+ reused: params?.reused ?? "inline",
24429
+ external: params?.external ?? undefined,
24430
+ };
24431
+ }
24432
+ function process$1(schema, ctx, _params = { path: [], schemaPath: [] }) {
24433
+ var _a;
24434
+ const def = schema._zod.def;
24435
+ // check for schema in seens
24436
+ const seen = ctx.seen.get(schema);
24437
+ if (seen) {
24438
+ seen.count++;
24439
+ // check if cycle
24440
+ const isCycle = _params.schemaPath.includes(schema);
24441
+ if (isCycle) {
24442
+ seen.cycle = _params.path;
24443
+ }
24444
+ return seen.schema;
24445
+ }
24446
+ // initialize
24447
+ const result = { schema: {}, count: 1, cycle: undefined, path: _params.path };
24448
+ ctx.seen.set(schema, result);
24449
+ // custom method overrides default behavior
24450
+ const overrideSchema = schema._zod.toJSONSchema?.();
24451
+ if (overrideSchema) {
24452
+ result.schema = overrideSchema;
24453
+ }
24454
+ else {
24455
+ const params = {
24456
+ ..._params,
24457
+ schemaPath: [..._params.schemaPath, schema],
24458
+ path: _params.path,
24459
+ };
24460
+ const parent = schema._zod.parent;
24461
+ if (parent) {
24462
+ // schema was cloned from another schema
24463
+ result.ref = parent;
24464
+ process$1(parent, ctx, params);
24465
+ ctx.seen.get(parent).isParent = true;
24466
+ }
24467
+ else if (schema._zod.processJSONSchema) {
24468
+ schema._zod.processJSONSchema(ctx, result.schema, params);
24469
+ }
24470
+ else {
24471
+ const _json = result.schema;
24472
+ const processor = ctx.processors[def.type];
24473
+ if (!processor) {
24474
+ throw new Error(`[toJSONSchema]: Non-representable type encountered: ${def.type}`);
24475
+ }
24476
+ processor(schema, ctx, _json, params);
24477
+ }
24478
+ }
24479
+ // metadata
24480
+ const meta = ctx.metadataRegistry.get(schema);
24481
+ if (meta)
24482
+ Object.assign(result.schema, meta);
24483
+ if (ctx.io === "input" && isTransforming(schema)) {
24484
+ // examples/defaults only apply to output type of pipe
24485
+ delete result.schema.examples;
24486
+ delete result.schema.default;
24487
+ }
24488
+ // set prefault as default
24489
+ if (ctx.io === "input" && result.schema._prefault)
24490
+ (_a = result.schema).default ?? (_a.default = result.schema._prefault);
24491
+ delete result.schema._prefault;
24492
+ // pulling fresh from ctx.seen in case it was overwritten
24493
+ const _result = ctx.seen.get(schema);
24494
+ return _result.schema;
24495
+ }
24496
+ function extractDefs(ctx, schema
24497
+ // params: EmitParams
24498
+ ) {
24499
+ // iterate over seen map;
24500
+ const root = ctx.seen.get(schema);
24501
+ if (!root)
24502
+ throw new Error("Unprocessed schema. This is a bug in Zod.");
24503
+ // returns a ref to the schema
24504
+ // defId will be empty if the ref points to an external schema (or #)
24505
+ const makeURI = (entry) => {
24506
+ // comparing the seen objects because sometimes
24507
+ // multiple schemas map to the same seen object.
24508
+ // e.g. lazy
24509
+ // external is configured
24510
+ const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
24511
+ if (ctx.external) {
24512
+ const externalId = ctx.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${ctx.counter++}`;
24513
+ // check if schema is in the external registry
24514
+ const uriGenerator = ctx.external.uri ?? ((id) => id);
24515
+ if (externalId) {
24516
+ return { ref: uriGenerator(externalId) };
24517
+ }
24518
+ // otherwise, add to __shared
24519
+ const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;
24520
+ entry[1].defId = id; // set defId so it will be reused if needed
24521
+ return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
24522
+ }
24523
+ if (entry[1] === root) {
24524
+ return { ref: "#" };
24525
+ }
24526
+ // self-contained schema
24527
+ const uriPrefix = `#`;
24528
+ const defUriPrefix = `${uriPrefix}/${defsSegment}/`;
24529
+ const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;
24530
+ return { defId, ref: defUriPrefix + defId };
24531
+ };
24532
+ // stored cached version in `def` property
24533
+ // remove all properties, set $ref
24534
+ const extractToDef = (entry) => {
24535
+ // if the schema is already a reference, do not extract it
24536
+ if (entry[1].schema.$ref) {
24537
+ return;
24538
+ }
24539
+ const seen = entry[1];
24540
+ const { ref, defId } = makeURI(entry);
24541
+ seen.def = { ...seen.schema };
24542
+ // defId won't be set if the schema is a reference to an external schema
24543
+ // or if the schema is the root schema
24544
+ if (defId)
24545
+ seen.defId = defId;
24546
+ // wipe away all properties except $ref
24547
+ const schema = seen.schema;
24548
+ for (const key in schema) {
24549
+ delete schema[key];
24550
+ }
24551
+ schema.$ref = ref;
24552
+ };
24553
+ // throw on cycles
24554
+ // break cycles
24555
+ if (ctx.cycles === "throw") {
24556
+ for (const entry of ctx.seen.entries()) {
24557
+ const seen = entry[1];
24558
+ if (seen.cycle) {
24559
+ throw new Error("Cycle detected: " +
24560
+ `#/${seen.cycle?.join("/")}/<root>` +
24561
+ '\n\nSet the `cycles` parameter to `"ref"` to resolve cyclical schemas with defs.');
24562
+ }
24563
+ }
24564
+ }
24565
+ // extract schemas into $defs
24566
+ for (const entry of ctx.seen.entries()) {
24567
+ const seen = entry[1];
24568
+ // convert root schema to # $ref
24569
+ if (schema === entry[0]) {
24570
+ extractToDef(entry); // this has special handling for the root schema
24571
+ continue;
24572
+ }
24573
+ // extract schemas that are in the external registry
24574
+ if (ctx.external) {
24575
+ const ext = ctx.external.registry.get(entry[0])?.id;
24576
+ if (schema !== entry[0] && ext) {
24577
+ extractToDef(entry);
24578
+ continue;
24579
+ }
24580
+ }
24581
+ // extract schemas with `id` meta
24582
+ const id = ctx.metadataRegistry.get(entry[0])?.id;
24583
+ if (id) {
24584
+ extractToDef(entry);
24585
+ continue;
24586
+ }
24587
+ // break cycles
24588
+ if (seen.cycle) {
24589
+ // any
24590
+ extractToDef(entry);
24591
+ continue;
24592
+ }
24593
+ // extract reused schemas
24594
+ if (seen.count > 1) {
24595
+ if (ctx.reused === "ref") {
24596
+ extractToDef(entry);
24597
+ // biome-ignore lint:
24598
+ continue;
24599
+ }
24600
+ }
24601
+ }
24602
+ }
24603
+ function finalize(ctx, schema) {
24604
+ //
24605
+ // iterate over seen map;
24606
+ const root = ctx.seen.get(schema);
24607
+ if (!root)
24608
+ throw new Error("Unprocessed schema. This is a bug in Zod.");
24609
+ // flatten _refs
24610
+ const flattenRef = (zodSchema) => {
24611
+ const seen = ctx.seen.get(zodSchema);
24612
+ const schema = seen.def ?? seen.schema;
24613
+ const _cached = { ...schema };
24614
+ // already seen
24615
+ if (seen.ref === null) {
24616
+ return;
24617
+ }
24618
+ // flatten ref if defined
24619
+ const ref = seen.ref;
24620
+ seen.ref = null; // prevent recursion
24621
+ if (ref) {
24622
+ flattenRef(ref);
24623
+ // merge referenced schema into current
24624
+ const refSchema = ctx.seen.get(ref).schema;
24625
+ if (refSchema.$ref && (ctx.target === "draft-07" || ctx.target === "draft-04" || ctx.target === "openapi-3.0")) {
24626
+ schema.allOf = schema.allOf ?? [];
24627
+ schema.allOf.push(refSchema);
24628
+ }
24629
+ else {
24630
+ Object.assign(schema, refSchema);
24631
+ Object.assign(schema, _cached); // prevent overwriting any fields in the original schema
24632
+ }
24633
+ }
24634
+ // execute overrides
24635
+ if (!seen.isParent)
24636
+ ctx.override({
24637
+ zodSchema: zodSchema,
24638
+ jsonSchema: schema,
24639
+ path: seen.path ?? [],
24640
+ });
24641
+ };
24642
+ for (const entry of [...ctx.seen.entries()].reverse()) {
24643
+ flattenRef(entry[0]);
24644
+ }
24645
+ const result = {};
24646
+ if (ctx.target === "draft-2020-12") {
24647
+ result.$schema = "https://json-schema.org/draft/2020-12/schema";
24648
+ }
24649
+ else if (ctx.target === "draft-07") {
24650
+ result.$schema = "http://json-schema.org/draft-07/schema#";
24651
+ }
24652
+ else if (ctx.target === "draft-04") {
24653
+ result.$schema = "http://json-schema.org/draft-04/schema#";
24654
+ }
24655
+ else if (ctx.target === "openapi-3.0") ;
24656
+ else ;
24657
+ if (ctx.external?.uri) {
24658
+ const id = ctx.external.registry.get(schema)?.id;
24659
+ if (!id)
24660
+ throw new Error("Schema is missing an `id` property");
24661
+ result.$id = ctx.external.uri(id);
24662
+ }
24663
+ Object.assign(result, root.def ?? root.schema);
24664
+ // build defs object
24665
+ const defs = ctx.external?.defs ?? {};
24666
+ for (const entry of ctx.seen.entries()) {
24667
+ const seen = entry[1];
24668
+ if (seen.def && seen.defId) {
24669
+ defs[seen.defId] = seen.def;
24670
+ }
24671
+ }
24672
+ // set definitions in result
24673
+ if (ctx.external) ;
24674
+ else {
24675
+ if (Object.keys(defs).length > 0) {
24676
+ if (ctx.target === "draft-2020-12") {
24677
+ result.$defs = defs;
24678
+ }
24679
+ else {
24680
+ result.definitions = defs;
24681
+ }
24682
+ }
24683
+ }
24684
+ try {
24685
+ // this "finalizes" this schema and ensures all cycles are removed
24686
+ // each call to finalize() is functionally independent
24687
+ // though the seen map is shared
24688
+ const finalized = JSON.parse(JSON.stringify(result));
24689
+ Object.defineProperty(finalized, "~standard", {
24690
+ value: {
24691
+ ...schema["~standard"],
24692
+ jsonSchema: {
24693
+ input: createStandardJSONSchemaMethod(schema, "input"),
24694
+ output: createStandardJSONSchemaMethod(schema, "output"),
24695
+ },
24696
+ },
24697
+ enumerable: false,
24698
+ writable: false,
24699
+ });
24700
+ return finalized;
24701
+ }
24702
+ catch (_err) {
24703
+ throw new Error("Error converting schema to JSON.");
24704
+ }
24705
+ }
24706
+ function isTransforming(_schema, _ctx) {
24707
+ const ctx = _ctx ?? { seen: new Set() };
24708
+ if (ctx.seen.has(_schema))
24709
+ return false;
24710
+ ctx.seen.add(_schema);
24711
+ const def = _schema._zod.def;
24712
+ if (def.type === "transform")
24713
+ return true;
24714
+ if (def.type === "array")
24715
+ return isTransforming(def.element, ctx);
24716
+ if (def.type === "set")
24717
+ return isTransforming(def.valueType, ctx);
24718
+ if (def.type === "lazy")
24719
+ return isTransforming(def.getter(), ctx);
24720
+ if (def.type === "promise" ||
24721
+ def.type === "optional" ||
24722
+ def.type === "nonoptional" ||
24723
+ def.type === "nullable" ||
24724
+ def.type === "readonly" ||
24725
+ def.type === "default" ||
24726
+ def.type === "prefault") {
24727
+ return isTransforming(def.innerType, ctx);
24728
+ }
24729
+ if (def.type === "intersection") {
24730
+ return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
24731
+ }
24732
+ if (def.type === "record" || def.type === "map") {
24733
+ return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
24734
+ }
24735
+ if (def.type === "pipe") {
24736
+ return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
24737
+ }
24738
+ if (def.type === "object") {
24739
+ for (const key in def.shape) {
24740
+ if (isTransforming(def.shape[key], ctx))
24741
+ return true;
24742
+ }
24743
+ return false;
24744
+ }
24745
+ if (def.type === "union") {
24746
+ for (const option of def.options) {
24747
+ if (isTransforming(option, ctx))
24748
+ return true;
24749
+ }
24750
+ return false;
24751
+ }
24752
+ if (def.type === "tuple") {
24753
+ for (const item of def.items) {
24754
+ if (isTransforming(item, ctx))
24755
+ return true;
24756
+ }
24757
+ if (def.rest && isTransforming(def.rest, ctx))
24758
+ return true;
24759
+ return false;
24760
+ }
24761
+ return false;
24762
+ }
24763
+ /**
24764
+ * Creates a toJSONSchema method for a schema instance.
24765
+ * This encapsulates the logic of initializing context, processing, extracting defs, and finalizing.
24766
+ */
24767
+ const createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
24768
+ const ctx = initializeContext({ ...params, processors });
24769
+ process$1(schema, ctx);
24770
+ extractDefs(ctx, schema);
24771
+ return finalize(ctx, schema);
24772
+ };
24773
+ const createStandardJSONSchemaMethod = (schema, io) => (params) => {
24774
+ const { libraryOptions, target } = params ?? {};
24775
+ const ctx = initializeContext({ ...(libraryOptions ?? {}), target, io, processors: {} });
24776
+ process$1(schema, ctx);
24777
+ extractDefs(ctx, schema);
24778
+ return finalize(ctx, schema);
24779
+ };
24780
+
24781
+ const formatMap = {
24782
+ guid: "uuid",
24783
+ url: "uri",
24784
+ datetime: "date-time",
24785
+ json_string: "json-string",
24786
+ regex: "", // do not set
24787
+ };
24788
+ // ==================== SIMPLE TYPE PROCESSORS ====================
24789
+ const stringProcessor = (schema, ctx, _json, _params) => {
24790
+ const json = _json;
24791
+ json.type = "string";
24792
+ const { minimum, maximum, format, patterns, contentEncoding } = schema._zod
24793
+ .bag;
24794
+ if (typeof minimum === "number")
24795
+ json.minLength = minimum;
24796
+ if (typeof maximum === "number")
24797
+ json.maxLength = maximum;
24798
+ // custom pattern overrides format
24799
+ if (format) {
24800
+ json.format = formatMap[format] ?? format;
24801
+ if (json.format === "")
24802
+ delete json.format; // empty format is not valid
24803
+ }
24804
+ if (contentEncoding)
24805
+ json.contentEncoding = contentEncoding;
24806
+ if (patterns && patterns.size > 0) {
24807
+ const regexes = [...patterns];
24808
+ if (regexes.length === 1)
24809
+ json.pattern = regexes[0].source;
24810
+ else if (regexes.length > 1) {
24811
+ json.allOf = [
24812
+ ...regexes.map((regex) => ({
24813
+ ...(ctx.target === "draft-07" || ctx.target === "draft-04" || ctx.target === "openapi-3.0"
24814
+ ? { type: "string" }
24815
+ : {}),
24816
+ pattern: regex.source,
24817
+ })),
24818
+ ];
24819
+ }
24820
+ }
24821
+ };
24822
+ const numberProcessor = (schema, ctx, _json, _params) => {
24823
+ const json = _json;
24824
+ const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
24825
+ if (typeof format === "string" && format.includes("int"))
24826
+ json.type = "integer";
24827
+ else
24828
+ json.type = "number";
24829
+ if (typeof exclusiveMinimum === "number") {
24830
+ if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
24831
+ json.minimum = exclusiveMinimum;
24832
+ json.exclusiveMinimum = true;
24833
+ }
24834
+ else {
24835
+ json.exclusiveMinimum = exclusiveMinimum;
24836
+ }
24837
+ }
24838
+ if (typeof minimum === "number") {
24839
+ json.minimum = minimum;
24840
+ if (typeof exclusiveMinimum === "number" && ctx.target !== "draft-04") {
24841
+ if (exclusiveMinimum >= minimum)
24842
+ delete json.minimum;
24843
+ else
24844
+ delete json.exclusiveMinimum;
24845
+ }
24846
+ }
24847
+ if (typeof exclusiveMaximum === "number") {
24848
+ if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
24849
+ json.maximum = exclusiveMaximum;
24850
+ json.exclusiveMaximum = true;
24851
+ }
24852
+ else {
24853
+ json.exclusiveMaximum = exclusiveMaximum;
24854
+ }
24855
+ }
24856
+ if (typeof maximum === "number") {
24857
+ json.maximum = maximum;
24858
+ if (typeof exclusiveMaximum === "number" && ctx.target !== "draft-04") {
24859
+ if (exclusiveMaximum <= maximum)
24860
+ delete json.maximum;
24861
+ else
24862
+ delete json.exclusiveMaximum;
24863
+ }
24864
+ }
24865
+ if (typeof multipleOf === "number")
24866
+ json.multipleOf = multipleOf;
24867
+ };
24868
+ const neverProcessor = (_schema, _ctx, json, _params) => {
24869
+ json.not = {};
24870
+ };
24871
+ const anyProcessor = (_schema, _ctx, _json, _params) => {
24872
+ // empty schema accepts anything
24873
+ };
24874
+ const unknownProcessor = (_schema, _ctx, _json, _params) => {
24875
+ // empty schema accepts anything
24876
+ };
24877
+ const enumProcessor = (schema, _ctx, json, _params) => {
24878
+ const def = schema._zod.def;
24879
+ const values = getEnumValues(def.entries);
24880
+ // Number enums can have both string and number values
24881
+ if (values.every((v) => typeof v === "number"))
24882
+ json.type = "number";
24883
+ if (values.every((v) => typeof v === "string"))
24884
+ json.type = "string";
24885
+ json.enum = values;
24886
+ };
24887
+ const customProcessor = (_schema, ctx, _json, _params) => {
24888
+ if (ctx.unrepresentable === "throw") {
24889
+ throw new Error("Custom types cannot be represented in JSON Schema");
24890
+ }
24891
+ };
24892
+ const transformProcessor = (_schema, ctx, _json, _params) => {
24893
+ if (ctx.unrepresentable === "throw") {
24894
+ throw new Error("Transforms cannot be represented in JSON Schema");
24895
+ }
24896
+ };
24897
+ // ==================== COMPOSITE TYPE PROCESSORS ====================
24898
+ const arrayProcessor = (schema, ctx, _json, params) => {
24899
+ const json = _json;
24900
+ const def = schema._zod.def;
24901
+ const { minimum, maximum } = schema._zod.bag;
24902
+ if (typeof minimum === "number")
24903
+ json.minItems = minimum;
24904
+ if (typeof maximum === "number")
24905
+ json.maxItems = maximum;
24906
+ json.type = "array";
24907
+ json.items = process$1(def.element, ctx, { ...params, path: [...params.path, "items"] });
24908
+ };
24909
+ const objectProcessor = (schema, ctx, _json, params) => {
24910
+ const json = _json;
24911
+ const def = schema._zod.def;
24912
+ json.type = "object";
24913
+ json.properties = {};
24914
+ const shape = def.shape;
24915
+ for (const key in shape) {
24916
+ json.properties[key] = process$1(shape[key], ctx, {
24917
+ ...params,
24918
+ path: [...params.path, "properties", key],
24919
+ });
24920
+ }
24921
+ // required keys
24922
+ const allKeys = new Set(Object.keys(shape));
24923
+ const requiredKeys = new Set([...allKeys].filter((key) => {
24924
+ const v = def.shape[key]._zod;
24925
+ if (ctx.io === "input") {
24926
+ return v.optin === undefined;
24927
+ }
24928
+ else {
24929
+ return v.optout === undefined;
24930
+ }
24931
+ }));
24932
+ if (requiredKeys.size > 0) {
24933
+ json.required = Array.from(requiredKeys);
24934
+ }
24935
+ // catchall
24936
+ if (def.catchall?._zod.def.type === "never") {
24937
+ // strict
24938
+ json.additionalProperties = false;
24939
+ }
24940
+ else if (!def.catchall) {
24941
+ // regular
24942
+ if (ctx.io === "output")
24943
+ json.additionalProperties = false;
24944
+ }
24945
+ else if (def.catchall) {
24946
+ json.additionalProperties = process$1(def.catchall, ctx, {
24947
+ ...params,
24948
+ path: [...params.path, "additionalProperties"],
24949
+ });
24950
+ }
24951
+ };
24952
+ const unionProcessor = (schema, ctx, json, params) => {
24953
+ const def = schema._zod.def;
24954
+ // Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches)
24955
+ // This includes both z.xor() and discriminated unions
24956
+ const isExclusive = def.inclusive === false;
24957
+ const options = def.options.map((x, i) => process$1(x, ctx, {
24958
+ ...params,
24959
+ path: [...params.path, isExclusive ? "oneOf" : "anyOf", i],
24960
+ }));
24961
+ if (isExclusive) {
24962
+ json.oneOf = options;
24963
+ }
24964
+ else {
24965
+ json.anyOf = options;
24966
+ }
24967
+ };
24968
+ const intersectionProcessor = (schema, ctx, json, params) => {
24969
+ const def = schema._zod.def;
24970
+ const a = process$1(def.left, ctx, {
24971
+ ...params,
24972
+ path: [...params.path, "allOf", 0],
24973
+ });
24974
+ const b = process$1(def.right, ctx, {
24975
+ ...params,
24976
+ path: [...params.path, "allOf", 1],
24977
+ });
24978
+ const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1;
24979
+ const allOf = [
24980
+ ...(isSimpleIntersection(a) ? a.allOf : [a]),
24981
+ ...(isSimpleIntersection(b) ? b.allOf : [b]),
24982
+ ];
24983
+ json.allOf = allOf;
24984
+ };
24985
+ const nullableProcessor = (schema, ctx, json, params) => {
24986
+ const def = schema._zod.def;
24987
+ const inner = process$1(def.innerType, ctx, params);
24988
+ const seen = ctx.seen.get(schema);
24989
+ if (ctx.target === "openapi-3.0") {
24990
+ seen.ref = def.innerType;
24991
+ json.nullable = true;
24992
+ }
24993
+ else {
24994
+ json.anyOf = [inner, { type: "null" }];
24995
+ }
24996
+ };
24997
+ const nonoptionalProcessor = (schema, ctx, _json, params) => {
24998
+ const def = schema._zod.def;
24999
+ process$1(def.innerType, ctx, params);
25000
+ const seen = ctx.seen.get(schema);
25001
+ seen.ref = def.innerType;
25002
+ };
25003
+ const defaultProcessor = (schema, ctx, json, params) => {
25004
+ const def = schema._zod.def;
25005
+ process$1(def.innerType, ctx, params);
25006
+ const seen = ctx.seen.get(schema);
25007
+ seen.ref = def.innerType;
25008
+ json.default = JSON.parse(JSON.stringify(def.defaultValue));
25009
+ };
25010
+ const prefaultProcessor = (schema, ctx, json, params) => {
25011
+ const def = schema._zod.def;
25012
+ process$1(def.innerType, ctx, params);
25013
+ const seen = ctx.seen.get(schema);
25014
+ seen.ref = def.innerType;
25015
+ if (ctx.io === "input")
25016
+ json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
25017
+ };
25018
+ const catchProcessor = (schema, ctx, json, params) => {
25019
+ const def = schema._zod.def;
25020
+ process$1(def.innerType, ctx, params);
25021
+ const seen = ctx.seen.get(schema);
25022
+ seen.ref = def.innerType;
25023
+ let catchValue;
25024
+ try {
25025
+ catchValue = def.catchValue(undefined);
25026
+ }
25027
+ catch {
25028
+ throw new Error("Dynamic catch values are not supported in JSON Schema");
25029
+ }
25030
+ json.default = catchValue;
25031
+ };
25032
+ const pipeProcessor = (schema, ctx, _json, params) => {
25033
+ const def = schema._zod.def;
25034
+ const innerType = ctx.io === "input" ? (def.in._zod.def.type === "transform" ? def.out : def.in) : def.out;
25035
+ process$1(innerType, ctx, params);
25036
+ const seen = ctx.seen.get(schema);
25037
+ seen.ref = innerType;
25038
+ };
25039
+ const readonlyProcessor = (schema, ctx, json, params) => {
25040
+ const def = schema._zod.def;
25041
+ process$1(def.innerType, ctx, params);
25042
+ const seen = ctx.seen.get(schema);
25043
+ seen.ref = def.innerType;
25044
+ json.readOnly = true;
25045
+ };
25046
+ const optionalProcessor = (schema, ctx, _json, params) => {
25047
+ const def = schema._zod.def;
25048
+ process$1(def.innerType, ctx, params);
25049
+ const seen = ctx.seen.get(schema);
25050
+ seen.ref = def.innerType;
25051
+ };
25052
+
23669
25053
  const ZodISODateTime = /*@__PURE__*/ $constructor("ZodISODateTime", (inst, def) => {
23670
25054
  $ZodISODateTime.init(inst, def);
23671
25055
  ZodStringFormat.init(inst, def);
@@ -23756,6 +25140,13 @@ const safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
23756
25140
 
23757
25141
  const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
23758
25142
  $ZodType.init(inst, def);
25143
+ Object.assign(inst["~standard"], {
25144
+ jsonSchema: {
25145
+ input: createStandardJSONSchemaMethod(inst, "input"),
25146
+ output: createStandardJSONSchemaMethod(inst, "output"),
25147
+ },
25148
+ });
25149
+ inst.toJSONSchema = createToJSONSchemaMethod(inst, {});
23759
25150
  inst.def = def;
23760
25151
  inst.type = def.type;
23761
25152
  Object.defineProperty(inst, "_def", { value: def });
@@ -23837,6 +25228,7 @@ const ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
23837
25228
  const _ZodString = /*@__PURE__*/ $constructor("_ZodString", (inst, def) => {
23838
25229
  $ZodString.init(inst, def);
23839
25230
  ZodType.init(inst, def);
25231
+ inst._zod.processJSONSchema = (ctx, json, params) => stringProcessor(inst, ctx, json);
23840
25232
  const bag = inst._zod.bag;
23841
25233
  inst.format = bag.format ?? null;
23842
25234
  inst.minLength = bag.minimum ?? null;
@@ -23994,6 +25386,7 @@ const ZodJWT = /*@__PURE__*/ $constructor("ZodJWT", (inst, def) => {
23994
25386
  const ZodNumber = /*@__PURE__*/ $constructor("ZodNumber", (inst, def) => {
23995
25387
  $ZodNumber.init(inst, def);
23996
25388
  ZodType.init(inst, def);
25389
+ inst._zod.processJSONSchema = (ctx, json, params) => numberProcessor(inst, ctx, json);
23997
25390
  inst.gt = (value, params) => inst.check(_gt(value, params));
23998
25391
  inst.gte = (value, params) => inst.check(_gte(value, params));
23999
25392
  inst.min = (value, params) => inst.check(_gte(value, params));
@@ -24032,6 +25425,7 @@ function int(params) {
24032
25425
  const ZodAny = /*@__PURE__*/ $constructor("ZodAny", (inst, def) => {
24033
25426
  $ZodAny.init(inst, def);
24034
25427
  ZodType.init(inst, def);
25428
+ inst._zod.processJSONSchema = (ctx, json, params) => anyProcessor();
24035
25429
  });
24036
25430
  function any() {
24037
25431
  return _any(ZodAny);
@@ -24039,6 +25433,7 @@ function any() {
24039
25433
  const ZodUnknown = /*@__PURE__*/ $constructor("ZodUnknown", (inst, def) => {
24040
25434
  $ZodUnknown.init(inst, def);
24041
25435
  ZodType.init(inst, def);
25436
+ inst._zod.processJSONSchema = (ctx, json, params) => unknownProcessor();
24042
25437
  });
24043
25438
  function unknown() {
24044
25439
  return _unknown(ZodUnknown);
@@ -24046,6 +25441,7 @@ function unknown() {
24046
25441
  const ZodNever = /*@__PURE__*/ $constructor("ZodNever", (inst, def) => {
24047
25442
  $ZodNever.init(inst, def);
24048
25443
  ZodType.init(inst, def);
25444
+ inst._zod.processJSONSchema = (ctx, json, params) => neverProcessor(inst, ctx, json);
24049
25445
  });
24050
25446
  function never(params) {
24051
25447
  return _never(ZodNever, params);
@@ -24053,6 +25449,7 @@ function never(params) {
24053
25449
  const ZodArray = /*@__PURE__*/ $constructor("ZodArray", (inst, def) => {
24054
25450
  $ZodArray.init(inst, def);
24055
25451
  ZodType.init(inst, def);
25452
+ inst._zod.processJSONSchema = (ctx, json, params) => arrayProcessor(inst, ctx, json, params);
24056
25453
  inst.element = def.element;
24057
25454
  inst.min = (minLength, params) => inst.check(_minLength(minLength, params));
24058
25455
  inst.nonempty = (params) => inst.check(_minLength(1, params));
@@ -24066,6 +25463,7 @@ function array(element, params) {
24066
25463
  const ZodObject = /*@__PURE__*/ $constructor("ZodObject", (inst, def) => {
24067
25464
  $ZodObjectJIT.init(inst, def);
24068
25465
  ZodType.init(inst, def);
25466
+ inst._zod.processJSONSchema = (ctx, json, params) => objectProcessor(inst, ctx, json, params);
24069
25467
  defineLazy(inst, "shape", () => {
24070
25468
  return def.shape;
24071
25469
  });
@@ -24098,6 +25496,7 @@ function object(shape, params) {
24098
25496
  const ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
24099
25497
  $ZodUnion.init(inst, def);
24100
25498
  ZodType.init(inst, def);
25499
+ inst._zod.processJSONSchema = (ctx, json, params) => unionProcessor(inst, ctx, json, params);
24101
25500
  inst.options = def.options;
24102
25501
  });
24103
25502
  function union(options, params) {
@@ -24110,6 +25509,7 @@ function union(options, params) {
24110
25509
  const ZodIntersection = /*@__PURE__*/ $constructor("ZodIntersection", (inst, def) => {
24111
25510
  $ZodIntersection.init(inst, def);
24112
25511
  ZodType.init(inst, def);
25512
+ inst._zod.processJSONSchema = (ctx, json, params) => intersectionProcessor(inst, ctx, json, params);
24113
25513
  });
24114
25514
  function intersection(left, right) {
24115
25515
  return new ZodIntersection({
@@ -24121,6 +25521,7 @@ function intersection(left, right) {
24121
25521
  const ZodEnum = /*@__PURE__*/ $constructor("ZodEnum", (inst, def) => {
24122
25522
  $ZodEnum.init(inst, def);
24123
25523
  ZodType.init(inst, def);
25524
+ inst._zod.processJSONSchema = (ctx, json, params) => enumProcessor(inst, ctx, json);
24124
25525
  inst.enum = def.entries;
24125
25526
  inst.options = Object.values(def.entries);
24126
25527
  const keys = new Set(Object.keys(def.entries));
@@ -24168,6 +25569,7 @@ function _enum(values, params) {
24168
25569
  const ZodTransform = /*@__PURE__*/ $constructor("ZodTransform", (inst, def) => {
24169
25570
  $ZodTransform.init(inst, def);
24170
25571
  ZodType.init(inst, def);
25572
+ inst._zod.processJSONSchema = (ctx, json, params) => transformProcessor(inst, ctx);
24171
25573
  inst._zod.parse = (payload, _ctx) => {
24172
25574
  if (_ctx.direction === "backward") {
24173
25575
  throw new $ZodEncodeError(inst.constructor.name);
@@ -24208,6 +25610,7 @@ function transform(fn) {
24208
25610
  const ZodOptional = /*@__PURE__*/ $constructor("ZodOptional", (inst, def) => {
24209
25611
  $ZodOptional.init(inst, def);
24210
25612
  ZodType.init(inst, def);
25613
+ inst._zod.processJSONSchema = (ctx, json, params) => optionalProcessor(inst, ctx, json, params);
24211
25614
  inst.unwrap = () => inst._zod.def.innerType;
24212
25615
  });
24213
25616
  function optional(innerType) {
@@ -24219,6 +25622,7 @@ function optional(innerType) {
24219
25622
  const ZodNullable = /*@__PURE__*/ $constructor("ZodNullable", (inst, def) => {
24220
25623
  $ZodNullable.init(inst, def);
24221
25624
  ZodType.init(inst, def);
25625
+ inst._zod.processJSONSchema = (ctx, json, params) => nullableProcessor(inst, ctx, json, params);
24222
25626
  inst.unwrap = () => inst._zod.def.innerType;
24223
25627
  });
24224
25628
  function nullable(innerType) {
@@ -24230,6 +25634,7 @@ function nullable(innerType) {
24230
25634
  const ZodDefault = /*@__PURE__*/ $constructor("ZodDefault", (inst, def) => {
24231
25635
  $ZodDefault.init(inst, def);
24232
25636
  ZodType.init(inst, def);
25637
+ inst._zod.processJSONSchema = (ctx, json, params) => defaultProcessor(inst, ctx, json, params);
24233
25638
  inst.unwrap = () => inst._zod.def.innerType;
24234
25639
  inst.removeDefault = inst.unwrap;
24235
25640
  });
@@ -24245,6 +25650,7 @@ function _default(innerType, defaultValue) {
24245
25650
  const ZodPrefault = /*@__PURE__*/ $constructor("ZodPrefault", (inst, def) => {
24246
25651
  $ZodPrefault.init(inst, def);
24247
25652
  ZodType.init(inst, def);
25653
+ inst._zod.processJSONSchema = (ctx, json, params) => prefaultProcessor(inst, ctx, json, params);
24248
25654
  inst.unwrap = () => inst._zod.def.innerType;
24249
25655
  });
24250
25656
  function prefault(innerType, defaultValue) {
@@ -24259,6 +25665,7 @@ function prefault(innerType, defaultValue) {
24259
25665
  const ZodNonOptional = /*@__PURE__*/ $constructor("ZodNonOptional", (inst, def) => {
24260
25666
  $ZodNonOptional.init(inst, def);
24261
25667
  ZodType.init(inst, def);
25668
+ inst._zod.processJSONSchema = (ctx, json, params) => nonoptionalProcessor(inst, ctx, json, params);
24262
25669
  inst.unwrap = () => inst._zod.def.innerType;
24263
25670
  });
24264
25671
  function nonoptional(innerType, params) {
@@ -24271,6 +25678,7 @@ function nonoptional(innerType, params) {
24271
25678
  const ZodCatch = /*@__PURE__*/ $constructor("ZodCatch", (inst, def) => {
24272
25679
  $ZodCatch.init(inst, def);
24273
25680
  ZodType.init(inst, def);
25681
+ inst._zod.processJSONSchema = (ctx, json, params) => catchProcessor(inst, ctx, json, params);
24274
25682
  inst.unwrap = () => inst._zod.def.innerType;
24275
25683
  inst.removeCatch = inst.unwrap;
24276
25684
  });
@@ -24284,6 +25692,7 @@ function _catch(innerType, catchValue) {
24284
25692
  const ZodPipe = /*@__PURE__*/ $constructor("ZodPipe", (inst, def) => {
24285
25693
  $ZodPipe.init(inst, def);
24286
25694
  ZodType.init(inst, def);
25695
+ inst._zod.processJSONSchema = (ctx, json, params) => pipeProcessor(inst, ctx, json, params);
24287
25696
  inst.in = def.in;
24288
25697
  inst.out = def.out;
24289
25698
  });
@@ -24298,6 +25707,7 @@ function pipe(in_, out) {
24298
25707
  const ZodReadonly = /*@__PURE__*/ $constructor("ZodReadonly", (inst, def) => {
24299
25708
  $ZodReadonly.init(inst, def);
24300
25709
  ZodType.init(inst, def);
25710
+ inst._zod.processJSONSchema = (ctx, json, params) => readonlyProcessor(inst, ctx, json, params);
24301
25711
  inst.unwrap = () => inst._zod.def.innerType;
24302
25712
  });
24303
25713
  function readonly(innerType) {
@@ -24309,6 +25719,7 @@ function readonly(innerType) {
24309
25719
  const ZodCustom = /*@__PURE__*/ $constructor("ZodCustom", (inst, def) => {
24310
25720
  $ZodCustom.init(inst, def);
24311
25721
  ZodType.init(inst, def);
25722
+ inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx);
24312
25723
  });
24313
25724
  function refine(fn, _params = {}) {
24314
25725
  return _refine(ZodCustom, fn, _params);
@@ -24318,6 +25729,99 @@ function superRefine(fn) {
24318
25729
  return _superRefine(fn);
24319
25730
  }
24320
25731
 
25732
+ class BaseRoom {
25733
+ constructor() {
25734
+ /**
25735
+ * Signal containing the room's database of items, classes, and other game data
25736
+ *
25737
+ * This database can be dynamically populated using `addInDatabase()` and
25738
+ * `removeInDatabase()` methods. It's used to store game entities like items,
25739
+ * classes, skills, etc. that are available in this room.
25740
+ *
25741
+ * @example
25742
+ * ```ts
25743
+ * // Add data to database
25744
+ * room.addInDatabase('Potion', PotionClass);
25745
+ *
25746
+ * // Access database
25747
+ * const potion = room.database()['Potion'];
25748
+ * ```
25749
+ */
25750
+ this.database = signal({});
25751
+ }
25752
+ /**
25753
+ * Add data to the room's database
25754
+ *
25755
+ * Adds an item, class, or other game entity to the room's database.
25756
+ * If the ID already exists and `force` is not enabled, the addition is ignored.
25757
+ *
25758
+ * ## Architecture
25759
+ *
25760
+ * This method is used by the item management system to store item definitions
25761
+ * in the room's database. When a player adds an item, the system first checks
25762
+ * if the item exists in the database, and if not, adds it using this method.
25763
+ *
25764
+ * @param id - Unique identifier for the data
25765
+ * @param data - The data to add (can be a class, object, etc.)
25766
+ * @param options - Optional configuration
25767
+ * @param options.force - If true, overwrites existing data with the same ID
25768
+ * @returns `true` if data was added, `false` if it was ignored (ID already exists)
25769
+ *
25770
+ * @example
25771
+ * ```ts
25772
+ * // Add a class to the database
25773
+ * room.addInDatabase('Potion', PotionClass);
25774
+ *
25775
+ * // Add an item object to the database
25776
+ * room.addInDatabase('custom-item', {
25777
+ * name: 'Custom Item',
25778
+ * price: 100
25779
+ * });
25780
+ *
25781
+ * // Force overwrite existing data
25782
+ * room.addInDatabase('Potion', UpdatedPotionClass, { force: true });
25783
+ * ```
25784
+ */
25785
+ addInDatabase(id, data, options) {
25786
+ const database = this.database();
25787
+ if (database[id] !== void 0 && !options?.force) {
25788
+ return false;
25789
+ }
25790
+ database[id] = data;
25791
+ this.database.set(database);
25792
+ return true;
25793
+ }
25794
+ /**
25795
+ * Remove data from the room's database
25796
+ *
25797
+ * This method allows you to remove items or data from the room's database.
25798
+ *
25799
+ * @param id - Unique identifier of the data to remove
25800
+ * @returns `true` if data was removed, `false` if ID didn't exist
25801
+ *
25802
+ * @example
25803
+ * ```ts
25804
+ * // Remove an item from the database
25805
+ * room.removeInDatabase('Potion');
25806
+ *
25807
+ * // Check if removal was successful
25808
+ * const removed = room.removeInDatabase('custom-item');
25809
+ * if (removed) {
25810
+ * console.log('Item removed successfully');
25811
+ * }
25812
+ * ```
25813
+ */
25814
+ removeInDatabase(id) {
25815
+ const database = this.database();
25816
+ if (database[id] === void 0) {
25817
+ return false;
25818
+ }
25819
+ delete database[id];
25820
+ this.database.set(database);
25821
+ return true;
25822
+ }
25823
+ }
25824
+
24321
25825
  var __defProp$1 = Object.defineProperty;
24322
25826
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
24323
25827
  var __decorateClass$1 = (decorators, target, key, kind) => {
@@ -24341,7 +25845,7 @@ const MapUpdateSchema = object({
24341
25845
  height: number()
24342
25846
  });
24343
25847
  let RpgMap = class extends RpgCommonMap {
24344
- constructor() {
25848
+ constructor(room) {
24345
25849
  super();
24346
25850
  this.players = signal({});
24347
25851
  this.events = signal({});
@@ -24418,9 +25922,19 @@ let RpgMap = class extends RpgCommonMap {
24418
25922
  this._shapeEntities = /* @__PURE__ */ new Map();
24419
25923
  /** Enable/disable automatic tick processing (useful for unit tests) */
24420
25924
  this._autoTickEnabled = true;
25925
+ this.autoSync = true;
24421
25926
  this.hooks.callHooks("server-map-onStart", this).subscribe();
24422
- this.throttleSync = this.isStandalone ? 0 : 50;
24423
- this.throttleStorage = this.isStandalone ? 0 : 1e3;
25927
+ const isTest = room.env.TEST === "true" ? true : false;
25928
+ if (isTest) {
25929
+ this.autoSync = false;
25930
+ this.setAutoTick(false);
25931
+ this.autoTickEnabled = false;
25932
+ this.throttleSync = 0;
25933
+ this.throttleStorage = 0;
25934
+ } else {
25935
+ this.throttleSync = this.isStandalone ? 1 : 50;
25936
+ this.throttleStorage = this.isStandalone ? 1 : 50;
25937
+ }
24424
25938
  this.sessionExpiryTime = 1e3 * 60 * 5;
24425
25939
  this.setupCollisionDetection();
24426
25940
  if (this._autoTickEnabled) {
@@ -24617,12 +26131,16 @@ let RpgMap = class extends RpgCommonMap {
24617
26131
  * ```
24618
26132
  */
24619
26133
  onJoin(player, conn) {
24620
- player.map = this;
26134
+ if (player.setMap) {
26135
+ player.setMap(this);
26136
+ } else {
26137
+ player.map = this;
26138
+ }
24621
26139
  player.context = context$1;
24622
26140
  player.conn = conn;
24623
26141
  player._onInit();
24624
26142
  this.dataIsReady$.pipe(
24625
- finalize(async () => {
26143
+ finalize$1(async () => {
24626
26144
  if (this.stopAllSoundsBeforeJoin) {
24627
26145
  player.stopAllSounds();
24628
26146
  }
@@ -24683,85 +26201,8 @@ let RpgMap = class extends RpgCommonMap {
24683
26201
  get hooks() {
24684
26202
  return inject$1(context$1, ModulesToken);
24685
26203
  }
24686
- /**
24687
- * Get the width of the map in pixels
24688
- *
24689
- * @returns The width of the map in pixels, or 0 if not loaded
24690
- *
24691
- * @example
24692
- * ```ts
24693
- * const width = map.widthPx;
24694
- * console.log(`Map width: ${width}px`);
24695
- * ```
24696
- */
24697
- get widthPx() {
24698
- return this.data()?.width ?? 0;
24699
- }
24700
- /**
24701
- * Get the height of the map in pixels
24702
- *
24703
- * @returns The height of the map in pixels, or 0 if not loaded
24704
- *
24705
- * @example
24706
- * ```ts
24707
- * const height = map.heightPx;
24708
- * console.log(`Map height: ${height}px`);
24709
- * ```
24710
- */
24711
- get heightPx() {
24712
- return this.data()?.height ?? 0;
24713
- }
24714
- /**
24715
- * Get the unique identifier of the map
24716
- *
24717
- * @returns The map ID, or empty string if not loaded
24718
- *
24719
- * @example
24720
- * ```ts
24721
- * const mapId = map.id;
24722
- * console.log(`Current map: ${mapId}`);
24723
- * ```
24724
- */
24725
- get id() {
24726
- return this.data()?.id ?? "";
24727
- }
24728
- /**
24729
- * Get the X position of this map in the world coordinate system
24730
- *
24731
- * This is used when maps are part of a larger world map. The world position
24732
- * indicates where this map is located relative to other maps.
24733
- *
24734
- * @returns The X position in world coordinates, or 0 if not in a world
24735
- *
24736
- * @example
24737
- * ```ts
24738
- * const worldX = map.worldX;
24739
- * console.log(`Map is at world position (${worldX}, ${map.worldY})`);
24740
- * ```
24741
- */
24742
- get worldX() {
24743
- const worldMaps = this.getWorldMapsManager?.();
24744
- return worldMaps?.getMapInfo(this.id)?.worldX ?? 0;
24745
- }
24746
- /**
24747
- * Get the Y position of this map in the world coordinate system
24748
- *
24749
- * This is used when maps are part of a larger world map. The world position
24750
- * indicates where this map is located relative to other maps.
24751
- *
24752
- * @returns The Y position in world coordinates, or 0 if not in a world
24753
- *
24754
- * @example
24755
- * ```ts
24756
- * const worldY = map.worldY;
24757
- * console.log(`Map is at world position (${map.worldX}, ${worldY})`);
24758
- * ```
24759
- */
24760
- get worldY() {
24761
- const worldMaps = this.getWorldMapsManager?.();
24762
- return worldMaps?.getMapInfo(this.id)?.worldY ?? 0;
24763
- }
24764
26204
  guiInteraction(player, value) {
26205
+ this.hooks.callHooks("server-player-guiInteraction", player, value);
24765
26206
  player.syncChanges();
24766
26207
  }
24767
26208
  guiExit(player, { guiId, data }) {
@@ -24804,6 +26245,7 @@ let RpgMap = class extends RpgCommonMap {
24804
26245
  };
24805
26246
  await lastValueFrom(this.hooks.callHooks("server-maps-load", this));
24806
26247
  await lastValueFrom(this.hooks.callHooks("server-worldMaps-load", this));
26248
+ await lastValueFrom(this.hooks.callHooks("server-databaseHooks-load", this));
24807
26249
  map.events = map.events ?? [];
24808
26250
  if (map.id) {
24809
26251
  const mapFound = this.maps.find((m) => m.id === map.id);
@@ -25151,8 +26593,7 @@ let RpgMap = class extends RpgCommonMap {
25151
26593
  /**
25152
26594
  * Add data to the map's database
25153
26595
  *
25154
- * This method allows you to dynamically add items, classes, or any data to the map's database.
25155
- * By default, if an ID already exists, the operation is ignored to prevent overwriting existing data.
26596
+ * This method delegates to BaseRoom's implementation to avoid code duplication.
25156
26597
  *
25157
26598
  * @param id - Unique identifier for the data
25158
26599
  * @param data - The data to store (can be a class, object, or any value)
@@ -25176,17 +26617,12 @@ let RpgMap = class extends RpgCommonMap {
25176
26617
  * ```
25177
26618
  */
25178
26619
  addInDatabase(id, data, options) {
25179
- const database = this.database();
25180
- if (database[id] !== void 0 && !options?.force) {
25181
- return false;
25182
- }
25183
- database[id] = data;
25184
- return true;
26620
+ return BaseRoom.prototype.addInDatabase.call(this, id, data, options);
25185
26621
  }
25186
26622
  /**
25187
26623
  * Remove data from the map's database
25188
26624
  *
25189
- * This method allows you to remove items or data from the map's database.
26625
+ * This method delegates to BaseRoom's implementation to avoid code duplication.
25190
26626
  *
25191
26627
  * @param id - Unique identifier of the data to remove
25192
26628
  * @returns true if data was removed, false if ID didn't exist
@@ -25204,12 +26640,7 @@ let RpgMap = class extends RpgCommonMap {
25204
26640
  * ```
25205
26641
  */
25206
26642
  removeInDatabase(id) {
25207
- const database = this.database();
25208
- if (database[id] === void 0) {
25209
- return false;
25210
- }
25211
- delete database[id];
25212
- return true;
26643
+ return BaseRoom.prototype.removeInDatabase.call(this, id);
25213
26644
  }
25214
26645
  /**
25215
26646
  * Creates a dynamic event on the map
@@ -25289,8 +26720,7 @@ let RpgMap = class extends RpgCommonMap {
25289
26720
  }
25290
26721
  eventInstance.map = this;
25291
26722
  eventInstance.context = context$1;
25292
- eventInstance.x.set(x);
25293
- eventInstance.y.set(y);
26723
+ await eventInstance.teleport({ x, y });
25294
26724
  this.events()[id] = eventInstance;
25295
26725
  await eventInstance.execMethod("onInit");
25296
26726
  }
@@ -25577,6 +27007,19 @@ let RpgMap = class extends RpgCommonMap {
25577
27007
  }, holder);
25578
27008
  }
25579
27009
  }
27010
+ /**
27011
+ * Apply sync to the client
27012
+ *
27013
+ * This method applies sync to the client by calling the `$applySync()` method.
27014
+ *
27015
+ * @example
27016
+ * ```ts
27017
+ * map.applySyncToClient();
27018
+ * ```
27019
+ */
27020
+ applySyncToClient() {
27021
+ this.$applySync();
27022
+ }
25580
27023
  /**
25581
27024
  * Create a shape dynamically on the map
25582
27025
  *
@@ -25983,9 +27426,15 @@ var __decorateClass = (decorators, target, key, kind) => {
25983
27426
  if (kind && result) __defProp(target, key, result);
25984
27427
  return result;
25985
27428
  };
25986
- let LobbyRoom = class {
25987
- constructor() {
27429
+ let LobbyRoom = class extends BaseRoom {
27430
+ constructor(room) {
27431
+ super();
25988
27432
  this.players = signal({});
27433
+ this.autoSync = true;
27434
+ const isTest = room.env.TEST === "true" ? true : false;
27435
+ if (isTest) {
27436
+ this.autoSync = false;
27437
+ }
25989
27438
  }
25990
27439
  onJoin(player, conn) {
25991
27440
  player.map = this;
@@ -26338,6 +27787,19 @@ function provideServerModules(modules) {
26338
27787
  }
26339
27788
  };
26340
27789
  }
27790
+ if (module.database && typeof module.database === "object") {
27791
+ const database = { ...module.database };
27792
+ module = {
27793
+ ...module,
27794
+ databaseHooks: {
27795
+ load: (engine) => {
27796
+ for (const key in database) {
27797
+ engine.addInDatabase(key, database[key]);
27798
+ }
27799
+ }
27800
+ }
27801
+ };
27802
+ }
26341
27803
  return module;
26342
27804
  });
26343
27805
  return modules2;