@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,251 +1,576 @@
1
1
  import {
2
- Constructor,
3
2
  isArray,
4
3
  isInstanceOf,
5
4
  isString,
6
- RpgCommonPlayer,
5
+ PlayerCtor,
6
+ Skill,
7
7
  } from "@rpgjs/common";
8
8
  import { SkillLog } from "../logs";
9
9
  import { RpgPlayer } from "./Player";
10
10
  import { Effect } from "./EffectManager";
11
11
 
12
12
  /**
13
- * Interface defining dependencies from other mixins that SkillManager needs
13
+ * Type for skill class constructor
14
14
  */
15
- interface SkillManagerDependencies {
16
- sp: number;
17
- skills(): any[];
18
- hasEffect(effect: string): boolean;
19
- databaseById(id: string): any;
20
- applyStates(player: RpgPlayer, skill: any): void;
15
+ type SkillClass = { new (...args: any[]): any };
16
+
17
+ /**
18
+ * Interface defining the hooks that can be implemented on skill classes or objects
19
+ *
20
+ * These hooks are called at specific moments during the skill lifecycle:
21
+ * - `onLearn`: When the skill is learned by the player
22
+ * - `onUse`: When the skill is successfully used
23
+ * - `onUseFailed`: When the skill usage fails (e.g., chance roll failed)
24
+ * - `onForget`: When the skill is forgotten
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const skillHooks: SkillHooks = {
29
+ * onLearn(player) {
30
+ * console.log('Skill learned!');
31
+ * },
32
+ * onUse(player, target) {
33
+ * console.log('Skill used on target');
34
+ * }
35
+ * };
36
+ * ```
37
+ */
38
+ export interface SkillHooks {
39
+ /**
40
+ * Called when the skill is learned by the player
41
+ *
42
+ * @param player - The player learning the skill
43
+ */
44
+ onLearn?: (player: RpgPlayer) => void | Promise<void>;
45
+
46
+ /**
47
+ * Called when the skill is successfully used
48
+ *
49
+ * @param player - The player using the skill
50
+ * @param target - The target player(s) if any
51
+ */
52
+ onUse?: (player: RpgPlayer, target?: RpgPlayer | RpgPlayer[]) => void | Promise<void>;
53
+
54
+ /**
55
+ * Called when the skill usage fails (e.g., chance roll failed)
56
+ *
57
+ * @param player - The player attempting to use the skill
58
+ * @param target - The intended target player(s) if any
59
+ */
60
+ onUseFailed?: (player: RpgPlayer, target?: RpgPlayer | RpgPlayer[]) => void | Promise<void>;
61
+
62
+ /**
63
+ * Called when the skill is forgotten
64
+ *
65
+ * @param player - The player forgetting the skill
66
+ */
67
+ onForget?: (player: RpgPlayer) => void | Promise<void>;
21
68
  }
22
69
 
23
70
  /**
24
- * Interface defining what SkillManager adds to a class
71
+ * Interface for skill object definition
72
+ *
73
+ * Defines the properties that a skill can have when defined as an object.
74
+ * Skills can be defined as objects, classes, or string IDs referencing the database.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const fireSkill: SkillObject = {
79
+ * id: 'fire',
80
+ * name: 'Fire',
81
+ * description: 'A basic fire spell',
82
+ * spCost: 10,
83
+ * hitRate: 0.9,
84
+ * power: 50,
85
+ * onUse(player) {
86
+ * console.log('Fire spell cast!');
87
+ * }
88
+ * };
89
+ *
90
+ * player.learnSkill(fireSkill);
91
+ * ```
25
92
  */
26
- export interface IWithSkillManager {
27
- getSkill(skillClass: any | string): any;
28
- learnSkill(skillId: any | string): any;
29
- forgetSkill(skillId: any | string): any;
30
- useSkill(skillId: any | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any;
93
+ export interface SkillObject extends SkillHooks {
94
+ /**
95
+ * Unique identifier for the skill
96
+ * If not provided, one will be auto-generated
97
+ */
98
+ id?: string;
99
+
100
+ /**
101
+ * Display name of the skill
102
+ */
103
+ name?: string;
104
+
105
+ /**
106
+ * Description of the skill
107
+ */
108
+ description?: string;
109
+
110
+ /**
111
+ * SP (Skill Points) cost to use the skill
112
+ * @default 0
113
+ */
114
+ spCost?: number;
115
+
116
+ /**
117
+ * Hit rate (0-1) - probability of successful skill usage
118
+ * @default 1
119
+ */
120
+ hitRate?: number;
121
+
122
+ /**
123
+ * Base power of the skill for damage calculation
124
+ */
125
+ power?: number;
126
+
127
+ /**
128
+ * Coefficient multipliers for damage calculation
129
+ */
130
+ coefficient?: Record<string, number>;
131
+
132
+ /**
133
+ * Type marker for database
134
+ */
135
+ _type?: 'skill';
136
+
137
+ /**
138
+ * Allow additional properties
139
+ */
140
+ [key: string]: any;
31
141
  }
32
142
 
33
- export function WithSkillManager<TBase extends Constructor<RpgCommonPlayer & SkillManagerDependencies>>(
34
- Base: TBase
35
- ): Constructor<IWithSkillManager> & TBase {
36
- return class extends Base implements IWithSkillManager {
37
- private _getSkillIndex(skillClass: any | string) {
38
- return this.skills().findIndex((skill) => {
39
- if (isString(skill)) {
40
- return skill.id == skillClass;
143
+ /**
144
+ * Skill Manager Mixin
145
+ *
146
+ * Provides skill management capabilities to any class. This mixin handles
147
+ * learning, forgetting, and using skills, including SP cost management,
148
+ * hit rate calculations, and skill effects application.
149
+ *
150
+ * Supports three input formats for skills:
151
+ * - **String ID**: References a skill in the database
152
+ * - **Class**: A skill class that will be instantiated
153
+ * - **Object**: A skill object with properties and hooks
154
+ *
155
+ * @param Base - The base class to extend with skill management
156
+ * @returns Extended class with skill management methods
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * // Using string ID (from database)
161
+ * player.learnSkill('fire');
162
+ *
163
+ * // Using skill class
164
+ * player.learnSkill(FireSkill);
165
+ *
166
+ * // Using skill object
167
+ * player.learnSkill({
168
+ * id: 'ice',
169
+ * name: 'Ice',
170
+ * spCost: 15,
171
+ * onUse(player) {
172
+ * console.log('Ice spell cast!');
173
+ * }
174
+ * });
175
+ * ```
176
+ */
177
+ export function WithSkillManager<TBase extends PlayerCtor>(Base: TBase): TBase {
178
+ return class extends (Base as any) {
179
+ private _getSkillMap(required: boolean = true) {
180
+ // Use this.map directly to support both RpgMap and LobbyRoom
181
+ const map = (this as any).getCurrentMap?.() || (this as any).map;
182
+ if (required && (!map || !map.database)) {
183
+ throw new Error('Player must be on a map to learn skills');
184
+ }
185
+ return map;
186
+ }
187
+
188
+ private _resolveSkillInput(
189
+ skillInput: SkillClass | SkillObject | string,
190
+ map: any,
191
+ databaseByIdOverride?: (id: string) => any
192
+ ) {
193
+ let skillId = '';
194
+ let skillData: any;
195
+ let skillInstance: any = null;
196
+
197
+ if (isString(skillInput)) {
198
+ skillId = skillInput as string;
199
+ skillData = databaseByIdOverride
200
+ ? databaseByIdOverride(skillId)
201
+ : (this as any).databaseById(skillId);
202
+ } else if (typeof skillInput === 'function') {
203
+ const SkillClassCtor = skillInput as SkillClass;
204
+ skillId = (SkillClassCtor as any).id || SkillClassCtor.name;
205
+
206
+ const existingData = map?.database()?.[skillId];
207
+ if (existingData) {
208
+ skillData = existingData;
209
+ } else if (map) {
210
+ map.addInDatabase(skillId, SkillClassCtor);
211
+ skillData = SkillClassCtor;
212
+ } else {
213
+ skillData = SkillClassCtor;
41
214
  }
42
- if (isString(skillClass)) {
43
- return skillClass == (skill.id || skill);
215
+
216
+ skillInstance = new SkillClassCtor();
217
+ skillData = { ...skillData, ...skillInstance, id: skillId };
218
+ } else {
219
+ const skillObj = skillInput as SkillObject;
220
+ skillId = skillObj.id || `skill-${Date.now()}`;
221
+ skillObj.id = skillId;
222
+
223
+ const existingData = map?.database()?.[skillId];
224
+ if (existingData) {
225
+ skillData = { ...existingData, ...skillObj };
226
+ if (map) {
227
+ map.addInDatabase(skillId, skillData, { force: true });
228
+ }
229
+ } else if (map) {
230
+ map.addInDatabase(skillId, skillObj);
231
+ skillData = skillObj;
232
+ } else {
233
+ skillData = skillObj;
234
+ }
235
+
236
+ skillInstance = skillObj;
237
+ }
238
+
239
+ return { skillId, skillData, skillInstance };
240
+ }
241
+
242
+ private _createSkillInstance(
243
+ skillId: string,
244
+ skillData: any,
245
+ skillInstance: any
246
+ ) {
247
+ const instance = new Skill(skillData);
248
+ instance.id.set(skillId);
249
+
250
+ if (skillInstance) {
251
+ (instance as any)._skillInstance = skillInstance;
252
+ }
253
+
254
+ return instance;
255
+ }
256
+
257
+ /**
258
+ * Create a skill instance without learning side effects.
259
+ */
260
+ createSkillInstance(skillInput: SkillClass | SkillObject | string) {
261
+ const map = this._getSkillMap();
262
+ const { skillId, skillData, skillInstance } = this._resolveSkillInput(skillInput, map);
263
+ const instance = this._createSkillInstance(skillId, skillData, skillInstance);
264
+ return { skillId, skillData, skillInstance, instance };
265
+ }
266
+
267
+ /**
268
+ * Resolve skill snapshot entries into Skill instances without side effects.
269
+ */
270
+ resolveSkillsSnapshot(snapshot: { skills?: any[] }, mapOverride?: any) {
271
+ if (!snapshot || !Array.isArray(snapshot.skills)) {
272
+ return snapshot;
273
+ }
274
+
275
+ const map = mapOverride ?? this._getSkillMap(false);
276
+ if (!map || !map.database) {
277
+ return snapshot;
278
+ }
279
+
280
+ const databaseByIdOverride = (id: string) => {
281
+ const data = map.database()[id];
282
+ if (!data) {
283
+ throw new Error(
284
+ `The ID=${id} data is not found in the database. Add the data in the property "database"`
285
+ );
44
286
  }
45
- return isInstanceOf(skill, skillClass);
287
+ return data;
288
+ };
289
+
290
+ const skills = snapshot.skills.map((entry: any) => {
291
+ const skillId = isString(entry) ? entry : entry?.id;
292
+ if (!skillId) {
293
+ return entry;
294
+ }
295
+
296
+ const { skillData, skillInstance } = this._resolveSkillInput(
297
+ skillId,
298
+ map,
299
+ databaseByIdOverride
300
+ );
301
+ return this._createSkillInstance(skillId, skillData, skillInstance);
302
+ });
303
+
304
+ return { ...snapshot, skills };
305
+ }
306
+
307
+ /**
308
+ * Find the index of a skill in the skills array
309
+ *
310
+ * Searches by ID for both string inputs and object/class inputs.
311
+ *
312
+ * @param skillInput - Skill ID, class, or object to find
313
+ * @returns Index of the skill or -1 if not found
314
+ */
315
+ _getSkillIndex(skillInput: SkillClass | SkillObject | string): number {
316
+ // Get the ID to search for
317
+ let searchId = '';
318
+
319
+ if (isString(skillInput)) {
320
+ searchId = skillInput as string;
321
+ } else if (typeof skillInput === 'function') {
322
+ // It's a class - use the class name as ID
323
+ searchId = (skillInput as any).id || skillInput.name;
324
+ } else {
325
+ // It's an object - use its id property
326
+ searchId = (skillInput as SkillObject).id || '';
327
+ }
328
+
329
+ return (this as any).skills().findIndex((skill: any) => {
330
+ const skillId = skill.id() || skill.name() || '';
331
+ return skillId === searchId;
46
332
  });
47
333
  }
48
334
 
49
335
  /**
50
- * Retrieves a learned skill. Returns null, if not found
336
+ * Retrieves a learned skill
337
+ *
338
+ * Searches the player's learned skills by ID, class, or object.
339
+ *
340
+ * @param skillInput - Skill ID, class, or object to find
341
+ * @returns The skill data if found, null otherwise
342
+ *
343
+ * @example
51
344
  * ```ts
52
- * import Fire from 'your-database/skills/fire'
53
- *
54
- * player.getSkill(Fire)
55
- * ```
56
- *
57
- * @title Get Skill
58
- * @method player.getSkill(skillClass)
59
- * @param {SkillClass | string} skillClass or data id
60
- * @returns {instance of SkillClass | null}
61
- * @memberof SkillManager
345
+ * const skill = player.getSkill('fire');
346
+ * if (skill) {
347
+ * console.log(`Fire skill costs ${skill.spCost} SP`);
348
+ * }
349
+ * ```
62
350
  */
63
- getSkill(skillClass: any | string) {
64
- const index = this._getSkillIndex(skillClass);
65
- return this.skills()[index] ?? null;
351
+ getSkill(skillInput: SkillClass | SkillObject | string): Skill | null {
352
+ const index = this._getSkillIndex(skillInput);
353
+ return (this as any).skills()[index] as Skill ?? null;
66
354
  }
67
355
 
68
356
  /**
69
- * Learn a skill. Attributes the coefficient 1 to the parameter INT (intelligence) if cd is not present on the class.
70
- *
71
- * `onLearn()` method is called on the SkillClass
72
- *
73
- * ```ts
74
- * import Fire from 'your-database/skills/fire'
75
- *
76
- * player.learnSkill(Fire)
77
- * ```
78
- *
79
- * @title Learn Skill
80
- * @method player.learnSkill(skillClass)
81
- * @param {SkillClass | string} skillId or data id
82
- * @throws {SkillLog} alreadyLearned
83
- * If the player already knows the skill
84
- * ```
85
- {
86
- id: SKILL_ALREADY_LEARNED,
87
- msg: '...'
88
- }
89
- ```
90
- * @returns {instance of SkillClass}
91
- * @memberof SkillManager
92
- */
93
- learnSkill(skillId: any | string) {
357
+ * Learn a new skill
358
+ *
359
+ * Adds a skill to the player's skill list. Supports three input formats:
360
+ * - **String ID**: Retrieves the skill from the database
361
+ * - **Class**: Creates an instance and adds to database if needed
362
+ * - **Object**: Uses directly and adds to database if needed
363
+ *
364
+ * @param skillInput - Skill ID, class, or object to learn
365
+ * @returns The learned skill data
366
+ * @throws SkillLog.alreadyLearned if the skill is already known
367
+ *
368
+ * @example
369
+ * ```ts
370
+ * // From database
371
+ * player.learnSkill('fire');
372
+ *
373
+ * // From class
374
+ * player.learnSkill(FireSkill);
375
+ *
376
+ * // From object
377
+ * player.learnSkill({
378
+ * id: 'custom-skill',
379
+ * name: 'Custom Skill',
380
+ * spCost: 20,
381
+ * onLearn(player) {
382
+ * console.log('Learned custom skill!');
383
+ * }
384
+ * });
385
+ * ```
386
+ */
387
+ learnSkill(skillInput: SkillClass | SkillObject | string): any {
388
+ const map = this._getSkillMap();
389
+ const { skillId, skillData, skillInstance } = this._resolveSkillInput(skillInput, map);
390
+
391
+ // Check if already learned
94
392
  if (this.getSkill(skillId)) {
95
- throw SkillLog.alreadyLearned(skillId);
393
+ throw SkillLog.alreadyLearned(skillData);
96
394
  }
97
- const instance = this.databaseById(skillId);
98
- this.skills().push(instance);
99
- this["execMethod"]("onLearn", [this], instance);
100
- return instance;
395
+
396
+ const instance = this._createSkillInstance(skillId, skillData, skillInstance);
397
+
398
+ (this as any).skills().push(instance);
399
+
400
+ // Call onLearn hook
401
+ const hookTarget = (instance as any)._skillInstance || instance;
402
+ this["execMethod"]("onLearn", [this], hookTarget);
403
+
404
+ return skillData;
101
405
  }
102
406
 
103
407
  /**
104
- * Forget a skill
105
- *
106
- * `onForget()` method is called on the SkillClass
107
- *
408
+ * Forget a learned skill
409
+ *
410
+ * Removes a skill from the player's skill list.
411
+ *
412
+ * @param skillInput - Skill ID, class, or object to forget
413
+ * @returns The forgotten skill data
414
+ * @throws SkillLog.notLearned if the skill is not known
415
+ *
416
+ * @example
108
417
  * ```ts
109
- * import Fire from 'your-database/skills/fire'
110
- *
111
- * try {
112
- * player.forgetSkill(Fire)
113
- * }
114
- * catch (err) {
115
- * console.log(err)
116
- * }
117
- * ```
118
- *
119
- * @title Forget Skill
120
- * @method player.learnSkill(skillClass)
121
- * @param {SkillClass | string} skillId or data id
122
- * @throws {SkillLog} notLearned
123
- * If trying to forget a skill not learned
124
- * ```
125
- * {
126
- * id: SKILL_NOT_LEARNED,
127
- * msg: '...'
128
- * }
418
+ * player.forgetSkill('fire');
419
+ * // or
420
+ * player.forgetSkill(FireSkill);
129
421
  * ```
130
- * @returns {instance of SkillClass}
131
- * @memberof SkillManager
132
422
  */
133
- forgetSkill(skillId: any | string) {
134
- if (isString(skillId)) skillId = this.databaseById(skillId);
135
- const index = this._getSkillIndex(skillId);
136
- if (index == -1) {
137
- throw SkillLog.notLearned(skillId);
423
+ forgetSkill(skillInput: SkillClass | SkillObject | string): any {
424
+ const index = this._getSkillIndex(skillInput);
425
+
426
+ if (index === -1) {
427
+ // Get skill data for error message
428
+ let skillData: any = skillInput;
429
+ if (isString(skillInput)) {
430
+ try {
431
+ skillData = (this as any).databaseById(skillInput);
432
+ } catch {
433
+ skillData = { name: skillInput, id: skillInput };
434
+ }
435
+ } else if (typeof skillInput === 'function') {
436
+ skillData = { name: (skillInput as SkillClass).name, id: (skillInput as any).id || (skillInput as SkillClass).name };
437
+ }
438
+ throw SkillLog.notLearned(skillData);
138
439
  }
139
- const instance = this.skills()[index];
140
- this.skills().splice(index, 1);
141
- this["execMethod"]("onForget", [this], instance);
142
- return instance;
440
+
441
+ const skillData = (this as any).skills()[index];
442
+ (this as any).skills().splice(index, 1);
443
+
444
+ // Call onForget hook
445
+ const hookTarget = (skillData as any)?._skillInstance || skillData;
446
+ this["execMethod"]("onForget", [this], hookTarget);
447
+
448
+ return skillData;
143
449
  }
144
450
 
145
451
  /**
146
- * Using a skill
147
- *
148
- * `onUse()` method is called on the SkillClass
149
- *
150
- * If other players are indicated then damage will be done to these other players. The method `applyDamage()` will be executed
151
- *
452
+ * Use a learned skill
453
+ *
454
+ * Executes a skill, consuming SP and applying effects to targets.
455
+ * The skill must be learned and the player must have enough SP.
456
+ *
457
+ * @param skillInput - Skill ID, class, or object to use
458
+ * @param otherPlayer - Optional target player(s) to apply skill effects to
459
+ * @returns The used skill data
460
+ * @throws SkillLog.restriction if player has CAN_NOT_SKILL effect
461
+ * @throws SkillLog.notLearned if skill is not known
462
+ * @throws SkillLog.notEnoughSp if not enough SP
463
+ * @throws SkillLog.chanceToUseFailed if hit rate check fails
464
+ *
465
+ * @example
152
466
  * ```ts
153
- * import Fire from 'your-database/skills/fire'
154
- *
155
- * try {
156
- * player.useSkill(Fire)
157
- * }
158
- * catch (err) {
159
- * console.log(err)
160
- * }
161
- * ```
162
- *
163
- * or
164
- *
165
- *
166
- * * ```ts
167
- * import Fire from 'your-database/skills/fire'
168
- *
169
- * try {
170
- * player.useSkill(Fire, otherPlayer)
171
- * }
172
- * catch (err) {
173
- * console.log(err)
174
- * }
175
- * ```
176
- *
177
- * @title Use Skill
178
- * @method player.useSkill(skillClass,otherPlayer)
179
- * @param {SkillClass | string} skillId or data id
180
- * @param {Array<RpgPlayer> | RpgPlayer} [otherPlayer]
181
- * @throws {SkillLog} restriction
182
- * If the player has the `Effect.CAN_NOT_SKILL` effect
183
- * ```
184
- * {
185
- * id: RESTRICTION_SKILL,
186
- * msg: '...'
187
- * }
188
- * ```
189
- * @throws {SkillLog} notLearned
190
- * If the player tries to use an unlearned skill
191
- * ```
192
- * {
193
- * id: SKILL_NOT_LEARNED,
194
- * msg: '...'
195
- * }
196
- * ```
197
- * @throws {SkillLog} notEnoughSp
198
- * If the player does not have enough SP to use the skill
199
- * ```
200
- * {
201
- * id: NOT_ENOUGH_SP,
202
- * msg: '...'
203
- * }
204
- * ```
205
- * @throws {SkillLog} chanceToUseFailed
206
- * If the chance to use the skill has failed (defined with the `hitRate` property)
207
- * ```
208
- * {
209
- * id: USE_CHANCE_SKILL_FAILED,
210
- * msg: '...'
211
- * }
467
+ * // Use skill without target
468
+ * player.useSkill('fire');
469
+ *
470
+ * // Use skill on a target
471
+ * player.useSkill('fire', enemy);
472
+ *
473
+ * // Use skill on multiple targets
474
+ * player.useSkill('fire', [enemy1, enemy2]);
212
475
  * ```
213
- *
214
- * `onUseFailed()` method is called on the SkillClass
215
- *
216
- * @returns {instance of SkillClass}
217
- * @memberof SkillManager
218
- * @todo
219
476
  */
220
- useSkill(skillId: any | string, otherPlayer?: RpgPlayer | RpgPlayer[]) {
221
- const skill = this.getSkill(skillId);
222
- if (this.hasEffect(Effect.CAN_NOT_SKILL)) {
223
- throw SkillLog.restriction(skillId);
477
+ useSkill(skillInput: SkillClass | SkillObject | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any {
478
+ const skill = this.getSkill(skillInput);
479
+
480
+ // Check for skill restriction effect
481
+ if ((this as any).hasEffect(Effect.CAN_NOT_SKILL)) {
482
+ throw SkillLog.restriction(skill || skillInput);
224
483
  }
484
+
485
+ // Check if skill is learned
225
486
  if (!skill) {
226
- throw SkillLog.notLearned(skillId);
487
+ throw SkillLog.notLearned(skillInput);
227
488
  }
228
- if (skill.spCost > this.sp) {
229
- throw SkillLog.notEnoughSp(skillId, skill.spCost, this.sp);
489
+
490
+ // Check SP cost
491
+ const spCost = skill.spCost || 0;
492
+ if (spCost > (this as any).sp) {
493
+ throw SkillLog.notEnoughSp(skill, spCost, (this as any).sp);
230
494
  }
231
- this.sp -= skill.spCost / (this.hasEffect(Effect.HALF_SP_COST) ? 2 : 1);
495
+
496
+ // Consume SP (halved if HALF_SP_COST effect is active)
497
+ const costMultiplier = (this as any).hasEffect(Effect.HALF_SP_COST) ? 2 : 1;
498
+ (this as any).sp -= spCost / costMultiplier;
499
+
500
+ // Check hit rate
232
501
  const hitRate = skill.hitRate ?? 1;
233
502
  if (Math.random() > hitRate) {
234
- this["execMethod"]("onUseFailed", [this, otherPlayer], skill);
235
- throw SkillLog.chanceToUseFailed(skillId);
503
+ const hookTarget = (skill as any)?._skillInstance || skill;
504
+ this["execMethod"]("onUseFailed", [this, otherPlayer], hookTarget);
505
+ throw SkillLog.chanceToUseFailed(skill);
236
506
  }
507
+
508
+ // Apply effects to targets
237
509
  if (otherPlayer) {
238
- let players: any = otherPlayer;
239
- if (!isArray(players)) {
240
- players = [otherPlayer];
241
- }
242
- for (let player of players) {
243
- this.applyStates(player, skill);
244
- player.applyDamage(this, skill);
510
+ const players: RpgPlayer[] = isArray(otherPlayer) ? otherPlayer as RpgPlayer[] : [otherPlayer as RpgPlayer];
511
+ for (const player of players) {
512
+ (this as any).applyStates(player, skill);
513
+ (player as any).applyDamage(this, skill);
245
514
  }
246
515
  }
247
- this["execMethod"]("onUse", [this, otherPlayer], skill);
516
+
517
+ // Call onUse hook
518
+ const hookTarget = (skill as any)?._skillInstance || skill;
519
+ this["execMethod"]("onUse", [this, otherPlayer], hookTarget);
520
+
248
521
  return skill;
249
522
  }
250
- };
523
+ } as unknown as TBase;
524
+ }
525
+
526
+ /**
527
+ * Interface for Skill Manager functionality
528
+ *
529
+ * Provides skill management capabilities including learning, forgetting, and using skills.
530
+ * This interface defines the public API of the SkillManager mixin.
531
+ */
532
+ export interface ISkillManager {
533
+ /**
534
+ * Retrieves a learned skill. Returns null if not found
535
+ *
536
+ * @param skillInput - Skill class, object, or data id
537
+ * @returns The skill data or null
538
+ */
539
+ getSkill(skillInput: SkillClass | SkillObject | string): any | null;
540
+
541
+ /**
542
+ * Learn a skill
543
+ *
544
+ * Supports three input formats:
545
+ * - String ID: Retrieves from database
546
+ * - Class: Creates instance and adds to database
547
+ * - Object: Uses directly and adds to database
548
+ *
549
+ * @param skillInput - Skill class, object, or data id
550
+ * @returns The learned skill data
551
+ * @throws SkillLog.alreadyLearned if the player already knows the skill
552
+ */
553
+ learnSkill(skillInput: SkillClass | SkillObject | string): any;
554
+
555
+ /**
556
+ * Forget a skill
557
+ *
558
+ * @param skillInput - Skill class, object, or data id
559
+ * @returns The forgotten skill data
560
+ * @throws SkillLog.notLearned if trying to forget a skill not learned
561
+ */
562
+ forgetSkill(skillInput: SkillClass | SkillObject | string): any;
563
+
564
+ /**
565
+ * Use a skill
566
+ *
567
+ * @param skillInput - Skill class, object, or data id
568
+ * @param otherPlayer - Optional target player(s) to apply skill to
569
+ * @returns The used skill data
570
+ * @throws SkillLog.restriction if player has Effect.CAN_NOT_SKILL
571
+ * @throws SkillLog.notLearned if player tries to use an unlearned skill
572
+ * @throws SkillLog.notEnoughSp if player does not have enough SP
573
+ * @throws SkillLog.chanceToUseFailed if the chance to use the skill has failed
574
+ */
575
+ useSkill(skillInput: SkillClass | SkillObject | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any;
251
576
  }