@rpgjs/server 5.0.0-alpha.8 → 5.0.0-beta.1

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 (116) 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 +44 -32
  11. package/dist/Player/ClassManager.d.ts +24 -4
  12. package/dist/Player/ComponentManager.d.ts +100 -7
  13. package/dist/Player/Components.d.ts +345 -0
  14. package/dist/Player/EffectManager.d.ts +50 -4
  15. package/dist/Player/ElementManager.d.ts +77 -4
  16. package/dist/Player/GoldManager.d.ts +1 -1
  17. package/dist/Player/GuiManager.d.ts +233 -5
  18. package/dist/Player/ItemFixture.d.ts +1 -1
  19. package/dist/Player/ItemManager.d.ts +431 -4
  20. package/dist/Player/MoveManager.d.ts +301 -34
  21. package/dist/Player/ParameterManager.d.ts +364 -28
  22. package/dist/Player/Player.d.ts +558 -14
  23. package/dist/Player/SkillManager.d.ts +187 -13
  24. package/dist/Player/StateManager.d.ts +75 -4
  25. package/dist/Player/VariableManager.d.ts +62 -4
  26. package/dist/RpgServer.d.ts +278 -63
  27. package/dist/RpgServerEngine.d.ts +2 -1
  28. package/dist/decorators/event.d.ts +46 -0
  29. package/dist/decorators/map.d.ts +299 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +17920 -29866
  32. package/dist/index.js.map +1 -1
  33. package/dist/logs/log.d.ts +2 -3
  34. package/dist/module-CaCW1SDh.js +11018 -0
  35. package/dist/module-CaCW1SDh.js.map +1 -0
  36. package/dist/module.d.ts +43 -1
  37. package/dist/node/connection.d.ts +51 -0
  38. package/dist/node/index.d.ts +5 -0
  39. package/dist/node/index.js +551 -0
  40. package/dist/node/index.js.map +1 -0
  41. package/dist/node/map.d.ts +16 -0
  42. package/dist/node/room.d.ts +21 -0
  43. package/dist/node/transport.d.ts +28 -0
  44. package/dist/node/types.d.ts +47 -0
  45. package/dist/presets/index.d.ts +0 -9
  46. package/dist/rooms/BaseRoom.d.ts +132 -0
  47. package/dist/rooms/lobby.d.ts +10 -2
  48. package/dist/rooms/map.d.ts +1359 -32
  49. package/dist/services/save.d.ts +43 -0
  50. package/dist/storage/index.d.ts +1 -0
  51. package/dist/storage/localStorage.d.ts +23 -0
  52. package/package.json +25 -10
  53. package/src/Gui/DialogGui.ts +19 -4
  54. package/src/Gui/GameoverGui.ts +39 -0
  55. package/src/Gui/Gui.ts +23 -1
  56. package/src/Gui/MenuGui.ts +155 -6
  57. package/src/Gui/NotificationGui.ts +1 -2
  58. package/src/Gui/SaveLoadGui.ts +60 -0
  59. package/src/Gui/ShopGui.ts +146 -16
  60. package/src/Gui/TitleGui.ts +39 -0
  61. package/src/Gui/index.ts +15 -2
  62. package/src/Player/BattleManager.ts +39 -56
  63. package/src/Player/ClassManager.ts +82 -74
  64. package/src/Player/ComponentManager.ts +401 -37
  65. package/src/Player/Components.ts +380 -0
  66. package/src/Player/EffectManager.ts +50 -96
  67. package/src/Player/ElementManager.ts +74 -152
  68. package/src/Player/GuiManager.ts +284 -149
  69. package/src/Player/ItemManager.ts +747 -341
  70. package/src/Player/MoveManager.ts +1532 -750
  71. package/src/Player/ParameterManager.ts +636 -106
  72. package/src/Player/Player.ts +1273 -79
  73. package/src/Player/SkillManager.ts +558 -197
  74. package/src/Player/StateManager.ts +131 -258
  75. package/src/Player/VariableManager.ts +85 -157
  76. package/src/RpgServer.ts +293 -62
  77. package/src/decorators/event.ts +61 -0
  78. package/src/decorators/map.ts +343 -0
  79. package/src/index.ts +11 -1
  80. package/src/logs/log.ts +10 -3
  81. package/src/module.ts +126 -3
  82. package/src/node/connection.ts +254 -0
  83. package/src/node/index.ts +22 -0
  84. package/src/node/map.ts +328 -0
  85. package/src/node/room.ts +63 -0
  86. package/src/node/transport.ts +532 -0
  87. package/src/node/types.ts +61 -0
  88. package/src/presets/index.ts +1 -10
  89. package/src/rooms/BaseRoom.ts +232 -0
  90. package/src/rooms/lobby.ts +25 -7
  91. package/src/rooms/map.ts +2682 -206
  92. package/src/services/save.ts +147 -0
  93. package/src/storage/index.ts +1 -0
  94. package/src/storage/localStorage.ts +76 -0
  95. package/tests/battle.spec.ts +375 -0
  96. package/tests/change-map.spec.ts +72 -0
  97. package/tests/class.spec.ts +274 -0
  98. package/tests/custom-websocket.spec.ts +127 -0
  99. package/tests/effect.spec.ts +219 -0
  100. package/tests/element.spec.ts +221 -0
  101. package/tests/event.spec.ts +80 -0
  102. package/tests/gold.spec.ts +99 -0
  103. package/tests/item.spec.ts +609 -0
  104. package/tests/module.spec.ts +38 -0
  105. package/tests/move.spec.ts +601 -0
  106. package/tests/node-transport.spec.ts +223 -0
  107. package/tests/player-param.spec.ts +45 -0
  108. package/tests/prediction-reconciliation.spec.ts +182 -0
  109. package/tests/random-move.spec.ts +65 -0
  110. package/tests/skill.spec.ts +658 -0
  111. package/tests/state.spec.ts +467 -0
  112. package/tests/variable.spec.ts +185 -0
  113. package/tests/world-maps.spec.ts +896 -0
  114. package/vite.config.ts +36 -3
  115. package/dist/Player/Event.d.ts +0 -0
  116. package/src/Player/Event.ts +0 -0
@@ -1,67 +1,580 @@
1
1
  import { isString, PlayerCtor } from "@rpgjs/common";
2
- import { MAXHP, MAXSP } from "../presets";
2
+ import { signal, computed, WritableSignal, ComputedSignal } from "@signe/reactive";
3
+ import { MAXHP, MAXSP } from "@rpgjs/common";
4
+ import { type } from "@signe/sync";
3
5
 
6
+ export type ExpCurve = {
7
+ basis: number;
8
+ extra: number;
9
+ accelerationA: number;
10
+ accelerationB: number;
11
+ };
12
+
13
+ export type ParameterCurve = {
14
+ start: number;
15
+ end: number;
16
+ };
17
+
18
+ export type ParameterValue = number | ParameterCurve;
19
+
20
+ const DEFAULT_EXP_CURVE: ExpCurve = {
21
+ basis: 30,
22
+ extra: 20,
23
+ accelerationA: 30,
24
+ accelerationB: 30
25
+ };
26
+
27
+ function isObject(value: unknown): value is Record<string, unknown> {
28
+ return typeof value === "object" && value !== null;
29
+ }
30
+
31
+ function toValidNumber(value: unknown, fallback: number): number {
32
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
33
+ }
34
+
35
+ function normalizeExpCurve(value: unknown): ExpCurve {
36
+ if (!isObject(value)) return DEFAULT_EXP_CURVE;
37
+
38
+ return {
39
+ basis: toValidNumber(value.basis, DEFAULT_EXP_CURVE.basis),
40
+ extra: toValidNumber(value.extra, DEFAULT_EXP_CURVE.extra),
41
+ accelerationA: toValidNumber(value.accelerationA, DEFAULT_EXP_CURVE.accelerationA),
42
+ accelerationB: toValidNumber(value.accelerationB, DEFAULT_EXP_CURVE.accelerationB)
43
+ };
44
+ }
45
+
46
+ function normalizeParameterCurve(value: unknown): ParameterCurve {
47
+ if (typeof value === "number") {
48
+ const normalized = toValidNumber(value, 0);
49
+ return {
50
+ start: normalized,
51
+ end: normalized
52
+ };
53
+ }
54
+
55
+ if (!isObject(value)) {
56
+ return {
57
+ start: 0,
58
+ end: 0
59
+ };
60
+ }
61
+
62
+ const start = toValidNumber(value.start, 0);
63
+ const end = toValidNumber(value.end, start);
64
+
65
+ return {
66
+ start,
67
+ end
68
+ };
69
+ }
4
70
 
5
71
  /**
6
- * Mixin that adds parameter management functionality to a player class.
7
- *
8
- * This mixin provides comprehensive parameter management including:
9
- * - Health Points (HP) and Skill Points (SP) management
10
- * - Experience and level progression system
11
- * - Custom parameter creation and modification
12
- * - Parameter modifiers for temporary stat changes
72
+ * Interface for Parameter Manager functionality
13
73
  *
14
- * @template TBase - The base class constructor type
15
- * @param Base - The base class to extend
16
- * @returns A new class that extends the base with parameter management capabilities
17
- *
18
- * @example
19
- * ```ts
20
- * class MyPlayer extends WithParameterManager(BasePlayer) {
21
- * constructor() {
22
- * super();
23
- * this.addParameter('strength', { start: 10, end: 100 });
24
- * }
25
- * }
26
- * ```
74
+ * Provides comprehensive parameter management including health points (HP), skill points (SP),
75
+ * experience and level progression, custom parameters, and parameter modifiers.
27
76
  */
77
+ export interface IParameterManager {
78
+ /**
79
+ * ```ts
80
+ * player.initialLevel = 5
81
+ * ```
82
+ *
83
+ * @title Set initial level
84
+ * @prop {number} player.initialLevel
85
+ * @default 1
86
+ * @memberof ParameterManager
87
+ * */
88
+ initialLevel: number;
89
+
90
+ /**
91
+ * ```ts
92
+ * player.finalLevel = 50
93
+ * ```
94
+ *
95
+ * @title Set final level
96
+ * @prop {number} player.finalLevel
97
+ * @default 99
98
+ * @memberof ParameterManager
99
+ * */
100
+ finalLevel: number;
101
+
102
+ /**
103
+ * With Object-based syntax, you can use following options:
104
+ * - `basis: number`
105
+ * - `extra: number`
106
+ * - `accelerationA: number`
107
+ * - `accelerationB: number`
108
+ * @title Change Experience Curve
109
+ * @prop {object} player.expCurve
110
+ * @default
111
+ * ```ts
112
+ * {
113
+ * basis: 30,
114
+ * extra: 20,
115
+ * accelerationA: 30,
116
+ * accelerationB: 30
117
+ * }
118
+ * ```
119
+ * @memberof ParameterManager
120
+ * */
121
+ expCurve: ExpCurve;
122
+
123
+ /**
124
+ * Changes the health points
125
+ * - Cannot exceed the MaxHP parameter
126
+ * - Cannot have a negative value
127
+ * - If the value is 0, a hook named `onDead()` is called in the RpgPlayer class.
128
+ *
129
+ * ```ts
130
+ * player.hp = 100
131
+ * ```
132
+ * @title Change HP
133
+ * @prop {number} player.hp
134
+ * @default MaxHPValue
135
+ * @memberof ParameterManager
136
+ * */
137
+ hp: number;
138
+
139
+ /**
140
+ * Changes the skill points
141
+ * - Cannot exceed the MaxSP parameter
142
+ * - Cannot have a negative value
143
+ *
144
+ * ```ts
145
+ * player.sp = 200
146
+ * ```
147
+ * @title Change SP
148
+ * @prop {number} player.sp
149
+ * @default MaxSPValue
150
+ * @memberof ParameterManager
151
+ * */
152
+ sp: number;
153
+
154
+ /**
155
+ * Changing the player's experience.
156
+ * ```ts
157
+ * player.exp += 100
158
+ * ```
159
+ *
160
+ * Levels are based on the experience curve.
161
+ *
162
+ * ```ts
163
+ * console.log(player.level) // 1
164
+ * console.log(player.expForNextlevel) // 150
165
+ * player.exp += 160
166
+ * console.log(player.level) // 2
167
+ * ```
168
+ *
169
+ * @title Change Experience
170
+ * @prop {number} player.exp
171
+ * @default 0
172
+ * @memberof ParameterManager
173
+ * */
174
+ exp: number;
175
+
176
+ /**
177
+ * Changing the player's level.
178
+ *
179
+ * ```ts
180
+ * player.level += 1
181
+ * ```
182
+ *
183
+ * The level will be between the initial level given by the `initialLevel` and final level given by `finalLevel`
184
+ *
185
+ * ```ts
186
+ * player.finalLevel = 50
187
+ * player.level = 60
188
+ * console.log(player.level) // 50
189
+ * ```
190
+ *
191
+ * @title Change Level
192
+ * @prop {number} player.level
193
+ * @default 1
194
+ * @memberof ParameterManager
195
+ * */
196
+ level: number;
197
+
198
+ /**
199
+ * ```ts
200
+ * console.log(player.expForNextlevel) // 150
201
+ * ```
202
+ * @title Experience for next level ?
203
+ * @prop {number} player.expForNextlevel
204
+ * @readonly
205
+ * @memberof ParameterManager
206
+ * */
207
+ readonly expForNextlevel: number;
208
+
209
+ /**
210
+ * Read the value of a parameter. Put the name of the parameter.
211
+ *
212
+ * ```ts
213
+ * import { Presets } from '@rpgjs/server'
214
+ *
215
+ * const { MAXHP } = Presets
216
+ *
217
+ * console.log(player.param[MAXHP])
218
+ * ```
219
+ *
220
+ * > Possible to use the `player.getParamValue(name)` method instead
221
+ * @title Get Param Value
222
+ * @prop {object} player.param
223
+ * @readonly
224
+ * @memberof ParameterManager
225
+ * */
226
+ readonly param: { [key: string]: number };
227
+
228
+ /**
229
+ * Direct parameter modifiers (reactive signal)
230
+ *
231
+ * > It is important that these parameters have been created beforehand with the `addParameter()` method.
232
+ * > By default, the following settings have been created:
233
+ * - maxhp
234
+ * - maxsp
235
+ * - str
236
+ * - int
237
+ * - dex
238
+ * - agi
239
+ *
240
+ * **Object Key**
241
+ *
242
+ * The key of the object is the name of the parameter
243
+ *
244
+ * > The good practice is to retrieve the name coming from a constant
245
+ *
246
+ * **Object Value**
247
+ *
248
+ * The value of the key is an object containing:
249
+ * ```
250
+ * {
251
+ * value: number,
252
+ * rate: number
253
+ * }
254
+ * ```
255
+ *
256
+ * - value: Adds a number to the parameter
257
+ * - rate: Adds a rate to the parameter
258
+ *
259
+ * > Note that you can put both (value and rate)
260
+ *
261
+ * This property uses reactive signals - changes automatically trigger parameter recalculation.
262
+ * The final parameter values in `param` include aggregated modifiers from equipment, states, etc.
263
+ *
264
+ * @prop {Object} [paramsModifier]
265
+ * @example
266
+ *
267
+ * ```ts
268
+ * import { Presets } from '@rpgjs/server'
269
+ *
270
+ * const { MAXHP } = Presets
271
+ *
272
+ * // Set direct modifiers (reactive)
273
+ * player.paramsModifier = {
274
+ * [MAXHP]: {
275
+ * value: 100
276
+ * }
277
+ * }
278
+ *
279
+ * // Parameters automatically recalculate
280
+ * console.log(player.param[MAXHP]); // Updated value
281
+ * ```
282
+ *
283
+ * @title Set Parameters Modifier
284
+ * @prop {object} paramsModifier
285
+ * @memberof ParameterManager
286
+ * */
287
+ paramsModifier: {
288
+ [key: string]: {
289
+ value?: number,
290
+ rate?: number
291
+ }
292
+ };
293
+
294
+ /**
295
+ * Get or set the parameters object
296
+ *
297
+ * @prop {object} parameters
298
+ * @memberof ParameterManager
299
+ */
300
+ parameters: { [key: string]: { start: number, end: number } };
301
+
302
+ /**
303
+ * Set a parameter with either a fixed value or a level curve
304
+ *
305
+ * A numeric value is stored as a fixed parameter where `start === end`.
306
+ *
307
+ * @param name - Parameter name
308
+ * @param value - Fixed value or parameter curve
309
+ */
310
+ setParameter(name: string, value: ParameterValue): void;
311
+
312
+ /**
313
+ * Get the value of a specific parameter by name
314
+ *
315
+ * @deprecated Use `player.param[name]` instead for better reactivity
316
+ * @param name - The name of the parameter to get
317
+ * @returns The calculated parameter value
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * import { Presets } from '@rpgjs/server'
322
+ *
323
+ * const { MAXHP } = Presets
324
+ *
325
+ * // Preferred way (reactive)
326
+ * const maxHp = player.param[MAXHP];
327
+ *
328
+ * // Legacy way (still works)
329
+ * const maxHp = player.getParamValue(MAXHP);
330
+ * ```
331
+ */
332
+ getParamValue(name: string): number;
333
+
334
+ /**
335
+ * Give a new parameter. Give a start value and an end value.
336
+ * 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`.
337
+ *
338
+ * ```ts
339
+ * const SPEED = 'speed'
340
+ *
341
+ * player.addParameter(SPEED, {
342
+ * start: 10,
343
+ * end: 100
344
+ * })
345
+ *
346
+ * player.param[SPEED] // 10
347
+ * player.level += 5
348
+ * player.param[SPEED] // 14
349
+ * ```
350
+ *
351
+ * @title Add custom parameters
352
+ * @method player.addParameter(name,curve)
353
+ * @param {string} name - The name of the parameter
354
+ * @param {object} curve - Scheme of the object: { start: number, end: number }
355
+ * @returns {void}
356
+ * @memberof ParameterManager
357
+ * */
358
+ addParameter(name: string, curve: { start: number, end: number }): void;
359
+
360
+ /**
361
+ * Gives back in percentage of health points to skill points
362
+ *
363
+ * ```ts
364
+ * import { Presets } from '@rpgjs/server'
365
+ *
366
+ * const { MAXHP } = Presets
367
+ *
368
+ * console.log(player.param[MAXHP]) // 800
369
+ * player.hp = 100
370
+ * player.recovery({ hp: 0.5 }) // = 800 * 0.5
371
+ * console.log(player.hp) // 400
372
+ * ```
373
+ *
374
+ * @title Recovery HP and/or SP
375
+ * @method player.recovery(params)
376
+ * @param {object} params - Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
377
+ * @returns {void}
378
+ * @memberof ParameterManager
379
+ * */
380
+ recovery(params: { hp?: number, sp?: number }): void;
381
+
382
+ /**
383
+ * restores all HP and SP
384
+ *
385
+ * ```ts
386
+ * import { Presets } from '@rpgjs/server'
387
+ *
388
+ * const { MAXHP, MAXSP } = Presets
389
+ *
390
+ * console.log(player.param[MAXHP], player.param[MAXSP]) // 800, 230
391
+ * player.hp = 100
392
+ * player.sp = 0
393
+ * player.allRecovery()
394
+ * console.log(player.hp, player.sp) // 800, 230
395
+ * ```
396
+ *
397
+ * @title All Recovery
398
+ * @method player.allRecovery()
399
+ * @returns {void}
400
+ * @memberof ParameterManager
401
+ * */
402
+ allRecovery(): void;
403
+ }
404
+
28
405
  /**
29
- * Parameter Manager Mixin
406
+ * Parameter Manager Mixin with Reactive Signals
30
407
  *
31
- * Provides comprehensive parameter management functionality to any class. This mixin handles
32
- * health points (HP), skill points (SP), experience and level progression, custom parameters,
33
- * and parameter modifiers for temporary stat changes.
408
+ * Provides comprehensive parameter management functionality using reactive signals from `@signe/reactive`.
409
+ * This mixin handles health points (HP), skill points (SP), experience and level progression,
410
+ * custom parameters, and parameter modifiers with automatic reactivity.
34
411
  *
412
+ * **Key Features:**
413
+ * - ✨ **Reactive Parameters**: All parameters automatically recalculate when level or modifiers change
414
+ * - 🚀 **Performance Optimized**: Uses computed signals to avoid unnecessary recalculations
415
+ * - 🔄 **Real-time Updates**: Changes propagate automatically throughout the system
416
+ * - 🎯 **Type Safe**: Full TypeScript support with proper type inference
417
+ *
418
+ * @template TBase - The base class constructor type
35
419
  * @param Base - The base class to extend with parameter management
36
- * @returns Extended class with parameter management methods
420
+ * @returns Extended class with reactive parameter management methods
37
421
  *
38
422
  * @example
39
423
  * ```ts
40
424
  * class MyPlayer extends WithParameterManager(BasePlayer) {
41
425
  * constructor() {
42
426
  * super();
427
+ *
428
+ * // Add custom parameters
43
429
  * this.addParameter('strength', { start: 10, end: 100 });
430
+ * this.addParameter('magic', { start: 5, end: 80 });
44
431
  * }
45
432
  * }
46
433
  *
47
434
  * const player = new MyPlayer();
48
- * player.hp = 100;
435
+ *
436
+ * // Reactive parameter updates
49
437
  * player.level = 5;
438
+ * console.log(player.param.strength); // Automatically calculated for level 5
439
+ *
440
+ * // Reactive modifiers
441
+ * player.paramsModifier = {
442
+ * [MAXHP]: { value: 100, rate: 1.2 }
443
+ * };
444
+ * console.log(player.param[MAXHP]); // Automatically includes modifiers
50
445
  * ```
51
446
  */
52
447
  export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
53
448
  return class extends Base {
54
- _paramsModifier: {
449
+ /**
450
+ * Signal for parameter modifiers - allows reactive updates when modifiers change
451
+ *
452
+ * This signal tracks temporary parameter modifications from equipment, states, etc.
453
+ * When updated, it automatically triggers recalculation of all computed parameters.
454
+ *
455
+ * @example
456
+ * ```ts
457
+ * // Set modifier that adds 100 to MaxHP
458
+ * player.paramsModifier = {
459
+ * [MAXHP]: { value: 100 }
460
+ * };
461
+ *
462
+ * // Parameters automatically recalculate
463
+ * console.log(player.param[MAXHP]); // Updated value
464
+ * ```
465
+ */
466
+ private _paramsModifierSignal = type(signal<{
55
467
  [key: string]: {
56
468
  value?: number,
57
469
  rate?: number
58
470
  }
59
- } = {}
471
+ }>({}) as any, '_paramsModifierSignal', { persist: true }, this as any)
472
+
473
+ /**
474
+ * Signal for base parameters configuration
475
+ *
476
+ * Stores the start and end values for each parameter's level curve.
477
+ * Changes to this signal trigger recalculation of all parameter values.
478
+ */
479
+ private _parametersSignal = type(signal<{
480
+ [key: string]: {
481
+ start: number,
482
+ end: number
483
+ }
484
+ }>({}) as any, '_parametersSignal', { persist: true }, this as any)
485
+
486
+ private _paramProxy: { [key: string]: number } | null = null
60
487
 
61
- _parameters: Map<string, {
62
- start: number,
63
- end: number
64
- }> = new Map()
488
+ /**
489
+ * Computed signal for all parameter values
490
+ *
491
+ * Automatically recalculates all parameter values when level or modifiers change.
492
+ * This provides reactive parameter updates throughout the system.
493
+ *
494
+ * @example
495
+ * ```ts
496
+ * // Access reactive parameters
497
+ * const maxHp = player.param[MAXHP]; // Always current value
498
+ *
499
+ * // Parameters update automatically when level changes
500
+ * player.level = 10;
501
+ * console.log(player.param[MAXHP]); // New calculated value
502
+ * ```
503
+ */
504
+ _param = type(computed(() => {
505
+ const obj = {}
506
+ const parameters = this._parametersSignal()
507
+ const allModifiers = this._getAggregatedModifiers()
508
+ const level = this._level()
509
+
510
+ for (const [name, paramConfig] of Object.entries(parameters)) {
511
+ let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start
512
+
513
+ const modifier = allModifiers[name]
514
+ if (modifier) {
515
+ if (modifier.rate) curveVal *= modifier.rate
516
+ if (modifier.value) curveVal += modifier.value
517
+ }
518
+
519
+ obj[name] = curveVal
520
+ }
521
+
522
+ return obj
523
+ }) as any, '_param', {}, this as any)
524
+
525
+ /**
526
+ * Aggregates parameter modifiers from all sources (direct modifiers, states, equipment)
527
+ *
528
+ * This method combines modifiers from multiple sources and calculates the final
529
+ * modifier values for each parameter. It handles both value and rate modifiers.
530
+ *
531
+ * @returns Aggregated parameter modifiers
532
+ *
533
+ * @example
534
+ * ```ts
535
+ * // Internal usage - gets modifiers from all sources
536
+ * const modifiers = this._getAggregatedModifiers();
537
+ * console.log(modifiers[MAXHP]); // { value: 100, rate: 1.2 }
538
+ * ```
539
+ */
540
+ private _getAggregatedModifiers(): { [key: string]: { value?: number, rate?: number } } {
541
+ const params = {}
542
+ const paramsAvg = {}
543
+
544
+ const changeParam = (paramsModifier) => {
545
+ for (let key in paramsModifier) {
546
+ const { rate, value } = paramsModifier[key]
547
+ if (!params[key]) params[key] = { rate: 0, value: 0 }
548
+ if (!paramsAvg[key]) paramsAvg[key] = 0
549
+ if (value) params[key].value += value
550
+ if (rate !== undefined) params[key].rate += rate
551
+ paramsAvg[key]++
552
+ }
553
+ }
554
+
555
+ const getModifier = (prop) => {
556
+ if (!isString(prop)) {
557
+ changeParam(prop)
558
+ return
559
+ }
560
+ for (let el of this[prop]()) {
561
+ if (!el.paramsModifier) continue
562
+ changeParam(el.paramsModifier)
563
+ }
564
+ }
565
+
566
+ // Aggregate modifiers from all sources
567
+ getModifier(this._paramsModifierSignal())
568
+ getModifier('states')
569
+ getModifier('equipments')
570
+
571
+ // Average the rates
572
+ for (let key in params) {
573
+ params[key].rate /= paramsAvg[key]
574
+ }
575
+
576
+ return params
577
+ }
65
578
 
66
579
  /**
67
580
  * ```ts
@@ -106,11 +619,22 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
106
619
  * ```
107
620
  * @memberof ParameterManager
108
621
  * */
109
- public expCurve: {
110
- basis: number,
111
- extra: number,
112
- accelerationA: number
113
- accelerationB: number
622
+ public _expCurveSignal = type(signal<string>(JSON.stringify(DEFAULT_EXP_CURVE)) as any, '_expCurveSignal', { persist: true }, this as any)
623
+
624
+ get expCurve(): ExpCurve {
625
+ const raw = this._expCurveSignal()
626
+ if (!raw) return DEFAULT_EXP_CURVE
627
+
628
+ try {
629
+ return normalizeExpCurve(JSON.parse(raw))
630
+ }
631
+ catch {
632
+ return DEFAULT_EXP_CURVE
633
+ }
634
+ }
635
+
636
+ set expCurve(val: ExpCurve) {
637
+ this._expCurveSignal.set(JSON.stringify(val))
114
638
  }
115
639
 
116
640
  /**
@@ -135,11 +659,11 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
135
659
  this['execMethod']('onDead')
136
660
  val = 0
137
661
  }
138
- (this as any)._hp.set(val)
662
+ this.hpSignal.set(val)
139
663
  }
140
664
 
141
665
  get hp(): number {
142
- return (this as any)._hp()
666
+ return this.hpSignal()
143
667
  }
144
668
 
145
669
  /**
@@ -159,12 +683,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
159
683
  if (val > this.param[MAXSP]) {
160
684
  val = this.param[MAXSP]
161
685
  }
162
- this._sp.set(val)
686
+ this.spSignal.set(val)
163
687
  }
164
688
 
165
689
  get sp(): number {
166
- return this._sp()
167
- }
690
+ return this.spSignal()
691
+ }
168
692
 
169
693
  /**
170
694
  * Changing the player's experience.
@@ -244,7 +768,6 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
244
768
  get level(): number {
245
769
  return this._level()
246
770
  }
247
-
248
771
  /**
249
772
  * ```ts
250
773
  * console.log(player.expForNextlevel) // 150
@@ -276,43 +799,50 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
276
799
  * @memberof ParameterManager
277
800
  * */
278
801
  get param() {
279
- const obj = {}
280
- this._parameters.forEach((val, name) => {
281
- obj[name] = this.getParamValue(name)
282
- })
283
- return obj
802
+ if (!this._paramProxy) {
803
+ this._paramProxy = new Proxy({}, {
804
+ get: (_target, property) => {
805
+ if (typeof property !== 'string') {
806
+ return undefined
807
+ }
808
+ return this._param()[property]
809
+ },
810
+ set: (_target, property, value) => {
811
+ if (typeof property !== 'string') {
812
+ return false
813
+ }
814
+ this.setParameter(property, value as ParameterValue)
815
+ return true
816
+ },
817
+ has: (_target, property) => {
818
+ if (typeof property !== 'string') {
819
+ return false
820
+ }
821
+ return property in this._param()
822
+ },
823
+ ownKeys: () => Reflect.ownKeys(this._param()),
824
+ getOwnPropertyDescriptor: (_target, property) => {
825
+ if (typeof property !== 'string') {
826
+ return undefined
827
+ }
828
+ const parameters = this._param()
829
+ if (!(property in parameters)) {
830
+ return undefined
831
+ }
832
+ return {
833
+ configurable: true,
834
+ enumerable: true,
835
+ writable: true,
836
+ value: parameters[property]
837
+ }
838
+ }
839
+ }) as { [key: string]: number }
840
+ }
841
+ return this._paramProxy
284
842
  }
285
843
 
286
844
  get paramsModifier() {
287
- const params = {}
288
- const paramsAvg = {}
289
- const changeParam = (paramsModifier) => {
290
- for (let key in paramsModifier) {
291
- const { rate, value } = paramsModifier[key]
292
- if (!params[key]) params[key] = { rate: 0, value: 0 }
293
- if (!paramsAvg[key]) paramsAvg[key] = 0
294
- if (value) params[key].value += value
295
- if (rate !== undefined) params[key].rate += rate
296
- paramsAvg[key]++
297
- }
298
- }
299
- const getModifier = (prop) => {
300
- if (!isString(prop)) {
301
- changeParam(prop)
302
- return
303
- }
304
- for (let el of this[prop]()) {
305
- if (!el.paramsModifier) continue
306
- changeParam(el.paramsModifier)
307
- }
308
- }
309
- getModifier(this._paramsModifier)
310
- getModifier('states')
311
- getModifier('equipments')
312
- for (let key in params) {
313
- params[key].rate /= paramsAvg[key]
314
- }
315
- return params
845
+ return this._paramsModifierSignal()
316
846
  }
317
847
 
318
848
  /**
@@ -378,15 +908,25 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
378
908
  rate?: number
379
909
  }
380
910
  }) {
381
- this._paramsModifier = val
911
+ this._paramsModifierSignal.set(val)
382
912
  }
383
913
 
384
914
  get parameters() {
385
- return this._parameters
915
+ return this._parametersSignal()
386
916
  }
387
917
 
388
918
  set parameters(val) {
389
- this._parameters = val
919
+ const normalizedParameters = {}
920
+ for (const [name, parameterValue] of Object.entries(val || {})) {
921
+ normalizedParameters[name] = normalizeParameterCurve(parameterValue)
922
+ }
923
+ this._parametersSignal.set(normalizedParameters)
924
+ if (MAXHP in normalizedParameters && this.hp > this.param[MAXHP]) {
925
+ this.hp = this.param[MAXHP]
926
+ }
927
+ if (MAXSP in normalizedParameters && this.sp > this.param[MAXSP]) {
928
+ this.sp = this.param[MAXSP]
929
+ }
390
930
  }
391
931
 
392
932
  private _expForLevel(level: number): number {
@@ -399,23 +939,8 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
399
939
  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)
400
940
  }
401
941
 
402
- private getParam(name: string) {
403
- const features = this._parameters.get(name)
404
- if (!features) {
405
- throw `Parameter ${name} not exists. Please use addParameter() before`
406
- }
407
- return features
408
- }
409
-
410
942
  getParamValue(name: string): number | never {
411
- const features = this.getParam(name)
412
- let curveVal = Math.floor((features.end - features.start) * ((this.level-1) / (this.finalLevel - this.initialLevel))) + features.start
413
- const modifier = this.paramsModifier[name]
414
- if (modifier) {
415
- if (modifier.rate) curveVal *= modifier.rate
416
- if (modifier.value) curveVal += modifier.value
417
- }
418
- return curveVal
943
+ return this.param[name]
419
944
  }
420
945
 
421
946
  /**
@@ -437,15 +962,18 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
437
962
  *
438
963
  * @title Add custom parameters
439
964
  * @method player.addParameter(name,curve)
440
- * @param {name} name
441
- * @param {object} curve Scheme of the object: { start: number, end: number }
965
+ * @param {string} name - The name of the parameter
966
+ * @param {object} curve - Scheme of the object: { start: number, end: number }
442
967
  * @returns {void}
443
968
  * @memberof ParameterManager
444
969
  * */
445
- addParameter(name: string, { start, end }: { start: number, end: number }): void {
446
- this._parameters.set(name, {
447
- start,
448
- end
970
+ setParameter(name: string, value: ParameterValue): void {
971
+ const normalizedValue = normalizeParameterCurve(value)
972
+ this._parametersSignal.mutate(parameters => {
973
+ parameters[name] = {
974
+ start: normalizedValue.start,
975
+ end: normalizedValue.end
976
+ }
449
977
  })
450
978
  const maxHp = this.param[MAXHP]
451
979
  const maxSp = this.param[MAXSP]
@@ -457,6 +985,10 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
457
985
  }
458
986
  }
459
987
 
988
+ addParameter(name: string, { start, end }: { start: number, end: number }): void {
989
+ this.setParameter(name, { start, end })
990
+ }
991
+
460
992
  /**
461
993
  * Gives back in percentage of health points to skill points
462
994
  *
@@ -473,7 +1005,7 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
473
1005
  *
474
1006
  * @title Recovery HP and/or SP
475
1007
  * @method player.recovery(params)
476
- * @param {object} params Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
1008
+ * @param {object} params - Scheme of the object: { hp: number, sp: number }. The values of the numbers must be in 0 and 1
477
1009
  * @returns {void}
478
1010
  * @memberof ParameterManager
479
1011
  * */
@@ -507,5 +1039,3 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
507
1039
  }
508
1040
  } as unknown as TBase;
509
1041
  }
510
-
511
- export type IParameterManager = InstanceType<ReturnType<typeof WithParameterManager>>;