@rpgjs/server 5.0.0-alpha.4 → 5.0.0-alpha.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/Gui/DialogGui.d.ts +5 -0
  2. package/dist/Gui/GameoverGui.d.ts +23 -0
  3. package/dist/Gui/Gui.d.ts +6 -0
  4. package/dist/Gui/MenuGui.d.ts +22 -3
  5. package/dist/Gui/NotificationGui.d.ts +1 -2
  6. package/dist/Gui/SaveLoadGui.d.ts +13 -0
  7. package/dist/Gui/ShopGui.d.ts +28 -3
  8. package/dist/Gui/TitleGui.d.ts +23 -0
  9. package/dist/Gui/index.d.ts +10 -1
  10. package/dist/Player/BattleManager.d.ts +34 -12
  11. package/dist/Player/ClassManager.d.ts +46 -13
  12. package/dist/Player/ComponentManager.d.ts +123 -0
  13. package/dist/Player/Components.d.ts +345 -0
  14. package/dist/Player/EffectManager.d.ts +86 -0
  15. package/dist/Player/ElementManager.d.ts +104 -0
  16. package/dist/Player/GoldManager.d.ts +22 -0
  17. package/dist/Player/GuiManager.d.ts +259 -0
  18. package/dist/Player/ItemFixture.d.ts +6 -0
  19. package/dist/Player/ItemManager.d.ts +450 -9
  20. package/dist/Player/MoveManager.d.ts +324 -69
  21. package/dist/Player/ParameterManager.d.ts +344 -14
  22. package/dist/Player/Player.d.ts +460 -8
  23. package/dist/Player/SkillManager.d.ts +197 -15
  24. package/dist/Player/StateManager.d.ts +89 -25
  25. package/dist/Player/VariableManager.d.ts +74 -0
  26. package/dist/RpgServer.d.ts +502 -64
  27. package/dist/RpgServerEngine.d.ts +2 -1
  28. package/dist/decorators/event.d.ts +46 -0
  29. package/dist/decorators/map.d.ts +287 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +21653 -20900
  32. package/dist/index.js.map +1 -1
  33. package/dist/logs/log.d.ts +2 -3
  34. package/dist/module.d.ts +43 -1
  35. package/dist/presets/index.d.ts +0 -9
  36. package/dist/rooms/BaseRoom.d.ts +132 -0
  37. package/dist/rooms/lobby.d.ts +10 -2
  38. package/dist/rooms/map.d.ts +1236 -17
  39. package/dist/services/save.d.ts +43 -0
  40. package/dist/storage/index.d.ts +1 -0
  41. package/dist/storage/localStorage.d.ts +23 -0
  42. package/package.json +14 -10
  43. package/src/Gui/DialogGui.ts +19 -4
  44. package/src/Gui/GameoverGui.ts +39 -0
  45. package/src/Gui/Gui.ts +23 -1
  46. package/src/Gui/MenuGui.ts +155 -6
  47. package/src/Gui/NotificationGui.ts +1 -2
  48. package/src/Gui/SaveLoadGui.ts +60 -0
  49. package/src/Gui/ShopGui.ts +146 -16
  50. package/src/Gui/TitleGui.ts +39 -0
  51. package/src/Gui/index.ts +15 -2
  52. package/src/Player/BattleManager.ts +91 -49
  53. package/src/Player/ClassManager.ts +118 -50
  54. package/src/Player/ComponentManager.ts +425 -19
  55. package/src/Player/Components.ts +380 -0
  56. package/src/Player/EffectManager.ts +81 -44
  57. package/src/Player/ElementManager.ts +109 -86
  58. package/src/Player/GoldManager.ts +32 -35
  59. package/src/Player/GuiManager.ts +308 -150
  60. package/src/Player/ItemFixture.ts +4 -5
  61. package/src/Player/ItemManager.ts +774 -355
  62. package/src/Player/MoveManager.ts +1544 -774
  63. package/src/Player/ParameterManager.ts +546 -104
  64. package/src/Player/Player.ts +1163 -88
  65. package/src/Player/SkillManager.ts +520 -195
  66. package/src/Player/StateManager.ts +170 -182
  67. package/src/Player/VariableManager.ts +101 -63
  68. package/src/RpgServer.ts +525 -63
  69. package/src/core/context.ts +1 -0
  70. package/src/decorators/event.ts +61 -0
  71. package/src/decorators/map.ts +327 -0
  72. package/src/index.ts +11 -1
  73. package/src/logs/log.ts +10 -3
  74. package/src/module.ts +126 -3
  75. package/src/presets/index.ts +1 -10
  76. package/src/rooms/BaseRoom.ts +232 -0
  77. package/src/rooms/lobby.ts +25 -7
  78. package/src/rooms/map.ts +2502 -194
  79. package/src/services/save.ts +147 -0
  80. package/src/storage/index.ts +1 -0
  81. package/src/storage/localStorage.ts +76 -0
  82. package/tests/battle.spec.ts +375 -0
  83. package/tests/change-map.spec.ts +72 -0
  84. package/tests/class.spec.ts +274 -0
  85. package/tests/effect.spec.ts +219 -0
  86. package/tests/element.spec.ts +221 -0
  87. package/tests/event.spec.ts +80 -0
  88. package/tests/gold.spec.ts +99 -0
  89. package/tests/item.spec.ts +609 -0
  90. package/tests/module.spec.ts +38 -0
  91. package/tests/move.spec.ts +601 -0
  92. package/tests/player-param.spec.ts +28 -0
  93. package/tests/prediction-reconciliation.spec.ts +182 -0
  94. package/tests/random-move.spec.ts +65 -0
  95. package/tests/skill.spec.ts +658 -0
  96. package/tests/state.spec.ts +467 -0
  97. package/tests/variable.spec.ts +185 -0
  98. package/tests/world-maps.spec.ts +896 -0
  99. package/vite.config.ts +16 -0
  100. package/dist/Player/Event.d.ts +0 -0
  101. package/src/Player/Event.ts +0 -0
@@ -1,59 +1,536 @@
1
- import { Constructor, isString, RpgCommonPlayer } from "@rpgjs/common";
2
- import { MAXHP, MAXSP } from "../presets";
3
-
4
- export interface IWithParameterManager {
5
- parameters: Map<string, any>
6
- hp: number
7
- sp: number
8
- exp: number
9
- level: number
10
- expForNextlevel: number
11
- param: { [key: string]: number }
12
- paramsModifier: { [key: string]: { value?: number, rate?: number } }
1
+ import { isString, PlayerCtor } from "@rpgjs/common";
2
+ import { signal, computed, WritableSignal, ComputedSignal } from "@signe/reactive";
3
+ import { MAXHP, MAXSP } from "@rpgjs/common";
4
+ import { type } from "@signe/sync";
5
+
6
+ export type ExpCurve = {
7
+ basis: number;
8
+ extra: number;
9
+ accelerationA: number;
10
+ accelerationB: number;
11
+ };
12
+
13
+ const DEFAULT_EXP_CURVE: ExpCurve = {
14
+ basis: 30,
15
+ extra: 20,
16
+ accelerationA: 30,
17
+ accelerationB: 30
18
+ };
19
+
20
+ function isObject(value: unknown): value is Record<string, unknown> {
21
+ return typeof value === "object" && value !== null;
13
22
  }
14
23
 
15
- interface PlayerWithMixins extends RpgCommonPlayer {
16
- databaseById?(id: string): any;
24
+ function toValidNumber(value: unknown, fallback: number): number {
25
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
26
+ }
27
+
28
+ function normalizeExpCurve(value: unknown): ExpCurve {
29
+ if (!isObject(value)) return DEFAULT_EXP_CURVE;
30
+
31
+ return {
32
+ basis: toValidNumber(value.basis, DEFAULT_EXP_CURVE.basis),
33
+ extra: toValidNumber(value.extra, DEFAULT_EXP_CURVE.extra),
34
+ accelerationA: toValidNumber(value.accelerationA, DEFAULT_EXP_CURVE.accelerationA),
35
+ accelerationB: toValidNumber(value.accelerationB, DEFAULT_EXP_CURVE.accelerationB)
36
+ };
17
37
  }
18
38
 
19
39
  /**
20
- * Mixin that adds parameter management functionality to a player class.
40
+ * Interface for Parameter Manager functionality
21
41
  *
22
- * This mixin provides comprehensive parameter management including:
23
- * - Health Points (HP) and Skill Points (SP) management
24
- * - Experience and level progression system
25
- * - Custom parameter creation and modification
26
- * - Parameter modifiers for temporary stat changes
42
+ * Provides comprehensive parameter management including health points (HP), skill points (SP),
43
+ * experience and level progression, custom parameters, and parameter modifiers.
44
+ */
45
+ export interface IParameterManager {
46
+ /**
47
+ * ```ts
48
+ * player.initialLevel = 5
49
+ * ```
50
+ *
51
+ * @title Set initial level
52
+ * @prop {number} player.initialLevel
53
+ * @default 1
54
+ * @memberof ParameterManager
55
+ * */
56
+ initialLevel: number;
57
+
58
+ /**
59
+ * ```ts
60
+ * player.finalLevel = 50
61
+ * ```
62
+ *
63
+ * @title Set final level
64
+ * @prop {number} player.finalLevel
65
+ * @default 99
66
+ * @memberof ParameterManager
67
+ * */
68
+ finalLevel: number;
69
+
70
+ /**
71
+ * With Object-based syntax, you can use following options:
72
+ * - `basis: number`
73
+ * - `extra: number`
74
+ * - `accelerationA: number`
75
+ * - `accelerationB: number`
76
+ * @title Change Experience Curve
77
+ * @prop {object} player.expCurve
78
+ * @default
79
+ * ```ts
80
+ * {
81
+ * basis: 30,
82
+ * extra: 20,
83
+ * accelerationA: 30,
84
+ * accelerationB: 30
85
+ * }
86
+ * ```
87
+ * @memberof ParameterManager
88
+ * */
89
+ expCurve: ExpCurve;
90
+
91
+ /**
92
+ * Changes the health points
93
+ * - Cannot exceed the MaxHP parameter
94
+ * - Cannot have a negative value
95
+ * - If the value is 0, a hook named `onDead()` is called in the RpgPlayer class.
96
+ *
97
+ * ```ts
98
+ * player.hp = 100
99
+ * ```
100
+ * @title Change HP
101
+ * @prop {number} player.hp
102
+ * @default MaxHPValue
103
+ * @memberof ParameterManager
104
+ * */
105
+ hp: number;
106
+
107
+ /**
108
+ * Changes the skill points
109
+ * - Cannot exceed the MaxSP parameter
110
+ * - Cannot have a negative value
111
+ *
112
+ * ```ts
113
+ * player.sp = 200
114
+ * ```
115
+ * @title Change SP
116
+ * @prop {number} player.sp
117
+ * @default MaxSPValue
118
+ * @memberof ParameterManager
119
+ * */
120
+ sp: number;
121
+
122
+ /**
123
+ * Changing the player's experience.
124
+ * ```ts
125
+ * player.exp += 100
126
+ * ```
127
+ *
128
+ * Levels are based on the experience curve.
129
+ *
130
+ * ```ts
131
+ * console.log(player.level) // 1
132
+ * console.log(player.expForNextlevel) // 150
133
+ * player.exp += 160
134
+ * console.log(player.level) // 2
135
+ * ```
136
+ *
137
+ * @title Change Experience
138
+ * @prop {number} player.exp
139
+ * @default 0
140
+ * @memberof ParameterManager
141
+ * */
142
+ exp: number;
143
+
144
+ /**
145
+ * Changing the player's level.
146
+ *
147
+ * ```ts
148
+ * player.level += 1
149
+ * ```
150
+ *
151
+ * The level will be between the initial level given by the `initialLevel` and final level given by `finalLevel`
152
+ *
153
+ * ```ts
154
+ * player.finalLevel = 50
155
+ * player.level = 60
156
+ * console.log(player.level) // 50
157
+ * ```
158
+ *
159
+ * @title Change Level
160
+ * @prop {number} player.level
161
+ * @default 1
162
+ * @memberof ParameterManager
163
+ * */
164
+ level: number;
165
+
166
+ /**
167
+ * ```ts
168
+ * console.log(player.expForNextlevel) // 150
169
+ * ```
170
+ * @title Experience for next level ?
171
+ * @prop {number} player.expForNextlevel
172
+ * @readonly
173
+ * @memberof ParameterManager
174
+ * */
175
+ readonly expForNextlevel: number;
176
+
177
+ /**
178
+ * Read the value of a parameter. Put the name of the parameter.
179
+ *
180
+ * ```ts
181
+ * import { Presets } from '@rpgjs/server'
182
+ *
183
+ * const { MAXHP } = Presets
184
+ *
185
+ * console.log(player.param[MAXHP])
186
+ * ```
187
+ *
188
+ * > Possible to use the `player.getParamValue(name)` method instead
189
+ * @title Get Param Value
190
+ * @prop {object} player.param
191
+ * @readonly
192
+ * @memberof ParameterManager
193
+ * */
194
+ readonly param: { [key: string]: number };
195
+
196
+ /**
197
+ * Direct parameter modifiers (reactive signal)
198
+ *
199
+ * > It is important that these parameters have been created beforehand with the `addParameter()` method.
200
+ * > By default, the following settings have been created:
201
+ * - maxhp
202
+ * - maxsp
203
+ * - str
204
+ * - int
205
+ * - dex
206
+ * - agi
207
+ *
208
+ * **Object Key**
209
+ *
210
+ * The key of the object is the name of the parameter
211
+ *
212
+ * > The good practice is to retrieve the name coming from a constant
213
+ *
214
+ * **Object Value**
215
+ *
216
+ * The value of the key is an object containing:
217
+ * ```
218
+ * {
219
+ * value: number,
220
+ * rate: number
221
+ * }
222
+ * ```
223
+ *
224
+ * - value: Adds a number to the parameter
225
+ * - rate: Adds a rate to the parameter
226
+ *
227
+ * > Note that you can put both (value and rate)
228
+ *
229
+ * This property uses reactive signals - changes automatically trigger parameter recalculation.
230
+ * The final parameter values in `param` include aggregated modifiers from equipment, states, etc.
231
+ *
232
+ * @prop {Object} [paramsModifier]
233
+ * @example
234
+ *
235
+ * ```ts
236
+ * import { Presets } from '@rpgjs/server'
237
+ *
238
+ * const { MAXHP } = Presets
239
+ *
240
+ * // Set direct modifiers (reactive)
241
+ * player.paramsModifier = {
242
+ * [MAXHP]: {
243
+ * value: 100
244
+ * }
245
+ * }
246
+ *
247
+ * // Parameters automatically recalculate
248
+ * console.log(player.param[MAXHP]); // Updated value
249
+ * ```
250
+ *
251
+ * @title Set Parameters Modifier
252
+ * @prop {object} paramsModifier
253
+ * @memberof ParameterManager
254
+ * */
255
+ paramsModifier: {
256
+ [key: string]: {
257
+ value?: number,
258
+ rate?: number
259
+ }
260
+ };
261
+
262
+ /**
263
+ * Get or set the parameters object
264
+ *
265
+ * @prop {object} parameters
266
+ * @memberof ParameterManager
267
+ */
268
+ parameters: { [key: string]: { start: number, end: number } };
269
+
270
+ /**
271
+ * Get the value of a specific parameter by name
272
+ *
273
+ * @deprecated Use `player.param[name]` instead for better reactivity
274
+ * @param name - The name of the parameter to get
275
+ * @returns The calculated parameter value
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * import { Presets } from '@rpgjs/server'
280
+ *
281
+ * const { MAXHP } = Presets
282
+ *
283
+ * // Preferred way (reactive)
284
+ * const maxHp = player.param[MAXHP];
285
+ *
286
+ * // Legacy way (still works)
287
+ * const maxHp = player.getParamValue(MAXHP);
288
+ * ```
289
+ */
290
+ getParamValue(name: string): number;
291
+
292
+ /**
293
+ * Give a new parameter. Give a start value and an end value.
294
+ * The start value will be set to the level set at `player.initialLevel` and the end value will be linked to the level set at `player.finalLevel`.
295
+ *
296
+ * ```ts
297
+ * const SPEED = 'speed'
298
+ *
299
+ * player.addParameter(SPEED, {
300
+ * start: 10,
301
+ * end: 100
302
+ * })
303
+ *
304
+ * player.param[SPEED] // 10
305
+ * player.level += 5
306
+ * player.param[SPEED] // 14
307
+ * ```
308
+ *
309
+ * @title Add custom parameters
310
+ * @method player.addParameter(name,curve)
311
+ * @param {string} name - The name of the parameter
312
+ * @param {object} curve - Scheme of the object: { start: number, end: number }
313
+ * @returns {void}
314
+ * @memberof ParameterManager
315
+ * */
316
+ addParameter(name: string, curve: { start: number, end: number }): void;
317
+
318
+ /**
319
+ * Gives back in percentage of health points to skill points
320
+ *
321
+ * ```ts
322
+ * import { Presets } from '@rpgjs/server'
323
+ *
324
+ * const { MAXHP } = Presets
325
+ *
326
+ * console.log(player.param[MAXHP]) // 800
327
+ * player.hp = 100
328
+ * player.recovery({ hp: 0.5 }) // = 800 * 0.5
329
+ * console.log(player.hp) // 400
330
+ * ```
331
+ *
332
+ * @title Recovery HP and/or SP
333
+ * @method player.recovery(params)
334
+ * @param {object} params - Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
335
+ * @returns {void}
336
+ * @memberof ParameterManager
337
+ * */
338
+ recovery(params: { hp?: number, sp?: number }): void;
339
+
340
+ /**
341
+ * restores all HP and SP
342
+ *
343
+ * ```ts
344
+ * import { Presets } from '@rpgjs/server'
345
+ *
346
+ * const { MAXHP, MAXSP } = Presets
347
+ *
348
+ * console.log(player.param[MAXHP], player.param[MAXSP]) // 800, 230
349
+ * player.hp = 100
350
+ * player.sp = 0
351
+ * player.allRecovery()
352
+ * console.log(player.hp, player.sp) // 800, 230
353
+ * ```
354
+ *
355
+ * @title All Recovery
356
+ * @method player.allRecovery()
357
+ * @returns {void}
358
+ * @memberof ParameterManager
359
+ * */
360
+ allRecovery(): void;
361
+ }
362
+
363
+ /**
364
+ * Parameter Manager Mixin with Reactive Signals
365
+ *
366
+ * Provides comprehensive parameter management functionality using reactive signals from `@signe/reactive`.
367
+ * This mixin handles health points (HP), skill points (SP), experience and level progression,
368
+ * custom parameters, and parameter modifiers with automatic reactivity.
369
+ *
370
+ * **Key Features:**
371
+ * - ✨ **Reactive Parameters**: All parameters automatically recalculate when level or modifiers change
372
+ * - 🚀 **Performance Optimized**: Uses computed signals to avoid unnecessary recalculations
373
+ * - 🔄 **Real-time Updates**: Changes propagate automatically throughout the system
374
+ * - 🎯 **Type Safe**: Full TypeScript support with proper type inference
27
375
  *
28
376
  * @template TBase - The base class constructor type
29
- * @param Base - The base class to extend
30
- * @returns A new class that extends the base with parameter management capabilities
377
+ * @param Base - The base class to extend with parameter management
378
+ * @returns Extended class with reactive parameter management methods
31
379
  *
32
380
  * @example
33
381
  * ```ts
34
382
  * class MyPlayer extends WithParameterManager(BasePlayer) {
35
383
  * constructor() {
36
384
  * super();
385
+ *
386
+ * // Add custom parameters
37
387
  * this.addParameter('strength', { start: 10, end: 100 });
388
+ * this.addParameter('magic', { start: 5, end: 80 });
38
389
  * }
39
390
  * }
391
+ *
392
+ * const player = new MyPlayer();
393
+ *
394
+ * // Reactive parameter updates
395
+ * player.level = 5;
396
+ * console.log(player.param.strength); // Automatically calculated for level 5
397
+ *
398
+ * // Reactive modifiers
399
+ * player.paramsModifier = {
400
+ * [MAXHP]: { value: 100, rate: 1.2 }
401
+ * };
402
+ * console.log(player.param[MAXHP]); // Automatically includes modifiers
40
403
  * ```
41
404
  */
42
- export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>(
43
- Base: TBase
44
- ): TBase & Constructor<IWithParameterManager> {
45
- return class extends Base implements IWithParameterManager {
46
- private _paramsModifier: {
405
+ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
406
+ return class extends Base {
407
+ /**
408
+ * Signal for parameter modifiers - allows reactive updates when modifiers change
409
+ *
410
+ * This signal tracks temporary parameter modifications from equipment, states, etc.
411
+ * When updated, it automatically triggers recalculation of all computed parameters.
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * // Set modifier that adds 100 to MaxHP
416
+ * player.paramsModifier = {
417
+ * [MAXHP]: { value: 100 }
418
+ * };
419
+ *
420
+ * // Parameters automatically recalculate
421
+ * console.log(player.param[MAXHP]); // Updated value
422
+ * ```
423
+ */
424
+ private _paramsModifierSignal = type(signal<{
47
425
  [key: string]: {
48
426
  value?: number,
49
427
  rate?: number
50
428
  }
51
- } = {}
429
+ }>({}) as any, '_paramsModifierSignal', { persist: true }, this as any)
52
430
 
53
- private _parameters: Map<string, {
54
- start: number,
55
- end: number
56
- }> = new Map()
431
+ /**
432
+ * Signal for base parameters configuration
433
+ *
434
+ * Stores the start and end values for each parameter's level curve.
435
+ * Changes to this signal trigger recalculation of all parameter values.
436
+ */
437
+ private _parametersSignal = type(signal<{
438
+ [key: string]: {
439
+ start: number,
440
+ end: number
441
+ }
442
+ }>({}) as any, '_parametersSignal', { persist: true }, this as any)
443
+
444
+ /**
445
+ * Computed signal for all parameter values
446
+ *
447
+ * Automatically recalculates all parameter values when level or modifiers change.
448
+ * This provides reactive parameter updates throughout the system.
449
+ *
450
+ * @example
451
+ * ```ts
452
+ * // Access reactive parameters
453
+ * const maxHp = player.param[MAXHP]; // Always current value
454
+ *
455
+ * // Parameters update automatically when level changes
456
+ * player.level = 10;
457
+ * console.log(player.param[MAXHP]); // New calculated value
458
+ * ```
459
+ */
460
+ _param = type(computed(() => {
461
+ const obj = {}
462
+ const parameters = this._parametersSignal()
463
+ const allModifiers = this._getAggregatedModifiers()
464
+ const level = this._level()
465
+
466
+ for (const [name, paramConfig] of Object.entries(parameters)) {
467
+ let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start
468
+
469
+ const modifier = allModifiers[name]
470
+ if (modifier) {
471
+ if (modifier.rate) curveVal *= modifier.rate
472
+ if (modifier.value) curveVal += modifier.value
473
+ }
474
+
475
+ obj[name] = curveVal
476
+ }
477
+
478
+ return obj
479
+ }) as any, '_param', {}, this as any)
480
+
481
+ /**
482
+ * Aggregates parameter modifiers from all sources (direct modifiers, states, equipment)
483
+ *
484
+ * This method combines modifiers from multiple sources and calculates the final
485
+ * modifier values for each parameter. It handles both value and rate modifiers.
486
+ *
487
+ * @returns Aggregated parameter modifiers
488
+ *
489
+ * @example
490
+ * ```ts
491
+ * // Internal usage - gets modifiers from all sources
492
+ * const modifiers = this._getAggregatedModifiers();
493
+ * console.log(modifiers[MAXHP]); // { value: 100, rate: 1.2 }
494
+ * ```
495
+ */
496
+ private _getAggregatedModifiers(): { [key: string]: { value?: number, rate?: number } } {
497
+ const params = {}
498
+ const paramsAvg = {}
499
+
500
+ const changeParam = (paramsModifier) => {
501
+ for (let key in paramsModifier) {
502
+ const { rate, value } = paramsModifier[key]
503
+ if (!params[key]) params[key] = { rate: 0, value: 0 }
504
+ if (!paramsAvg[key]) paramsAvg[key] = 0
505
+ if (value) params[key].value += value
506
+ if (rate !== undefined) params[key].rate += rate
507
+ paramsAvg[key]++
508
+ }
509
+ }
510
+
511
+ const getModifier = (prop) => {
512
+ if (!isString(prop)) {
513
+ changeParam(prop)
514
+ return
515
+ }
516
+ for (let el of this[prop]()) {
517
+ if (!el.paramsModifier) continue
518
+ changeParam(el.paramsModifier)
519
+ }
520
+ }
521
+
522
+ // Aggregate modifiers from all sources
523
+ getModifier(this._paramsModifierSignal())
524
+ getModifier('states')
525
+ getModifier('equipments')
526
+
527
+ // Average the rates
528
+ for (let key in params) {
529
+ params[key].rate /= paramsAvg[key]
530
+ }
531
+
532
+ return params
533
+ }
57
534
 
58
535
  /**
59
536
  * ```ts
@@ -98,11 +575,22 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
98
575
  * ```
99
576
  * @memberof ParameterManager
100
577
  * */
101
- public expCurve: {
102
- basis: number,
103
- extra: number,
104
- accelerationA: number
105
- accelerationB: number
578
+ public _expCurveSignal = type(signal<string>(JSON.stringify(DEFAULT_EXP_CURVE)) as any, '_expCurveSignal', { persist: true }, this as any)
579
+
580
+ get expCurve(): ExpCurve {
581
+ const raw = this._expCurveSignal()
582
+ if (!raw) return DEFAULT_EXP_CURVE
583
+
584
+ try {
585
+ return normalizeExpCurve(JSON.parse(raw))
586
+ }
587
+ catch {
588
+ return DEFAULT_EXP_CURVE
589
+ }
590
+ }
591
+
592
+ set expCurve(val: ExpCurve) {
593
+ this._expCurveSignal.set(JSON.stringify(val))
106
594
  }
107
595
 
108
596
  /**
@@ -127,11 +615,11 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
127
615
  this['execMethod']('onDead')
128
616
  val = 0
129
617
  }
130
- this._hp.set(val)
618
+ this.hpSignal.set(val)
131
619
  }
132
620
 
133
621
  get hp(): number {
134
- return this._hp()
622
+ return this.hpSignal()
135
623
  }
136
624
 
137
625
  /**
@@ -151,12 +639,12 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
151
639
  if (val > this.param[MAXSP]) {
152
640
  val = this.param[MAXSP]
153
641
  }
154
- this._sp.set(val)
642
+ this.spSignal.set(val)
155
643
  }
156
644
 
157
645
  get sp(): number {
158
- return this._sp()
159
- }
646
+ return this.spSignal()
647
+ }
160
648
 
161
649
  /**
162
650
  * Changing the player's experience.
@@ -236,7 +724,6 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
236
724
  get level(): number {
237
725
  return this._level()
238
726
  }
239
-
240
727
  /**
241
728
  * ```ts
242
729
  * console.log(player.expForNextlevel) // 150
@@ -268,43 +755,11 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
268
755
  * @memberof ParameterManager
269
756
  * */
270
757
  get param() {
271
- const obj = {}
272
- this._parameters.forEach((val, name) => {
273
- obj[name] = this.getParamValue(name)
274
- })
275
- return obj
758
+ return this._param()
276
759
  }
277
760
 
278
761
  get paramsModifier() {
279
- const params = {}
280
- const paramsAvg = {}
281
- const changeParam = (paramsModifier) => {
282
- for (let key in paramsModifier) {
283
- const { rate, value } = paramsModifier[key]
284
- if (!params[key]) params[key] = { rate: 0, value: 0 }
285
- if (!paramsAvg[key]) paramsAvg[key] = 0
286
- if (value) params[key].value += value
287
- if (rate !== undefined) params[key].rate += rate
288
- paramsAvg[key]++
289
- }
290
- }
291
- const getModifier = (prop) => {
292
- if (!isString(prop)) {
293
- changeParam(prop)
294
- return
295
- }
296
- for (let el of this[prop]()) {
297
- if (!el.paramsModifier) continue
298
- changeParam(el.paramsModifier)
299
- }
300
- }
301
- getModifier(this._paramsModifier)
302
- getModifier('states')
303
- getModifier('equipments')
304
- for (let key in params) {
305
- params[key].rate /= paramsAvg[key]
306
- }
307
- return params
762
+ return this._paramsModifierSignal()
308
763
  }
309
764
 
310
765
  /**
@@ -370,15 +825,15 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
370
825
  rate?: number
371
826
  }
372
827
  }) {
373
- this._paramsModifier = val
828
+ this._paramsModifierSignal.set(val)
374
829
  }
375
830
 
376
831
  get parameters() {
377
- return this._parameters
832
+ return this._parametersSignal()
378
833
  }
379
834
 
380
835
  set parameters(val) {
381
- this._parameters = val
836
+ this._parametersSignal.set(val)
382
837
  }
383
838
 
384
839
  private _expForLevel(level: number): number {
@@ -391,23 +846,8 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
391
846
  return Math.round(basis * (Math.pow(level - 1, 0.9 + accelerationA / 250)) * level * (level + 1) / (6 + Math.pow(level, 2) / 50 / accelerationB) + (level - 1) * extra)
392
847
  }
393
848
 
394
- private getParam(name: string) {
395
- const features = this._parameters.get(name)
396
- if (!features) {
397
- throw `Parameter ${name} not exists. Please use addParameter() before`
398
- }
399
- return features
400
- }
401
-
402
849
  getParamValue(name: string): number | never {
403
- const features = this.getParam(name)
404
- let curveVal = Math.floor((features.end - features.start) * ((this.level-1) / (this.finalLevel - this.initialLevel))) + features.start
405
- const modifier = this.paramsModifier[name]
406
- if (modifier) {
407
- if (modifier.rate) curveVal *= modifier.rate
408
- if (modifier.value) curveVal += modifier.value
409
- }
410
- return curveVal
850
+ return this.param[name]
411
851
  }
412
852
 
413
853
  /**
@@ -429,15 +869,17 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
429
869
  *
430
870
  * @title Add custom parameters
431
871
  * @method player.addParameter(name,curve)
432
- * @param {name} name
433
- * @param {object} curve Scheme of the object: { start: number, end: number }
872
+ * @param {string} name - The name of the parameter
873
+ * @param {object} curve - Scheme of the object: { start: number, end: number }
434
874
  * @returns {void}
435
875
  * @memberof ParameterManager
436
876
  * */
437
877
  addParameter(name: string, { start, end }: { start: number, end: number }): void {
438
- this._parameters.set(name, {
439
- start,
440
- end
878
+ this._parametersSignal.mutate(parameters => {
879
+ parameters[name] = {
880
+ start,
881
+ end
882
+ }
441
883
  })
442
884
  const maxHp = this.param[MAXHP]
443
885
  const maxSp = this.param[MAXSP]
@@ -465,7 +907,7 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
465
907
  *
466
908
  * @title Recovery HP and/or SP
467
909
  * @method player.recovery(params)
468
- * @param {object} params Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
910
+ * @param {object} params - Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
469
911
  * @returns {void}
470
912
  * @memberof ParameterManager
471
913
  * */
@@ -497,5 +939,5 @@ export function WithParameterManager<TBase extends Constructor<RpgCommonPlayer>>
497
939
  allRecovery(): void {
498
940
  this.recovery({ hp: 1, sp: 1 })
499
941
  }
500
- }
501
- }
942
+ } as unknown as TBase;
943
+ }