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