@rpgjs/client 5.0.0-beta.7 → 5.0.0-beta.9
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 +27 -0
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/Event.js.map +1 -1
- package/dist/Game/Map.d.ts +9 -1
- package/dist/Game/Map.js +63 -5
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.d.ts +17 -9
- package/dist/Game/Object.js +1 -12
- package/dist/Game/Object.js.map +1 -1
- package/dist/Game/Player.js.map +1 -1
- package/dist/Gui/Gui.d.ts +17 -4
- 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.js.map +1 -1
- package/dist/Resource.js +1 -1
- package/dist/Resource.js.map +1 -1
- package/dist/RpgClient.d.ts +35 -2
- package/dist/RpgClientEngine.d.ts +41 -5
- package/dist/RpgClientEngine.js +50 -5
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/Sound.js.map +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.128.0 → _@oxc-project_runtime@0.130.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.128.0 → _@oxc-project_runtime@0.130.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/animations/index.js.map +1 -1
- package/dist/components/character.ce.js +259 -5
- 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 +3 -0
- 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 +3 -3
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +1 -1
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js +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.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 +1 -1
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +2 -2
- 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 +147 -4
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +2 -8
- 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/decorators/spritesheet.d.ts +1 -0
- package/dist/decorators/spritesheet.js +11 -0
- package/dist/decorators/spritesheet.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -2
- package/dist/module.js +4 -1
- package/dist/module.js.map +1 -1
- package/dist/node_modules/.pnpm/{@signe_di@2.10.0 → @signe_di@3.0.1}/node_modules/@signe/di/dist/index.js +1 -1
- package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_reactive@2.9.2 → @signe_reactive@3.0.1}/node_modules/@signe/reactive/dist/index.js +15 -3
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_room@2.10.0 → @signe_room@3.0.1}/node_modules/@signe/room/dist/index.js +124 -39
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_sync@2.10.0 → @signe_sync@3.0.1}/node_modules/@signe/sync/dist/client/index.js +1 -1
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_sync@2.10.0 → @signe_sync@3.0.1}/node_modules/@signe/sync/dist/index.js +36 -13
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/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/index.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 +7 -3
- 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 +10 -10
- package/src/Game/Map.ts +91 -2
- package/src/Game/Object.ts +22 -35
- 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 +36 -2
- package/src/RpgClientEngine.ts +74 -11
- package/src/components/character.ce +318 -9
- 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/components/scenes/canvas.ce +165 -6
- package/src/components/scenes/draw-map.ce +2 -15
- package/src/components/scenes/event-layer.ce +1 -2
- package/src/core/setup.ts +2 -2
- package/src/decorators/spritesheet.ts +8 -0
- package/src/index.ts +1 -0
- package/src/module.ts +5 -1
- package/src/services/loadMap.ts +2 -0
- package/src/services/mmorpg.ts +8 -2
- package/dist/node_modules/.pnpm/@signe_di@2.10.0/node_modules/@signe/di/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js +0 -45
- package/dist/node_modules/.pnpm/@signe_reactive@2.10.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.2/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_room@2.10.0/node_modules/@signe/room/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.10.0/node_modules/@signe/sync/dist/index.js.map +0 -1
|
@@ -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
|
+
});
|
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,12 @@ 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
|
+
}
|
|
10
16
|
|
|
11
17
|
export interface RpgSpriteBeforeRemoveContext {
|
|
12
18
|
reason?: string
|
|
@@ -93,7 +99,7 @@ export interface RpgSpriteHooks {
|
|
|
93
99
|
* }
|
|
94
100
|
* ```
|
|
95
101
|
*/
|
|
96
|
-
componentsBehind?:
|
|
102
|
+
componentsBehind?: SpriteComponentConfig[]
|
|
97
103
|
|
|
98
104
|
/**
|
|
99
105
|
* Array of components to render in front of the sprite
|
|
@@ -108,7 +114,28 @@ export interface RpgSpriteHooks {
|
|
|
108
114
|
* }
|
|
109
115
|
* ```
|
|
110
116
|
*/
|
|
111
|
-
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>
|
|
112
139
|
|
|
113
140
|
/**
|
|
114
141
|
* As soon as the sprite is initialized
|
|
@@ -512,6 +539,13 @@ export interface RpgClient {
|
|
|
512
539
|
* ```
|
|
513
540
|
*/
|
|
514
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
|
|
515
549
|
} | any)[],
|
|
516
550
|
|
|
517
551
|
/**
|