@rpgjs/client 5.0.0-beta.6 → 5.0.0-beta.8
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/CHANGELOG.md +14 -0
- package/dist/Game/AnimationManager.d.ts +2 -2
- package/dist/Game/AnimationManager.js +18 -9
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/AnimationManager.spec.d.ts +1 -0
- package/dist/Game/Map.d.ts +7 -9
- package/dist/Game/Map.js +5 -4
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.d.ts +44 -20
- package/dist/Game/Object.js +28 -14
- package/dist/Game/Object.js.map +1 -1
- package/dist/Gui/Gui.d.ts +19 -6
- package/dist/Gui/Gui.js +64 -34
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/Gui/Gui.spec.d.ts +1 -0
- package/dist/Gui/NotificationManager.d.ts +1 -1
- package/dist/Gui/NotificationManager.js.map +1 -1
- package/dist/Resource.js +1 -1
- package/dist/Resource.js.map +1 -1
- package/dist/RpgClient.d.ts +57 -2
- package/dist/RpgClientEngine.d.ts +55 -16
- package/dist/RpgClientEngine.js +60 -5
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/Sound.js.map +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.128.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.127.0 → _@oxc-project_runtime@0.128.0}/helpers/decorateMetadata.js +1 -1
- package/dist/components/animations/animation.ce.js.map +1 -1
- package/dist/components/animations/hit.ce.js.map +1 -1
- package/dist/components/character.ce.js +280 -3
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/dynamics/bar.ce.js +96 -0
- package/dist/components/dynamics/bar.ce.js.map +1 -0
- package/dist/components/dynamics/image.ce.js +23 -0
- package/dist/components/dynamics/image.ce.js.map +1 -0
- package/dist/components/dynamics/parse-value.d.ts +4 -1
- package/dist/components/dynamics/parse-value.js +51 -35
- package/dist/components/dynamics/parse-value.js.map +1 -1
- package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
- package/dist/components/dynamics/shape-utils.d.ts +16 -0
- package/dist/components/dynamics/shape-utils.js +73 -0
- package/dist/components/dynamics/shape-utils.js.map +1 -0
- package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
- package/dist/components/dynamics/shape.ce.js +83 -0
- package/dist/components/dynamics/shape.ce.js.map +1 -0
- package/dist/components/dynamics/text.ce.js +28 -41
- package/dist/components/dynamics/text.ce.js.map +1 -1
- package/dist/components/gui/box.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/mobile/index.d.ts +1 -1
- package/dist/components/gui/mobile/index.js.map +1 -1
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js.map +1 -1
- package/dist/components/player-components-utils.d.ts +67 -0
- package/dist/components/player-components-utils.js +162 -0
- package/dist/components/player-components-utils.js.map +1 -0
- package/dist/components/player-components-utils.spec.d.ts +1 -0
- package/dist/components/player-components.ce.js +188 -0
- package/dist/components/player-components.ce.js.map +1 -0
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/core/inject.js +1 -1
- package/dist/core/inject.js.map +1 -1
- package/dist/core/setup.js +1 -1
- package/dist/core/setup.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/module.js +4 -1
- package/dist/module.js.map +1 -1
- package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@2.10.0}/node_modules/@signe/di/dist/index.js +7 -117
- package/dist/node_modules/.pnpm/@signe_di@2.10.0/node_modules/@signe/di/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js +239 -0
- package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@2.10.0/node_modules/@signe/room/dist/index.js +611 -0
- package/dist/node_modules/.pnpm/@signe_room@2.10.0/node_modules/@signe/room/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/client/index.js +44 -0
- package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/client/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@2.10.0}/node_modules/@signe/sync/dist/index.js +29 -136
- package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
- package/dist/presets/animation.js.map +1 -1
- package/dist/presets/faceset.js.map +1 -1
- package/dist/presets/icon.js.map +1 -1
- package/dist/presets/lpc.js.map +1 -1
- package/dist/presets/rmspritesheet.js.map +1 -1
- package/dist/services/AbstractSocket.js.map +1 -1
- package/dist/services/keyboardControls.js.map +1 -1
- package/dist/services/loadMap.d.ts +6 -0
- package/dist/services/loadMap.js +1 -1
- package/dist/services/loadMap.js.map +1 -1
- package/dist/services/mmorpg.js +1 -1
- package/dist/services/mmorpg.js.map +1 -1
- package/dist/services/save.js.map +1 -1
- package/dist/services/standalone.js +1 -1
- package/dist/services/standalone.js.map +1 -1
- package/dist/utils/getEntityProp.js.map +1 -1
- package/package.json +8 -8
- package/src/Game/AnimationManager.spec.ts +30 -0
- package/src/Game/AnimationManager.ts +22 -10
- package/src/Game/Map.ts +12 -2
- package/src/Game/Object.ts +68 -43
- package/src/Gui/Gui.spec.ts +273 -0
- package/src/Gui/Gui.ts +105 -50
- package/src/Resource.ts +1 -2
- package/src/RpgClient.ts +63 -2
- package/src/RpgClientEngine.ts +82 -12
- package/src/components/character.ce +353 -1
- package/src/components/dynamics/bar.ce +87 -0
- package/src/components/dynamics/image.ce +20 -0
- package/src/components/dynamics/parse-value.spec.ts +41 -0
- package/src/components/dynamics/parse-value.ts +102 -37
- package/src/components/dynamics/shape-utils.spec.ts +46 -0
- package/src/components/dynamics/shape-utils.ts +61 -0
- package/src/components/dynamics/shape.ce +89 -0
- package/src/components/dynamics/text.ce +34 -149
- package/src/components/player-components-utils.spec.ts +109 -0
- package/src/components/player-components-utils.ts +205 -0
- package/src/components/player-components.ce +221 -0
- package/src/core/setup.ts +2 -2
- package/src/module.ts +5 -1
- package/src/services/loadMap.ts +2 -0
- package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
package/src/Game/Object.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { Hooks, ModulesToken, RpgCommonPlayer } from "@rpgjs/common";
|
|
2
|
-
import { trigger, signal,
|
|
3
|
-
import {
|
|
2
|
+
import { trigger, signal, type Trigger } from "canvasengine";
|
|
3
|
+
import { from, map, of, Subscription, switchMap } from "rxjs";
|
|
4
4
|
import { inject } from "../core/inject";
|
|
5
5
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
6
|
-
import TextComponent from "../components/dynamics/text.ce";
|
|
7
|
-
|
|
8
|
-
const DYNAMIC_COMPONENTS = {
|
|
9
|
-
text: TextComponent,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
type Frame = { x: number; y: number; ts: number };
|
|
13
7
|
|
|
14
8
|
type AnimationRestoreOptions = {
|
|
15
9
|
restoreAnimationName?: string;
|
|
16
10
|
restoreGraphics?: any[];
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type FlashType = 'alpha' | 'tint' | 'both';
|
|
15
|
+
|
|
16
|
+
type FlashOptions = {
|
|
17
|
+
type?: FlashType;
|
|
18
|
+
duration?: number;
|
|
19
|
+
cycles?: number;
|
|
20
|
+
alpha?: number;
|
|
21
|
+
tint?: number | string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type FlashTriggerOptions = Omit<FlashOptions, "tint"> & {
|
|
25
|
+
tint: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type ConfigurableTrigger<T> = Omit<Trigger<T>, "start"> & {
|
|
29
|
+
start(config?: T): Promise<void>;
|
|
17
30
|
};
|
|
18
31
|
|
|
19
32
|
export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
@@ -25,8 +38,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
25
38
|
_param = signal({});
|
|
26
39
|
frames: Frame[] = [];
|
|
27
40
|
graphicsSignals = signal<any[]>([]);
|
|
28
|
-
|
|
29
|
-
flashTrigger = trigger();
|
|
41
|
+
flashTrigger: ConfigurableTrigger<FlashTriggerOptions> = trigger<FlashTriggerOptions>();
|
|
30
42
|
private animationRestoreState?: {
|
|
31
43
|
animationName: string;
|
|
32
44
|
graphics: any[];
|
|
@@ -57,24 +69,6 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
57
69
|
this.graphicsSignals.set(sheets);
|
|
58
70
|
});
|
|
59
71
|
|
|
60
|
-
this.componentsTop.observable
|
|
61
|
-
.pipe(
|
|
62
|
-
filter(value => value !== null && value !== undefined),
|
|
63
|
-
map((value) => typeof value === 'string' ? JSON.parse(value) : value),
|
|
64
|
-
)
|
|
65
|
-
.subscribe(({components}) => {
|
|
66
|
-
for (const component of components) {
|
|
67
|
-
for (const [key, value] of Object.entries(component)) {
|
|
68
|
-
this._component = value as any; // temporary component memory
|
|
69
|
-
console.log(value)
|
|
70
|
-
const type = (value as any).type as keyof typeof DYNAMIC_COMPONENTS;
|
|
71
|
-
if (DYNAMIC_COMPONENTS[type]) {
|
|
72
|
-
this.engine.addSpriteComponentInFront(DYNAMIC_COMPONENTS[type]);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
72
|
this.engine.tick
|
|
79
73
|
.pipe
|
|
80
74
|
//throttleTime(10)
|
|
@@ -113,6 +107,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
113
107
|
|
|
114
108
|
private animationSubscription?: Subscription;
|
|
115
109
|
private animationResetTimeout?: ReturnType<typeof setTimeout>;
|
|
110
|
+
private animationWaitResolve?: () => void;
|
|
116
111
|
|
|
117
112
|
private clearAnimationControls() {
|
|
118
113
|
if (this.animationSubscription) {
|
|
@@ -125,6 +120,12 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
125
120
|
}
|
|
126
121
|
}
|
|
127
122
|
|
|
123
|
+
private resolveAnimationWait() {
|
|
124
|
+
const resolve = this.animationWaitResolve;
|
|
125
|
+
this.animationWaitResolve = undefined;
|
|
126
|
+
resolve?.();
|
|
127
|
+
}
|
|
128
|
+
|
|
128
129
|
private finishTemporaryAnimation() {
|
|
129
130
|
const restoreState = this.animationRestoreState;
|
|
130
131
|
this.clearAnimationControls();
|
|
@@ -135,6 +136,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
135
136
|
}
|
|
136
137
|
this.animationRestoreState = undefined;
|
|
137
138
|
this.animationIsPlaying.set(false);
|
|
139
|
+
this.resolveAnimationWait();
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
/**
|
|
@@ -183,13 +185,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
183
185
|
* });
|
|
184
186
|
* ```
|
|
185
187
|
*/
|
|
186
|
-
flash(options?: {
|
|
187
|
-
type?: 'alpha' | 'tint' | 'both';
|
|
188
|
-
duration?: number;
|
|
189
|
-
cycles?: number;
|
|
190
|
-
alpha?: number;
|
|
191
|
-
tint?: number | string;
|
|
192
|
-
}): void {
|
|
188
|
+
flash(options?: FlashOptions): void {
|
|
193
189
|
const flashOptions = {
|
|
194
190
|
type: options?.type || 'alpha',
|
|
195
191
|
duration: options?.duration ?? 300,
|
|
@@ -241,6 +237,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
241
237
|
this.animationIsPlaying.set(false);
|
|
242
238
|
this.animationCurrentIndex.set(0);
|
|
243
239
|
this.clearAnimationControls();
|
|
240
|
+
this.resolveAnimationWait();
|
|
244
241
|
}
|
|
245
242
|
|
|
246
243
|
/**
|
|
@@ -252,17 +249,19 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
252
249
|
*
|
|
253
250
|
* @param animationName - Name of the animation to play
|
|
254
251
|
* @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)
|
|
252
|
+
* @param options - Restore and timeout options
|
|
253
|
+
* @returns A promise resolved when a finite animation finishes, is interrupted, or times out
|
|
255
254
|
*
|
|
256
255
|
* @example
|
|
257
256
|
* ```ts
|
|
258
257
|
* // Play attack animation 3 times
|
|
259
|
-
* player.setAnimation('attack', 3);
|
|
258
|
+
* await player.setAnimation('attack', 3);
|
|
260
259
|
*
|
|
261
260
|
* // Play continuous spell animation
|
|
262
261
|
* player.setAnimation('spell');
|
|
263
262
|
* ```
|
|
264
263
|
*/
|
|
265
|
-
setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): void
|
|
264
|
+
setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;
|
|
266
265
|
/**
|
|
267
266
|
* Set a custom animation with temporary graphic change
|
|
268
267
|
*
|
|
@@ -273,20 +272,22 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
273
272
|
* @param animationName - Name of the animation to play
|
|
274
273
|
* @param graphic - The graphic(s) to temporarily use during the animation
|
|
275
274
|
* @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)
|
|
275
|
+
* @param options - Restore and timeout options
|
|
276
|
+
* @returns A promise resolved when a finite animation finishes, is interrupted, or times out
|
|
276
277
|
*
|
|
277
278
|
* @example
|
|
278
279
|
* ```ts
|
|
279
280
|
* // Play attack animation with temporary graphic change
|
|
280
|
-
* player.setAnimation('attack', 'hero_attack', 3);
|
|
281
|
+
* await player.setAnimation('attack', 'hero_attack', 3);
|
|
281
282
|
* ```
|
|
282
283
|
*/
|
|
283
|
-
setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): void
|
|
284
|
+
setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;
|
|
284
285
|
setAnimation(
|
|
285
286
|
animationName: string,
|
|
286
287
|
graphicOrNbTimes?: string | string[] | number,
|
|
287
288
|
nbTimesOrOptions?: number | AnimationRestoreOptions,
|
|
288
289
|
options?: AnimationRestoreOptions
|
|
289
|
-
): void {
|
|
290
|
+
): Promise<void> {
|
|
290
291
|
let graphic: string | string[] | undefined;
|
|
291
292
|
let finalNbTimes: number = Infinity;
|
|
292
293
|
let restoreOptions: AnimationRestoreOptions | undefined = options;
|
|
@@ -314,6 +315,13 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
314
315
|
this.finishTemporaryAnimation();
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
const waitPromise =
|
|
319
|
+
finalNbTimes === Infinity
|
|
320
|
+
? Promise.resolve()
|
|
321
|
+
: new Promise<void>((resolve) => {
|
|
322
|
+
this.animationWaitResolve = resolve;
|
|
323
|
+
});
|
|
324
|
+
|
|
317
325
|
this.animationIsPlaying.set(true);
|
|
318
326
|
const previousAnimationName =
|
|
319
327
|
restoreOptions?.restoreAnimationName ?? this.animationName();
|
|
@@ -349,10 +357,12 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
349
357
|
if (this.animationIsPlaying()) {
|
|
350
358
|
this.finishTemporaryAnimation();
|
|
351
359
|
}
|
|
352
|
-
}, Math.max(1000, finalNbTimes * 1000));
|
|
360
|
+
}, restoreOptions?.timeoutMs ?? Math.max(1000, finalNbTimes * 1000));
|
|
353
361
|
}
|
|
354
362
|
|
|
355
363
|
this.animationName.set(animationName);
|
|
364
|
+
|
|
365
|
+
return waitPromise;
|
|
356
366
|
}
|
|
357
367
|
|
|
358
368
|
/**
|
|
@@ -360,10 +370,25 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
360
370
|
*
|
|
361
371
|
* @param id - Identifier of the component animation to play.
|
|
362
372
|
* @param params - Parameters forwarded to the animation effect.
|
|
373
|
+
* @returns A promise resolved when the animation component calls `onFinish`.
|
|
363
374
|
*/
|
|
364
|
-
showComponentAnimation(id: string, params: any) {
|
|
375
|
+
showComponentAnimation(id: string, params: any): Promise<void> {
|
|
365
376
|
const engine = inject(RpgClientEngine);
|
|
366
|
-
engine.getComponentAnimation(id).displayEffect(params, this);
|
|
377
|
+
return engine.getComponentAnimation(id).displayEffect(params, this);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Display a registered spritesheet animation effect on this object.
|
|
382
|
+
*
|
|
383
|
+
* @param graphic - Identifier of the spritesheet to use.
|
|
384
|
+
* @param animationName - Name of the animation inside the spritesheet.
|
|
385
|
+
* @returns A promise resolved when the animation component calls `onFinish`.
|
|
386
|
+
*/
|
|
387
|
+
showAnimation(graphic: string, animationName: string = 'default'): Promise<void> {
|
|
388
|
+
return this.showComponentAnimation('animation', {
|
|
389
|
+
graphic,
|
|
390
|
+
animationName,
|
|
391
|
+
});
|
|
367
392
|
}
|
|
368
393
|
|
|
369
394
|
/**
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { Context, injector } from "@signe/di";
|
|
3
|
+
import { signal } from "canvasengine";
|
|
4
|
+
import { PrebuiltGui } from "@rpgjs/common";
|
|
5
|
+
import { WebSocketToken } from "../services/AbstractSocket";
|
|
6
|
+
|
|
7
|
+
vi.mock("../components/gui", () => {
|
|
8
|
+
const component = () => null;
|
|
9
|
+
return {
|
|
10
|
+
DialogboxComponent: component,
|
|
11
|
+
ShopComponent: component,
|
|
12
|
+
SaveLoadComponent: component,
|
|
13
|
+
MainMenuComponent: component,
|
|
14
|
+
NotificationComponent: component,
|
|
15
|
+
TitleScreenComponent: component,
|
|
16
|
+
GameoverComponent: component,
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const createGui = async () => {
|
|
21
|
+
const { RpgGui } = await import("./Gui");
|
|
22
|
+
const context = new Context();
|
|
23
|
+
const socket = {
|
|
24
|
+
on: vi.fn(),
|
|
25
|
+
emit: vi.fn(),
|
|
26
|
+
};
|
|
27
|
+
await injector(context, [
|
|
28
|
+
{
|
|
29
|
+
provide: WebSocketToken,
|
|
30
|
+
useValue: socket,
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
gui: new RpgGui(context),
|
|
35
|
+
socket,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const CanvasGui = () => null;
|
|
40
|
+
const VueInventory = {
|
|
41
|
+
name: "inventory",
|
|
42
|
+
render() {
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
const VueDialog = {
|
|
47
|
+
name: PrebuiltGui.Dialog,
|
|
48
|
+
render() {
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const VueMainMenu = {
|
|
53
|
+
name: PrebuiltGui.MainMenu,
|
|
54
|
+
render() {
|
|
55
|
+
return null;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const VueTooltip = {
|
|
59
|
+
name: "tooltip",
|
|
60
|
+
rpgAttachToSprite: true,
|
|
61
|
+
render() {
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
describe("RpgGui Vue integration", () => {
|
|
67
|
+
test("separates CanvasEngine and Vue GUI registries", async () => {
|
|
68
|
+
const { gui } = await createGui();
|
|
69
|
+
|
|
70
|
+
gui.add({
|
|
71
|
+
id: "canvas-tooltip",
|
|
72
|
+
component: CanvasGui,
|
|
73
|
+
attachToSprite: true,
|
|
74
|
+
});
|
|
75
|
+
gui.add({
|
|
76
|
+
id: "inventory",
|
|
77
|
+
component: VueInventory,
|
|
78
|
+
});
|
|
79
|
+
gui.add(VueTooltip);
|
|
80
|
+
|
|
81
|
+
expect(gui.get("canvas-tooltip")?.component).toBe(CanvasGui);
|
|
82
|
+
expect(gui.get("inventory")?.component).toBe(VueInventory);
|
|
83
|
+
expect(gui.get("tooltip")?.component).toBe(VueTooltip);
|
|
84
|
+
expect(gui.getAttachedGuis().map(item => item.name)).toEqual(["canvas-tooltip"]);
|
|
85
|
+
expect(gui.getAttachedVueGuis().map(item => item.name)).toEqual(["tooltip"]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("synchronizes Vue GUI display and hide states through the Vue bridge", async () => {
|
|
89
|
+
const { gui } = await createGui();
|
|
90
|
+
const bridge = {
|
|
91
|
+
updateGuiState: vi.fn(),
|
|
92
|
+
initializeGuiStates: vi.fn(),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
gui.add({
|
|
96
|
+
id: "inventory",
|
|
97
|
+
component: VueInventory,
|
|
98
|
+
});
|
|
99
|
+
gui._setVueGuiInstance(bridge);
|
|
100
|
+
|
|
101
|
+
expect(bridge.initializeGuiStates).toHaveBeenCalledWith([
|
|
102
|
+
expect.objectContaining({
|
|
103
|
+
name: "inventory",
|
|
104
|
+
display: false,
|
|
105
|
+
data: {},
|
|
106
|
+
attachToSprite: false,
|
|
107
|
+
}),
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
gui.display("inventory", { gold: 12 });
|
|
111
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
name: "inventory",
|
|
114
|
+
display: true,
|
|
115
|
+
data: { gold: 12 },
|
|
116
|
+
attachToSprite: false,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
gui.hide("inventory");
|
|
121
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
122
|
+
expect.objectContaining({
|
|
123
|
+
name: "inventory",
|
|
124
|
+
display: false,
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("waits for Vue GUI dependencies before display", async () => {
|
|
130
|
+
const { gui } = await createGui();
|
|
131
|
+
const bridge = {
|
|
132
|
+
updateGuiState: vi.fn(),
|
|
133
|
+
initializeGuiStates: vi.fn(),
|
|
134
|
+
};
|
|
135
|
+
const dependency = signal<any>(undefined);
|
|
136
|
+
|
|
137
|
+
gui.add({
|
|
138
|
+
id: "inventory",
|
|
139
|
+
component: VueInventory,
|
|
140
|
+
dependencies: () => [dependency],
|
|
141
|
+
});
|
|
142
|
+
gui._setVueGuiInstance(bridge);
|
|
143
|
+
gui.display("inventory", { items: ["potion"] });
|
|
144
|
+
|
|
145
|
+
expect(gui.isDisplaying("inventory")).toBe(false);
|
|
146
|
+
expect(bridge.updateGuiState).not.toHaveBeenCalledWith(
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
display: true,
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
dependency.set({ id: "player" });
|
|
153
|
+
|
|
154
|
+
expect(gui.isDisplaying("inventory")).toBe(true);
|
|
155
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
156
|
+
expect.objectContaining({
|
|
157
|
+
name: "inventory",
|
|
158
|
+
display: true,
|
|
159
|
+
data: { items: ["potion"] },
|
|
160
|
+
}),
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("allows Vue GUI entries to replace prebuilt CanvasEngine GUIs", async () => {
|
|
165
|
+
const { gui } = await createGui();
|
|
166
|
+
const bridge = {
|
|
167
|
+
updateGuiState: vi.fn(),
|
|
168
|
+
initializeGuiStates: vi.fn(),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
gui._setVueGuiInstance(bridge);
|
|
172
|
+
gui.add({
|
|
173
|
+
id: PrebuiltGui.Dialog,
|
|
174
|
+
component: VueDialog,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(gui.get(PrebuiltGui.Dialog)?.component).toBe(VueDialog);
|
|
178
|
+
expect(gui.getAll()[PrebuiltGui.Dialog].component).toBe(VueDialog);
|
|
179
|
+
expect((gui as any).gui()[PrebuiltGui.Dialog]).toBeUndefined();
|
|
180
|
+
expect(gui.getVueGuis().filter(item => item.name === PrebuiltGui.Dialog)).toHaveLength(1);
|
|
181
|
+
|
|
182
|
+
gui.display(PrebuiltGui.Dialog, { text: "Hello" });
|
|
183
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
184
|
+
expect.objectContaining({
|
|
185
|
+
name: PrebuiltGui.Dialog,
|
|
186
|
+
display: true,
|
|
187
|
+
data: { text: "Hello" },
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
gui.hide(PrebuiltGui.Dialog);
|
|
192
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
193
|
+
expect.objectContaining({
|
|
194
|
+
name: PrebuiltGui.Dialog,
|
|
195
|
+
display: false,
|
|
196
|
+
}),
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("allows CanvasEngine GUI entries to replace Vue GUI entries with the same id", async () => {
|
|
201
|
+
const { gui } = await createGui();
|
|
202
|
+
const bridge = {
|
|
203
|
+
updateGuiState: vi.fn(),
|
|
204
|
+
initializeGuiStates: vi.fn(),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
gui._setVueGuiInstance(bridge);
|
|
208
|
+
gui.add({
|
|
209
|
+
id: PrebuiltGui.Dialog,
|
|
210
|
+
component: VueDialog,
|
|
211
|
+
});
|
|
212
|
+
gui.add({
|
|
213
|
+
id: PrebuiltGui.Dialog,
|
|
214
|
+
component: CanvasGui,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(gui.get(PrebuiltGui.Dialog)?.component).toBe(CanvasGui);
|
|
218
|
+
expect(gui.getVueGuis().some(item => item.name === PrebuiltGui.Dialog)).toBe(false);
|
|
219
|
+
expect((gui as any).gui()[PrebuiltGui.Dialog].component).toBe(CanvasGui);
|
|
220
|
+
expect(bridge.initializeGuiStates).toHaveBeenLastCalledWith([]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("keeps main menu optimistic reducers when a Vue GUI replaces the prebuilt component", async () => {
|
|
224
|
+
const { gui, socket } = await createGui();
|
|
225
|
+
const bridge = {
|
|
226
|
+
updateGuiState: vi.fn(),
|
|
227
|
+
initializeGuiStates: vi.fn(),
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
gui.add({
|
|
231
|
+
id: PrebuiltGui.MainMenu,
|
|
232
|
+
component: VueMainMenu,
|
|
233
|
+
});
|
|
234
|
+
gui._setVueGuiInstance(bridge);
|
|
235
|
+
gui.display(PrebuiltGui.MainMenu, {
|
|
236
|
+
items: [
|
|
237
|
+
{
|
|
238
|
+
id: "potion",
|
|
239
|
+
quantity: 2,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
gui.guiInteraction(PrebuiltGui.MainMenu, "useItem", { id: "potion" });
|
|
245
|
+
|
|
246
|
+
expect(gui.get(PrebuiltGui.MainMenu)?.data().items).toEqual([
|
|
247
|
+
{
|
|
248
|
+
id: "potion",
|
|
249
|
+
quantity: 1,
|
|
250
|
+
},
|
|
251
|
+
]);
|
|
252
|
+
expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
|
|
253
|
+
expect.objectContaining({
|
|
254
|
+
name: PrebuiltGui.MainMenu,
|
|
255
|
+
data: {
|
|
256
|
+
items: [
|
|
257
|
+
{
|
|
258
|
+
id: "potion",
|
|
259
|
+
quantity: 1,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
expect(socket.emit).toHaveBeenCalledWith(
|
|
266
|
+
"gui.interaction",
|
|
267
|
+
expect.objectContaining({
|
|
268
|
+
guiId: PrebuiltGui.MainMenu,
|
|
269
|
+
name: "useItem",
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
});
|