@rpgjs/server 5.0.0-alpha.31 → 5.0.0-alpha.33
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/ShopGui.d.ts +5 -1
- package/dist/Player/ItemManager.d.ts +8 -9
- package/dist/RpgServer.d.ts +2 -2
- package/dist/index.js +115 -54
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/Gui/MenuGui.ts +1 -1
- package/src/Gui/ShopGui.ts +4 -3
- package/src/Player/ClassManager.ts +7 -4
- package/src/Player/ItemManager.ts +21 -18
- package/src/Player/MoveManager.ts +98 -48
- package/src/Player/ParameterManager.ts +1 -3
- package/src/RpgServer.ts +2 -2
- package/src/module.ts +12 -6
- package/tests/item.spec.ts +19 -1
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.33",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,14 +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.
|
|
16
|
-
"@rpgjs/testing": "5.0.0-alpha.
|
|
14
|
+
"@rpgjs/common": "5.0.0-alpha.33",
|
|
15
|
+
"@rpgjs/physic": "5.0.0-alpha.33",
|
|
16
|
+
"@rpgjs/testing": "5.0.0-alpha.33",
|
|
17
17
|
"@rpgjs/database": "^4.3.0",
|
|
18
|
-
"@signe/di": "^2.8.
|
|
19
|
-
"@signe/reactive": "^2.8.
|
|
20
|
-
"@signe/room": "^2.8.
|
|
21
|
-
"@signe/sync": "^2.8.
|
|
18
|
+
"@signe/di": "^2.8.3",
|
|
19
|
+
"@signe/reactive": "^2.8.3",
|
|
20
|
+
"@signe/room": "^2.8.3",
|
|
21
|
+
"@signe/sync": "^2.8.3",
|
|
22
22
|
"rxjs": "^7.8.2",
|
|
23
23
|
"zod": "^4.3.5"
|
|
24
24
|
},
|
package/src/Gui/MenuGui.ts
CHANGED
|
@@ -119,7 +119,7 @@ export class MenuGui extends Gui {
|
|
|
119
119
|
}))
|
|
120
120
|
const saveLoad = this.buildSaveLoad(options)
|
|
121
121
|
|
|
122
|
-
return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats() }
|
|
122
|
+
return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats(), expForNextlevel: player.expForNextlevel }
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
private refreshMenu(clientActionId?: string) {
|
package/src/Gui/ShopGui.ts
CHANGED
|
@@ -3,9 +3,10 @@ import { Gui } from './Gui'
|
|
|
3
3
|
import { RpgPlayer } from '../Player/Player'
|
|
4
4
|
|
|
5
5
|
export type ShopSellList = Record<string, number> | Array<{ id: string; multiplier: number }>
|
|
6
|
+
export type ShopItemInput = string | { id?: string; [key: string]: any }
|
|
6
7
|
|
|
7
8
|
export interface ShopGuiOptions {
|
|
8
|
-
items:
|
|
9
|
+
items: ShopItemInput[]
|
|
9
10
|
sell?: ShopSellList
|
|
10
11
|
sellMultiplier?: number
|
|
11
12
|
message?: string
|
|
@@ -16,7 +17,7 @@ export interface ShopGuiOptions {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export class ShopGui extends Gui {
|
|
19
|
-
private itemsInput:
|
|
20
|
+
private itemsInput: ShopItemInput[] = []
|
|
20
21
|
private sellMultipliers: Record<string, number> = {}
|
|
21
22
|
private baseSellMultiplier = 0.5
|
|
22
23
|
private messageInput?: string
|
|
@@ -65,7 +66,7 @@ export class ShopGui extends Gui {
|
|
|
65
66
|
return undefined
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const buildItemData = (item, overrides: { price?: number; quantity?: number } = {}) => {
|
|
69
|
+
const buildItemData = (item: ShopItemInput, overrides: { price?: number; quantity?: number } = {}) => {
|
|
69
70
|
const rawId = typeof item === 'string'
|
|
70
71
|
? item
|
|
71
72
|
: (typeof item?.id === 'function' ? item.id() : (item?.id ?? item?.name))
|
|
@@ -6,8 +6,8 @@ type ActorClass = any;
|
|
|
6
6
|
interface PlayerWithMixins extends RpgCommonPlayer {
|
|
7
7
|
databaseById(id: string): any;
|
|
8
8
|
addParameter(name: string, { start, end }: { start: number, end: number }): void;
|
|
9
|
-
addItem(item: any):
|
|
10
|
-
equip(
|
|
9
|
+
addItem(item: any): any;
|
|
10
|
+
equip(itemId: string, equip?: boolean | 'auto'): void;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -108,8 +108,11 @@ export function WithClassManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
108
108
|
(this as any).addParameter(param, actor.parameters[param]);
|
|
109
109
|
}
|
|
110
110
|
for (let item of actor.startingEquipment) {
|
|
111
|
-
(this as any).addItem(item);
|
|
112
|
-
|
|
111
|
+
const inventory = (this as any).addItem(item);
|
|
112
|
+
const itemId = inventory?.id?.();
|
|
113
|
+
if (itemId) {
|
|
114
|
+
(this as any).equip(itemId, true);
|
|
115
|
+
}
|
|
113
116
|
}
|
|
114
117
|
if (actor.class) this.setClass(actor.class);
|
|
115
118
|
(this as any)["execMethod"]("onSet", [this], actor);
|
|
@@ -581,15 +581,19 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
equip(
|
|
584
|
-
|
|
585
|
-
equip: boolean = true
|
|
584
|
+
itemId: string,
|
|
585
|
+
equip: boolean | 'auto' = true
|
|
586
586
|
): void {
|
|
587
|
-
const
|
|
588
|
-
const
|
|
587
|
+
const autoAdd = equip === 'auto';
|
|
588
|
+
const equipState = equip === 'auto' ? true : equip;
|
|
589
|
+
const data = (this as any).databaseById(itemId);
|
|
590
|
+
let inventory: Item = this.getItem(itemId);
|
|
591
|
+
if (!inventory && autoAdd) {
|
|
592
|
+
inventory = this.addItem(itemId, 1);
|
|
593
|
+
}
|
|
589
594
|
if (!inventory) {
|
|
590
595
|
throw ItemLog.notInInventory(itemId);
|
|
591
596
|
}
|
|
592
|
-
const data = (this as any).databaseById(itemId);
|
|
593
597
|
if (data._type == "item") {
|
|
594
598
|
throw ItemLog.invalidToEquiped(itemId);
|
|
595
599
|
}
|
|
@@ -607,11 +611,11 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
607
611
|
|
|
608
612
|
const item = inventory;
|
|
609
613
|
|
|
610
|
-
if ((item as any).equipped &&
|
|
614
|
+
if ((item as any).equipped && equipState) {
|
|
611
615
|
throw ItemLog.isAlreadyEquiped(itemId);
|
|
612
616
|
}
|
|
613
|
-
(item as any).equipped =
|
|
614
|
-
if (!
|
|
617
|
+
(item as any).equipped = equipState;
|
|
618
|
+
if (!equipState) {
|
|
615
619
|
const index = this.equipments().findIndex((it) => it.id() == item.id());
|
|
616
620
|
this.equipments().splice(index, 1);
|
|
617
621
|
} else {
|
|
@@ -619,7 +623,7 @@ export function WithItemManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
619
623
|
}
|
|
620
624
|
// Call onEquip hook - use stored instance if available
|
|
621
625
|
const hookTarget = (item as any)._itemInstance || item;
|
|
622
|
-
this["execMethod"]("onEquip", [this,
|
|
626
|
+
this["execMethod"]("onEquip", [this, equipState], hookTarget);
|
|
623
627
|
}
|
|
624
628
|
} as unknown as TBase;
|
|
625
629
|
}
|
|
@@ -852,12 +856,13 @@ export interface IItemManager {
|
|
|
852
856
|
/**
|
|
853
857
|
* Equips a weapon or armor on a player
|
|
854
858
|
*
|
|
855
|
-
* Think first to add the item in the inventory with the `addItem()` method before equipping the item
|
|
859
|
+
* Think first to add the item in the inventory with the `addItem()` method before equipping the item,
|
|
860
|
+
* or pass `"auto"` to add the item if it is missing and equip it.
|
|
856
861
|
*
|
|
857
862
|
* The `onEquip()` method is called on the ItemClass when the item is equipped or unequipped.
|
|
858
863
|
*
|
|
859
|
-
* @param
|
|
860
|
-
* @param equip - Equip the item if `true`, unequip if `false` (default: `true`)
|
|
864
|
+
* @param itemId - Item identifier to resolve from the database
|
|
865
|
+
* @param equip - Equip the item if `true`, unequip if `false`, or `"auto"` to add then equip (default: `true`)
|
|
861
866
|
* @throws {Object} ItemLog.notInInventory - If the item is not in the inventory
|
|
862
867
|
* - `id`: `ITEM_NOT_INVENTORY`
|
|
863
868
|
* - `msg`: Error message
|
|
@@ -870,19 +875,17 @@ export interface IItemManager {
|
|
|
870
875
|
*
|
|
871
876
|
* @example
|
|
872
877
|
* ```ts
|
|
873
|
-
* import Sword from 'your-database/sword'
|
|
874
|
-
*
|
|
875
878
|
* try {
|
|
876
|
-
* player.addItem(
|
|
877
|
-
* player.equip(
|
|
879
|
+
* player.addItem('sword')
|
|
880
|
+
* player.equip('sword')
|
|
878
881
|
* // Later, unequip it
|
|
879
|
-
* player.equip(
|
|
882
|
+
* player.equip('sword', false)
|
|
880
883
|
* } catch (err) {
|
|
881
884
|
* console.log(err)
|
|
882
885
|
* }
|
|
883
886
|
* ```
|
|
884
887
|
*/
|
|
885
|
-
equip(
|
|
888
|
+
equip(itemId: string, equip?: boolean | 'auto'): void;
|
|
886
889
|
|
|
887
890
|
/**
|
|
888
891
|
* Get the player's attack (sum of items equipped)
|
|
@@ -1183,6 +1183,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1183
1183
|
private readonly onStuck?: MoveRoutesOptions['onStuck'];
|
|
1184
1184
|
private readonly stuckTimeout: number;
|
|
1185
1185
|
private readonly stuckThreshold: number;
|
|
1186
|
+
private remainingDistance = 0;
|
|
1187
|
+
private segmentDirection: Direction | null = null;
|
|
1188
|
+
private segmentStep = 0;
|
|
1186
1189
|
|
|
1187
1190
|
// Frequency wait state
|
|
1188
1191
|
private waitingForFrequency = false;
|
|
@@ -1224,6 +1227,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1224
1227
|
// Reset frequency wait state when processing a new route
|
|
1225
1228
|
this.waitingForFrequency = false;
|
|
1226
1229
|
this.frequencyWaitStartTime = 0;
|
|
1230
|
+
this.remainingDistance = 0;
|
|
1231
|
+
this.segmentDirection = null;
|
|
1232
|
+
this.segmentStep = 0;
|
|
1227
1233
|
|
|
1228
1234
|
// Check if we've completed all routes
|
|
1229
1235
|
if (this.routeIndex >= this.routes.length) {
|
|
@@ -1328,52 +1334,15 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1328
1334
|
}
|
|
1329
1335
|
}
|
|
1330
1336
|
|
|
1331
|
-
//
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
case Direction.Right:
|
|
1337
|
-
case 'right' as any:
|
|
1338
|
-
targetTopLeftX = currentTopLeftX + distance;
|
|
1339
|
-
break;
|
|
1340
|
-
case Direction.Left:
|
|
1341
|
-
case 'left' as any:
|
|
1342
|
-
targetTopLeftX = currentTopLeftX - distance;
|
|
1343
|
-
break;
|
|
1344
|
-
case Direction.Down:
|
|
1345
|
-
case 'down' as any:
|
|
1346
|
-
targetTopLeftY = currentTopLeftY + distance;
|
|
1347
|
-
break;
|
|
1348
|
-
case Direction.Up:
|
|
1349
|
-
case 'up' as any:
|
|
1350
|
-
targetTopLeftY = currentTopLeftY - distance;
|
|
1351
|
-
break;
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
// Convert target top-left to center position for physics engine
|
|
1355
|
-
// Get entity to access hitbox dimensions
|
|
1356
|
-
const entity = map.physic.getEntityByUUID(this.player.id);
|
|
1357
|
-
if (!entity) {
|
|
1358
|
-
this.finished = true;
|
|
1359
|
-
this.onComplete(false);
|
|
1360
|
-
return;
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
// Get hitbox dimensions for conversion
|
|
1364
|
-
const hitbox = this.player.hitbox();
|
|
1365
|
-
const hitboxWidth = hitbox?.w ?? 32;
|
|
1366
|
-
const hitboxHeight = hitbox?.h ?? 32;
|
|
1367
|
-
|
|
1368
|
-
// Convert top-left to center: center = topLeft + (size / 2)
|
|
1369
|
-
const targetX = targetTopLeftX + hitboxWidth / 2;
|
|
1370
|
-
const targetY = targetTopLeftY + hitboxHeight / 2;
|
|
1371
|
-
|
|
1372
|
-
this.currentTarget = { x: targetX, y: targetY }; // Center position for physics engine
|
|
1373
|
-
this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY }; // Top-left position for player.x() comparison
|
|
1374
|
-
this.currentDirection = { x: 0, y: 0 };
|
|
1337
|
+
// Prepare segmented movement (per tile)
|
|
1338
|
+
this.remainingDistance = distance;
|
|
1339
|
+
this.segmentDirection = moveDirection;
|
|
1340
|
+
this.segmentStep = this.getTileStepDistance(playerSpeed);
|
|
1341
|
+
this.setNextSegmentTarget(currentTopLeftX, currentTopLeftY);
|
|
1375
1342
|
|
|
1376
|
-
|
|
1343
|
+
if (this.currentTargetTopLeft) {
|
|
1344
|
+
this.debugLog(`MOVE direction=${moveDirection} from=(${currentTopLeftX.toFixed(1)}, ${currentTopLeftY.toFixed(1)}) to=(${this.currentTargetTopLeft.x.toFixed(1)}, ${this.currentTargetTopLeft.y.toFixed(1)}) dist=${distance.toFixed(1)}`);
|
|
1345
|
+
}
|
|
1377
1346
|
|
|
1378
1347
|
// Reset stuck detection when starting a new movement
|
|
1379
1348
|
this.lastPosition = null;
|
|
@@ -1420,7 +1389,13 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1420
1389
|
|
|
1421
1390
|
if (frequencyMs > 0 && Date.now() - this.frequencyWaitStartTime >= frequencyMs * this.ratioFrequency) {
|
|
1422
1391
|
this.waitingForFrequency = false;
|
|
1423
|
-
this.
|
|
1392
|
+
if (this.remainingDistance > 0) {
|
|
1393
|
+
const currentTopLeftX = this.player.x();
|
|
1394
|
+
const currentTopLeftY = this.player.y();
|
|
1395
|
+
this.setNextSegmentTarget(currentTopLeftX, currentTopLeftY);
|
|
1396
|
+
} else {
|
|
1397
|
+
this.processNextRoute();
|
|
1398
|
+
}
|
|
1424
1399
|
}
|
|
1425
1400
|
return;
|
|
1426
1401
|
}
|
|
@@ -1478,12 +1453,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1478
1453
|
this.lastDistanceToTarget = null;
|
|
1479
1454
|
this.stuckCheckInitialized = false;
|
|
1480
1455
|
|
|
1481
|
-
// Wait for frequency before processing next route
|
|
1456
|
+
// Wait for frequency before processing next route or segment
|
|
1482
1457
|
if (!this.finished) {
|
|
1483
1458
|
const playerFrequency = this.player.frequency;
|
|
1484
1459
|
if (playerFrequency && playerFrequency > 0) {
|
|
1485
1460
|
this.waitingForFrequency = true;
|
|
1486
1461
|
this.frequencyWaitStartTime = Date.now();
|
|
1462
|
+
} else if (this.remainingDistance > 0) {
|
|
1463
|
+
const nextTopLeftX = this.player.x();
|
|
1464
|
+
const nextTopLeftY = this.player.y();
|
|
1465
|
+
this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
|
|
1487
1466
|
} else {
|
|
1488
1467
|
// No frequency delay, process immediately
|
|
1489
1468
|
this.processNextRoute();
|
|
@@ -1510,12 +1489,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1510
1489
|
this.lastDistanceToTarget = null;
|
|
1511
1490
|
this.stuckCheckInitialized = false;
|
|
1512
1491
|
|
|
1513
|
-
// Wait for frequency before processing next route
|
|
1492
|
+
// Wait for frequency before processing next route or segment
|
|
1514
1493
|
if (!this.finished) {
|
|
1515
1494
|
const playerFrequency = player.frequency;
|
|
1516
1495
|
if (playerFrequency && playerFrequency > 0) {
|
|
1517
1496
|
this.waitingForFrequency = true;
|
|
1518
1497
|
this.frequencyWaitStartTime = Date.now();
|
|
1498
|
+
} else if (this.remainingDistance > 0) {
|
|
1499
|
+
const nextTopLeftX = this.player.x();
|
|
1500
|
+
const nextTopLeftY = this.player.y();
|
|
1501
|
+
this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
|
|
1519
1502
|
} else {
|
|
1520
1503
|
// No frequency delay, process immediately
|
|
1521
1504
|
this.processNextRoute();
|
|
@@ -1628,6 +1611,10 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1628
1611
|
if (playerFrequency && playerFrequency > 0) {
|
|
1629
1612
|
this.waitingForFrequency = true;
|
|
1630
1613
|
this.frequencyWaitStartTime = Date.now();
|
|
1614
|
+
} else if (this.remainingDistance > 0) {
|
|
1615
|
+
const nextTopLeftX = this.player.x();
|
|
1616
|
+
const nextTopLeftY = this.player.y();
|
|
1617
|
+
this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
|
|
1631
1618
|
} else {
|
|
1632
1619
|
// No frequency delay, process immediately
|
|
1633
1620
|
this.processNextRoute();
|
|
@@ -1657,6 +1644,69 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1657
1644
|
onFinished(): void {
|
|
1658
1645
|
this.onComplete(true);
|
|
1659
1646
|
}
|
|
1647
|
+
|
|
1648
|
+
private getTileStepDistance(playerSpeed: number): number {
|
|
1649
|
+
if (!Number.isFinite(playerSpeed) || playerSpeed <= 0) {
|
|
1650
|
+
return this.tileSize;
|
|
1651
|
+
}
|
|
1652
|
+
const stepsPerTile = Math.max(1, Math.floor(this.tileSize / playerSpeed));
|
|
1653
|
+
return stepsPerTile * playerSpeed;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
private setNextSegmentTarget(currentTopLeftX: number, currentTopLeftY: number): void {
|
|
1657
|
+
if (!this.segmentDirection || this.remainingDistance <= 0) {
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
const map = this.player.getCurrentMap() as any;
|
|
1662
|
+
if (!map) {
|
|
1663
|
+
this.finished = true;
|
|
1664
|
+
this.onComplete(false);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
const entity = map.physic.getEntityByUUID(this.player.id);
|
|
1669
|
+
if (!entity) {
|
|
1670
|
+
this.finished = true;
|
|
1671
|
+
this.onComplete(false);
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
const segmentDistance = Math.min(this.segmentStep || this.remainingDistance, this.remainingDistance);
|
|
1676
|
+
let targetTopLeftX = currentTopLeftX;
|
|
1677
|
+
let targetTopLeftY = currentTopLeftY;
|
|
1678
|
+
|
|
1679
|
+
switch (this.segmentDirection) {
|
|
1680
|
+
case Direction.Right:
|
|
1681
|
+
case 'right' as any:
|
|
1682
|
+
targetTopLeftX = currentTopLeftX + segmentDistance;
|
|
1683
|
+
break;
|
|
1684
|
+
case Direction.Left:
|
|
1685
|
+
case 'left' as any:
|
|
1686
|
+
targetTopLeftX = currentTopLeftX - segmentDistance;
|
|
1687
|
+
break;
|
|
1688
|
+
case Direction.Down:
|
|
1689
|
+
case 'down' as any:
|
|
1690
|
+
targetTopLeftY = currentTopLeftY + segmentDistance;
|
|
1691
|
+
break;
|
|
1692
|
+
case Direction.Up:
|
|
1693
|
+
case 'up' as any:
|
|
1694
|
+
targetTopLeftY = currentTopLeftY - segmentDistance;
|
|
1695
|
+
break;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
const hitbox = this.player.hitbox();
|
|
1699
|
+
const hitboxWidth = hitbox?.w ?? 32;
|
|
1700
|
+
const hitboxHeight = hitbox?.h ?? 32;
|
|
1701
|
+
|
|
1702
|
+
const targetX = targetTopLeftX + hitboxWidth / 2;
|
|
1703
|
+
const targetY = targetTopLeftY + hitboxHeight / 2;
|
|
1704
|
+
|
|
1705
|
+
this.currentTarget = { x: targetX, y: targetY };
|
|
1706
|
+
this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY };
|
|
1707
|
+
this.currentDirection = { x: 0, y: 0 };
|
|
1708
|
+
this.remainingDistance -= segmentDistance;
|
|
1709
|
+
}
|
|
1660
1710
|
}
|
|
1661
1711
|
|
|
1662
1712
|
// Create and add the route movement strategy
|
|
@@ -434,13 +434,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
434
434
|
_param = type(computed(() => {
|
|
435
435
|
const obj = {}
|
|
436
436
|
const parameters = this._parametersSignal()
|
|
437
|
+
const allModifiers = this._getAggregatedModifiers()
|
|
437
438
|
const level = this._level()
|
|
438
439
|
|
|
439
440
|
for (const [name, paramConfig] of Object.entries(parameters)) {
|
|
440
441
|
let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start
|
|
441
442
|
|
|
442
|
-
// Apply modifiers from equipment, states, etc.
|
|
443
|
-
const allModifiers = this._getAggregatedModifiers()
|
|
444
443
|
const modifier = allModifiers[name]
|
|
445
444
|
if (modifier) {
|
|
446
445
|
if (modifier.rate) curveVal *= modifier.rate
|
|
@@ -691,7 +690,6 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
691
690
|
get level(): number {
|
|
692
691
|
return this._level()
|
|
693
692
|
}
|
|
694
|
-
|
|
695
693
|
/**
|
|
696
694
|
* ```ts
|
|
697
695
|
* console.log(player.expForNextlevel) // 150
|
package/src/RpgServer.ts
CHANGED
|
@@ -696,10 +696,10 @@ export interface RpgServer {
|
|
|
696
696
|
* })
|
|
697
697
|
* ```
|
|
698
698
|
*
|
|
699
|
-
* @prop { { [dataName]: data } } [database]
|
|
699
|
+
* @prop { { [dataName]: data } | (engine: RpgMap) => { [dataName]: data } | Promise<{ [dataName]: data }> } [database]
|
|
700
700
|
* @memberof RpgServer
|
|
701
701
|
* */
|
|
702
|
-
database?: object | any[],
|
|
702
|
+
database?: object | any[] | ((engine: RpgMap) => object | any[] | Promise<object | any[]>),
|
|
703
703
|
|
|
704
704
|
/**
|
|
705
705
|
* Array of all maps. Each element can be either a class (decorated with `@MapData` or not) or a `MapOptions` object
|
package/src/module.ts
CHANGED
|
@@ -127,14 +127,20 @@ export function provideServerModules(modules: RpgServerModule[]): FactoryProvide
|
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
-
if (module.database
|
|
131
|
-
const database =
|
|
130
|
+
if (module.database) {
|
|
131
|
+
const database = module.database;
|
|
132
132
|
module = {
|
|
133
133
|
...module,
|
|
134
134
|
databaseHooks: {
|
|
135
|
-
load: (engine: RpgMap) => {
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
load: async (engine: RpgMap) => {
|
|
136
|
+
const data = typeof database === 'function'
|
|
137
|
+
? await database(engine)
|
|
138
|
+
: database;
|
|
139
|
+
if (!data || typeof data !== 'object') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
for (const key in data) {
|
|
143
|
+
engine.addInDatabase(key, data[key]);
|
|
138
144
|
}
|
|
139
145
|
},
|
|
140
146
|
}
|
|
@@ -145,4 +151,4 @@ export function provideServerModules(modules: RpgServerModule[]): FactoryProvide
|
|
|
145
151
|
return modules
|
|
146
152
|
});
|
|
147
153
|
}
|
|
148
|
-
|
|
154
|
+
|
package/tests/item.spec.ts
CHANGED
|
@@ -385,6 +385,24 @@ describe("Item Management - Equipment", () => {
|
|
|
385
385
|
expect((item as any).equipped).toBe(true);
|
|
386
386
|
});
|
|
387
387
|
|
|
388
|
+
test("should auto add and equip item", () => {
|
|
389
|
+
player.equip("TestSword", "auto");
|
|
390
|
+
const item = player.getItem("TestSword");
|
|
391
|
+
expect(item).toBeDefined();
|
|
392
|
+
expect(item?.quantity()).toBe(1);
|
|
393
|
+
expect((item as any).equipped).toBe(true);
|
|
394
|
+
expect(player.equipments().some((eq) => eq.id() === "TestSword")).toBe(
|
|
395
|
+
true
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("should not add duplicate when auto equipping existing item", () => {
|
|
400
|
+
player.addItem("TestSword", 2);
|
|
401
|
+
player.equip("TestSword", "auto");
|
|
402
|
+
const item = player.getItem("TestSword");
|
|
403
|
+
expect(item?.quantity()).toBe(2);
|
|
404
|
+
});
|
|
405
|
+
|
|
388
406
|
test("should throw error when equipping non-existent item", () => {
|
|
389
407
|
expect(() => {
|
|
390
408
|
player.equip("TestSword", true);
|
|
@@ -588,4 +606,4 @@ describe("Item Management - Edge Cases", () => {
|
|
|
588
606
|
expect(player.getItem("TestSword")?.quantity()).toBe(1);
|
|
589
607
|
expect(player.getItem("TestArmor")?.quantity()).toBe(2);
|
|
590
608
|
});
|
|
591
|
-
});
|
|
609
|
+
});
|