@rpgjs/server 5.0.0-alpha.30 → 5.0.0-alpha.32
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/ParameterManager.d.ts +7 -6
- package/dist/Player/Player.d.ts +2 -0
- package/dist/RpgServer.d.ts +2 -2
- package/dist/index.js +165 -65
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/Gui/MenuGui.ts +1 -1
- package/src/Player/MoveManager.ts +109 -55
- package/src/Player/ParameterManager.ts +23 -20
- package/src/Player/Player.ts +33 -16
- package/src/RpgServer.ts +2 -2
- package/src/module.ts +12 -6
- package/src/rooms/lobby.ts +1 -0
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.32",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,9 +11,9 @@
|
|
|
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.32",
|
|
15
|
+
"@rpgjs/physic": "5.0.0-alpha.32",
|
|
16
|
+
"@rpgjs/testing": "5.0.0-alpha.32",
|
|
17
17
|
"@rpgjs/database": "^4.3.0",
|
|
18
18
|
"@signe/di": "^2.8.2",
|
|
19
19
|
"@signe/reactive": "^2.8.2",
|
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) {
|
|
@@ -744,7 +744,8 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
744
744
|
* Move toward a target player or position using AI pathfinding
|
|
745
745
|
*
|
|
746
746
|
* Uses the `SeekAvoid` strategy to navigate toward the target while avoiding obstacles.
|
|
747
|
-
* The movement speed is based on the player's current `speed`
|
|
747
|
+
* The movement speed is based on the player's current `speed` and `frequency` settings,
|
|
748
|
+
* scaled appropriately.
|
|
748
749
|
*
|
|
749
750
|
* @param target - Target player or position `{ x, y }` to move toward
|
|
750
751
|
*
|
|
@@ -764,10 +765,14 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
764
765
|
const playerId = (this as unknown as PlayerWithMixins).id;
|
|
765
766
|
const engine = map.physic;
|
|
766
767
|
|
|
767
|
-
// Calculate maxSpeed based on player's speed
|
|
768
|
+
// Calculate maxSpeed based on player's speed and frequency
|
|
768
769
|
// Original values: 180 for player target, 80 for position target (with default speed=4)
|
|
769
770
|
// Factor: 45 for player (180/4), 20 for position (80/4)
|
|
770
771
|
const playerSpeed = (this as any).speed();
|
|
772
|
+
const rawFrequency = (this as any).frequency;
|
|
773
|
+
const playerFrequency = typeof rawFrequency === 'function' ? rawFrequency() : rawFrequency;
|
|
774
|
+
const frequencyScale = playerFrequency > 0 ? Frequency.High / playerFrequency : 1;
|
|
775
|
+
const normalizedFrequencyScale = Number.isFinite(frequencyScale) && frequencyScale > 0 ? frequencyScale : 1;
|
|
771
776
|
|
|
772
777
|
// Remove ALL movement strategies that could interfere with SeekAvoid
|
|
773
778
|
// This includes SeekAvoid, Dash, Knockback, and LinearRepulsion
|
|
@@ -789,7 +794,7 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
789
794
|
return body;
|
|
790
795
|
};
|
|
791
796
|
// Factor 45: with speed=4 gives 180 (original value)
|
|
792
|
-
const maxSpeed = playerSpeed * 45;
|
|
797
|
+
const maxSpeed = playerSpeed * 45 * normalizedFrequencyScale;
|
|
793
798
|
map.moveManager.add(
|
|
794
799
|
playerId,
|
|
795
800
|
new SeekAvoid(engine, targetProvider, maxSpeed, 140, 80, 48)
|
|
@@ -804,7 +809,7 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
804
809
|
staticTarget.freeze();
|
|
805
810
|
|
|
806
811
|
// Factor 20: with speed=4 gives 80 (original value)
|
|
807
|
-
const maxSpeed = playerSpeed * 20;
|
|
812
|
+
const maxSpeed = playerSpeed * 20 * normalizedFrequencyScale;
|
|
808
813
|
map.moveManager.add(
|
|
809
814
|
playerId,
|
|
810
815
|
new SeekAvoid(engine, () => staticTarget, maxSpeed, 140, 80, 48)
|
|
@@ -920,8 +925,8 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
920
925
|
this.getActiveMovements().some(s => s instanceof Knockback || s instanceof AdditiveKnockback);
|
|
921
926
|
|
|
922
927
|
const setAnimationName = (name: string): void => {
|
|
923
|
-
if (typeof selfAny.
|
|
924
|
-
selfAny.
|
|
928
|
+
if (typeof selfAny.setGraphicAnimation === 'function') {
|
|
929
|
+
selfAny.setGraphicAnimation(name);
|
|
925
930
|
return;
|
|
926
931
|
}
|
|
927
932
|
const animSignal = selfAny.animationName;
|
|
@@ -1178,6 +1183,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1178
1183
|
private readonly onStuck?: MoveRoutesOptions['onStuck'];
|
|
1179
1184
|
private readonly stuckTimeout: number;
|
|
1180
1185
|
private readonly stuckThreshold: number;
|
|
1186
|
+
private remainingDistance = 0;
|
|
1187
|
+
private segmentDirection: Direction | null = null;
|
|
1188
|
+
private segmentStep = 0;
|
|
1181
1189
|
|
|
1182
1190
|
// Frequency wait state
|
|
1183
1191
|
private waitingForFrequency = false;
|
|
@@ -1219,6 +1227,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1219
1227
|
// Reset frequency wait state when processing a new route
|
|
1220
1228
|
this.waitingForFrequency = false;
|
|
1221
1229
|
this.frequencyWaitStartTime = 0;
|
|
1230
|
+
this.remainingDistance = 0;
|
|
1231
|
+
this.segmentDirection = null;
|
|
1232
|
+
this.segmentStep = 0;
|
|
1222
1233
|
|
|
1223
1234
|
// Check if we've completed all routes
|
|
1224
1235
|
if (this.routeIndex >= this.routes.length) {
|
|
@@ -1323,52 +1334,15 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1323
1334
|
}
|
|
1324
1335
|
}
|
|
1325
1336
|
|
|
1326
|
-
//
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
case Direction.Right:
|
|
1332
|
-
case 'right' as any:
|
|
1333
|
-
targetTopLeftX = currentTopLeftX + distance;
|
|
1334
|
-
break;
|
|
1335
|
-
case Direction.Left:
|
|
1336
|
-
case 'left' as any:
|
|
1337
|
-
targetTopLeftX = currentTopLeftX - distance;
|
|
1338
|
-
break;
|
|
1339
|
-
case Direction.Down:
|
|
1340
|
-
case 'down' as any:
|
|
1341
|
-
targetTopLeftY = currentTopLeftY + distance;
|
|
1342
|
-
break;
|
|
1343
|
-
case Direction.Up:
|
|
1344
|
-
case 'up' as any:
|
|
1345
|
-
targetTopLeftY = currentTopLeftY - distance;
|
|
1346
|
-
break;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
// Convert target top-left to center position for physics engine
|
|
1350
|
-
// Get entity to access hitbox dimensions
|
|
1351
|
-
const entity = map.physic.getEntityByUUID(this.player.id);
|
|
1352
|
-
if (!entity) {
|
|
1353
|
-
this.finished = true;
|
|
1354
|
-
this.onComplete(false);
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// Get hitbox dimensions for conversion
|
|
1359
|
-
const hitbox = this.player.hitbox();
|
|
1360
|
-
const hitboxWidth = hitbox?.w ?? 32;
|
|
1361
|
-
const hitboxHeight = hitbox?.h ?? 32;
|
|
1362
|
-
|
|
1363
|
-
// Convert top-left to center: center = topLeft + (size / 2)
|
|
1364
|
-
const targetX = targetTopLeftX + hitboxWidth / 2;
|
|
1365
|
-
const targetY = targetTopLeftY + hitboxHeight / 2;
|
|
1366
|
-
|
|
1367
|
-
this.currentTarget = { x: targetX, y: targetY }; // Center position for physics engine
|
|
1368
|
-
this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY }; // Top-left position for player.x() comparison
|
|
1369
|
-
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);
|
|
1370
1342
|
|
|
1371
|
-
|
|
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
|
+
}
|
|
1372
1346
|
|
|
1373
1347
|
// Reset stuck detection when starting a new movement
|
|
1374
1348
|
this.lastPosition = null;
|
|
@@ -1415,7 +1389,13 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1415
1389
|
|
|
1416
1390
|
if (frequencyMs > 0 && Date.now() - this.frequencyWaitStartTime >= frequencyMs * this.ratioFrequency) {
|
|
1417
1391
|
this.waitingForFrequency = false;
|
|
1418
|
-
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
|
+
}
|
|
1419
1399
|
}
|
|
1420
1400
|
return;
|
|
1421
1401
|
}
|
|
@@ -1473,12 +1453,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1473
1453
|
this.lastDistanceToTarget = null;
|
|
1474
1454
|
this.stuckCheckInitialized = false;
|
|
1475
1455
|
|
|
1476
|
-
// Wait for frequency before processing next route
|
|
1456
|
+
// Wait for frequency before processing next route or segment
|
|
1477
1457
|
if (!this.finished) {
|
|
1478
1458
|
const playerFrequency = this.player.frequency;
|
|
1479
1459
|
if (playerFrequency && playerFrequency > 0) {
|
|
1480
1460
|
this.waitingForFrequency = true;
|
|
1481
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);
|
|
1482
1466
|
} else {
|
|
1483
1467
|
// No frequency delay, process immediately
|
|
1484
1468
|
this.processNextRoute();
|
|
@@ -1505,12 +1489,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1505
1489
|
this.lastDistanceToTarget = null;
|
|
1506
1490
|
this.stuckCheckInitialized = false;
|
|
1507
1491
|
|
|
1508
|
-
// Wait for frequency before processing next route
|
|
1492
|
+
// Wait for frequency before processing next route or segment
|
|
1509
1493
|
if (!this.finished) {
|
|
1510
1494
|
const playerFrequency = player.frequency;
|
|
1511
1495
|
if (playerFrequency && playerFrequency > 0) {
|
|
1512
1496
|
this.waitingForFrequency = true;
|
|
1513
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);
|
|
1514
1502
|
} else {
|
|
1515
1503
|
// No frequency delay, process immediately
|
|
1516
1504
|
this.processNextRoute();
|
|
@@ -1623,6 +1611,10 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1623
1611
|
if (playerFrequency && playerFrequency > 0) {
|
|
1624
1612
|
this.waitingForFrequency = true;
|
|
1625
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);
|
|
1626
1618
|
} else {
|
|
1627
1619
|
// No frequency delay, process immediately
|
|
1628
1620
|
this.processNextRoute();
|
|
@@ -1652,6 +1644,69 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
1652
1644
|
onFinished(): void {
|
|
1653
1645
|
this.onComplete(true);
|
|
1654
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
|
+
}
|
|
1655
1710
|
}
|
|
1656
1711
|
|
|
1657
1712
|
// Create and add the route movement strategy
|
|
@@ -1948,4 +2003,3 @@ export interface IMoveManager {
|
|
|
1948
2003
|
*/
|
|
1949
2004
|
replayRoutes(): void;
|
|
1950
2005
|
}
|
|
1951
|
-
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { isString, PlayerCtor } from "@rpgjs/common";
|
|
2
2
|
import { signal, computed, WritableSignal, ComputedSignal } from "@signe/reactive";
|
|
3
3
|
import { MAXHP, MAXSP } from "@rpgjs/common";
|
|
4
|
-
import {
|
|
4
|
+
import { type } from "@signe/sync";
|
|
5
|
+
|
|
6
|
+
export type ExpCurve = {
|
|
7
|
+
basis: number;
|
|
8
|
+
extra: number;
|
|
9
|
+
accelerationA: number;
|
|
10
|
+
accelerationB: number;
|
|
11
|
+
};
|
|
5
12
|
|
|
6
13
|
/**
|
|
7
14
|
* Interface for Parameter Manager functionality
|
|
@@ -53,12 +60,7 @@ export interface IParameterManager {
|
|
|
53
60
|
* ```
|
|
54
61
|
* @memberof ParameterManager
|
|
55
62
|
* */
|
|
56
|
-
expCurve:
|
|
57
|
-
basis: number,
|
|
58
|
-
extra: number,
|
|
59
|
-
accelerationA: number
|
|
60
|
-
accelerationB: number
|
|
61
|
-
};
|
|
63
|
+
expCurve: ExpCurve;
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
66
|
* Changes the health points
|
|
@@ -393,12 +395,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
393
395
|
* console.log(player.param[MAXHP]); // Updated value
|
|
394
396
|
* ```
|
|
395
397
|
*/
|
|
396
|
-
private _paramsModifierSignal = signal<{
|
|
398
|
+
private _paramsModifierSignal = type(signal<{
|
|
397
399
|
[key: string]: {
|
|
398
400
|
value?: number,
|
|
399
401
|
rate?: number
|
|
400
402
|
}
|
|
401
|
-
}>({})
|
|
403
|
+
}>({}) as any, '_paramsModifierSignal', { persist: true }, this as any)
|
|
402
404
|
|
|
403
405
|
/**
|
|
404
406
|
* Signal for base parameters configuration
|
|
@@ -406,12 +408,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
406
408
|
* Stores the start and end values for each parameter's level curve.
|
|
407
409
|
* Changes to this signal trigger recalculation of all parameter values.
|
|
408
410
|
*/
|
|
409
|
-
private _parametersSignal = signal<{
|
|
411
|
+
private _parametersSignal = type(signal<{
|
|
410
412
|
[key: string]: {
|
|
411
413
|
start: number,
|
|
412
414
|
end: number
|
|
413
415
|
}
|
|
414
|
-
}>({})
|
|
416
|
+
}>({}) as any, '_parametersSignal', { persist: true }, this as any)
|
|
415
417
|
|
|
416
418
|
/**
|
|
417
419
|
* Computed signal for all parameter values
|
|
@@ -432,13 +434,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
432
434
|
_param = type(computed(() => {
|
|
433
435
|
const obj = {}
|
|
434
436
|
const parameters = this._parametersSignal()
|
|
437
|
+
const allModifiers = this._getAggregatedModifiers()
|
|
435
438
|
const level = this._level()
|
|
436
439
|
|
|
437
440
|
for (const [name, paramConfig] of Object.entries(parameters)) {
|
|
438
441
|
let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start
|
|
439
442
|
|
|
440
|
-
// Apply modifiers from equipment, states, etc.
|
|
441
|
-
const allModifiers = this._getAggregatedModifiers()
|
|
442
443
|
const modifier = allModifiers[name]
|
|
443
444
|
if (modifier) {
|
|
444
445
|
if (modifier.rate) curveVal *= modifier.rate
|
|
@@ -548,11 +549,14 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
548
549
|
* ```
|
|
549
550
|
* @memberof ParameterManager
|
|
550
551
|
* */
|
|
551
|
-
public
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
552
|
+
public _expCurveSignal = type(signal<string>('') as any, '_expCurveSignal', { persist: true }, this as any)
|
|
553
|
+
|
|
554
|
+
get expCurve(): ExpCurve {
|
|
555
|
+
return JSON.parse(this._expCurveSignal())
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
set expCurve(val: ExpCurve) {
|
|
559
|
+
this._expCurveSignal.set(JSON.stringify(val))
|
|
556
560
|
}
|
|
557
561
|
|
|
558
562
|
/**
|
|
@@ -686,7 +690,6 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
686
690
|
get level(): number {
|
|
687
691
|
return this._level()
|
|
688
692
|
}
|
|
689
|
-
|
|
690
693
|
/**
|
|
691
694
|
* ```ts
|
|
692
695
|
* console.log(player.expForNextlevel) // 150
|
|
@@ -903,4 +906,4 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
903
906
|
this.recovery({ hp: 1, sp: 1 })
|
|
904
907
|
}
|
|
905
908
|
} as unknown as TBase;
|
|
906
|
-
}
|
|
909
|
+
}
|
package/src/Player/Player.ts
CHANGED
|
@@ -186,21 +186,6 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
186
186
|
|
|
187
187
|
constructor() {
|
|
188
188
|
super();
|
|
189
|
-
// Use type assertion to access mixin properties
|
|
190
|
-
(this as any).expCurve = {
|
|
191
|
-
basis: 30,
|
|
192
|
-
extra: 20,
|
|
193
|
-
accelerationA: 30,
|
|
194
|
-
accelerationB: 30
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
(this as any).addParameter(MAXHP, MAXHP_CURVE);
|
|
198
|
-
(this as any).addParameter(MAXSP, MAXSP_CURVE);
|
|
199
|
-
(this as any).addParameter(STR, STR_CURVE);
|
|
200
|
-
(this as any).addParameter(INT, INT_CURVE);
|
|
201
|
-
(this as any).addParameter(DEX, DEX_CURVE);
|
|
202
|
-
(this as any).addParameter(AGI, AGI_CURVE);
|
|
203
|
-
(this as any).allRecovery();
|
|
204
189
|
|
|
205
190
|
let lastEmitted: { x: number; y: number } | null = null;
|
|
206
191
|
let pendingUpdate: { x: number; y: number } | null = null;
|
|
@@ -238,6 +223,24 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
238
223
|
this.hooks.callHooks("server-playerProps-load", this).subscribe();
|
|
239
224
|
}
|
|
240
225
|
|
|
226
|
+
onGameStart() {
|
|
227
|
+
// Use type assertion to access mixin properties
|
|
228
|
+
(this as any).expCurve = {
|
|
229
|
+
basis: 30,
|
|
230
|
+
extra: 20,
|
|
231
|
+
accelerationA: 30,
|
|
232
|
+
accelerationB: 30
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
;(this as any).addParameter(MAXHP, MAXHP_CURVE);
|
|
236
|
+
(this as any).addParameter(MAXSP, MAXSP_CURVE);
|
|
237
|
+
(this as any).addParameter(STR, STR_CURVE);
|
|
238
|
+
(this as any).addParameter(INT, INT_CURVE);
|
|
239
|
+
(this as any).addParameter(DEX, DEX_CURVE);
|
|
240
|
+
(this as any).addParameter(AGI, AGI_CURVE);
|
|
241
|
+
(this as any).allRecovery();
|
|
242
|
+
}
|
|
243
|
+
|
|
241
244
|
get hooks() {
|
|
242
245
|
return inject<Hooks>(this.context as any, ModulesToken);
|
|
243
246
|
}
|
|
@@ -415,7 +418,12 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
415
418
|
}
|
|
416
419
|
|
|
417
420
|
snapshot() {
|
|
418
|
-
|
|
421
|
+
const snapshot = createStatesSnapshotDeep(this);
|
|
422
|
+
const expCurve = (this as any).expCurve;
|
|
423
|
+
if (expCurve) {
|
|
424
|
+
snapshot.expCurve = { ...expCurve };
|
|
425
|
+
}
|
|
426
|
+
return snapshot;
|
|
419
427
|
}
|
|
420
428
|
|
|
421
429
|
async applySnapshot(snapshot: string | object) {
|
|
@@ -426,6 +434,9 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
426
434
|
const withClass = (this as any).resolveClassSnapshot?.(withStates) ?? withStates;
|
|
427
435
|
const resolvedSnapshot = (this as any).resolveEquipmentsSnapshot?.(withClass) ?? withClass;
|
|
428
436
|
load(this, resolvedSnapshot);
|
|
437
|
+
if (resolvedSnapshot.expCurve) {
|
|
438
|
+
(this as any).expCurve = resolvedSnapshot.expCurve;
|
|
439
|
+
}
|
|
429
440
|
if (Array.isArray(resolvedSnapshot.items)) {
|
|
430
441
|
this.items.set(resolvedSnapshot.items);
|
|
431
442
|
}
|
|
@@ -1258,6 +1269,12 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
1258
1269
|
}
|
|
1259
1270
|
|
|
1260
1271
|
export class RpgEvent extends RpgPlayer {
|
|
1272
|
+
|
|
1273
|
+
constructor() {
|
|
1274
|
+
super();
|
|
1275
|
+
this.onGameStart()
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1261
1278
|
override async execMethod(methodName: string, methodData: any[] = [], instance = this) {
|
|
1262
1279
|
await lastValueFrom(this.hooks
|
|
1263
1280
|
.callHooks(`server-event-${methodName}`, instance, ...methodData));
|
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/src/rooms/lobby.ts
CHANGED
|
@@ -35,6 +35,7 @@ export class LobbyRoom extends BaseRoom {
|
|
|
35
35
|
async guiInteraction(player: RpgPlayer, value: { guiId: string, name: string, data: any }) {
|
|
36
36
|
const id = value.data.id
|
|
37
37
|
if (id === 'start') {
|
|
38
|
+
player.onGameStart();
|
|
38
39
|
this.hooks.callHooks("server-player-onStart", player).subscribe();
|
|
39
40
|
}
|
|
40
41
|
}
|