@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.
- package/dist/Gui/DialogGui.d.ts +5 -0
- package/dist/Gui/GameoverGui.d.ts +23 -0
- package/dist/Gui/Gui.d.ts +6 -0
- package/dist/Gui/MenuGui.d.ts +22 -3
- package/dist/Gui/NotificationGui.d.ts +1 -2
- package/dist/Gui/SaveLoadGui.d.ts +13 -0
- package/dist/Gui/ShopGui.d.ts +28 -3
- package/dist/Gui/TitleGui.d.ts +23 -0
- package/dist/Gui/index.d.ts +10 -1
- package/dist/Player/BattleManager.d.ts +34 -12
- package/dist/Player/ClassManager.d.ts +46 -13
- package/dist/Player/ComponentManager.d.ts +123 -0
- package/dist/Player/Components.d.ts +345 -0
- package/dist/Player/EffectManager.d.ts +86 -0
- package/dist/Player/ElementManager.d.ts +104 -0
- package/dist/Player/GoldManager.d.ts +22 -0
- package/dist/Player/GuiManager.d.ts +259 -0
- package/dist/Player/ItemFixture.d.ts +6 -0
- package/dist/Player/ItemManager.d.ts +450 -9
- package/dist/Player/MoveManager.d.ts +324 -69
- package/dist/Player/ParameterManager.d.ts +344 -14
- package/dist/Player/Player.d.ts +460 -8
- package/dist/Player/SkillManager.d.ts +197 -15
- package/dist/Player/StateManager.d.ts +89 -25
- package/dist/Player/VariableManager.d.ts +74 -0
- package/dist/RpgServer.d.ts +502 -64
- package/dist/RpgServerEngine.d.ts +2 -1
- package/dist/decorators/event.d.ts +46 -0
- package/dist/decorators/map.d.ts +287 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +21653 -20900
- package/dist/index.js.map +1 -1
- package/dist/logs/log.d.ts +2 -3
- package/dist/module.d.ts +43 -1
- package/dist/presets/index.d.ts +0 -9
- package/dist/rooms/BaseRoom.d.ts +132 -0
- package/dist/rooms/lobby.d.ts +10 -2
- package/dist/rooms/map.d.ts +1236 -17
- package/dist/services/save.d.ts +43 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/localStorage.d.ts +23 -0
- package/package.json +14 -10
- package/src/Gui/DialogGui.ts +19 -4
- package/src/Gui/GameoverGui.ts +39 -0
- package/src/Gui/Gui.ts +23 -1
- package/src/Gui/MenuGui.ts +155 -6
- package/src/Gui/NotificationGui.ts +1 -2
- package/src/Gui/SaveLoadGui.ts +60 -0
- package/src/Gui/ShopGui.ts +146 -16
- package/src/Gui/TitleGui.ts +39 -0
- package/src/Gui/index.ts +15 -2
- package/src/Player/BattleManager.ts +91 -49
- package/src/Player/ClassManager.ts +118 -50
- package/src/Player/ComponentManager.ts +425 -19
- package/src/Player/Components.ts +380 -0
- package/src/Player/EffectManager.ts +81 -44
- package/src/Player/ElementManager.ts +109 -86
- package/src/Player/GoldManager.ts +32 -35
- package/src/Player/GuiManager.ts +308 -150
- package/src/Player/ItemFixture.ts +4 -5
- package/src/Player/ItemManager.ts +774 -355
- package/src/Player/MoveManager.ts +1544 -774
- package/src/Player/ParameterManager.ts +546 -104
- package/src/Player/Player.ts +1163 -88
- package/src/Player/SkillManager.ts +520 -195
- package/src/Player/StateManager.ts +170 -182
- package/src/Player/VariableManager.ts +101 -63
- package/src/RpgServer.ts +525 -63
- package/src/core/context.ts +1 -0
- package/src/decorators/event.ts +61 -0
- package/src/decorators/map.ts +327 -0
- package/src/index.ts +11 -1
- package/src/logs/log.ts +10 -3
- package/src/module.ts +126 -3
- package/src/presets/index.ts +1 -10
- package/src/rooms/BaseRoom.ts +232 -0
- package/src/rooms/lobby.ts +25 -7
- package/src/rooms/map.ts +2502 -194
- package/src/services/save.ts +147 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/localStorage.ts +76 -0
- package/tests/battle.spec.ts +375 -0
- package/tests/change-map.spec.ts +72 -0
- package/tests/class.spec.ts +274 -0
- package/tests/effect.spec.ts +219 -0
- package/tests/element.spec.ts +221 -0
- package/tests/event.spec.ts +80 -0
- package/tests/gold.spec.ts +99 -0
- package/tests/item.spec.ts +609 -0
- package/tests/module.spec.ts +38 -0
- package/tests/move.spec.ts +601 -0
- package/tests/player-param.spec.ts +28 -0
- package/tests/prediction-reconciliation.spec.ts +182 -0
- package/tests/random-move.spec.ts +65 -0
- package/tests/skill.spec.ts +658 -0
- package/tests/state.spec.ts +467 -0
- package/tests/variable.spec.ts +185 -0
- package/tests/world-maps.spec.ts +896 -0
- package/vite.config.ts +16 -0
- package/dist/Player/Event.d.ts +0 -0
- 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
|
-
|
|
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
|
-
*
|
|
13
|
+
* Type for skill class constructor
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
|
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
|
|
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
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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(
|
|
64
|
-
const index = this._getSkillIndex(
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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(
|
|
393
|
+
throw SkillLog.alreadyLearned(skillData);
|
|
96
394
|
}
|
|
97
|
-
|
|
98
|
-
this.
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
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(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (index
|
|
137
|
-
|
|
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
|
-
|
|
140
|
-
this.skills()
|
|
141
|
-
this
|
|
142
|
-
|
|
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
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
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
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
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(
|
|
221
|
-
const skill = this.getSkill(
|
|
222
|
-
|
|
223
|
-
|
|
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(
|
|
487
|
+
throw SkillLog.notLearned(skillInput);
|
|
227
488
|
}
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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
|
}
|