@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/Gui/Gui.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { signal, Signal, WritableSignal } from "canvasengine";
|
|
|
3
3
|
import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
|
|
4
4
|
import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent } from "../components/gui";
|
|
5
5
|
import { combineLatest, Subscription } from "rxjs";
|
|
6
|
-
import {
|
|
6
|
+
import { PrebuiltGui } from "@rpgjs/common";
|
|
7
7
|
|
|
8
8
|
interface GuiOptions {
|
|
9
9
|
name?: string;
|
|
10
10
|
id?: string;
|
|
11
|
-
component
|
|
11
|
+
component?: any;
|
|
12
12
|
display?: boolean;
|
|
13
13
|
data?: any;
|
|
14
14
|
/**
|
|
@@ -28,19 +28,36 @@ interface GuiOptions {
|
|
|
28
28
|
* @default false
|
|
29
29
|
*/
|
|
30
30
|
attachToSprite?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.
|
|
33
|
+
*/
|
|
34
|
+
rpgAttachToSprite?: boolean;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
interface GuiInstance {
|
|
37
|
+
export interface GuiInstance {
|
|
34
38
|
name: string;
|
|
35
39
|
component: any;
|
|
36
40
|
display: WritableSignal<boolean>;
|
|
37
41
|
data: WritableSignal<any>;
|
|
38
42
|
autoDisplay: boolean;
|
|
39
|
-
dependencies?:
|
|
43
|
+
dependencies?: Signal[];
|
|
40
44
|
subscription?: Subscription;
|
|
41
45
|
attachToSprite?: boolean;
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
type GuiState = {
|
|
49
|
+
name: string;
|
|
50
|
+
component: any;
|
|
51
|
+
display: boolean;
|
|
52
|
+
data: any;
|
|
53
|
+
attachToSprite: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type VueGuiBridge = {
|
|
57
|
+
updateGuiState?: (state: GuiState) => void;
|
|
58
|
+
initializeGuiStates?: (states: GuiState[]) => void;
|
|
59
|
+
};
|
|
60
|
+
|
|
44
61
|
interface GuiAction {
|
|
45
62
|
guiId: string;
|
|
46
63
|
name: string;
|
|
@@ -117,7 +134,7 @@ export class RpgGui {
|
|
|
117
134
|
private webSocket: AbstractWebsocket;
|
|
118
135
|
gui = signal<Record<string, GuiInstance>>({});
|
|
119
136
|
extraGuis: GuiInstance[] = [];
|
|
120
|
-
private vueGuiInstance:
|
|
137
|
+
private vueGuiInstance: VueGuiBridge | null = null;
|
|
121
138
|
private optimisticReducers = new Map<string, OptimisticReducer[]>();
|
|
122
139
|
private pendingActions = new Map<string, GuiAction[]>();
|
|
123
140
|
/**
|
|
@@ -196,6 +213,7 @@ export class RpgGui {
|
|
|
196
213
|
*/
|
|
197
214
|
_setVueGuiInstance(vueGuiInstance: any) {
|
|
198
215
|
this.vueGuiInstance = vueGuiInstance;
|
|
216
|
+
this._initializeVueComponents();
|
|
199
217
|
}
|
|
200
218
|
|
|
201
219
|
/**
|
|
@@ -207,21 +225,9 @@ export class RpgGui {
|
|
|
207
225
|
* @param data - Component data
|
|
208
226
|
*/
|
|
209
227
|
private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (extraGui) {
|
|
214
|
-
// Update the Vue component's display state and data
|
|
215
|
-
this.vueGuiInstance.vm.gui[guiId] = {
|
|
216
|
-
name: guiId,
|
|
217
|
-
display,
|
|
218
|
-
data,
|
|
219
|
-
attachToSprite: extraGui.attachToSprite || false
|
|
220
|
-
};
|
|
221
|
-
// Trigger Vue reactivity
|
|
222
|
-
this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
228
|
+
const extraGui = this.extraGuis.find(gui => gui.name === guiId);
|
|
229
|
+
if (!extraGui) return;
|
|
230
|
+
this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));
|
|
225
231
|
}
|
|
226
232
|
|
|
227
233
|
/**
|
|
@@ -229,20 +235,9 @@ export class RpgGui {
|
|
|
229
235
|
* This should be called after VueGui is mounted
|
|
230
236
|
*/
|
|
231
237
|
_initializeVueComponents() {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
this.vueGuiInstance.vm.gui[gui.name] = {
|
|
236
|
-
name: gui.name,
|
|
237
|
-
display: gui.display(),
|
|
238
|
-
data: gui.data(),
|
|
239
|
-
attachToSprite: gui.attachToSprite || false
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Trigger Vue reactivity
|
|
244
|
-
this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
|
|
245
|
-
}
|
|
238
|
+
this.vueGuiInstance?.initializeGuiStates?.(
|
|
239
|
+
this.extraGuis.map(gui => this.toGuiState(gui))
|
|
240
|
+
);
|
|
246
241
|
}
|
|
247
242
|
|
|
248
243
|
guiInteraction(guiId: string, name: string, data: any) {
|
|
@@ -301,35 +296,46 @@ export class RpgGui {
|
|
|
301
296
|
* });
|
|
302
297
|
* ```
|
|
303
298
|
*/
|
|
304
|
-
add(gui: GuiOptions) {
|
|
305
|
-
const
|
|
299
|
+
add(gui: GuiOptions | any) {
|
|
300
|
+
const component = this.resolveComponent(gui);
|
|
301
|
+
const guiId = this.resolveGuiId(gui, component);
|
|
306
302
|
if (!guiId) {
|
|
307
303
|
throw new Error("GUI must have a name or id");
|
|
308
304
|
}
|
|
305
|
+
const attachToSprite = this.resolveAttachToSprite(gui, component);
|
|
309
306
|
const guiInstance: GuiInstance = {
|
|
310
307
|
name: guiId,
|
|
311
|
-
component
|
|
312
|
-
display: signal(gui.display || false),
|
|
313
|
-
data: signal(gui.data || {}),
|
|
308
|
+
component,
|
|
309
|
+
display: signal<boolean>(gui.display || false),
|
|
310
|
+
data: signal<any>(gui.data || {}),
|
|
314
311
|
autoDisplay: gui.autoDisplay || false,
|
|
315
312
|
dependencies: gui.dependencies ? gui.dependencies() : [],
|
|
316
|
-
attachToSprite
|
|
313
|
+
attachToSprite,
|
|
317
314
|
};
|
|
318
315
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
316
|
+
if (this.isVueComponentInstance(guiInstance)) {
|
|
317
|
+
this.removeCanvasGui(guiId);
|
|
318
|
+
const existingIndex = this.extraGuis.findIndex(existing => existing.name === guiId);
|
|
319
|
+
if (existingIndex >= 0) {
|
|
320
|
+
this.extraGuis[existingIndex].subscription?.unsubscribe();
|
|
321
|
+
this.extraGuis[existingIndex] = guiInstance;
|
|
322
|
+
} else {
|
|
323
|
+
this.extraGuis.push(guiInstance);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this._initializeVueComponents();
|
|
324
327
|
|
|
325
|
-
// Auto display Vue components if enabled
|
|
326
328
|
if (guiInstance.autoDisplay) {
|
|
327
|
-
this.
|
|
329
|
+
this.display(guiId, gui.data);
|
|
330
|
+
} else {
|
|
331
|
+
this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());
|
|
328
332
|
}
|
|
329
333
|
return;
|
|
330
334
|
}
|
|
331
335
|
|
|
336
|
+
this.removeVueGui(guiId);
|
|
332
337
|
this.gui()[guiId] = guiInstance;
|
|
338
|
+
this._initializeVueComponents();
|
|
333
339
|
|
|
334
340
|
// Auto display if enabled and it's a CanvasEngine component
|
|
335
341
|
if (guiInstance.autoDisplay && typeof gui.component === 'function') {
|
|
@@ -357,8 +363,15 @@ export class RpgGui {
|
|
|
357
363
|
* ```
|
|
358
364
|
*/
|
|
359
365
|
getAttachedGuis(): GuiInstance[] {
|
|
360
|
-
|
|
361
|
-
|
|
366
|
+
return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
getVueGuis(): GuiInstance[] {
|
|
370
|
+
return [...this.extraGuis];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
getAttachedVueGuis(): GuiInstance[] {
|
|
374
|
+
return this.extraGuis.filter(gui => gui.attachToSprite === true);
|
|
362
375
|
}
|
|
363
376
|
|
|
364
377
|
/**
|
|
@@ -461,7 +474,7 @@ export class RpgGui {
|
|
|
461
474
|
// Use runtime dependencies or config dependencies
|
|
462
475
|
const deps = dependencies.length > 0
|
|
463
476
|
? dependencies
|
|
464
|
-
: (guiInstance.dependencies
|
|
477
|
+
: (guiInstance.dependencies ?? []);
|
|
465
478
|
|
|
466
479
|
if (deps.length > 0) {
|
|
467
480
|
// Subscribe to dependencies
|
|
@@ -522,6 +535,48 @@ export class RpgGui {
|
|
|
522
535
|
return this.extraGuis.some(gui => gui.name === id);
|
|
523
536
|
}
|
|
524
537
|
|
|
538
|
+
private isVueComponentInstance(gui: GuiInstance) {
|
|
539
|
+
return typeof gui.component !== "function";
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
private removeCanvasGui(guiId: string) {
|
|
543
|
+
const current = this.gui();
|
|
544
|
+
if (!(guiId in current)) return;
|
|
545
|
+
const next = { ...current };
|
|
546
|
+
delete next[guiId];
|
|
547
|
+
this.gui.set(next);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private removeVueGui(guiId: string) {
|
|
551
|
+
const removed = this.extraGuis.filter(existing => existing.name === guiId);
|
|
552
|
+
removed.forEach(gui => gui.subscription?.unsubscribe());
|
|
553
|
+
if (removed.length > 0) {
|
|
554
|
+
this.extraGuis = this.extraGuis.filter(existing => existing.name !== guiId);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private resolveComponent(gui: GuiOptions | any) {
|
|
559
|
+
return gui?.component ?? gui;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
private resolveGuiId(gui: GuiOptions | any, component: any) {
|
|
563
|
+
return gui?.name || gui?.id || component?.name || component?.__name;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private resolveAttachToSprite(gui: GuiOptions | any, component: any) {
|
|
567
|
+
return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {
|
|
571
|
+
return {
|
|
572
|
+
name: gui.name,
|
|
573
|
+
component: gui.component,
|
|
574
|
+
display,
|
|
575
|
+
data,
|
|
576
|
+
attachToSprite: gui.attachToSprite || false,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
525
580
|
private clearPendingActions(guiId: string) {
|
|
526
581
|
this.pendingActions.delete(guiId);
|
|
527
582
|
}
|
package/src/Resource.ts
CHANGED
|
@@ -64,7 +64,7 @@ export class RpgResource {
|
|
|
64
64
|
// Extract image path from spritesheet
|
|
65
65
|
const imageLink = spritesheet?.image || spritesheet?.imageSource || undefined;
|
|
66
66
|
if (imageLink) {
|
|
67
|
-
RpgResource._spritesheets.set(id, imageLink);
|
|
67
|
+
RpgResource._spritesheets.set(String(id), imageLink);
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -147,4 +147,3 @@ export class RpgResource {
|
|
|
147
147
|
return RpgResource._sounds;
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
|
package/src/RpgClient.ts
CHANGED
|
@@ -7,6 +7,24 @@ import { type MapPhysicsEntityContext, type MapPhysicsInitContext } from '@rpgjs
|
|
|
7
7
|
type RpgClass<T = any> = new (...args: any[]) => T
|
|
8
8
|
type RpgComponent = RpgClientObject
|
|
9
9
|
type SceneMap = Container
|
|
10
|
+
export type SpriteComponentConfig = ComponentFunction | {
|
|
11
|
+
component: ComponentFunction
|
|
12
|
+
props?: Record<string, any> | ((object: RpgClientObject) => Record<string, any>)
|
|
13
|
+
data?: Record<string, any> | ((object: RpgClientObject) => Record<string, any>)
|
|
14
|
+
dependencies?: (object: RpgClientObject) => any[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RpgSpriteBeforeRemoveContext {
|
|
18
|
+
reason?: string
|
|
19
|
+
data?: any
|
|
20
|
+
transition?: {
|
|
21
|
+
animation?: string
|
|
22
|
+
graphic?: string | string[]
|
|
23
|
+
duration?: number
|
|
24
|
+
effect?: string
|
|
25
|
+
}
|
|
26
|
+
timeoutMs?: number
|
|
27
|
+
}
|
|
10
28
|
|
|
11
29
|
export interface RpgClientEngineHooks {
|
|
12
30
|
/**
|
|
@@ -81,7 +99,7 @@ export interface RpgSpriteHooks {
|
|
|
81
99
|
* }
|
|
82
100
|
* ```
|
|
83
101
|
*/
|
|
84
|
-
componentsBehind?:
|
|
102
|
+
componentsBehind?: SpriteComponentConfig[]
|
|
85
103
|
|
|
86
104
|
/**
|
|
87
105
|
* Array of components to render in front of the sprite
|
|
@@ -96,7 +114,28 @@ export interface RpgSpriteHooks {
|
|
|
96
114
|
* }
|
|
97
115
|
* ```
|
|
98
116
|
*/
|
|
99
|
-
componentsInFront?:
|
|
117
|
+
componentsInFront?: SpriteComponentConfig[]
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Reusable sprite components addressable by server-side component definitions.
|
|
121
|
+
*
|
|
122
|
+
* The server sends only the component id and serializable props. The client
|
|
123
|
+
* registry maps that id to the CanvasEngine component that renders it.
|
|
124
|
+
*
|
|
125
|
+
* @prop {Record<string, ComponentFunction>} [components]
|
|
126
|
+
* @memberof RpgSpriteHooks
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* import GuildBadge from './components/guild-badge.ce'
|
|
130
|
+
*
|
|
131
|
+
* const sprite: RpgSpriteHooks = {
|
|
132
|
+
* components: {
|
|
133
|
+
* guildBadge: GuildBadge
|
|
134
|
+
* }
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
components?: Record<string, ComponentFunction>
|
|
100
139
|
|
|
101
140
|
/**
|
|
102
141
|
* As soon as the sprite is initialized
|
|
@@ -114,6 +153,21 @@ export interface RpgSpriteHooks {
|
|
|
114
153
|
*/
|
|
115
154
|
onDestroy?: (sprite: RpgComponent) => any
|
|
116
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Called when a sprite removal is requested, before it disappears from the scene.
|
|
158
|
+
*
|
|
159
|
+
* Return a promise to keep the sprite visible while an animation, effect, or
|
|
160
|
+
* sound transition is running. The server still owns gameplay removal and
|
|
161
|
+
* uses the timeout carried by the remove request as a safety limit.
|
|
162
|
+
*
|
|
163
|
+
* @prop { (sprite: RpgSprite, context: RpgSpriteBeforeRemoveContext) => any } [onBeforeRemove]
|
|
164
|
+
* @memberof RpgSpriteHooks
|
|
165
|
+
*/
|
|
166
|
+
onBeforeRemove?: (
|
|
167
|
+
sprite: RpgComponent,
|
|
168
|
+
context: RpgSpriteBeforeRemoveContext
|
|
169
|
+
) => any
|
|
170
|
+
|
|
117
171
|
/**
|
|
118
172
|
* As soon as a data is changed on the server side (the name for example), you are able to know the new data but also the old data.
|
|
119
173
|
*
|
|
@@ -485,6 +539,13 @@ export interface RpgClient {
|
|
|
485
539
|
* ```
|
|
486
540
|
*/
|
|
487
541
|
attachToSprite?: boolean
|
|
542
|
+
/**
|
|
543
|
+
* Vue v4 compatibility alias for `attachToSprite`.
|
|
544
|
+
*
|
|
545
|
+
* Prefer `attachToSprite` in v5 projects. This is read by `@rpgjs/vue`
|
|
546
|
+
* for Vue GUI components migrated from the v4 GUI API.
|
|
547
|
+
*/
|
|
548
|
+
rpgAttachToSprite?: boolean
|
|
488
549
|
} | any)[],
|
|
489
550
|
|
|
490
551
|
/**
|
package/src/RpgClientEngine.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Canvas from "./components/scenes/canvas.ce";
|
|
2
2
|
import { inject } from './core/inject'
|
|
3
|
-
import { signal, bootstrapCanvas, Howl, trigger } from "canvasengine";
|
|
3
|
+
import { signal, bootstrapCanvas, Howl, trigger, type Trigger } from "canvasengine";
|
|
4
4
|
import { AbstractWebsocket, WebSocketToken } from "./services/AbstractSocket";
|
|
5
5
|
import { LoadMapService, LoadMapToken } from "./services/loadMap";
|
|
6
6
|
import { RpgSound } from "./Sound";
|
|
@@ -14,6 +14,10 @@ import { lastValueFrom, Observable, combineLatest, BehaviorSubject, filter, swit
|
|
|
14
14
|
import { GlobalConfigToken } from "./module";
|
|
15
15
|
import * as PIXI from "pixi.js";
|
|
16
16
|
import { PrebuiltComponentAnimations } from "./components/animations";
|
|
17
|
+
import TextComponent from "./components/dynamics/text.ce";
|
|
18
|
+
import BarComponent from "./components/dynamics/bar.ce";
|
|
19
|
+
import ShapeComponent from "./components/dynamics/shape.ce";
|
|
20
|
+
import ImageComponent from "./components/dynamics/image.ce";
|
|
17
21
|
import {
|
|
18
22
|
PredictionController,
|
|
19
23
|
type PredictionHistoryEntry,
|
|
@@ -32,6 +36,17 @@ interface MovementTrajectoryPoint {
|
|
|
32
36
|
direction?: Direction;
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
type ConfigurableTrigger<T> = Omit<Trigger<T>, "start"> & {
|
|
40
|
+
start(config?: T): Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type MapShakeOptions = {
|
|
44
|
+
intensity?: number;
|
|
45
|
+
duration?: number;
|
|
46
|
+
frequency?: number;
|
|
47
|
+
direction?: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
35
50
|
export class RpgClientEngine<T = any> {
|
|
36
51
|
private guiService: RpgGui;
|
|
37
52
|
private webSocket: AbstractWebsocket;
|
|
@@ -44,10 +59,10 @@ export class RpgClientEngine<T = any> {
|
|
|
44
59
|
stopProcessingInput = false;
|
|
45
60
|
width = signal("100%");
|
|
46
61
|
height = signal("100%");
|
|
47
|
-
spritesheets: Map<string, any> = new Map();
|
|
62
|
+
spritesheets: Map<string | number, any> = new Map();
|
|
48
63
|
sounds: Map<string, any> = new Map();
|
|
49
64
|
componentAnimations: any[] = [];
|
|
50
|
-
private spritesheetResolver?: (id: string) => any | Promise<any>;
|
|
65
|
+
private spritesheetResolver?: (id: string | number) => any | Promise<any>;
|
|
51
66
|
private soundResolver?: (id: string) => any | Promise<any>;
|
|
52
67
|
particleSettings: {
|
|
53
68
|
emitters: any[]
|
|
@@ -61,10 +76,11 @@ export class RpgClientEngine<T = any> {
|
|
|
61
76
|
playerIdSignal = signal<string | null>(null);
|
|
62
77
|
spriteComponentsBehind = signal<any[]>([]);
|
|
63
78
|
spriteComponentsInFront = signal<any[]>([]);
|
|
79
|
+
spriteComponents: Map<string, any> = new Map();
|
|
64
80
|
/** ID of the sprite that the camera should follow. null means follow the current player */
|
|
65
81
|
cameraFollowTargetId = signal<string | null>(null);
|
|
66
82
|
/** Trigger for map shake animation */
|
|
67
|
-
mapShakeTrigger = trigger();
|
|
83
|
+
mapShakeTrigger: ConfigurableTrigger<MapShakeOptions> = trigger<MapShakeOptions>();
|
|
68
84
|
|
|
69
85
|
controlsReady = signal(undefined);
|
|
70
86
|
gamePause = signal(false);
|
|
@@ -123,6 +139,13 @@ export class RpgClientEngine<T = any> {
|
|
|
123
139
|
component: PrebuiltComponentAnimations.Animation
|
|
124
140
|
})
|
|
125
141
|
|
|
142
|
+
this.registerSpriteComponent("rpg:text", TextComponent);
|
|
143
|
+
this.registerSpriteComponent("rpg:hpBar", BarComponent);
|
|
144
|
+
this.registerSpriteComponent("rpg:spBar", BarComponent);
|
|
145
|
+
this.registerSpriteComponent("rpg:bar", BarComponent);
|
|
146
|
+
this.registerSpriteComponent("rpg:shape", ShapeComponent);
|
|
147
|
+
this.registerSpriteComponent("rpg:image", ImageComponent);
|
|
148
|
+
|
|
126
149
|
this.predictionEnabled = (this.globalConfig as any)?.prediction?.enabled !== false;
|
|
127
150
|
this.initializePredictionController();
|
|
128
151
|
}
|
|
@@ -233,7 +256,7 @@ export class RpgClientEngine<T = any> {
|
|
|
233
256
|
|
|
234
257
|
await this.webSocket.connection(() => {
|
|
235
258
|
const saveClient = inject(SaveClientService);
|
|
236
|
-
saveClient.initialize(
|
|
259
|
+
saveClient.initialize();
|
|
237
260
|
this.initListeners()
|
|
238
261
|
this.guiService._initialize()
|
|
239
262
|
this.startPingPong();
|
|
@@ -425,7 +448,7 @@ export class RpgClientEngine<T = any> {
|
|
|
425
448
|
|
|
426
449
|
this.webSocket.on("shakeMap", (data) => {
|
|
427
450
|
const { intensity, duration, frequency, direction } = data || {};
|
|
428
|
-
|
|
451
|
+
this.mapShakeTrigger.start({
|
|
429
452
|
intensity,
|
|
430
453
|
duration,
|
|
431
454
|
frequency,
|
|
@@ -574,7 +597,7 @@ export class RpgClientEngine<T = any> {
|
|
|
574
597
|
})
|
|
575
598
|
await this.webSocket.reconnect(() => {
|
|
576
599
|
const saveClient = inject(SaveClientService);
|
|
577
|
-
saveClient.initialize(
|
|
600
|
+
saveClient.initialize();
|
|
578
601
|
this.initListeners()
|
|
579
602
|
this.guiService._initialize()
|
|
580
603
|
})
|
|
@@ -635,7 +658,7 @@ export class RpgClientEngine<T = any> {
|
|
|
635
658
|
* });
|
|
636
659
|
* ```
|
|
637
660
|
*/
|
|
638
|
-
setSpritesheetResolver(resolver: (id: string) => any | Promise<any>): void {
|
|
661
|
+
setSpritesheetResolver(resolver: (id: string | number) => any | Promise<any>): void {
|
|
639
662
|
this.spritesheetResolver = resolver;
|
|
640
663
|
}
|
|
641
664
|
|
|
@@ -646,7 +669,7 @@ export class RpgClientEngine<T = any> {
|
|
|
646
669
|
* If not found and a resolver is set, it calls the resolver to create the spritesheet.
|
|
647
670
|
* The resolved spritesheet is automatically cached for future use.
|
|
648
671
|
*
|
|
649
|
-
* @param id - The spritesheet ID to retrieve
|
|
672
|
+
* @param id - The spritesheet ID or legacy tile ID to retrieve
|
|
650
673
|
* @returns The spritesheet if found or created, or undefined if not found and no resolver
|
|
651
674
|
* @returns Promise<any> if the resolver is asynchronous
|
|
652
675
|
*
|
|
@@ -659,7 +682,7 @@ export class RpgClientEngine<T = any> {
|
|
|
659
682
|
* const spritesheet = await engine.getSpriteSheet('dynamic-sprite');
|
|
660
683
|
* ```
|
|
661
684
|
*/
|
|
662
|
-
getSpriteSheet(id: string): any | Promise<any> {
|
|
685
|
+
getSpriteSheet(id: string | number): any | Promise<any> {
|
|
663
686
|
// Check cache first
|
|
664
687
|
if (this.spritesheets.has(id)) {
|
|
665
688
|
return this.spritesheets.get(id);
|
|
@@ -1093,6 +1116,37 @@ export class RpgClientEngine<T = any> {
|
|
|
1093
1116
|
return component
|
|
1094
1117
|
}
|
|
1095
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* Register a reusable sprite component that can be addressed by the server.
|
|
1121
|
+
*
|
|
1122
|
+
* Server-side component definitions only carry the component id and
|
|
1123
|
+
* serializable props. The client registry maps that id to the CanvasEngine
|
|
1124
|
+
* component that performs the actual rendering.
|
|
1125
|
+
*
|
|
1126
|
+
* @param id - Stable component id used by server component definitions
|
|
1127
|
+
* @param component - CanvasEngine component to render for this id
|
|
1128
|
+
* @returns The registered component
|
|
1129
|
+
*
|
|
1130
|
+
* @example
|
|
1131
|
+
* ```ts
|
|
1132
|
+
* engine.registerSpriteComponent('guildBadge', GuildBadgeComponent);
|
|
1133
|
+
* ```
|
|
1134
|
+
*/
|
|
1135
|
+
registerSpriteComponent(id: string, component: any) {
|
|
1136
|
+
this.spriteComponents.set(id, component);
|
|
1137
|
+
return component;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Get a reusable sprite component by id.
|
|
1142
|
+
*
|
|
1143
|
+
* @param id - Component id registered on the client
|
|
1144
|
+
* @returns The CanvasEngine component, or undefined when missing
|
|
1145
|
+
*/
|
|
1146
|
+
getSpriteComponent(id: string) {
|
|
1147
|
+
return this.spriteComponents.get(id);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1096
1150
|
/**
|
|
1097
1151
|
* Add a component animation to the engine
|
|
1098
1152
|
*
|
|
@@ -1176,13 +1230,29 @@ export class RpgClientEngine<T = any> {
|
|
|
1176
1230
|
* duration: 1000,
|
|
1177
1231
|
* onFinish: () => console.log('Fade complete')
|
|
1178
1232
|
* });
|
|
1233
|
+
*
|
|
1234
|
+
* // Wait until the transition component calls onFinish
|
|
1235
|
+
* await engine.startTransition('fade', { duration: 1000 });
|
|
1179
1236
|
* ```
|
|
1180
1237
|
*/
|
|
1181
|
-
startTransition(id: string, props: any = {}) {
|
|
1238
|
+
startTransition(id: string, props: any = {}): Promise<void> {
|
|
1182
1239
|
if (!this.guiService.exists(id)) {
|
|
1183
1240
|
throw new Error(`Transition with id ${id} not found. Make sure to add it using engine.addTransition() or in your module's transitions property.`);
|
|
1184
1241
|
}
|
|
1185
|
-
|
|
1242
|
+
return new Promise<void>((resolve) => {
|
|
1243
|
+
let finished = false;
|
|
1244
|
+
const finish = (data?: any) => {
|
|
1245
|
+
if (finished) return;
|
|
1246
|
+
finished = true;
|
|
1247
|
+
props?.onFinish?.(data);
|
|
1248
|
+
resolve();
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
this.guiService.display(id, {
|
|
1252
|
+
...props,
|
|
1253
|
+
onFinish: finish,
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1186
1256
|
}
|
|
1187
1257
|
|
|
1188
1258
|
async processInput({ input }: { input: Direction }) {
|