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

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.
@@ -1,4 +1,10 @@
1
1
  import { PlayerCtor } from '../../../common/src';
2
+ export type ExpCurve = {
3
+ basis: number;
4
+ extra: number;
5
+ accelerationA: number;
6
+ accelerationB: number;
7
+ };
2
8
  /**
3
9
  * Interface for Parameter Manager functionality
4
10
  *
@@ -47,12 +53,7 @@ export interface IParameterManager {
47
53
  * ```
48
54
  * @memberof ParameterManager
49
55
  * */
50
- expCurve: {
51
- basis: number;
52
- extra: number;
53
- accelerationA: number;
54
- accelerationB: number;
55
- };
56
+ expCurve: ExpCurve;
56
57
  /**
57
58
  * Changes the health points
58
59
  * - Cannot exceed the MaxHP parameter
@@ -94,6 +94,7 @@ export declare class RpgPlayer extends RpgPlayer_base {
94
94
  events: import('@signe/reactive').WritableArraySignal<RpgEvent[]>;
95
95
  constructor();
96
96
  _onInit(): void;
97
+ onGameStart(): void;
97
98
  get hooks(): Hooks;
98
99
  get server(): RpgMap | null;
99
100
  setMap(map: RpgMap): void;
@@ -502,6 +503,7 @@ export declare class RpgPlayer extends RpgPlayer_base {
502
503
  isEvent(): boolean;
503
504
  }
504
505
  export declare class RpgEvent extends RpgPlayer {
506
+ constructor();
505
507
  execMethod(methodName: string, methodData?: any[], instance?: this): Promise<any>;
506
508
  /**
507
509
  * Remove this event from the map
@@ -659,10 +659,10 @@ export interface RpgServer {
659
659
  * })
660
660
  * ```
661
661
  *
662
- * @prop { { [dataName]: data } } [database]
662
+ * @prop { { [dataName]: data } | (engine: RpgMap) => { [dataName]: data } | Promise<{ [dataName]: data }> } [database]
663
663
  * @memberof RpgServer
664
664
  * */
665
- database?: object | any[];
665
+ database?: object | any[] | ((engine: RpgMap) => object | any[] | Promise<object | any[]>);
666
666
  /**
667
667
  * Array of all maps. Each element can be either a class (decorated with `@MapData` or not) or a `MapOptions` object
668
668
  *
package/dist/index.js CHANGED
@@ -17005,13 +17005,13 @@ class RpgCommonMap {
17005
17005
  const ownerHasAnimationName = animationNameSignal && typeof animationNameSignal === "object" && typeof animationNameSignal.set === "function";
17006
17006
  if (isMoving && intensity > LOW_INTENSITY_THRESHOLD) {
17007
17007
  if (hasSetAnimation) {
17008
- owner2.setAnimation("walk");
17008
+ owner2.setGraphicAnimation("walk");
17009
17009
  } else if (ownerHasAnimationName) {
17010
17010
  animationNameSignal.set("walk");
17011
17011
  }
17012
17012
  } else if (!isMoving) {
17013
17013
  if (hasSetAnimation) {
17014
- owner2.setAnimation("stand");
17014
+ owner2.setGraphicAnimation("stand");
17015
17015
  } else if (ownerHasAnimationName) {
17016
17016
  animationNameSignal.set("stand");
17017
17017
  }
@@ -19118,7 +19118,8 @@ function WithMoveManager(Base) {
19118
19118
  * Move toward a target player or position using AI pathfinding
19119
19119
  *
19120
19120
  * Uses the `SeekAvoid` strategy to navigate toward the target while avoiding obstacles.
19121
- * The movement speed is based on the player's current `speed` property, scaled appropriately.
19121
+ * The movement speed is based on the player's current `speed` and `frequency` settings,
19122
+ * scaled appropriately.
19122
19123
  *
19123
19124
  * @param target - Target player or position `{ x, y }` to move toward
19124
19125
  *
@@ -19137,6 +19138,10 @@ function WithMoveManager(Base) {
19137
19138
  const playerId = this.id;
19138
19139
  const engine = map.physic;
19139
19140
  const playerSpeed = this.speed();
19141
+ const rawFrequency = this.frequency;
19142
+ const playerFrequency = typeof rawFrequency === "function" ? rawFrequency() : rawFrequency;
19143
+ const frequencyScale = playerFrequency > 0 ? 100 /* High */ / playerFrequency : 1;
19144
+ const normalizedFrequencyScale = Number.isFinite(frequencyScale) && frequencyScale > 0 ? frequencyScale : 1;
19140
19145
  const existingStrategies = this.getActiveMovements();
19141
19146
  const conflictingStrategies = existingStrategies.filter(
19142
19147
  (s) => s instanceof SeekAvoid || s instanceof Dash || s instanceof Knockback || s instanceof LinearRepulsion
@@ -19149,7 +19154,7 @@ function WithMoveManager(Base) {
19149
19154
  const body = map.getBody(target.id) ?? null;
19150
19155
  return body;
19151
19156
  };
19152
- const maxSpeed2 = playerSpeed * 45;
19157
+ const maxSpeed2 = playerSpeed * 45 * normalizedFrequencyScale;
19153
19158
  map.moveManager.add(
19154
19159
  playerId,
19155
19160
  new SeekAvoid(engine, targetProvider, maxSpeed2, 140, 80, 48)
@@ -19161,7 +19166,7 @@ function WithMoveManager(Base) {
19161
19166
  mass: Infinity
19162
19167
  });
19163
19168
  staticTarget.freeze();
19164
- const maxSpeed = playerSpeed * 20;
19169
+ const maxSpeed = playerSpeed * 20 * normalizedFrequencyScale;
19165
19170
  map.moveManager.add(
19166
19171
  playerId,
19167
19172
  new SeekAvoid(engine, () => staticTarget, maxSpeed, 140, 80, 48)
@@ -19259,8 +19264,8 @@ function WithMoveManager(Base) {
19259
19264
  };
19260
19265
  const hasActiveKnockback = () => this.getActiveMovements().some((s) => s instanceof Knockback || s instanceof AdditiveKnockback);
19261
19266
  const setAnimationName = (name) => {
19262
- if (typeof selfAny.setAnimation === "function") {
19263
- selfAny.setAnimation(name);
19267
+ if (typeof selfAny.setGraphicAnimation === "function") {
19268
+ selfAny.setGraphicAnimation(name);
19264
19269
  return;
19265
19270
  }
19266
19271
  const animSignal = selfAny.animationName;
@@ -19471,6 +19476,9 @@ function WithMoveManager(Base) {
19471
19476
  this.waitingForPromise = false;
19472
19477
  this.promiseStartTime = 0;
19473
19478
  this.promiseDuration = 0;
19479
+ this.remainingDistance = 0;
19480
+ this.segmentDirection = null;
19481
+ this.segmentStep = 0;
19474
19482
  // Frequency wait state
19475
19483
  this.waitingForFrequency = false;
19476
19484
  this.frequencyWaitStartTime = 0;
@@ -19497,6 +19505,9 @@ function WithMoveManager(Base) {
19497
19505
  processNextRoute() {
19498
19506
  this.waitingForFrequency = false;
19499
19507
  this.frequencyWaitStartTime = 0;
19508
+ this.remainingDistance = 0;
19509
+ this.segmentDirection = null;
19510
+ this.segmentStep = 0;
19500
19511
  if (this.routeIndex >= this.routes.length) {
19501
19512
  this.debugLog("COMPLETE all routes finished");
19502
19513
  this.finished = true;
@@ -19573,41 +19584,13 @@ function WithMoveManager(Base) {
19573
19584
  break;
19574
19585
  }
19575
19586
  }
19576
- let targetTopLeftX = currentTopLeftX;
19577
- let targetTopLeftY = currentTopLeftY;
19578
- switch (moveDirection) {
19579
- case Direction.Right:
19580
- case "right":
19581
- targetTopLeftX = currentTopLeftX + distance;
19582
- break;
19583
- case Direction.Left:
19584
- case "left":
19585
- targetTopLeftX = currentTopLeftX - distance;
19586
- break;
19587
- case Direction.Down:
19588
- case "down":
19589
- targetTopLeftY = currentTopLeftY + distance;
19590
- break;
19591
- case Direction.Up:
19592
- case "up":
19593
- targetTopLeftY = currentTopLeftY - distance;
19594
- break;
19595
- }
19596
- const entity = map.physic.getEntityByUUID(this.player.id);
19597
- if (!entity) {
19598
- this.finished = true;
19599
- this.onComplete(false);
19600
- return;
19587
+ this.remainingDistance = distance;
19588
+ this.segmentDirection = moveDirection;
19589
+ this.segmentStep = this.getTileStepDistance(playerSpeed);
19590
+ this.setNextSegmentTarget(currentTopLeftX, currentTopLeftY);
19591
+ if (this.currentTargetTopLeft) {
19592
+ this.debugLog(`MOVE direction=${moveDirection} from=(${currentTopLeftX.toFixed(1)}, ${currentTopLeftY.toFixed(1)}) to=(${this.currentTargetTopLeft.x.toFixed(1)}, ${this.currentTargetTopLeft.y.toFixed(1)}) dist=${distance.toFixed(1)}`);
19601
19593
  }
19602
- const hitbox = this.player.hitbox();
19603
- const hitboxWidth = hitbox?.w ?? 32;
19604
- const hitboxHeight = hitbox?.h ?? 32;
19605
- const targetX = targetTopLeftX + hitboxWidth / 2;
19606
- const targetY = targetTopLeftY + hitboxHeight / 2;
19607
- this.currentTarget = { x: targetX, y: targetY };
19608
- this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY };
19609
- this.currentDirection = { x: 0, y: 0 };
19610
- this.debugLog(`MOVE direction=${moveDirection} from=(${currentTopLeftX.toFixed(1)}, ${currentTopLeftY.toFixed(1)}) to=(${targetTopLeftX.toFixed(1)}, ${targetTopLeftY.toFixed(1)}) dist=${distance.toFixed(1)}`);
19611
19594
  this.lastPosition = null;
19612
19595
  this.isCurrentlyStuck = false;
19613
19596
  this.stuckCheckStartTime = 0;
@@ -19643,7 +19626,13 @@ function WithMoveManager(Base) {
19643
19626
  const frequencyMs = playerFrequency || 0;
19644
19627
  if (frequencyMs > 0 && Date.now() - this.frequencyWaitStartTime >= frequencyMs * this.ratioFrequency) {
19645
19628
  this.waitingForFrequency = false;
19646
- this.processNextRoute();
19629
+ if (this.remainingDistance > 0) {
19630
+ const currentTopLeftX2 = this.player.x();
19631
+ const currentTopLeftY2 = this.player.y();
19632
+ this.setNextSegmentTarget(currentTopLeftX2, currentTopLeftY2);
19633
+ } else {
19634
+ this.processNextRoute();
19635
+ }
19647
19636
  }
19648
19637
  return;
19649
19638
  }
@@ -19691,6 +19680,10 @@ function WithMoveManager(Base) {
19691
19680
  if (playerFrequency && playerFrequency > 0) {
19692
19681
  this.waitingForFrequency = true;
19693
19682
  this.frequencyWaitStartTime = Date.now();
19683
+ } else if (this.remainingDistance > 0) {
19684
+ const nextTopLeftX = this.player.x();
19685
+ const nextTopLeftY = this.player.y();
19686
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
19694
19687
  } else {
19695
19688
  this.processNextRoute();
19696
19689
  }
@@ -19715,6 +19708,10 @@ function WithMoveManager(Base) {
19715
19708
  if (playerFrequency && playerFrequency > 0) {
19716
19709
  this.waitingForFrequency = true;
19717
19710
  this.frequencyWaitStartTime = Date.now();
19711
+ } else if (this.remainingDistance > 0) {
19712
+ const nextTopLeftX = this.player.x();
19713
+ const nextTopLeftY = this.player.y();
19714
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
19718
19715
  } else {
19719
19716
  this.processNextRoute();
19720
19717
  }
@@ -19789,6 +19786,10 @@ function WithMoveManager(Base) {
19789
19786
  if (playerFrequency && playerFrequency > 0) {
19790
19787
  this.waitingForFrequency = true;
19791
19788
  this.frequencyWaitStartTime = Date.now();
19789
+ } else if (this.remainingDistance > 0) {
19790
+ const nextTopLeftX = this.player.x();
19791
+ const nextTopLeftY = this.player.y();
19792
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
19792
19793
  } else {
19793
19794
  this.processNextRoute();
19794
19795
  }
@@ -19811,6 +19812,60 @@ function WithMoveManager(Base) {
19811
19812
  onFinished() {
19812
19813
  this.onComplete(true);
19813
19814
  }
19815
+ getTileStepDistance(playerSpeed) {
19816
+ if (!Number.isFinite(playerSpeed) || playerSpeed <= 0) {
19817
+ return this.tileSize;
19818
+ }
19819
+ const stepsPerTile = Math.max(1, Math.floor(this.tileSize / playerSpeed));
19820
+ return stepsPerTile * playerSpeed;
19821
+ }
19822
+ setNextSegmentTarget(currentTopLeftX, currentTopLeftY) {
19823
+ if (!this.segmentDirection || this.remainingDistance <= 0) {
19824
+ return;
19825
+ }
19826
+ const map = this.player.getCurrentMap();
19827
+ if (!map) {
19828
+ this.finished = true;
19829
+ this.onComplete(false);
19830
+ return;
19831
+ }
19832
+ const entity = map.physic.getEntityByUUID(this.player.id);
19833
+ if (!entity) {
19834
+ this.finished = true;
19835
+ this.onComplete(false);
19836
+ return;
19837
+ }
19838
+ const segmentDistance = Math.min(this.segmentStep || this.remainingDistance, this.remainingDistance);
19839
+ let targetTopLeftX = currentTopLeftX;
19840
+ let targetTopLeftY = currentTopLeftY;
19841
+ switch (this.segmentDirection) {
19842
+ case Direction.Right:
19843
+ case "right":
19844
+ targetTopLeftX = currentTopLeftX + segmentDistance;
19845
+ break;
19846
+ case Direction.Left:
19847
+ case "left":
19848
+ targetTopLeftX = currentTopLeftX - segmentDistance;
19849
+ break;
19850
+ case Direction.Down:
19851
+ case "down":
19852
+ targetTopLeftY = currentTopLeftY + segmentDistance;
19853
+ break;
19854
+ case Direction.Up:
19855
+ case "up":
19856
+ targetTopLeftY = currentTopLeftY - segmentDistance;
19857
+ break;
19858
+ }
19859
+ const hitbox = this.player.hitbox();
19860
+ const hitboxWidth = hitbox?.w ?? 32;
19861
+ const hitboxHeight = hitbox?.h ?? 32;
19862
+ const targetX = targetTopLeftX + hitboxWidth / 2;
19863
+ const targetY = targetTopLeftY + hitboxHeight / 2;
19864
+ this.currentTarget = { x: targetX, y: targetY };
19865
+ this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY };
19866
+ this.currentDirection = { x: 0, y: 0 };
19867
+ this.remainingDistance -= segmentDistance;
19868
+ }
19814
19869
  }
19815
19870
  const routeStrategy = new RouteMovementStrategy(
19816
19871
  finalRoutes,
@@ -20173,7 +20228,7 @@ class MenuGui extends Gui {
20173
20228
  spCost: skill?.spCost() ?? 0
20174
20229
  }));
20175
20230
  const saveLoad = this.buildSaveLoad(options);
20176
- return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats() };
20231
+ return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats(), expForNextlevel: player2.expForNextlevel };
20177
20232
  }
20178
20233
  refreshMenu(clientActionId) {
20179
20234
  const data = this.buildMenuData(this.menuOptions);
@@ -20635,14 +20690,14 @@ function WithParameterManager(Base) {
20635
20690
  * console.log(player.param[MAXHP]); // Updated value
20636
20691
  * ```
20637
20692
  */
20638
- this._paramsModifierSignal = signal({});
20693
+ this._paramsModifierSignal = type(signal({}), "_paramsModifierSignal", { persist: true }, this);
20639
20694
  /**
20640
20695
  * Signal for base parameters configuration
20641
20696
  *
20642
20697
  * Stores the start and end values for each parameter's level curve.
20643
20698
  * Changes to this signal trigger recalculation of all parameter values.
20644
20699
  */
20645
- this._parametersSignal = signal({});
20700
+ this._parametersSignal = type(signal({}), "_parametersSignal", { persist: true }, this);
20646
20701
  /**
20647
20702
  * Computed signal for all parameter values
20648
20703
  *
@@ -20662,10 +20717,10 @@ function WithParameterManager(Base) {
20662
20717
  this._param = type(computed(() => {
20663
20718
  const obj = {};
20664
20719
  const parameters = this._parametersSignal();
20720
+ const allModifiers = this._getAggregatedModifiers();
20665
20721
  const level = this._level();
20666
20722
  for (const [name, paramConfig] of Object.entries(parameters)) {
20667
20723
  let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start;
20668
- const allModifiers = this._getAggregatedModifiers();
20669
20724
  const modifier = allModifiers[name];
20670
20725
  if (modifier) {
20671
20726
  if (modifier.rate) curveVal *= modifier.rate;
@@ -20697,6 +20752,26 @@ function WithParameterManager(Base) {
20697
20752
  * @memberof ParameterManager
20698
20753
  * */
20699
20754
  this.finalLevel = 99;
20755
+ /**
20756
+ * With Object-based syntax, you can use following options:
20757
+ * - `basis: number`
20758
+ * - `extra: number`
20759
+ * - `accelerationA: number`
20760
+ * - `accelerationB: number`
20761
+ * @title Change Experience Curve
20762
+ * @prop {object} player.expCurve
20763
+ * @default
20764
+ * ```ts
20765
+ * {
20766
+ * basis: 30,
20767
+ * extra: 20,
20768
+ * accelerationA: 30,
20769
+ * accelerationB: 30
20770
+ * }
20771
+ * ```
20772
+ * @memberof ParameterManager
20773
+ * */
20774
+ this._expCurveSignal = type(signal(""), "_expCurveSignal", { persist: true }, this);
20700
20775
  }
20701
20776
  /**
20702
20777
  * Aggregates parameter modifiers from all sources (direct modifiers, states, equipment)
@@ -20744,6 +20819,12 @@ function WithParameterManager(Base) {
20744
20819
  }
20745
20820
  return params;
20746
20821
  }
20822
+ get expCurve() {
20823
+ return JSON.parse(this._expCurveSignal());
20824
+ }
20825
+ set expCurve(val) {
20826
+ this._expCurveSignal.set(JSON.stringify(val));
20827
+ }
20747
20828
  /**
20748
20829
  * Changes the health points
20749
20830
  * - Cannot exceed the MaxHP parameter
@@ -22284,19 +22365,6 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
22284
22365
  this._lastFramePositions = null;
22285
22366
  this.frames = [];
22286
22367
  this.events = signal([]);
22287
- this.expCurve = {
22288
- basis: 30,
22289
- extra: 20,
22290
- accelerationA: 30,
22291
- accelerationB: 30
22292
- };
22293
- this.addParameter(MAXHP, MAXHP_CURVE);
22294
- this.addParameter(MAXSP, MAXSP_CURVE);
22295
- this.addParameter(STR, STR_CURVE);
22296
- this.addParameter(INT, INT_CURVE);
22297
- this.addParameter(DEX, DEX_CURVE);
22298
- this.addParameter(AGI, AGI_CURVE);
22299
- this.allRecovery();
22300
22368
  let lastEmitted = null;
22301
22369
  let pendingUpdate = null;
22302
22370
  let updateScheduled = false;
@@ -22375,6 +22443,21 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
22375
22443
  _onInit() {
22376
22444
  this.hooks.callHooks("server-playerProps-load", this).subscribe();
22377
22445
  }
22446
+ onGameStart() {
22447
+ this.expCurve = {
22448
+ basis: 30,
22449
+ extra: 20,
22450
+ accelerationA: 30,
22451
+ accelerationB: 30
22452
+ };
22453
+ this.addParameter(MAXHP, MAXHP_CURVE);
22454
+ this.addParameter(MAXSP, MAXSP_CURVE);
22455
+ this.addParameter(STR, STR_CURVE);
22456
+ this.addParameter(INT, INT_CURVE);
22457
+ this.addParameter(DEX, DEX_CURVE);
22458
+ this.addParameter(AGI, AGI_CURVE);
22459
+ this.allRecovery();
22460
+ }
22378
22461
  get hooks() {
22379
22462
  return inject$1(this.context, ModulesToken);
22380
22463
  }
@@ -22522,7 +22605,12 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
22522
22605
  });
22523
22606
  }
22524
22607
  snapshot() {
22525
- return createStatesSnapshotDeep(this);
22608
+ const snapshot = createStatesSnapshotDeep(this);
22609
+ const expCurve = this.expCurve;
22610
+ if (expCurve) {
22611
+ snapshot.expCurve = { ...expCurve };
22612
+ }
22613
+ return snapshot;
22526
22614
  }
22527
22615
  async applySnapshot(snapshot) {
22528
22616
  const data = typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot;
@@ -22532,6 +22620,9 @@ const _RpgPlayer = class _RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
22532
22620
  const withClass = this.resolveClassSnapshot?.(withStates) ?? withStates;
22533
22621
  const resolvedSnapshot = this.resolveEquipmentsSnapshot?.(withClass) ?? withClass;
22534
22622
  load(this, resolvedSnapshot);
22623
+ if (resolvedSnapshot.expCurve) {
22624
+ this.expCurve = resolvedSnapshot.expCurve;
22625
+ }
22535
22626
  if (Array.isArray(resolvedSnapshot.items)) {
22536
22627
  this.items.set(resolvedSnapshot.items);
22537
22628
  }
@@ -23222,6 +23313,10 @@ __decorateClass$3([
23222
23313
  ], _RpgPlayer.prototype, "events");
23223
23314
  let RpgPlayer = _RpgPlayer;
23224
23315
  class RpgEvent extends RpgPlayer {
23316
+ constructor() {
23317
+ super();
23318
+ this.onGameStart();
23319
+ }
23225
23320
  async execMethod(methodName, methodData = [], instance = this) {
23226
23321
  await lastValueFrom(this.hooks.callHooks(`server-event-${methodName}`, instance, ...methodData));
23227
23322
  if (!instance[methodName]) {
@@ -29491,6 +29586,7 @@ let LobbyRoom = class extends BaseRoom {
29491
29586
  async guiInteraction(player, value) {
29492
29587
  const id = value.data.id;
29493
29588
  if (id === "start") {
29589
+ player.onGameStart();
29494
29590
  this.hooks.callHooks("server-player-onStart", player).subscribe();
29495
29591
  }
29496
29592
  }
@@ -29828,14 +29924,18 @@ function provideServerModules(modules) {
29828
29924
  }
29829
29925
  };
29830
29926
  }
29831
- if (module.database && typeof module.database === "object") {
29832
- const database = { ...module.database };
29927
+ if (module.database) {
29928
+ const database = module.database;
29833
29929
  module = {
29834
29930
  ...module,
29835
29931
  databaseHooks: {
29836
- load: (engine) => {
29837
- for (const key in database) {
29838
- engine.addInDatabase(key, database[key]);
29932
+ load: async (engine) => {
29933
+ const data = typeof database === "function" ? await database(engine) : database;
29934
+ if (!data || typeof data !== "object") {
29935
+ return;
29936
+ }
29937
+ for (const key in data) {
29938
+ engine.addInDatabase(key, data[key]);
29839
29939
  }
29840
29940
  }
29841
29941
  }