@rpgjs/server 5.0.0-alpha.32 → 5.0.0-alpha.35
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/decorators/map.d.ts +22 -0
- package/dist/index.js +406 -336
- package/dist/index.js.map +1 -1
- package/dist/logs/log.d.ts +2 -3
- package/dist/rooms/map.d.ts +38 -3
- package/package.json +9 -9
- package/src/Gui/ShopGui.ts +4 -3
- package/src/Player/ClassManager.ts +7 -4
- package/src/Player/ItemManager.ts +21 -18
- package/src/Player/ParameterManager.ts +36 -2
- package/src/Player/Player.ts +30 -8
- package/src/decorators/map.ts +26 -1
- package/src/logs/log.ts +10 -3
- package/src/module.ts +1 -0
- package/src/rooms/map.ts +296 -50
- package/tests/item.spec.ts +19 -1
- package/tests/prediction-reconciliation.spec.ts +182 -0
- package/tests/world-maps.spec.ts +83 -1
package/dist/logs/log.d.ts
CHANGED
package/dist/rooms/map.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MockConnection, RoomMethods, RoomOnJoin } from '@signe/room';
|
|
2
|
-
import { Hooks, RpgCommonMap, RpgShape, WorldMapsManager, WorldMapConfig } from '../../../common/src';
|
|
2
|
+
import { Hooks, RpgCommonMap, RpgShape, WorldMapsManager, WeatherState, 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';
|
|
@@ -17,6 +17,8 @@ export interface Controls {
|
|
|
17
17
|
minTimeBetweenInputs?: number;
|
|
18
18
|
/** Whether to enable anti-cheat validation */
|
|
19
19
|
enableAntiCheat?: boolean;
|
|
20
|
+
/** Maximum number of queued inputs processed per server tick */
|
|
21
|
+
maxInputsPerTick?: number;
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Interface representing hook methods available for map events
|
|
@@ -58,6 +60,9 @@ export type EventPosOption = {
|
|
|
58
60
|
*/
|
|
59
61
|
event: EventConstructor | (EventHooks & Record<string, any>);
|
|
60
62
|
};
|
|
63
|
+
interface WeatherSetOptions {
|
|
64
|
+
sync?: boolean;
|
|
65
|
+
}
|
|
61
66
|
export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoin {
|
|
62
67
|
/**
|
|
63
68
|
* Synchronized signal containing all players currently on the map
|
|
@@ -158,6 +163,7 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
158
163
|
* with custom formulas when the map is loaded.
|
|
159
164
|
*/
|
|
160
165
|
damageFormulas: any;
|
|
166
|
+
private _weatherState;
|
|
161
167
|
/** Internal: Map of shapes by name */
|
|
162
168
|
private _shapes;
|
|
163
169
|
/** Internal: Map of shape entity UUIDs to RpgShape instances */
|
|
@@ -169,6 +175,8 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
169
175
|
autoSync: boolean;
|
|
170
176
|
constructor(room: any);
|
|
171
177
|
onStart(): Promise<void>;
|
|
178
|
+
private isPositiveNumber;
|
|
179
|
+
private resolveTrustedMapDimensions;
|
|
172
180
|
/**
|
|
173
181
|
* Setup collision detection between players, events, and shapes
|
|
174
182
|
*
|
|
@@ -391,6 +399,10 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
391
399
|
* ```
|
|
392
400
|
*/
|
|
393
401
|
onInput(player: RpgPlayer, input: any): Promise<void>;
|
|
402
|
+
onPing(player: RpgPlayer, payload: {
|
|
403
|
+
clientTime?: number;
|
|
404
|
+
clientFrame?: number;
|
|
405
|
+
}): void;
|
|
394
406
|
saveSlot(player: RpgPlayer, value: {
|
|
395
407
|
requestId: string;
|
|
396
408
|
index: number;
|
|
@@ -463,10 +475,11 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
463
475
|
/**
|
|
464
476
|
* Process pending inputs for a player with anti-cheat validation
|
|
465
477
|
*
|
|
466
|
-
* This method processes
|
|
478
|
+
* This method processes pending inputs for a player while performing
|
|
467
479
|
* anti-cheat validation to prevent time manipulation and frame skipping.
|
|
468
480
|
* It validates the time deltas between inputs and ensures they are within
|
|
469
|
-
* acceptable ranges.
|
|
481
|
+
* acceptable ranges. To preserve movement itinerary under network bursts,
|
|
482
|
+
* the number of inputs processed per call is capped.
|
|
470
483
|
*
|
|
471
484
|
* ## Architecture
|
|
472
485
|
*
|
|
@@ -924,6 +937,27 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
924
937
|
x: number;
|
|
925
938
|
y: number;
|
|
926
939
|
}, graphic: string, animationName?: string): void;
|
|
940
|
+
private cloneWeatherState;
|
|
941
|
+
/**
|
|
942
|
+
* Get the current map weather state.
|
|
943
|
+
*/
|
|
944
|
+
getWeather(): WeatherState | null;
|
|
945
|
+
/**
|
|
946
|
+
* Set the full weather state for this map.
|
|
947
|
+
*
|
|
948
|
+
* When `sync` is true (default), all connected clients receive the new weather.
|
|
949
|
+
*/
|
|
950
|
+
setWeather(next: WeatherState | null, options?: WeatherSetOptions): WeatherState | null;
|
|
951
|
+
/**
|
|
952
|
+
* Patch the current weather state.
|
|
953
|
+
*
|
|
954
|
+
* Nested `params` values are merged.
|
|
955
|
+
*/
|
|
956
|
+
patchWeather(patch: Partial<WeatherState>, options?: WeatherSetOptions): WeatherState | null;
|
|
957
|
+
/**
|
|
958
|
+
* Clear weather for this map.
|
|
959
|
+
*/
|
|
960
|
+
clearWeather(options?: WeatherSetOptions): void;
|
|
927
961
|
/**
|
|
928
962
|
* Configure runtime synchronized properties on the map
|
|
929
963
|
*
|
|
@@ -1257,3 +1291,4 @@ export declare class RpgMap extends RpgCommonMap<RpgPlayer> implements RoomOnJoi
|
|
|
1257
1291
|
}
|
|
1258
1292
|
export interface RpgMap extends RoomMethods {
|
|
1259
1293
|
}
|
|
1294
|
+
export {};
|
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.35",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -11,16 +11,16 @@
|
|
|
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.35",
|
|
15
|
+
"@rpgjs/physic": "5.0.0-alpha.35",
|
|
16
|
+
"@rpgjs/testing": "5.0.0-alpha.35",
|
|
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
|
-
"zod": "^4.3.
|
|
23
|
+
"zod": "^4.3.6"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"vite": "^7.3.1",
|
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)
|
|
@@ -10,6 +10,32 @@ export type ExpCurve = {
|
|
|
10
10
|
accelerationB: number;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const DEFAULT_EXP_CURVE: ExpCurve = {
|
|
14
|
+
basis: 30,
|
|
15
|
+
extra: 20,
|
|
16
|
+
accelerationA: 30,
|
|
17
|
+
accelerationB: 30
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
21
|
+
return typeof value === "object" && value !== null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toValidNumber(value: unknown, fallback: number): number {
|
|
25
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeExpCurve(value: unknown): ExpCurve {
|
|
29
|
+
if (!isObject(value)) return DEFAULT_EXP_CURVE;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
basis: toValidNumber(value.basis, DEFAULT_EXP_CURVE.basis),
|
|
33
|
+
extra: toValidNumber(value.extra, DEFAULT_EXP_CURVE.extra),
|
|
34
|
+
accelerationA: toValidNumber(value.accelerationA, DEFAULT_EXP_CURVE.accelerationA),
|
|
35
|
+
accelerationB: toValidNumber(value.accelerationB, DEFAULT_EXP_CURVE.accelerationB)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
13
39
|
/**
|
|
14
40
|
* Interface for Parameter Manager functionality
|
|
15
41
|
*
|
|
@@ -549,10 +575,18 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
549
575
|
* ```
|
|
550
576
|
* @memberof ParameterManager
|
|
551
577
|
* */
|
|
552
|
-
public _expCurveSignal = type(signal<string>(
|
|
578
|
+
public _expCurveSignal = type(signal<string>(JSON.stringify(DEFAULT_EXP_CURVE)) as any, '_expCurveSignal', { persist: true }, this as any)
|
|
553
579
|
|
|
554
580
|
get expCurve(): ExpCurve {
|
|
555
|
-
|
|
581
|
+
const raw = this._expCurveSignal()
|
|
582
|
+
if (!raw) return DEFAULT_EXP_CURVE
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
return normalizeExpCurve(JSON.parse(raw))
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
return DEFAULT_EXP_CURVE
|
|
589
|
+
}
|
|
556
590
|
}
|
|
557
591
|
|
|
558
592
|
set expCurve(val: ExpCurve) {
|
package/src/Player/Player.ts
CHANGED
|
@@ -187,7 +187,9 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
187
187
|
constructor() {
|
|
188
188
|
super();
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
const initialX = typeof this.x === "function" ? Number(this.x()) || 0 : 0;
|
|
191
|
+
const initialY = typeof this.y === "function" ? Number(this.y()) || 0 : 0;
|
|
192
|
+
let lastEmitted: { x: number; y: number } | null = { x: initialX, y: initialY };
|
|
191
193
|
let pendingUpdate: { x: number; y: number } | null = null;
|
|
192
194
|
let updateScheduled = false;
|
|
193
195
|
|
|
@@ -252,6 +254,8 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
252
254
|
|
|
253
255
|
setMap(map: RpgMap) {
|
|
254
256
|
this.map = map;
|
|
257
|
+
// Prevent immediate ping-pong map transfers when spawning near a border.
|
|
258
|
+
this.touchSide = true;
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
applyFrames() {
|
|
@@ -303,12 +307,13 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
303
307
|
if (canChange.some(v => v === false)) return false;
|
|
304
308
|
|
|
305
309
|
if (positions && typeof positions === 'object') {
|
|
306
|
-
this.teleport(positions)
|
|
310
|
+
await this.teleport(positions)
|
|
307
311
|
}
|
|
308
|
-
await room?.$sessionTransfer(this.conn, realMapId);
|
|
312
|
+
const transferToken = await room?.$sessionTransfer(this.conn, realMapId);
|
|
309
313
|
this.emit("changeMap", {
|
|
310
314
|
mapId: realMapId,
|
|
311
315
|
positions,
|
|
316
|
+
transferToken: typeof transferToken === 'string' ? transferToken : undefined,
|
|
312
317
|
});
|
|
313
318
|
return true;
|
|
314
319
|
}
|
|
@@ -321,17 +326,34 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
|
|
|
321
326
|
const direction = this.getDirection()
|
|
322
327
|
const marginLeftRight = map.tileWidth / 2
|
|
323
328
|
const marginTopDown = map.tileHeight / 2
|
|
329
|
+
const hitbox = this.hitbox()
|
|
330
|
+
const currentX = this.x()
|
|
331
|
+
const currentY = this.y()
|
|
332
|
+
const nearBorder =
|
|
333
|
+
currentX < marginLeftRight ||
|
|
334
|
+
currentX > map.widthPx - hitbox.w - marginLeftRight ||
|
|
335
|
+
currentY < marginTopDown ||
|
|
336
|
+
currentY > map.heightPx - hitbox.h - marginTopDown
|
|
337
|
+
|
|
338
|
+
if (this.touchSide) {
|
|
339
|
+
if (nearBorder) {
|
|
340
|
+
return false
|
|
341
|
+
}
|
|
342
|
+
this.touchSide = false
|
|
343
|
+
}
|
|
324
344
|
|
|
325
345
|
const changeMap = async (adjacent, to) => {
|
|
326
|
-
|
|
346
|
+
const [nextMap] = worldMaps.getAdjacentMaps(map, adjacent)
|
|
347
|
+
if (!nextMap) {
|
|
327
348
|
return false
|
|
328
349
|
}
|
|
329
|
-
this.touchSide = true
|
|
330
|
-
const [nextMap] = worldMaps.getAdjacentMaps(map, adjacent)
|
|
331
|
-
if (!nextMap) return false
|
|
332
350
|
const id = nextMap.id as string
|
|
333
351
|
const nextMapInfo = worldMaps.getMapInfo(id)
|
|
334
|
-
|
|
352
|
+
const changed = !!(await this.changeMap(id, to(nextMapInfo)))
|
|
353
|
+
if (changed) {
|
|
354
|
+
this.touchSide = true
|
|
355
|
+
}
|
|
356
|
+
return changed
|
|
335
357
|
}
|
|
336
358
|
|
|
337
359
|
if (nextPosition.x < marginLeftRight && direction == Direction.Left) {
|
package/src/decorators/map.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WeatherState } from "@rpgjs/common";
|
|
2
|
+
|
|
1
3
|
export interface MapOptions {
|
|
2
4
|
/**
|
|
3
5
|
* Map identifier. Allows to go to the map (for example with player.changeMap())
|
|
@@ -97,6 +99,28 @@ export interface MapOptions {
|
|
|
97
99
|
* */
|
|
98
100
|
sounds?: string[]
|
|
99
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Initial weather state for this map.
|
|
104
|
+
*
|
|
105
|
+
* This value is applied when the map is loaded and can later be updated
|
|
106
|
+
* at runtime with `map.setWeather()` from server logic.
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* @MapData({
|
|
110
|
+
* id: 'forest',
|
|
111
|
+
* file: require('./tmx/forest.tmx'),
|
|
112
|
+
* weather: {
|
|
113
|
+
* effect: 'fog',
|
|
114
|
+
* preset: 'rpgForestFog',
|
|
115
|
+
* params: { density: 1.2, height: 0.75 },
|
|
116
|
+
* transitionMs: 1200
|
|
117
|
+
* }
|
|
118
|
+
* })
|
|
119
|
+
* class ForestMap extends RpgMap {}
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
weather?: WeatherState | null
|
|
123
|
+
|
|
100
124
|
/**
|
|
101
125
|
* Whether to stop all sounds before playing the map sounds when a player joins.
|
|
102
126
|
*
|
|
@@ -278,6 +302,7 @@ export function MapData(options: MapOptions) {
|
|
|
278
302
|
target.prototype.file = options.file
|
|
279
303
|
target.prototype.id = options.id
|
|
280
304
|
target.prototype.sounds = options.sounds
|
|
305
|
+
target.prototype.weather = options.weather
|
|
281
306
|
target.prototype.lowMemory = options.lowMemory
|
|
282
307
|
target.prototype.stopAllSoundsBeforeJoin = options.stopAllSoundsBeforeJoin
|
|
283
308
|
|
|
@@ -299,4 +324,4 @@ export function MapData(options: MapOptions) {
|
|
|
299
324
|
target.prototype.onLeave = options.onLeave
|
|
300
325
|
}
|
|
301
326
|
}
|
|
302
|
-
}
|
|
327
|
+
}
|
package/src/logs/log.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
export class Log {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export class Log extends Error {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
|
|
4
|
+
constructor(id: string, msg: string) {
|
|
5
|
+
super(`[${id}] ${msg}`);
|
|
6
|
+
this.name = "RpgLog";
|
|
7
|
+
this.id = id;
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
}
|
|
10
|
+
}
|
package/src/module.ts
CHANGED
|
@@ -97,6 +97,7 @@ export function provideServerModules(modules: RpgServerModule[]): FactoryProvide
|
|
|
97
97
|
type: MapClass.type,
|
|
98
98
|
name: MapClass.prototype?.name,
|
|
99
99
|
sounds: MapClass.prototype?.sounds,
|
|
100
|
+
weather: MapClass.prototype?.weather,
|
|
100
101
|
lowMemory: MapClass.prototype?.lowMemory,
|
|
101
102
|
stopAllSoundsBeforeJoin: MapClass.prototype?.stopAllSoundsBeforeJoin,
|
|
102
103
|
events: MapClass.prototype?._events,
|