@rpgjs/server 5.0.0-alpha.24 → 5.0.0-alpha.26
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/BattleManager.d.ts +1 -1
- package/dist/Player/ClassManager.d.ts +1 -1
- package/dist/Player/ComponentManager.d.ts +1 -1
- package/dist/Player/EffectManager.d.ts +1 -1
- package/dist/Player/ElementManager.d.ts +1 -1
- package/dist/Player/GoldManager.d.ts +1 -1
- package/dist/Player/GuiManager.d.ts +1 -1
- package/dist/Player/ItemFixture.d.ts +1 -1
- package/dist/Player/ItemManager.d.ts +1 -1
- package/dist/Player/MoveManager.d.ts +1 -1
- package/dist/Player/ParameterManager.d.ts +1 -1
- package/dist/Player/Player.d.ts +32 -23
- package/dist/Player/SkillManager.d.ts +1 -1
- package/dist/Player/StateManager.d.ts +1 -1
- package/dist/Player/VariableManager.d.ts +1 -1
- package/dist/RpgServer.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8267 -10267
- package/dist/index.js.map +1 -1
- package/dist/rooms/BaseRoom.d.ts +95 -0
- package/dist/rooms/lobby.d.ts +4 -1
- package/dist/rooms/map.d.ts +67 -81
- package/package.json +10 -8
- package/src/Player/ItemManager.ts +55 -16
- package/src/Player/ParameterManager.ts +6 -1
- package/src/Player/Player.ts +164 -148
- package/src/module.ts +13 -0
- package/src/rooms/BaseRoom.ts +120 -0
- package/src/rooms/lobby.ts +11 -1
- package/src/rooms/map.ts +148 -152
- package/tests/change-map.spec.ts +72 -0
- package/tests/item.spec.ts +591 -0
- package/tests/module.spec.ts +38 -0
- package/tests/player-param.spec.ts +28 -0
- package/tests/world-maps.spec.ts +814 -0
- package/vite.config.ts +16 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for rooms that need database functionality
|
|
3
|
+
*
|
|
4
|
+
* This class provides common database management functionality that is shared
|
|
5
|
+
* between RpgMap and LobbyRoom. It includes methods for adding and managing
|
|
6
|
+
* items, classes, and other game data in the room's database.
|
|
7
|
+
*
|
|
8
|
+
* ## Architecture
|
|
9
|
+
*
|
|
10
|
+
* Both RpgMap and LobbyRoom need to store game entities (items, classes, skills, etc.)
|
|
11
|
+
* in a database. This base class provides the common implementation to avoid code duplication.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* class MyCustomRoom extends BaseRoom {
|
|
16
|
+
* // Your custom room implementation
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare abstract class BaseRoom {
|
|
21
|
+
/**
|
|
22
|
+
* Signal containing the room's database of items, classes, and other game data
|
|
23
|
+
*
|
|
24
|
+
* This database can be dynamically populated using `addInDatabase()` and
|
|
25
|
+
* `removeInDatabase()` methods. It's used to store game entities like items,
|
|
26
|
+
* classes, skills, etc. that are available in this room.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* // Add data to database
|
|
31
|
+
* room.addInDatabase('Potion', PotionClass);
|
|
32
|
+
*
|
|
33
|
+
* // Access database
|
|
34
|
+
* const potion = room.database()['Potion'];
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
database: import('@signe/reactive').WritableObjectSignal<{}>;
|
|
38
|
+
/**
|
|
39
|
+
* Add data to the room's database
|
|
40
|
+
*
|
|
41
|
+
* Adds an item, class, or other game entity to the room's database.
|
|
42
|
+
* If the ID already exists and `force` is not enabled, the addition is ignored.
|
|
43
|
+
*
|
|
44
|
+
* ## Architecture
|
|
45
|
+
*
|
|
46
|
+
* This method is used by the item management system to store item definitions
|
|
47
|
+
* in the room's database. When a player adds an item, the system first checks
|
|
48
|
+
* if the item exists in the database, and if not, adds it using this method.
|
|
49
|
+
*
|
|
50
|
+
* @param id - Unique identifier for the data
|
|
51
|
+
* @param data - The data to add (can be a class, object, etc.)
|
|
52
|
+
* @param options - Optional configuration
|
|
53
|
+
* @param options.force - If true, overwrites existing data with the same ID
|
|
54
|
+
* @returns `true` if data was added, `false` if it was ignored (ID already exists)
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* // Add a class to the database
|
|
59
|
+
* room.addInDatabase('Potion', PotionClass);
|
|
60
|
+
*
|
|
61
|
+
* // Add an item object to the database
|
|
62
|
+
* room.addInDatabase('custom-item', {
|
|
63
|
+
* name: 'Custom Item',
|
|
64
|
+
* price: 100
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* // Force overwrite existing data
|
|
68
|
+
* room.addInDatabase('Potion', UpdatedPotionClass, { force: true });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
addInDatabase(id: string, data: any, options?: {
|
|
72
|
+
force?: boolean;
|
|
73
|
+
}): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Remove data from the room's database
|
|
76
|
+
*
|
|
77
|
+
* This method allows you to remove items or data from the room's database.
|
|
78
|
+
*
|
|
79
|
+
* @param id - Unique identifier of the data to remove
|
|
80
|
+
* @returns `true` if data was removed, `false` if ID didn't exist
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* // Remove an item from the database
|
|
85
|
+
* room.removeInDatabase('Potion');
|
|
86
|
+
*
|
|
87
|
+
* // Check if removal was successful
|
|
88
|
+
* const removed = room.removeInDatabase('custom-item');
|
|
89
|
+
* if (removed) {
|
|
90
|
+
* console.log('Item removed successfully');
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
removeInDatabase(id: string): boolean;
|
|
95
|
+
}
|
package/dist/rooms/lobby.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { MockConnection } from '@signe/room';
|
|
2
2
|
import { RpgPlayer } from '../Player/Player';
|
|
3
|
-
|
|
3
|
+
import { BaseRoom } from './BaseRoom';
|
|
4
|
+
export declare class LobbyRoom extends BaseRoom {
|
|
4
5
|
players: import('@signe/reactive').WritableObjectSignal<{}>;
|
|
6
|
+
autoSync: boolean;
|
|
7
|
+
constructor(room: any);
|
|
5
8
|
onJoin(player: RpgPlayer, conn: MockConnection): void;
|
|
6
9
|
}
|
package/dist/rooms/map.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MockConnection, RoomOnJoin } from '@signe/room';
|
|
2
|
-
import { Hooks, RpgCommonMap, RpgShape, WorldMapsManager, WorldMapConfig } from '
|
|
1
|
+
import { MockConnection, RoomMethods, RoomOnJoin } from '@signe/room';
|
|
2
|
+
import { Hooks, RpgCommonMap, RpgShape, WorldMapsManager, WorldMapConfig } from '../../../common/src';
|
|
3
3
|
import { RpgPlayer, RpgEvent } from '../Player/Player';
|
|
4
4
|
import { BehaviorSubject } from 'rxjs';
|
|
5
5
|
import { MapOptions } from '../decorators/map';
|
|
@@ -162,7 +162,12 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
162
162
|
private _shapes;
|
|
163
163
|
/** Internal: Map of shape entity UUIDs to RpgShape instances */
|
|
164
164
|
private _shapeEntities;
|
|
165
|
-
|
|
165
|
+
/** Internal: Subscription for the input processing loop */
|
|
166
|
+
private _inputLoopSubscription?;
|
|
167
|
+
/** Enable/disable automatic tick processing (useful for unit tests) */
|
|
168
|
+
private _autoTickEnabled;
|
|
169
|
+
autoSync: boolean;
|
|
170
|
+
constructor(room: any);
|
|
166
171
|
/**
|
|
167
172
|
* Setup collision detection between players, events, and shapes
|
|
168
173
|
*
|
|
@@ -296,72 +301,6 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
296
301
|
* ```
|
|
297
302
|
*/
|
|
298
303
|
get hooks(): Hooks;
|
|
299
|
-
/**
|
|
300
|
-
* Get the width of the map in pixels
|
|
301
|
-
*
|
|
302
|
-
* @returns The width of the map in pixels, or 0 if not loaded
|
|
303
|
-
*
|
|
304
|
-
* @example
|
|
305
|
-
* ```ts
|
|
306
|
-
* const width = map.widthPx;
|
|
307
|
-
* console.log(`Map width: ${width}px`);
|
|
308
|
-
* ```
|
|
309
|
-
*/
|
|
310
|
-
get widthPx(): number;
|
|
311
|
-
/**
|
|
312
|
-
* Get the height of the map in pixels
|
|
313
|
-
*
|
|
314
|
-
* @returns The height of the map in pixels, or 0 if not loaded
|
|
315
|
-
*
|
|
316
|
-
* @example
|
|
317
|
-
* ```ts
|
|
318
|
-
* const height = map.heightPx;
|
|
319
|
-
* console.log(`Map height: ${height}px`);
|
|
320
|
-
* ```
|
|
321
|
-
*/
|
|
322
|
-
get heightPx(): number;
|
|
323
|
-
/**
|
|
324
|
-
* Get the unique identifier of the map
|
|
325
|
-
*
|
|
326
|
-
* @returns The map ID, or empty string if not loaded
|
|
327
|
-
*
|
|
328
|
-
* @example
|
|
329
|
-
* ```ts
|
|
330
|
-
* const mapId = map.id;
|
|
331
|
-
* console.log(`Current map: ${mapId}`);
|
|
332
|
-
* ```
|
|
333
|
-
*/
|
|
334
|
-
get id(): string;
|
|
335
|
-
/**
|
|
336
|
-
* Get the X position of this map in the world coordinate system
|
|
337
|
-
*
|
|
338
|
-
* This is used when maps are part of a larger world map. The world position
|
|
339
|
-
* indicates where this map is located relative to other maps.
|
|
340
|
-
*
|
|
341
|
-
* @returns The X position in world coordinates, or 0 if not in a world
|
|
342
|
-
*
|
|
343
|
-
* @example
|
|
344
|
-
* ```ts
|
|
345
|
-
* const worldX = map.worldX;
|
|
346
|
-
* console.log(`Map is at world position (${worldX}, ${map.worldY})`);
|
|
347
|
-
* ```
|
|
348
|
-
*/
|
|
349
|
-
get worldX(): number;
|
|
350
|
-
/**
|
|
351
|
-
* Get the Y position of this map in the world coordinate system
|
|
352
|
-
*
|
|
353
|
-
* This is used when maps are part of a larger world map. The world position
|
|
354
|
-
* indicates where this map is located relative to other maps.
|
|
355
|
-
*
|
|
356
|
-
* @returns The Y position in world coordinates, or 0 if not in a world
|
|
357
|
-
*
|
|
358
|
-
* @example
|
|
359
|
-
* ```ts
|
|
360
|
-
* const worldY = map.worldY;
|
|
361
|
-
* console.log(`Map is at world position (${map.worldX}, ${worldY})`);
|
|
362
|
-
* ```
|
|
363
|
-
*/
|
|
364
|
-
get worldY(): number;
|
|
365
304
|
/**
|
|
366
305
|
* Handle GUI interaction from a player
|
|
367
306
|
*
|
|
@@ -543,24 +482,43 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
543
482
|
/**
|
|
544
483
|
* Main game loop that processes player inputs
|
|
545
484
|
*
|
|
546
|
-
* This private method
|
|
547
|
-
* for all players on the map. It ensures inputs are
|
|
548
|
-
* prevents concurrent processing for the same player.
|
|
485
|
+
* This private method subscribes to tick$ and processes pending inputs
|
|
486
|
+
* for all players on the map with a throttle of 50ms. It ensures inputs are
|
|
487
|
+
* processed in order and prevents concurrent processing for the same player.
|
|
549
488
|
*
|
|
550
489
|
* ## Architecture
|
|
551
490
|
*
|
|
552
|
-
* -
|
|
491
|
+
* - Subscribes to tick$ with throttleTime(50ms) for responsive input processing
|
|
553
492
|
* - Processes inputs for each player with pending inputs
|
|
554
493
|
* - Uses a flag to prevent concurrent processing for the same player
|
|
555
494
|
* - Calls `processInput()` to handle anti-cheat validation and movement
|
|
556
495
|
*
|
|
557
496
|
* @example
|
|
558
497
|
* ```ts
|
|
559
|
-
* // This method is called automatically in the constructor
|
|
498
|
+
* // This method is called automatically in the constructor if autoTick is enabled
|
|
560
499
|
* // You typically don't call it directly
|
|
561
500
|
* ```
|
|
562
501
|
*/
|
|
563
502
|
private loop;
|
|
503
|
+
/**
|
|
504
|
+
* Enable or disable automatic tick processing
|
|
505
|
+
*
|
|
506
|
+
* When disabled, the input processing loop will not run automatically.
|
|
507
|
+
* This is useful for unit tests where you want manual control over when
|
|
508
|
+
* inputs are processed.
|
|
509
|
+
*
|
|
510
|
+
* @param enabled - Whether to enable automatic tick processing (default: true)
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```ts
|
|
514
|
+
* // Disable auto tick for testing
|
|
515
|
+
* map.setAutoTick(false);
|
|
516
|
+
*
|
|
517
|
+
* // Manually trigger tick processing
|
|
518
|
+
* await map.processInput('player1');
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
setAutoTick(enabled: boolean): void;
|
|
564
522
|
/**
|
|
565
523
|
* Get a world manager by id
|
|
566
524
|
*
|
|
@@ -647,8 +605,7 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
647
605
|
/**
|
|
648
606
|
* Add data to the map's database
|
|
649
607
|
*
|
|
650
|
-
* This method
|
|
651
|
-
* By default, if an ID already exists, the operation is ignored to prevent overwriting existing data.
|
|
608
|
+
* This method delegates to BaseRoom's implementation to avoid code duplication.
|
|
652
609
|
*
|
|
653
610
|
* @param id - Unique identifier for the data
|
|
654
611
|
* @param data - The data to store (can be a class, object, or any value)
|
|
@@ -677,7 +634,7 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
677
634
|
/**
|
|
678
635
|
* Remove data from the map's database
|
|
679
636
|
*
|
|
680
|
-
* This method
|
|
637
|
+
* This method delegates to BaseRoom's implementation to avoid code duplication.
|
|
681
638
|
*
|
|
682
639
|
* @param id - Unique identifier of the data to remove
|
|
683
640
|
* @returns true if data was removed, false if ID didn't exist
|
|
@@ -986,6 +943,17 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
986
943
|
* ```
|
|
987
944
|
*/
|
|
988
945
|
setSync(schema: Record<string, any>): void;
|
|
946
|
+
/**
|
|
947
|
+
* Apply sync to the client
|
|
948
|
+
*
|
|
949
|
+
* This method applies sync to the client by calling the `$applySync()` method.
|
|
950
|
+
*
|
|
951
|
+
* @example
|
|
952
|
+
* ```ts
|
|
953
|
+
* map.applySyncToClient();
|
|
954
|
+
* ```
|
|
955
|
+
*/
|
|
956
|
+
applySyncToClient(): void;
|
|
989
957
|
/**
|
|
990
958
|
* Create a shape dynamically on the map
|
|
991
959
|
*
|
|
@@ -1244,9 +1212,27 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
1244
1212
|
frequency?: number;
|
|
1245
1213
|
direction?: 'x' | 'y' | 'both';
|
|
1246
1214
|
}): void;
|
|
1215
|
+
/**
|
|
1216
|
+
* Clear all server resources and reset state
|
|
1217
|
+
*
|
|
1218
|
+
* This method should be called to clean up all server-side resources when
|
|
1219
|
+
* shutting down or resetting the map. It stops the input processing loop
|
|
1220
|
+
* and ensures that all subscriptions are properly cleaned up.
|
|
1221
|
+
*
|
|
1222
|
+
* ## Design
|
|
1223
|
+
*
|
|
1224
|
+
* This method is used primarily in testing environments to ensure clean
|
|
1225
|
+
* state between tests. It stops the tick subscription to prevent memory leaks.
|
|
1226
|
+
*
|
|
1227
|
+
* @example
|
|
1228
|
+
* ```ts
|
|
1229
|
+
* // In test cleanup
|
|
1230
|
+
* afterEach(() => {
|
|
1231
|
+
* map.clear();
|
|
1232
|
+
* });
|
|
1233
|
+
* ```
|
|
1234
|
+
*/
|
|
1235
|
+
clear(): void;
|
|
1247
1236
|
}
|
|
1248
|
-
export interface RpgMap {
|
|
1249
|
-
$send: (conn: MockConnection, data: any) => void;
|
|
1250
|
-
$broadcast: (data: any) => void;
|
|
1251
|
-
$sessionTransfer: (userOrPublicId: any | string, targetRoomId: string) => void;
|
|
1237
|
+
export interface RpgMap extends RoomMethods {
|
|
1252
1238
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpgjs/server",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.26",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"description": "",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@rpgjs/common": "5.0.0-alpha.
|
|
15
|
-
"@rpgjs/physic": "5.0.0-alpha.
|
|
14
|
+
"@rpgjs/common": "5.0.0-alpha.26",
|
|
15
|
+
"@rpgjs/physic": "5.0.0-alpha.26",
|
|
16
|
+
"@rpgjs/testing": "5.0.0-alpha.26",
|
|
16
17
|
"@rpgjs/database": "^4.3.0",
|
|
17
|
-
"@signe/di": "^2.
|
|
18
|
-
"@signe/reactive": "^2.
|
|
19
|
-
"@signe/room": "^2.
|
|
20
|
-
"@signe/sync": "^2.
|
|
18
|
+
"@signe/di": "^2.7.2",
|
|
19
|
+
"@signe/reactive": "^2.7.2",
|
|
20
|
+
"@signe/room": "^2.7.2",
|
|
21
|
+
"@signe/sync": "^2.7.2",
|
|
21
22
|
"rxjs": "^7.8.2",
|
|
22
23
|
"zod": "^4.1.13"
|
|
23
24
|
},
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"type": "module",
|
|
29
30
|
"scripts": {
|
|
30
31
|
"dev": "vite build --watch",
|
|
31
|
-
"build": "vite build"
|
|
32
|
+
"build": "vite build",
|
|
33
|
+
"test": "vitest"
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -206,8 +206,10 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
208
|
addItem(item: ItemClass | ItemObject | string, nb: number = 1): Item {
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
// Use this.map directly to support both RpgMap and LobbyRoom
|
|
210
|
+
// If no map, player is in Lobby
|
|
211
|
+
const map = (this as any).getCurrentMap() || (this as any).map;
|
|
212
|
+
if (!map || !map.database) {
|
|
211
213
|
throw new Error('Player must be on a map to add items');
|
|
212
214
|
}
|
|
213
215
|
|
|
@@ -219,11 +221,6 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
219
221
|
if (isString(item)) {
|
|
220
222
|
itemId = item as string;
|
|
221
223
|
data = (this as any).databaseById(itemId);
|
|
222
|
-
if (!data) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`The ID=${itemId} data is not found in the database. Add the data in the property "database"`
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
224
|
}
|
|
228
225
|
// Handle class: create instance and add to database if needed
|
|
229
226
|
else if (typeof item === 'function' || (item as any).prototype) {
|
|
@@ -271,13 +268,34 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
271
268
|
let instance: Item;
|
|
272
269
|
|
|
273
270
|
if (existingItem) {
|
|
274
|
-
// Item already exists,
|
|
271
|
+
// Item already exists, update quantity and merge properties
|
|
275
272
|
instance = existingItem;
|
|
276
273
|
instance.quantity.update((it) => it + nb);
|
|
274
|
+
|
|
275
|
+
// Update item properties from merged data (e.g., name, description, price)
|
|
276
|
+
if (data.name !== undefined) {
|
|
277
|
+
instance.name.set(data.name);
|
|
278
|
+
}
|
|
279
|
+
if (data.description !== undefined) {
|
|
280
|
+
instance.description.set(data.description);
|
|
281
|
+
}
|
|
282
|
+
if (data.price !== undefined) {
|
|
283
|
+
instance.price.set(data.price);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Update stored instance if it's an object with hooks
|
|
287
|
+
if (itemInstance && typeof itemInstance === 'object' && !(itemInstance instanceof Function)) {
|
|
288
|
+
(instance as any)._itemInstance = itemInstance;
|
|
289
|
+
// Update hooks if they exist
|
|
290
|
+
if (itemInstance.onAdd) {
|
|
291
|
+
instance.onAdd = itemInstance.onAdd.bind(itemInstance);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
277
294
|
} else {
|
|
278
295
|
// Create new item instance
|
|
279
296
|
instance = new Item(data);
|
|
280
297
|
instance.id.set(itemId);
|
|
298
|
+
instance.quantity.set(nb);
|
|
281
299
|
|
|
282
300
|
// Attach hooks from class instance or object
|
|
283
301
|
if (itemInstance) {
|
|
@@ -294,6 +312,7 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
294
312
|
|
|
295
313
|
// Call onAdd hook - use stored instance if available
|
|
296
314
|
const hookTarget = (instance as any)._itemInstance || instance;
|
|
315
|
+
// Only call onAdd if it exists and is a function
|
|
297
316
|
(this as any)["execMethod"]("onAdd", [this], hookTarget);
|
|
298
317
|
return instance;
|
|
299
318
|
}
|
|
@@ -315,7 +334,9 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
315
334
|
}
|
|
316
335
|
// Call onRemove hook - use stored instance if available
|
|
317
336
|
const hookTarget = (item as any)._itemInstance || item;
|
|
318
|
-
|
|
337
|
+
if (hookTarget && typeof hookTarget.onRemove === 'function') {
|
|
338
|
+
this["execMethod"]("onRemove", [this], hookTarget);
|
|
339
|
+
}
|
|
319
340
|
return this.items()[itemIndex];
|
|
320
341
|
}
|
|
321
342
|
|
|
@@ -373,7 +394,13 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
373
394
|
getParamItem(name: string): number {
|
|
374
395
|
let nb = 0;
|
|
375
396
|
for (let item of this.equipments()) {
|
|
376
|
-
|
|
397
|
+
// Retrieve item data from database to get properties like atk, pdef, sdef
|
|
398
|
+
try {
|
|
399
|
+
const itemData = (this as any).databaseById(item.id());
|
|
400
|
+
nb += itemData[name] || 0;
|
|
401
|
+
} catch {
|
|
402
|
+
// If item not in database, skip it
|
|
403
|
+
}
|
|
377
404
|
}
|
|
378
405
|
const modifier = (this as any).paramsModifier?.[name];
|
|
379
406
|
if (modifier) {
|
|
@@ -404,19 +431,31 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
404
431
|
if (!inventory) {
|
|
405
432
|
throw ItemLog.notInInventory(itemId);
|
|
406
433
|
}
|
|
407
|
-
|
|
408
|
-
|
|
434
|
+
|
|
435
|
+
// Retrieve item data from database to check consumable and hitRate
|
|
436
|
+
const itemData = (this as any).databaseById(itemId);
|
|
437
|
+
const consumable = itemData?.consumable;
|
|
438
|
+
|
|
439
|
+
// If consumable is explicitly false, throw error
|
|
440
|
+
if (consumable === false) {
|
|
409
441
|
throw ItemLog.notUseItem(itemId);
|
|
410
442
|
}
|
|
411
|
-
|
|
412
|
-
|
|
443
|
+
|
|
444
|
+
// If consumable is undefined and item is not of type 'item', it's not consumable
|
|
445
|
+
if (consumable === undefined && itemData?._type && itemData._type !== 'item') {
|
|
446
|
+
throw ItemLog.notUseItem(itemId);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const hitRate = itemData?.hitRate ?? 1;
|
|
450
|
+
const hookTarget = (inventory as any)._itemInstance || inventory;
|
|
451
|
+
|
|
413
452
|
if (Math.random() > hitRate) {
|
|
414
453
|
this.removeItem(itemClass);
|
|
415
454
|
this["execMethod"]("onUseFailed", [this], hookTarget);
|
|
416
455
|
throw ItemLog.chanceToUseFailed(itemId);
|
|
417
456
|
}
|
|
418
|
-
(this as any).applyEffect?.(
|
|
419
|
-
(this as any).applyStates?.(this,
|
|
457
|
+
(this as any).applyEffect?.(itemData);
|
|
458
|
+
(this as any).applyStates?.(this, itemData);
|
|
420
459
|
this["execMethod"]("onUse", [this], hookTarget);
|
|
421
460
|
this.removeItem(itemClass);
|
|
422
461
|
return inventory;
|
|
@@ -580,6 +580,9 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
580
580
|
this.hpSignal.set(val)
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
get hp(): number {
|
|
584
|
+
return this.hpSignal()
|
|
585
|
+
}
|
|
583
586
|
|
|
584
587
|
/**
|
|
585
588
|
* Changes the skill points
|
|
@@ -601,7 +604,9 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
601
604
|
this.spSignal.set(val)
|
|
602
605
|
}
|
|
603
606
|
|
|
604
|
-
|
|
607
|
+
get sp(): number {
|
|
608
|
+
return this.spSignal()
|
|
609
|
+
}
|
|
605
610
|
|
|
606
611
|
/**
|
|
607
612
|
* Changing the player's experience.
|