@rpgjs/server 5.0.0-alpha.27 → 5.0.0-alpha.29
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/Player/MoveManager.d.ts +115 -50
- package/dist/Player/Player.d.ts +43 -19
- package/dist/Player/SkillManager.d.ts +157 -22
- package/dist/index.js +1007 -197
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/Player/MoveManager.ts +659 -213
- package/src/Player/Player.ts +89 -20
- package/src/Player/SkillManager.ts +401 -73
- package/src/rooms/map.ts +18 -0
- package/tests/battle.spec.ts +375 -0
- package/tests/class.spec.ts +274 -0
- package/tests/effect.spec.ts +219 -0
- package/tests/element.spec.ts +221 -0
- package/tests/gold.spec.ts +99 -0
- package/tests/skill.spec.ts +658 -0
- package/tests/state.spec.ts +467 -0
- package/tests/variable.spec.ts +185 -0
|
@@ -1,27 +1,143 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Constructor,
|
|
3
2
|
isArray,
|
|
4
3
|
isInstanceOf,
|
|
5
4
|
isString,
|
|
6
5
|
PlayerCtor,
|
|
7
|
-
RpgCommonPlayer,
|
|
8
6
|
} from "@rpgjs/common";
|
|
9
7
|
import { SkillLog } from "../logs";
|
|
10
8
|
import { RpgPlayer } from "./Player";
|
|
11
9
|
import { Effect } from "./EffectManager";
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
|
-
*
|
|
12
|
+
* Type for skill class constructor
|
|
15
13
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
type SkillClass = { new (...args: any[]): any };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Interface defining the hooks that can be implemented on skill classes or objects
|
|
18
|
+
*
|
|
19
|
+
* These hooks are called at specific moments during the skill lifecycle:
|
|
20
|
+
* - `onLearn`: When the skill is learned by the player
|
|
21
|
+
* - `onUse`: When the skill is successfully used
|
|
22
|
+
* - `onUseFailed`: When the skill usage fails (e.g., chance roll failed)
|
|
23
|
+
* - `onForget`: When the skill is forgotten
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const skillHooks: SkillHooks = {
|
|
28
|
+
* onLearn(player) {
|
|
29
|
+
* console.log('Skill learned!');
|
|
30
|
+
* },
|
|
31
|
+
* onUse(player, target) {
|
|
32
|
+
* console.log('Skill used on target');
|
|
33
|
+
* }
|
|
34
|
+
* };
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface SkillHooks {
|
|
38
|
+
/**
|
|
39
|
+
* Called when the skill is learned by the player
|
|
40
|
+
*
|
|
41
|
+
* @param player - The player learning the skill
|
|
42
|
+
*/
|
|
43
|
+
onLearn?: (player: RpgPlayer) => void | Promise<void>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Called when the skill is successfully used
|
|
47
|
+
*
|
|
48
|
+
* @param player - The player using the skill
|
|
49
|
+
* @param target - The target player(s) if any
|
|
50
|
+
*/
|
|
51
|
+
onUse?: (player: RpgPlayer, target?: RpgPlayer | RpgPlayer[]) => void | Promise<void>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Called when the skill usage fails (e.g., chance roll failed)
|
|
55
|
+
*
|
|
56
|
+
* @param player - The player attempting to use the skill
|
|
57
|
+
* @param target - The intended target player(s) if any
|
|
58
|
+
*/
|
|
59
|
+
onUseFailed?: (player: RpgPlayer, target?: RpgPlayer | RpgPlayer[]) => void | Promise<void>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Called when the skill is forgotten
|
|
63
|
+
*
|
|
64
|
+
* @param player - The player forgetting the skill
|
|
65
|
+
*/
|
|
66
|
+
onForget?: (player: RpgPlayer) => void | Promise<void>;
|
|
22
67
|
}
|
|
23
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Interface for skill object definition
|
|
71
|
+
*
|
|
72
|
+
* Defines the properties that a skill can have when defined as an object.
|
|
73
|
+
* Skills can be defined as objects, classes, or string IDs referencing the database.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const fireSkill: SkillObject = {
|
|
78
|
+
* id: 'fire',
|
|
79
|
+
* name: 'Fire',
|
|
80
|
+
* description: 'A basic fire spell',
|
|
81
|
+
* spCost: 10,
|
|
82
|
+
* hitRate: 0.9,
|
|
83
|
+
* power: 50,
|
|
84
|
+
* onUse(player) {
|
|
85
|
+
* console.log('Fire spell cast!');
|
|
86
|
+
* }
|
|
87
|
+
* };
|
|
88
|
+
*
|
|
89
|
+
* player.learnSkill(fireSkill);
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export interface SkillObject extends SkillHooks {
|
|
93
|
+
/**
|
|
94
|
+
* Unique identifier for the skill
|
|
95
|
+
* If not provided, one will be auto-generated
|
|
96
|
+
*/
|
|
97
|
+
id?: string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Display name of the skill
|
|
101
|
+
*/
|
|
102
|
+
name?: string;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Description of the skill
|
|
106
|
+
*/
|
|
107
|
+
description?: string;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* SP (Skill Points) cost to use the skill
|
|
111
|
+
* @default 0
|
|
112
|
+
*/
|
|
113
|
+
spCost?: number;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Hit rate (0-1) - probability of successful skill usage
|
|
117
|
+
* @default 1
|
|
118
|
+
*/
|
|
119
|
+
hitRate?: number;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Base power of the skill for damage calculation
|
|
123
|
+
*/
|
|
124
|
+
power?: number;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Coefficient multipliers for damage calculation
|
|
128
|
+
*/
|
|
129
|
+
coefficient?: Record<string, number>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Type marker for database
|
|
133
|
+
*/
|
|
134
|
+
_type?: 'skill';
|
|
24
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Allow additional properties
|
|
138
|
+
*/
|
|
139
|
+
[key: string]: any;
|
|
140
|
+
}
|
|
25
141
|
|
|
26
142
|
/**
|
|
27
143
|
* Skill Manager Mixin
|
|
@@ -30,92 +146,299 @@ interface SkillManagerDependencies {
|
|
|
30
146
|
* learning, forgetting, and using skills, including SP cost management,
|
|
31
147
|
* hit rate calculations, and skill effects application.
|
|
32
148
|
*
|
|
149
|
+
* Supports three input formats for skills:
|
|
150
|
+
* - **String ID**: References a skill in the database
|
|
151
|
+
* - **Class**: A skill class that will be instantiated
|
|
152
|
+
* - **Object**: A skill object with properties and hooks
|
|
153
|
+
*
|
|
33
154
|
* @param Base - The base class to extend with skill management
|
|
34
155
|
* @returns Extended class with skill management methods
|
|
35
156
|
*
|
|
36
157
|
* @example
|
|
37
158
|
* ```ts
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* }
|
|
159
|
+
* // Using string ID (from database)
|
|
160
|
+
* player.learnSkill('fire');
|
|
161
|
+
*
|
|
162
|
+
* // Using skill class
|
|
163
|
+
* player.learnSkill(FireSkill);
|
|
44
164
|
*
|
|
45
|
-
*
|
|
46
|
-
* player.learnSkill(
|
|
47
|
-
*
|
|
165
|
+
* // Using skill object
|
|
166
|
+
* player.learnSkill({
|
|
167
|
+
* id: 'ice',
|
|
168
|
+
* name: 'Ice',
|
|
169
|
+
* spCost: 15,
|
|
170
|
+
* onUse(player) {
|
|
171
|
+
* console.log('Ice spell cast!');
|
|
172
|
+
* }
|
|
173
|
+
* });
|
|
48
174
|
* ```
|
|
49
175
|
*/
|
|
50
|
-
export function WithSkillManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
51
|
-
return class extends Base {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
176
|
+
export function WithSkillManager<TBase extends PlayerCtor>(Base: TBase): TBase {
|
|
177
|
+
return class extends (Base as any) {
|
|
178
|
+
/**
|
|
179
|
+
* Find the index of a skill in the skills array
|
|
180
|
+
*
|
|
181
|
+
* Searches by ID for both string inputs and object/class inputs.
|
|
182
|
+
*
|
|
183
|
+
* @param skillInput - Skill ID, class, or object to find
|
|
184
|
+
* @returns Index of the skill or -1 if not found
|
|
185
|
+
*/
|
|
186
|
+
_getSkillIndex(skillInput: SkillClass | SkillObject | string): number {
|
|
187
|
+
// Get the ID to search for
|
|
188
|
+
let searchId = '';
|
|
189
|
+
|
|
190
|
+
if (isString(skillInput)) {
|
|
191
|
+
searchId = skillInput as string;
|
|
192
|
+
} else if (typeof skillInput === 'function') {
|
|
193
|
+
// It's a class - use the class name as ID
|
|
194
|
+
searchId = (skillInput as any).id || skillInput.name;
|
|
195
|
+
} else {
|
|
196
|
+
// It's an object - use its id property
|
|
197
|
+
searchId = (skillInput as SkillObject).id || '';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return (this as any).skills().findIndex((skill: any) => {
|
|
201
|
+
const skillId = skill.id || skill.name || '';
|
|
202
|
+
return skillId === searchId;
|
|
61
203
|
});
|
|
62
204
|
}
|
|
63
205
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Retrieves a learned skill
|
|
208
|
+
*
|
|
209
|
+
* Searches the player's learned skills by ID, class, or object.
|
|
210
|
+
*
|
|
211
|
+
* @param skillInput - Skill ID, class, or object to find
|
|
212
|
+
* @returns The skill data if found, null otherwise
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* const skill = player.getSkill('fire');
|
|
217
|
+
* if (skill) {
|
|
218
|
+
* console.log(`Fire skill costs ${skill.spCost} SP`);
|
|
219
|
+
* }
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
getSkill(skillInput: SkillClass | SkillObject | string): any | null {
|
|
223
|
+
const index = this._getSkillIndex(skillInput);
|
|
224
|
+
return (this as any).skills()[index] ?? null;
|
|
67
225
|
}
|
|
68
226
|
|
|
69
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Learn a new skill
|
|
229
|
+
*
|
|
230
|
+
* Adds a skill to the player's skill list. Supports three input formats:
|
|
231
|
+
* - **String ID**: Retrieves the skill from the database
|
|
232
|
+
* - **Class**: Creates an instance and adds to database if needed
|
|
233
|
+
* - **Object**: Uses directly and adds to database if needed
|
|
234
|
+
*
|
|
235
|
+
* @param skillInput - Skill ID, class, or object to learn
|
|
236
|
+
* @returns The learned skill data
|
|
237
|
+
* @throws SkillLog.alreadyLearned if the skill is already known
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* // From database
|
|
242
|
+
* player.learnSkill('fire');
|
|
243
|
+
*
|
|
244
|
+
* // From class
|
|
245
|
+
* player.learnSkill(FireSkill);
|
|
246
|
+
*
|
|
247
|
+
* // From object
|
|
248
|
+
* player.learnSkill({
|
|
249
|
+
* id: 'custom-skill',
|
|
250
|
+
* name: 'Custom Skill',
|
|
251
|
+
* spCost: 20,
|
|
252
|
+
* onLearn(player) {
|
|
253
|
+
* console.log('Learned custom skill!');
|
|
254
|
+
* }
|
|
255
|
+
* });
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
learnSkill(skillInput: SkillClass | SkillObject | string): any {
|
|
259
|
+
// Get the map for database operations
|
|
260
|
+
const map = (this as any).getCurrentMap() || (this as any).map;
|
|
261
|
+
|
|
262
|
+
let skillId = '';
|
|
263
|
+
let skillData: any;
|
|
264
|
+
|
|
265
|
+
// Handle string: retrieve from database
|
|
266
|
+
if (isString(skillInput)) {
|
|
267
|
+
skillId = skillInput as string;
|
|
268
|
+
skillData = (this as any).databaseById(skillId);
|
|
269
|
+
}
|
|
270
|
+
// Handle class: create instance and add to database if needed
|
|
271
|
+
else if (typeof skillInput === 'function') {
|
|
272
|
+
const SkillClassCtor = skillInput as SkillClass;
|
|
273
|
+
skillId = (SkillClassCtor as any).id || SkillClassCtor.name;
|
|
274
|
+
|
|
275
|
+
// Check if already in database
|
|
276
|
+
const existingData = map?.database()?.[skillId];
|
|
277
|
+
if (existingData) {
|
|
278
|
+
skillData = existingData;
|
|
279
|
+
} else if (map) {
|
|
280
|
+
// Add the class to the database
|
|
281
|
+
map.addInDatabase(skillId, SkillClassCtor);
|
|
282
|
+
skillData = SkillClassCtor;
|
|
283
|
+
} else {
|
|
284
|
+
skillData = SkillClassCtor;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Create instance of the class for hooks
|
|
288
|
+
const skillInstance = new SkillClassCtor();
|
|
289
|
+
// Merge instance properties with class static properties
|
|
290
|
+
skillData = { ...skillData, ...skillInstance, id: skillId };
|
|
291
|
+
}
|
|
292
|
+
// Handle object: use directly and add to database if needed
|
|
293
|
+
else {
|
|
294
|
+
const skillObj = skillInput as SkillObject;
|
|
295
|
+
skillId = skillObj.id || `skill-${Date.now()}`;
|
|
296
|
+
|
|
297
|
+
// Ensure the object has an id
|
|
298
|
+
skillObj.id = skillId;
|
|
299
|
+
|
|
300
|
+
// Check if already in database
|
|
301
|
+
const existingData = map?.database()?.[skillId];
|
|
302
|
+
if (existingData) {
|
|
303
|
+
// Merge with existing data
|
|
304
|
+
skillData = { ...existingData, ...skillObj };
|
|
305
|
+
if (map) {
|
|
306
|
+
map.addInDatabase(skillId, skillData, { force: true });
|
|
307
|
+
}
|
|
308
|
+
} else if (map) {
|
|
309
|
+
// Add the object to the database
|
|
310
|
+
map.addInDatabase(skillId, skillObj);
|
|
311
|
+
skillData = skillObj;
|
|
312
|
+
} else {
|
|
313
|
+
skillData = skillObj;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check if already learned
|
|
70
318
|
if (this.getSkill(skillId)) {
|
|
71
|
-
throw SkillLog.alreadyLearned(
|
|
319
|
+
throw SkillLog.alreadyLearned(skillData);
|
|
72
320
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
321
|
+
|
|
322
|
+
// Add to skills list
|
|
323
|
+
(this as any).skills().push(skillData);
|
|
324
|
+
|
|
325
|
+
// Call onLearn hook
|
|
326
|
+
this["execMethod"]("onLearn", [this], skillData);
|
|
327
|
+
|
|
328
|
+
return skillData;
|
|
77
329
|
}
|
|
78
330
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
331
|
+
/**
|
|
332
|
+
* Forget a learned skill
|
|
333
|
+
*
|
|
334
|
+
* Removes a skill from the player's skill list.
|
|
335
|
+
*
|
|
336
|
+
* @param skillInput - Skill ID, class, or object to forget
|
|
337
|
+
* @returns The forgotten skill data
|
|
338
|
+
* @throws SkillLog.notLearned if the skill is not known
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* player.forgetSkill('fire');
|
|
343
|
+
* // or
|
|
344
|
+
* player.forgetSkill(FireSkill);
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
forgetSkill(skillInput: SkillClass | SkillObject | string): any {
|
|
348
|
+
const index = this._getSkillIndex(skillInput);
|
|
349
|
+
|
|
350
|
+
if (index === -1) {
|
|
351
|
+
// Get skill data for error message
|
|
352
|
+
let skillData: any = skillInput;
|
|
353
|
+
if (isString(skillInput)) {
|
|
354
|
+
try {
|
|
355
|
+
skillData = (this as any).databaseById(skillInput);
|
|
356
|
+
} catch {
|
|
357
|
+
skillData = { name: skillInput, id: skillInput };
|
|
358
|
+
}
|
|
359
|
+
} else if (typeof skillInput === 'function') {
|
|
360
|
+
skillData = { name: (skillInput as SkillClass).name, id: (skillInput as any).id || (skillInput as SkillClass).name };
|
|
361
|
+
}
|
|
362
|
+
throw SkillLog.notLearned(skillData);
|
|
84
363
|
}
|
|
85
|
-
|
|
86
|
-
this.skills()
|
|
87
|
-
this
|
|
88
|
-
|
|
364
|
+
|
|
365
|
+
const skillData = (this as any).skills()[index];
|
|
366
|
+
(this as any).skills().splice(index, 1);
|
|
367
|
+
|
|
368
|
+
// Call onForget hook
|
|
369
|
+
this["execMethod"]("onForget", [this], skillData);
|
|
370
|
+
|
|
371
|
+
return skillData;
|
|
89
372
|
}
|
|
90
373
|
|
|
91
|
-
|
|
92
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Use a learned skill
|
|
376
|
+
*
|
|
377
|
+
* Executes a skill, consuming SP and applying effects to targets.
|
|
378
|
+
* The skill must be learned and the player must have enough SP.
|
|
379
|
+
*
|
|
380
|
+
* @param skillInput - Skill ID, class, or object to use
|
|
381
|
+
* @param otherPlayer - Optional target player(s) to apply skill effects to
|
|
382
|
+
* @returns The used skill data
|
|
383
|
+
* @throws SkillLog.restriction if player has CAN_NOT_SKILL effect
|
|
384
|
+
* @throws SkillLog.notLearned if skill is not known
|
|
385
|
+
* @throws SkillLog.notEnoughSp if not enough SP
|
|
386
|
+
* @throws SkillLog.chanceToUseFailed if hit rate check fails
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```ts
|
|
390
|
+
* // Use skill without target
|
|
391
|
+
* player.useSkill('fire');
|
|
392
|
+
*
|
|
393
|
+
* // Use skill on a target
|
|
394
|
+
* player.useSkill('fire', enemy);
|
|
395
|
+
*
|
|
396
|
+
* // Use skill on multiple targets
|
|
397
|
+
* player.useSkill('fire', [enemy1, enemy2]);
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
useSkill(skillInput: SkillClass | SkillObject | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any {
|
|
401
|
+
const skill = this.getSkill(skillInput);
|
|
402
|
+
|
|
403
|
+
// Check for skill restriction effect
|
|
93
404
|
if ((this as any).hasEffect(Effect.CAN_NOT_SKILL)) {
|
|
94
|
-
throw SkillLog.restriction(
|
|
405
|
+
throw SkillLog.restriction(skill || skillInput);
|
|
95
406
|
}
|
|
407
|
+
|
|
408
|
+
// Check if skill is learned
|
|
96
409
|
if (!skill) {
|
|
97
|
-
throw SkillLog.notLearned(
|
|
410
|
+
throw SkillLog.notLearned(skillInput);
|
|
98
411
|
}
|
|
99
|
-
|
|
100
|
-
|
|
412
|
+
|
|
413
|
+
// Check SP cost
|
|
414
|
+
const spCost = skill.spCost || 0;
|
|
415
|
+
if (spCost > (this as any).sp) {
|
|
416
|
+
throw SkillLog.notEnoughSp(skill, spCost, (this as any).sp);
|
|
101
417
|
}
|
|
102
|
-
|
|
418
|
+
|
|
419
|
+
// Consume SP (halved if HALF_SP_COST effect is active)
|
|
420
|
+
const costMultiplier = (this as any).hasEffect(Effect.HALF_SP_COST) ? 2 : 1;
|
|
421
|
+
(this as any).sp -= spCost / costMultiplier;
|
|
422
|
+
|
|
423
|
+
// Check hit rate
|
|
103
424
|
const hitRate = skill.hitRate ?? 1;
|
|
104
425
|
if (Math.random() > hitRate) {
|
|
105
426
|
this["execMethod"]("onUseFailed", [this, otherPlayer], skill);
|
|
106
|
-
throw SkillLog.chanceToUseFailed(
|
|
427
|
+
throw SkillLog.chanceToUseFailed(skill);
|
|
107
428
|
}
|
|
429
|
+
|
|
430
|
+
// Apply effects to targets
|
|
108
431
|
if (otherPlayer) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
players = [otherPlayer];
|
|
112
|
-
}
|
|
113
|
-
for (let player of players) {
|
|
432
|
+
const players: RpgPlayer[] = isArray(otherPlayer) ? otherPlayer as RpgPlayer[] : [otherPlayer as RpgPlayer];
|
|
433
|
+
for (const player of players) {
|
|
114
434
|
(this as any).applyStates(player, skill);
|
|
115
435
|
(player as any).applyDamage(this, skill);
|
|
116
436
|
}
|
|
117
437
|
}
|
|
438
|
+
|
|
439
|
+
// Call onUse hook
|
|
118
440
|
this["execMethod"]("onUse", [this, otherPlayer], skill);
|
|
441
|
+
|
|
119
442
|
return skill;
|
|
120
443
|
}
|
|
121
444
|
} as unknown as TBase;
|
|
@@ -131,39 +454,44 @@ export interface ISkillManager {
|
|
|
131
454
|
/**
|
|
132
455
|
* Retrieves a learned skill. Returns null if not found
|
|
133
456
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @returns
|
|
457
|
+
* @param skillInput - Skill class, object, or data id
|
|
458
|
+
* @returns The skill data or null
|
|
136
459
|
*/
|
|
137
|
-
getSkill(
|
|
460
|
+
getSkill(skillInput: SkillClass | SkillObject | string): any | null;
|
|
138
461
|
|
|
139
462
|
/**
|
|
140
463
|
* Learn a skill
|
|
141
464
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
465
|
+
* Supports three input formats:
|
|
466
|
+
* - String ID: Retrieves from database
|
|
467
|
+
* - Class: Creates instance and adds to database
|
|
468
|
+
* - Object: Uses directly and adds to database
|
|
469
|
+
*
|
|
470
|
+
* @param skillInput - Skill class, object, or data id
|
|
471
|
+
* @returns The learned skill data
|
|
144
472
|
* @throws SkillLog.alreadyLearned if the player already knows the skill
|
|
145
473
|
*/
|
|
146
|
-
learnSkill(
|
|
474
|
+
learnSkill(skillInput: SkillClass | SkillObject | string): any;
|
|
147
475
|
|
|
148
476
|
/**
|
|
149
477
|
* Forget a skill
|
|
150
478
|
*
|
|
151
|
-
* @param
|
|
152
|
-
* @returns
|
|
479
|
+
* @param skillInput - Skill class, object, or data id
|
|
480
|
+
* @returns The forgotten skill data
|
|
153
481
|
* @throws SkillLog.notLearned if trying to forget a skill not learned
|
|
154
482
|
*/
|
|
155
|
-
forgetSkill(
|
|
483
|
+
forgetSkill(skillInput: SkillClass | SkillObject | string): any;
|
|
156
484
|
|
|
157
485
|
/**
|
|
158
|
-
*
|
|
486
|
+
* Use a skill
|
|
159
487
|
*
|
|
160
|
-
* @param
|
|
488
|
+
* @param skillInput - Skill class, object, or data id
|
|
161
489
|
* @param otherPlayer - Optional target player(s) to apply skill to
|
|
162
|
-
* @returns
|
|
490
|
+
* @returns The used skill data
|
|
163
491
|
* @throws SkillLog.restriction if player has Effect.CAN_NOT_SKILL
|
|
164
492
|
* @throws SkillLog.notLearned if player tries to use an unlearned skill
|
|
165
493
|
* @throws SkillLog.notEnoughSp if player does not have enough SP
|
|
166
494
|
* @throws SkillLog.chanceToUseFailed if the chance to use the skill has failed
|
|
167
495
|
*/
|
|
168
|
-
useSkill(
|
|
496
|
+
useSkill(skillInput: SkillClass | SkillObject | string, otherPlayer?: RpgPlayer | RpgPlayer[]): any;
|
|
169
497
|
}
|
package/src/rooms/map.ts
CHANGED
|
@@ -281,11 +281,24 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
281
281
|
const activeCollisions = new Set<string>();
|
|
282
282
|
const activeShapeCollisions = new Set<string>();
|
|
283
283
|
|
|
284
|
+
// Helper function to check if entities have different z (height)
|
|
285
|
+
const hasDifferentZ = (entityA: any, entityB: any): boolean => {
|
|
286
|
+
const zA = entityA.owner.z();
|
|
287
|
+
const zB = entityB.owner.z();
|
|
288
|
+
return zA !== zB;
|
|
289
|
+
};
|
|
290
|
+
|
|
284
291
|
// Listen to collision enter events
|
|
285
292
|
this.physic.getEvents().onCollisionEnter((collision) => {
|
|
286
293
|
const entityA = collision.entityA;
|
|
287
294
|
const entityB = collision.entityB;
|
|
288
295
|
|
|
296
|
+
// Skip collision callbacks if entities have different z (height)
|
|
297
|
+
// Higher z entities should not trigger collision callbacks with lower z entities
|
|
298
|
+
if (hasDifferentZ(entityA, entityB)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
289
302
|
// Create a unique key for this collision pair
|
|
290
303
|
const collisionKey = entityA.uuid < entityB.uuid
|
|
291
304
|
? `${entityA.uuid}-${entityB.uuid}`
|
|
@@ -351,6 +364,11 @@ export class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
|
351
364
|
const entityA = collision.entityA;
|
|
352
365
|
const entityB = collision.entityB;
|
|
353
366
|
|
|
367
|
+
// Skip collision callbacks if entities have different z (height)
|
|
368
|
+
if (hasDifferentZ(entityA, entityB)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
354
372
|
const collisionKey = entityA.uuid < entityB.uuid
|
|
355
373
|
? `${entityA.uuid}-${entityB.uuid}`
|
|
356
374
|
: `${entityB.uuid}-${entityA.uuid}`;
|