@rpgjs/client 5.0.0-alpha.43 → 5.0.0-alpha.44

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.
Files changed (137) hide show
  1. package/dist/Game/AnimationManager.js +25 -24
  2. package/dist/Game/AnimationManager.js.map +1 -1
  3. package/dist/Game/Event.js +11 -10
  4. package/dist/Game/Event.js.map +1 -1
  5. package/dist/Game/Map.js +78 -94
  6. package/dist/Game/Map.js.map +1 -1
  7. package/dist/Game/Object.js +182 -216
  8. package/dist/Game/Object.js.map +1 -1
  9. package/dist/Game/Player.js +11 -10
  10. package/dist/Game/Player.js.map +1 -1
  11. package/dist/Gui/Gui.js +440 -473
  12. package/dist/Gui/Gui.js.map +1 -1
  13. package/dist/Gui/NotificationManager.js +48 -50
  14. package/dist/Gui/NotificationManager.js.map +1 -1
  15. package/dist/Resource.js +132 -113
  16. package/dist/Resource.js.map +1 -1
  17. package/dist/RpgClientEngine.js +1333 -1486
  18. package/dist/RpgClientEngine.js.map +1 -1
  19. package/dist/Sound.js +162 -92
  20. package/dist/Sound.js.map +1 -1
  21. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/decorate.js +9 -0
  22. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/decorateMetadata.js +6 -0
  23. package/dist/components/animations/animation.ce.js +22 -19
  24. package/dist/components/animations/animation.ce.js.map +1 -1
  25. package/dist/components/animations/hit.ce.js +67 -68
  26. package/dist/components/animations/hit.ce.js.map +1 -1
  27. package/dist/components/animations/index.js +9 -8
  28. package/dist/components/animations/index.js.map +1 -1
  29. package/dist/components/character.ce.js +390 -314
  30. package/dist/components/character.ce.js.map +1 -1
  31. package/dist/components/dynamics/parse-value.js +42 -52
  32. package/dist/components/dynamics/parse-value.js.map +1 -1
  33. package/dist/components/dynamics/text.ce.js +71 -139
  34. package/dist/components/dynamics/text.ce.js.map +1 -1
  35. package/dist/components/gui/box.ce.js +26 -25
  36. package/dist/components/gui/box.ce.js.map +1 -1
  37. package/dist/components/gui/dialogbox/index.ce.js +202 -149
  38. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  39. package/dist/components/gui/gameover.ce.js +190 -138
  40. package/dist/components/gui/gameover.ce.js.map +1 -1
  41. package/dist/components/gui/hud/hud.ce.js +90 -33
  42. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  43. package/dist/components/gui/index.js +14 -0
  44. package/dist/components/gui/menu/equip-menu.ce.js +478 -346
  45. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
  46. package/dist/components/gui/menu/exit-menu.ce.js +52 -33
  47. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
  48. package/dist/components/gui/menu/items-menu.ce.js +341 -226
  49. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  50. package/dist/components/gui/menu/main-menu.ce.js +414 -205
  51. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  52. package/dist/components/gui/menu/options-menu.ce.js +46 -26
  53. package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
  54. package/dist/components/gui/menu/skills-menu.ce.js +104 -50
  55. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
  56. package/dist/components/gui/mobile/index.js +18 -21
  57. package/dist/components/gui/mobile/index.js.map +1 -1
  58. package/dist/components/gui/mobile/mobile.ce.js +76 -15
  59. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  60. package/dist/components/gui/notification/notification.ce.js +62 -36
  61. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  62. package/dist/components/gui/save-load.ce.js +386 -239
  63. package/dist/components/gui/save-load.ce.js.map +1 -1
  64. package/dist/components/gui/shop/shop.ce.js +649 -319
  65. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  66. package/dist/components/gui/title-screen.ce.js +187 -145
  67. package/dist/components/gui/title-screen.ce.js.map +1 -1
  68. package/dist/components/index.js +4 -0
  69. package/dist/components/prebuilt/hp-bar.ce.js +114 -104
  70. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  71. package/dist/components/prebuilt/index.js +2 -0
  72. package/dist/components/prebuilt/light-halo.ce.js +92 -74
  73. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  74. package/dist/components/scenes/canvas.ce.js +58 -42
  75. package/dist/components/scenes/canvas.ce.js.map +1 -1
  76. package/dist/components/scenes/draw-map.ce.js +87 -64
  77. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  78. package/dist/components/scenes/event-layer.ce.js +26 -16
  79. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  80. package/dist/core/inject.js +11 -10
  81. package/dist/core/inject.js.map +1 -1
  82. package/dist/core/setup.js +13 -13
  83. package/dist/core/setup.js.map +1 -1
  84. package/dist/index.js +44 -41
  85. package/dist/module.js +169 -168
  86. package/dist/module.js.map +1 -1
  87. package/dist/node_modules/.pnpm/@signe_di@2.8.3/node_modules/@signe/di/dist/index.js +209 -298
  88. package/dist/node_modules/.pnpm/@signe_di@2.8.3/node_modules/@signe/di/dist/index.js.map +1 -1
  89. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +430 -507
  90. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +1 -1
  91. package/dist/node_modules/.pnpm/@signe_room@2.8.3/node_modules/@signe/room/dist/index.js +2062 -2571
  92. package/dist/node_modules/.pnpm/@signe_room@2.8.3/node_modules/@signe/room/dist/index.js.map +1 -1
  93. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +8 -3
  94. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +1 -1
  95. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/client/index.js +78 -101
  96. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/client/index.js.map +1 -1
  97. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/index.js +264 -401
  98. package/dist/node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/index.js.map +1 -1
  99. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +9 -7
  100. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +1 -1
  101. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js +106 -165
  102. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
  103. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js +361 -461
  104. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
  105. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +2 -0
  106. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +3633 -4280
  107. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
  108. package/dist/presets/animation.js +37 -25
  109. package/dist/presets/animation.js.map +1 -1
  110. package/dist/presets/faceset.js +49 -22
  111. package/dist/presets/faceset.js.map +1 -1
  112. package/dist/presets/icon.js +13 -13
  113. package/dist/presets/icon.js.map +1 -1
  114. package/dist/presets/index.js +15 -14
  115. package/dist/presets/index.js.map +1 -1
  116. package/dist/presets/lpc.js +96 -93
  117. package/dist/presets/lpc.js.map +1 -1
  118. package/dist/presets/rmspritesheet.js +40 -39
  119. package/dist/presets/rmspritesheet.js.map +1 -1
  120. package/dist/services/AbstractSocket.js +10 -8
  121. package/dist/services/AbstractSocket.js.map +1 -1
  122. package/dist/services/keyboardControls.js +20 -18
  123. package/dist/services/keyboardControls.js.map +1 -1
  124. package/dist/services/loadMap.js +120 -36
  125. package/dist/services/loadMap.js.map +1 -1
  126. package/dist/services/mmorpg.js +128 -136
  127. package/dist/services/mmorpg.js.map +1 -1
  128. package/dist/services/save.js +74 -66
  129. package/dist/services/save.js.map +1 -1
  130. package/dist/services/standalone.js +165 -167
  131. package/dist/services/standalone.js.map +1 -1
  132. package/dist/utils/getEntityProp.js +49 -51
  133. package/dist/utils/getEntityProp.js.map +1 -1
  134. package/package.json +8 -8
  135. package/src/components/character.ce +1 -1
  136. package/src/components/scenes/draw-map.ce +3 -1
  137. package/dist/index.js.map +0 -1
package/dist/Gui/Gui.js CHANGED
@@ -1,478 +1,445 @@
1
- import { inject } from '../node_modules/.pnpm/@signe_di@2.8.3/node_modules/@signe/di/dist/index.js';
2
- import { signal } from 'canvasengine';
3
- import { WebSocketToken } from '../services/AbstractSocket.js';
4
- import component from '../components/gui/dialogbox/index.ce.js';
5
- import '@canvasengine/presets';
6
- import { combineLatest } from 'rxjs';
7
- import { PrebuiltGui } from '@rpgjs/common';
8
- import '../Sound.js';
9
- import '../Resource.js';
10
- import '../node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/index.js';
11
- import '../Game/Map.js';
12
- import 'pixi.js';
13
- import '../node_modules/.pnpm/@signe_room@2.8.3/node_modules/@signe/room/dist/index.js';
14
- import '../node_modules/.pnpm/@signe_sync@2.8.3/node_modules/@signe/sync/dist/client/index.js';
15
- import component$2 from '../components/gui/shop/shop.ce.js';
16
- import component$4 from '../components/gui/save-load.ce.js';
17
- import component$1 from '../components/gui/menu/main-menu.ce.js';
18
- import component$3 from '../components/gui/notification/notification.ce.js';
19
- import component$5 from '../components/gui/title-screen.ce.js';
20
- import component$6 from '../components/gui/gameover.ce.js';
21
-
22
- const throwError = (id) => {
23
- throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;
1
+ import { inject } from "../node_modules/.pnpm/@signe_di@2.8.3/node_modules/@signe/di/dist/index.js";
2
+ import { WebSocketToken } from "../services/AbstractSocket.js";
3
+ import component from "../components/gui/dialogbox/index.ce.js";
4
+ import component$1 from "../components/gui/shop/shop.ce.js";
5
+ import component$2 from "../components/gui/save-load.ce.js";
6
+ import component$3 from "../components/gui/menu/main-menu.ce.js";
7
+ import component$4 from "../components/gui/notification/notification.ce.js";
8
+ import component$5 from "../components/gui/title-screen.ce.js";
9
+ import component$6 from "../components/gui/gameover.ce.js";
10
+ import "../components/gui/index.js";
11
+ import { signal } from "canvasengine";
12
+ import { PrebuiltGui } from "@rpgjs/common";
13
+ import { combineLatest } from "rxjs";
14
+ //#region src/Gui/Gui.ts
15
+ var throwError = (id) => {
16
+ throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;
24
17
  };
25
- const updateItemQuantity = (items, id) => {
26
- const index = items.findIndex((item2) => item2?.id === id);
27
- if (index === -1) return items;
28
- const item = items[index];
29
- if (item?.usable === false) return items;
30
- if (item?.consumable === false) return items;
31
- const quantity = typeof item?.quantity === "number" ? item.quantity : 1;
32
- const nextQuantity = Math.max(0, quantity - 1);
33
- if (nextQuantity === quantity) return items;
34
- if (nextQuantity <= 0) {
35
- return items.filter((_, idx) => idx !== index);
36
- }
37
- const nextItems = items.slice();
38
- nextItems[index] = { ...item, quantity: nextQuantity };
39
- return nextItems;
18
+ var updateItemQuantity = (items, id) => {
19
+ const index = items.findIndex((item) => item?.id === id);
20
+ if (index === -1) return items;
21
+ const item = items[index];
22
+ if (item?.usable === false) return items;
23
+ if (item?.consumable === false) return items;
24
+ const quantity = typeof item?.quantity === "number" ? item.quantity : 1;
25
+ const nextQuantity = Math.max(0, quantity - 1);
26
+ if (nextQuantity === quantity) return items;
27
+ if (nextQuantity <= 0) return items.filter((_, idx) => idx !== index);
28
+ const nextItems = items.slice();
29
+ nextItems[index] = {
30
+ ...item,
31
+ quantity: nextQuantity
32
+ };
33
+ return nextItems;
40
34
  };
41
- const updateEquippedFlag = (items, id, equip) => {
42
- const index = items.findIndex((item2) => item2?.id === id);
43
- if (index === -1) return items;
44
- const item = items[index];
45
- if (item?.equipped === equip) return items;
46
- const nextItems = items.slice();
47
- nextItems[index] = { ...item, equipped: equip };
48
- return nextItems;
35
+ var updateEquippedFlag = (items, id, equip) => {
36
+ const index = items.findIndex((item) => item?.id === id);
37
+ if (index === -1) return items;
38
+ const item = items[index];
39
+ if (item?.equipped === equip) return items;
40
+ const nextItems = items.slice();
41
+ nextItems[index] = {
42
+ ...item,
43
+ equipped: equip
44
+ };
45
+ return nextItems;
49
46
  };
50
- const mainMenuOptimisticReducer = (data, action) => {
51
- if (!data || typeof data !== "object") return data;
52
- if (action.name === "useItem") {
53
- if (!Array.isArray(data.items)) return data;
54
- const id = action.data?.id;
55
- if (!id) return data;
56
- const nextItems = updateItemQuantity(data.items, id);
57
- if (nextItems === data.items) return data;
58
- return { ...data, items: nextItems };
59
- }
60
- if (action.name === "equipItem") {
61
- const id = action.data?.id;
62
- if (!id || typeof action.data?.equip !== "boolean") return data;
63
- const equip = action.data.equip;
64
- let nextItems = data.items;
65
- let nextEquips = data.equips;
66
- if (Array.isArray(data.items)) {
67
- nextItems = updateEquippedFlag(data.items, id, equip);
68
- }
69
- if (Array.isArray(data.equips)) {
70
- nextEquips = updateEquippedFlag(data.equips, id, equip);
71
- }
72
- if (nextItems === data.items && nextEquips === data.equips) return data;
73
- return {
74
- ...data,
75
- ...nextItems !== data.items ? { items: nextItems } : {},
76
- ...nextEquips !== data.equips ? { equips: nextEquips } : {}
77
- };
78
- }
79
- return data;
47
+ var mainMenuOptimisticReducer = (data, action) => {
48
+ if (!data || typeof data !== "object") return data;
49
+ if (action.name === "useItem") {
50
+ if (!Array.isArray(data.items)) return data;
51
+ const id = action.data?.id;
52
+ if (!id) return data;
53
+ const nextItems = updateItemQuantity(data.items, id);
54
+ if (nextItems === data.items) return data;
55
+ return {
56
+ ...data,
57
+ items: nextItems
58
+ };
59
+ }
60
+ if (action.name === "equipItem") {
61
+ const id = action.data?.id;
62
+ if (!id || typeof action.data?.equip !== "boolean") return data;
63
+ const equip = action.data.equip;
64
+ let nextItems = data.items;
65
+ let nextEquips = data.equips;
66
+ if (Array.isArray(data.items)) nextItems = updateEquippedFlag(data.items, id, equip);
67
+ if (Array.isArray(data.equips)) nextEquips = updateEquippedFlag(data.equips, id, equip);
68
+ if (nextItems === data.items && nextEquips === data.equips) return data;
69
+ return {
70
+ ...data,
71
+ ...nextItems !== data.items ? { items: nextItems } : {},
72
+ ...nextEquips !== data.equips ? { equips: nextEquips } : {}
73
+ };
74
+ }
75
+ return data;
80
76
  };
81
- class RpgGui {
82
- constructor(context) {
83
- this.context = context;
84
- this.gui = signal({});
85
- this.extraGuis = [];
86
- this.vueGuiInstance = null;
87
- // Reference to VueGui instance
88
- this.optimisticReducers = /* @__PURE__ */ new Map();
89
- this.pendingActions = /* @__PURE__ */ new Map();
90
- /**
91
- * Signal tracking which player IDs should display attached GUIs
92
- * Key: player ID, Value: boolean (true = show, false = hide)
93
- */
94
- this.attachedGuiDisplayState = signal({});
95
- this.webSocket = inject(context, WebSocketToken);
96
- this.add({
97
- name: "rpg-dialog",
98
- component: component
99
- });
100
- this.add({
101
- name: PrebuiltGui.MainMenu,
102
- component: component$1
103
- });
104
- this.add({
105
- name: PrebuiltGui.Shop,
106
- component: component$2
107
- });
108
- this.add({
109
- name: PrebuiltGui.Notification,
110
- component: component$3,
111
- autoDisplay: true
112
- });
113
- this.add({
114
- name: PrebuiltGui.Save,
115
- component: component$4
116
- });
117
- this.add({
118
- name: PrebuiltGui.TitleScreen,
119
- component: component$5
120
- });
121
- this.add({
122
- name: PrebuiltGui.Gameover,
123
- component: component$6
124
- });
125
- this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
126
- }
127
- async _initialize() {
128
- this.webSocket.on("gui.open", (data) => {
129
- this.clearPendingActions(data.guiId);
130
- this.display(data.guiId, data.data);
131
- });
132
- this.webSocket.on("gui.exit", (guiId) => {
133
- this.hide(guiId);
134
- });
135
- this.webSocket.on("gui.update", (payload) => {
136
- this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);
137
- });
138
- this.webSocket.on("gui.tooltip", (data) => {
139
- const currentState = { ...this.attachedGuiDisplayState() };
140
- data.players.forEach((playerId) => {
141
- currentState[playerId] = data.display;
142
- });
143
- this.attachedGuiDisplayState.set(currentState);
144
- });
145
- }
146
- /**
147
- * Set the VueGui instance reference for Vue component management
148
- * This is called by VueGui when it's initialized
149
- *
150
- * @param vueGuiInstance - The VueGui instance
151
- */
152
- _setVueGuiInstance(vueGuiInstance) {
153
- this.vueGuiInstance = vueGuiInstance;
154
- }
155
- /**
156
- * Notify VueGui about GUI state changes
157
- * This synchronizes the Vue component display state
158
- *
159
- * @param guiId - The GUI component ID
160
- * @param display - Display state
161
- * @param data - Component data
162
- */
163
- _notifyVueGui(guiId, display, data = {}) {
164
- if (this.vueGuiInstance && this.vueGuiInstance.vm) {
165
- const extraGui = this.extraGuis.find((gui) => gui.name === guiId);
166
- if (extraGui) {
167
- this.vueGuiInstance.vm.gui[guiId] = {
168
- name: guiId,
169
- display,
170
- data,
171
- attachToSprite: extraGui.attachToSprite || false
172
- };
173
- this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
174
- }
175
- }
176
- }
177
- /**
178
- * Initialize Vue components in the VueGui instance
179
- * This should be called after VueGui is mounted
180
- */
181
- _initializeVueComponents() {
182
- if (this.vueGuiInstance && this.vueGuiInstance.vm) {
183
- this.extraGuis.forEach((gui) => {
184
- this.vueGuiInstance.vm.gui[gui.name] = {
185
- name: gui.name,
186
- display: gui.display(),
187
- data: gui.data(),
188
- attachToSprite: gui.attachToSprite || false
189
- };
190
- });
191
- this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
192
- }
193
- }
194
- guiInteraction(guiId, name, data) {
195
- const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;
196
- const actionData = { ...data || {}, clientActionId };
197
- this.applyOptimisticAction({
198
- guiId,
199
- name,
200
- data: actionData,
201
- clientActionId
202
- });
203
- this.webSocket.emit("gui.interaction", {
204
- guiId,
205
- name,
206
- data: actionData
207
- });
208
- }
209
- guiClose(guiId, data) {
210
- this.webSocket.emit("gui.exit", {
211
- guiId,
212
- data
213
- });
214
- }
215
- /**
216
- * Add a GUI component to the system
217
- *
218
- * By default, only CanvasEngine components (.ce files) are accepted.
219
- * Vue components should be handled by the @rpgjs/vue package.
220
- *
221
- * @param gui - GUI configuration options
222
- * @param gui.name - Name or ID of the GUI component
223
- * @param gui.id - Alternative ID if name is not provided
224
- * @param gui.component - The component to render (must be a CanvasEngine component)
225
- * @param gui.display - Initial display state (default: false)
226
- * @param gui.data - Initial data for the component
227
- * @param gui.autoDisplay - Auto display when added (default: false)
228
- * @param gui.dependencies - Function returning Signal dependencies
229
- * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)
230
- *
231
- * @example
232
- * ```ts
233
- * gui.add({
234
- * name: 'inventory',
235
- * component: InventoryComponent, // Must be a .ce component
236
- * autoDisplay: true,
237
- * dependencies: () => [playerSignal, inventorySignal]
238
- * });
239
- *
240
- * // Attach to sprites
241
- * gui.add({
242
- * name: 'tooltip',
243
- * component: TooltipComponent,
244
- * attachToSprite: true
245
- * });
246
- * ```
247
- */
248
- add(gui) {
249
- const guiId = gui.name || gui.id;
250
- if (!guiId) {
251
- throw new Error("GUI must have a name or id");
252
- }
253
- const guiInstance = {
254
- name: guiId,
255
- component: gui.component,
256
- display: signal(gui.display || false),
257
- data: signal(gui.data || {}),
258
- autoDisplay: gui.autoDisplay || false,
259
- dependencies: gui.dependencies ? gui.dependencies() : [],
260
- attachToSprite: gui.attachToSprite || false
261
- };
262
- if (typeof gui.component !== "function") {
263
- guiInstance.component = gui;
264
- this.extraGuis.push(guiInstance);
265
- if (guiInstance.autoDisplay) {
266
- this._notifyVueGui(guiId, true, gui.data || {});
267
- }
268
- return;
269
- }
270
- this.gui()[guiId] = guiInstance;
271
- if (guiInstance.autoDisplay && typeof gui.component === "function") {
272
- this.display(guiId, gui.data);
273
- }
274
- }
275
- registerOptimisticReducer(guiId, reducer) {
276
- const existing = this.optimisticReducers.get(guiId) || [];
277
- this.optimisticReducers.set(guiId, existing.concat(reducer));
278
- }
279
- /**
280
- * Get all attached GUI components (attachToSprite: true)
281
- *
282
- * Returns all GUI instances that are configured to be attached to sprites.
283
- * These GUIs should be rendered in character.ce instead of canvas.ce.
284
- *
285
- * @returns Array of GUI instances with attachToSprite: true
286
- *
287
- * @example
288
- * ```ts
289
- * const attachedGuis = gui.getAttachedGuis();
290
- * // Use in character.ce to render tooltips
291
- * ```
292
- */
293
- getAttachedGuis() {
294
- const allGuis = this.getAll();
295
- return Object.values(allGuis).filter((gui) => gui.attachToSprite === true);
296
- }
297
- /**
298
- * Check if a player should display attached GUIs
299
- *
300
- * @param playerId - The player ID to check
301
- * @returns true if attached GUIs should be displayed for this player
302
- */
303
- shouldDisplayAttachedGui(playerId) {
304
- return this.attachedGuiDisplayState()[playerId] === true;
305
- }
306
- get(id) {
307
- const canvasGui = this.gui()[id];
308
- if (canvasGui) {
309
- return canvasGui;
310
- }
311
- return this.extraGuis.find((gui) => gui.name === id);
312
- }
313
- exists(id) {
314
- return !!this.get(id);
315
- }
316
- getAll() {
317
- const allGuis = { ...this.gui() };
318
- this.extraGuis.forEach((gui) => {
319
- allGuis[gui.name] = gui;
320
- });
321
- return allGuis;
322
- }
323
- /**
324
- * Display a GUI component
325
- *
326
- * Displays the GUI immediately if no dependencies are configured,
327
- * or waits for all dependencies to be resolved if dependencies are present.
328
- * Automatically manages subscriptions to prevent memory leaks.
329
- * Works with both CanvasEngine components and Vue components.
330
- *
331
- * @param id - The GUI component ID
332
- * @param data - Data to pass to the component
333
- * @param dependencies - Optional runtime dependencies (overrides config dependencies)
334
- *
335
- * @example
336
- * ```ts
337
- * // Display immediately
338
- * gui.display('inventory', { items: [] });
339
- *
340
- * // Display with runtime dependencies
341
- * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
342
- * ```
343
- */
344
- display(id, data = {}, dependencies = []) {
345
- if (!this.exists(id)) {
346
- throw throwError(id);
347
- }
348
- const guiInstance = this.get(id);
349
- const isVueComponent = this.extraGuis.some((gui) => gui.name === id);
350
- if (isVueComponent) {
351
- this._handleVueComponentDisplay(id, data, dependencies, guiInstance);
352
- } else {
353
- guiInstance.data.set(data);
354
- guiInstance.display.set(true);
355
- }
356
- }
357
- isDisplaying(id) {
358
- const guiInstance = this.get(id);
359
- if (!guiInstance) return false;
360
- return guiInstance.display();
361
- }
362
- /**
363
- * Handle Vue component display logic
364
- *
365
- * @param id - GUI component ID
366
- * @param data - Component data
367
- * @param dependencies - Runtime dependencies
368
- * @param guiInstance - GUI instance
369
- */
370
- _handleVueComponentDisplay(id, data, dependencies, guiInstance) {
371
- if (guiInstance.subscription) {
372
- guiInstance.subscription.unsubscribe();
373
- guiInstance.subscription = void 0;
374
- }
375
- const deps = dependencies.length > 0 ? dependencies : guiInstance.dependencies ? guiInstance.dependencies() : [];
376
- if (deps.length > 0) {
377
- guiInstance.subscription = combineLatest(
378
- deps.map((dependency) => dependency.observable)
379
- ).subscribe((values) => {
380
- if (values.every((value) => value !== void 0)) {
381
- guiInstance.data.set(data);
382
- guiInstance.display.set(true);
383
- this._notifyVueGui(id, true, data);
384
- }
385
- });
386
- return;
387
- }
388
- guiInstance.data.set(data);
389
- guiInstance.display.set(true);
390
- this._notifyVueGui(id, true, data);
391
- }
392
- /**
393
- * Hide a GUI component
394
- *
395
- * Hides the GUI and cleans up any active subscriptions.
396
- * Works with both CanvasEngine components and Vue components.
397
- *
398
- * @param id - The GUI component ID
399
- *
400
- * @example
401
- * ```ts
402
- * gui.hide('inventory');
403
- * ```
404
- */
405
- hide(id) {
406
- if (!this.exists(id)) {
407
- throw throwError(id);
408
- }
409
- const guiInstance = this.get(id);
410
- if (guiInstance.subscription) {
411
- guiInstance.subscription.unsubscribe();
412
- guiInstance.subscription = void 0;
413
- }
414
- guiInstance.display.set(false);
415
- const isVueComponent = this.extraGuis.some((gui) => gui.name === id);
416
- if (isVueComponent) {
417
- this._notifyVueGui(id, false);
418
- }
419
- }
420
- isVueComponent(id) {
421
- return this.extraGuis.some((gui) => gui.name === id);
422
- }
423
- clearPendingActions(guiId) {
424
- this.pendingActions.delete(guiId);
425
- }
426
- applyReducers(guiId, data, actions) {
427
- const reducers = this.optimisticReducers.get(guiId);
428
- if (!reducers || reducers.length === 0) return data;
429
- let next = data;
430
- for (const action of actions) {
431
- for (const reducer of reducers) {
432
- const updated = reducer(next, action);
433
- if (updated !== void 0 && updated !== null && updated !== next) {
434
- next = updated;
435
- }
436
- }
437
- }
438
- return next;
439
- }
440
- applyOptimisticAction(action) {
441
- const guiInstance = this.get(action.guiId);
442
- if (!guiInstance) return;
443
- const reducers = this.optimisticReducers.get(action.guiId);
444
- if (!reducers || reducers.length === 0) return;
445
- const currentData = guiInstance.data();
446
- const nextData = this.applyReducers(action.guiId, currentData, [action]);
447
- if (nextData === currentData) return;
448
- guiInstance.data.set(nextData);
449
- const pending = this.pendingActions.get(action.guiId) || [];
450
- pending.push(action);
451
- this.pendingActions.set(action.guiId, pending);
452
- if (this.isVueComponent(action.guiId)) {
453
- this._notifyVueGui(action.guiId, guiInstance.display(), nextData);
454
- }
455
- }
456
- applyServerUpdate(guiId, data, clientActionId) {
457
- const guiInstance = this.get(guiId);
458
- if (!guiInstance) return;
459
- let pending = this.pendingActions.get(guiId) || [];
460
- if (clientActionId) {
461
- pending = pending.filter((action) => action.clientActionId !== clientActionId);
462
- } else {
463
- pending = [];
464
- }
465
- let nextData = data;
466
- if (pending.length) {
467
- nextData = this.applyReducers(guiId, nextData, pending);
468
- }
469
- guiInstance.data.set(nextData);
470
- this.pendingActions.set(guiId, pending);
471
- if (this.isVueComponent(guiId)) {
472
- this._notifyVueGui(guiId, guiInstance.display(), nextData);
473
- }
474
- }
475
- }
476
-
77
+ var RpgGui = class {
78
+ constructor(context) {
79
+ this.context = context;
80
+ this.gui = signal({});
81
+ this.extraGuis = [];
82
+ this.vueGuiInstance = null;
83
+ this.optimisticReducers = /* @__PURE__ */ new Map();
84
+ this.pendingActions = /* @__PURE__ */ new Map();
85
+ this.attachedGuiDisplayState = signal({});
86
+ this.webSocket = inject(context, WebSocketToken);
87
+ this.add({
88
+ name: "rpg-dialog",
89
+ component
90
+ });
91
+ this.add({
92
+ name: PrebuiltGui.MainMenu,
93
+ component: component$3
94
+ });
95
+ this.add({
96
+ name: PrebuiltGui.Shop,
97
+ component: component$1
98
+ });
99
+ this.add({
100
+ name: PrebuiltGui.Notification,
101
+ component: component$4,
102
+ autoDisplay: true
103
+ });
104
+ this.add({
105
+ name: PrebuiltGui.Save,
106
+ component: component$2
107
+ });
108
+ this.add({
109
+ name: PrebuiltGui.TitleScreen,
110
+ component: component$5
111
+ });
112
+ this.add({
113
+ name: PrebuiltGui.Gameover,
114
+ component: component$6
115
+ });
116
+ this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
117
+ }
118
+ async _initialize() {
119
+ this.webSocket.on("gui.open", (data) => {
120
+ this.clearPendingActions(data.guiId);
121
+ this.display(data.guiId, data.data);
122
+ });
123
+ this.webSocket.on("gui.exit", (guiId) => {
124
+ this.hide(guiId);
125
+ });
126
+ this.webSocket.on("gui.update", (payload) => {
127
+ this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);
128
+ });
129
+ /**
130
+ * Listen for tooltip display state changes from server
131
+ * This is triggered by showAttachedGui/hideAttachedGui on the server
132
+ */
133
+ this.webSocket.on("gui.tooltip", (data) => {
134
+ const currentState = { ...this.attachedGuiDisplayState() };
135
+ data.players.forEach((playerId) => {
136
+ currentState[playerId] = data.display;
137
+ });
138
+ this.attachedGuiDisplayState.set(currentState);
139
+ });
140
+ }
141
+ /**
142
+ * Set the VueGui instance reference for Vue component management
143
+ * This is called by VueGui when it's initialized
144
+ *
145
+ * @param vueGuiInstance - The VueGui instance
146
+ */
147
+ _setVueGuiInstance(vueGuiInstance) {
148
+ this.vueGuiInstance = vueGuiInstance;
149
+ }
150
+ /**
151
+ * Notify VueGui about GUI state changes
152
+ * This synchronizes the Vue component display state
153
+ *
154
+ * @param guiId - The GUI component ID
155
+ * @param display - Display state
156
+ * @param data - Component data
157
+ */
158
+ _notifyVueGui(guiId, display, data = {}) {
159
+ if (this.vueGuiInstance && this.vueGuiInstance.vm) {
160
+ const extraGui = this.extraGuis.find((gui) => gui.name === guiId);
161
+ if (extraGui) {
162
+ this.vueGuiInstance.vm.gui[guiId] = {
163
+ name: guiId,
164
+ display,
165
+ data,
166
+ attachToSprite: extraGui.attachToSprite || false
167
+ };
168
+ this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
169
+ }
170
+ }
171
+ }
172
+ /**
173
+ * Initialize Vue components in the VueGui instance
174
+ * This should be called after VueGui is mounted
175
+ */
176
+ _initializeVueComponents() {
177
+ if (this.vueGuiInstance && this.vueGuiInstance.vm) {
178
+ this.extraGuis.forEach((gui) => {
179
+ this.vueGuiInstance.vm.gui[gui.name] = {
180
+ name: gui.name,
181
+ display: gui.display(),
182
+ data: gui.data(),
183
+ attachToSprite: gui.attachToSprite || false
184
+ };
185
+ });
186
+ this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
187
+ }
188
+ }
189
+ guiInteraction(guiId, name, data) {
190
+ const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;
191
+ const actionData = {
192
+ ...data || {},
193
+ clientActionId
194
+ };
195
+ this.applyOptimisticAction({
196
+ guiId,
197
+ name,
198
+ data: actionData,
199
+ clientActionId
200
+ });
201
+ this.webSocket.emit("gui.interaction", {
202
+ guiId,
203
+ name,
204
+ data: actionData
205
+ });
206
+ }
207
+ guiClose(guiId, data) {
208
+ this.webSocket.emit("gui.exit", {
209
+ guiId,
210
+ data
211
+ });
212
+ }
213
+ /**
214
+ * Add a GUI component to the system
215
+ *
216
+ * By default, only CanvasEngine components (.ce files) are accepted.
217
+ * Vue components should be handled by the @rpgjs/vue package.
218
+ *
219
+ * @param gui - GUI configuration options
220
+ * @param gui.name - Name or ID of the GUI component
221
+ * @param gui.id - Alternative ID if name is not provided
222
+ * @param gui.component - The component to render (must be a CanvasEngine component)
223
+ * @param gui.display - Initial display state (default: false)
224
+ * @param gui.data - Initial data for the component
225
+ * @param gui.autoDisplay - Auto display when added (default: false)
226
+ * @param gui.dependencies - Function returning Signal dependencies
227
+ * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * gui.add({
232
+ * name: 'inventory',
233
+ * component: InventoryComponent, // Must be a .ce component
234
+ * autoDisplay: true,
235
+ * dependencies: () => [playerSignal, inventorySignal]
236
+ * });
237
+ *
238
+ * // Attach to sprites
239
+ * gui.add({
240
+ * name: 'tooltip',
241
+ * component: TooltipComponent,
242
+ * attachToSprite: true
243
+ * });
244
+ * ```
245
+ */
246
+ add(gui) {
247
+ const guiId = gui.name || gui.id;
248
+ if (!guiId) throw new Error("GUI must have a name or id");
249
+ const guiInstance = {
250
+ name: guiId,
251
+ component: gui.component,
252
+ display: signal(gui.display || false),
253
+ data: signal(gui.data || {}),
254
+ autoDisplay: gui.autoDisplay || false,
255
+ dependencies: gui.dependencies ? gui.dependencies() : [],
256
+ attachToSprite: gui.attachToSprite || false
257
+ };
258
+ if (typeof gui.component !== "function") {
259
+ guiInstance.component = gui;
260
+ this.extraGuis.push(guiInstance);
261
+ if (guiInstance.autoDisplay) this._notifyVueGui(guiId, true, gui.data || {});
262
+ return;
263
+ }
264
+ this.gui()[guiId] = guiInstance;
265
+ if (guiInstance.autoDisplay && typeof gui.component === "function") this.display(guiId, gui.data);
266
+ }
267
+ registerOptimisticReducer(guiId, reducer) {
268
+ const existing = this.optimisticReducers.get(guiId) || [];
269
+ this.optimisticReducers.set(guiId, existing.concat(reducer));
270
+ }
271
+ /**
272
+ * Get all attached GUI components (attachToSprite: true)
273
+ *
274
+ * Returns all GUI instances that are configured to be attached to sprites.
275
+ * These GUIs should be rendered in character.ce instead of canvas.ce.
276
+ *
277
+ * @returns Array of GUI instances with attachToSprite: true
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * const attachedGuis = gui.getAttachedGuis();
282
+ * // Use in character.ce to render tooltips
283
+ * ```
284
+ */
285
+ getAttachedGuis() {
286
+ const allGuis = this.getAll();
287
+ return Object.values(allGuis).filter((gui) => gui.attachToSprite === true);
288
+ }
289
+ /**
290
+ * Check if a player should display attached GUIs
291
+ *
292
+ * @param playerId - The player ID to check
293
+ * @returns true if attached GUIs should be displayed for this player
294
+ */
295
+ shouldDisplayAttachedGui(playerId) {
296
+ return this.attachedGuiDisplayState()[playerId] === true;
297
+ }
298
+ get(id) {
299
+ const canvasGui = this.gui()[id];
300
+ if (canvasGui) return canvasGui;
301
+ return this.extraGuis.find((gui) => gui.name === id);
302
+ }
303
+ exists(id) {
304
+ return !!this.get(id);
305
+ }
306
+ getAll() {
307
+ const allGuis = { ...this.gui() };
308
+ this.extraGuis.forEach((gui) => {
309
+ allGuis[gui.name] = gui;
310
+ });
311
+ return allGuis;
312
+ }
313
+ /**
314
+ * Display a GUI component
315
+ *
316
+ * Displays the GUI immediately if no dependencies are configured,
317
+ * or waits for all dependencies to be resolved if dependencies are present.
318
+ * Automatically manages subscriptions to prevent memory leaks.
319
+ * Works with both CanvasEngine components and Vue components.
320
+ *
321
+ * @param id - The GUI component ID
322
+ * @param data - Data to pass to the component
323
+ * @param dependencies - Optional runtime dependencies (overrides config dependencies)
324
+ *
325
+ * @example
326
+ * ```ts
327
+ * // Display immediately
328
+ * gui.display('inventory', { items: [] });
329
+ *
330
+ * // Display with runtime dependencies
331
+ * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
332
+ * ```
333
+ */
334
+ display(id, data = {}, dependencies = []) {
335
+ if (!this.exists(id)) throw throwError(id);
336
+ const guiInstance = this.get(id);
337
+ if (this.extraGuis.some((gui) => gui.name === id)) this._handleVueComponentDisplay(id, data, dependencies, guiInstance);
338
+ else {
339
+ guiInstance.data.set(data);
340
+ guiInstance.display.set(true);
341
+ }
342
+ }
343
+ isDisplaying(id) {
344
+ const guiInstance = this.get(id);
345
+ if (!guiInstance) return false;
346
+ return guiInstance.display();
347
+ }
348
+ /**
349
+ * Handle Vue component display logic
350
+ *
351
+ * @param id - GUI component ID
352
+ * @param data - Component data
353
+ * @param dependencies - Runtime dependencies
354
+ * @param guiInstance - GUI instance
355
+ */
356
+ _handleVueComponentDisplay(id, data, dependencies, guiInstance) {
357
+ if (guiInstance.subscription) {
358
+ guiInstance.subscription.unsubscribe();
359
+ guiInstance.subscription = void 0;
360
+ }
361
+ const deps = dependencies.length > 0 ? dependencies : guiInstance.dependencies ? guiInstance.dependencies() : [];
362
+ if (deps.length > 0) {
363
+ guiInstance.subscription = combineLatest(deps.map((dependency) => dependency.observable)).subscribe((values) => {
364
+ if (values.every((value) => value !== void 0)) {
365
+ guiInstance.data.set(data);
366
+ guiInstance.display.set(true);
367
+ this._notifyVueGui(id, true, data);
368
+ }
369
+ });
370
+ return;
371
+ }
372
+ guiInstance.data.set(data);
373
+ guiInstance.display.set(true);
374
+ this._notifyVueGui(id, true, data);
375
+ }
376
+ /**
377
+ * Hide a GUI component
378
+ *
379
+ * Hides the GUI and cleans up any active subscriptions.
380
+ * Works with both CanvasEngine components and Vue components.
381
+ *
382
+ * @param id - The GUI component ID
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * gui.hide('inventory');
387
+ * ```
388
+ */
389
+ hide(id) {
390
+ if (!this.exists(id)) throw throwError(id);
391
+ const guiInstance = this.get(id);
392
+ if (guiInstance.subscription) {
393
+ guiInstance.subscription.unsubscribe();
394
+ guiInstance.subscription = void 0;
395
+ }
396
+ guiInstance.display.set(false);
397
+ if (this.extraGuis.some((gui) => gui.name === id)) this._notifyVueGui(id, false);
398
+ }
399
+ isVueComponent(id) {
400
+ return this.extraGuis.some((gui) => gui.name === id);
401
+ }
402
+ clearPendingActions(guiId) {
403
+ this.pendingActions.delete(guiId);
404
+ }
405
+ applyReducers(guiId, data, actions) {
406
+ const reducers = this.optimisticReducers.get(guiId);
407
+ if (!reducers || reducers.length === 0) return data;
408
+ let next = data;
409
+ for (const action of actions) for (const reducer of reducers) {
410
+ const updated = reducer(next, action);
411
+ if (updated !== void 0 && updated !== null && updated !== next) next = updated;
412
+ }
413
+ return next;
414
+ }
415
+ applyOptimisticAction(action) {
416
+ const guiInstance = this.get(action.guiId);
417
+ if (!guiInstance) return;
418
+ const reducers = this.optimisticReducers.get(action.guiId);
419
+ if (!reducers || reducers.length === 0) return;
420
+ const currentData = guiInstance.data();
421
+ const nextData = this.applyReducers(action.guiId, currentData, [action]);
422
+ if (nextData === currentData) return;
423
+ guiInstance.data.set(nextData);
424
+ const pending = this.pendingActions.get(action.guiId) || [];
425
+ pending.push(action);
426
+ this.pendingActions.set(action.guiId, pending);
427
+ if (this.isVueComponent(action.guiId)) this._notifyVueGui(action.guiId, guiInstance.display(), nextData);
428
+ }
429
+ applyServerUpdate(guiId, data, clientActionId) {
430
+ const guiInstance = this.get(guiId);
431
+ if (!guiInstance) return;
432
+ let pending = this.pendingActions.get(guiId) || [];
433
+ if (clientActionId) pending = pending.filter((action) => action.clientActionId !== clientActionId);
434
+ else pending = [];
435
+ let nextData = data;
436
+ if (pending.length) nextData = this.applyReducers(guiId, nextData, pending);
437
+ guiInstance.data.set(nextData);
438
+ this.pendingActions.set(guiId, pending);
439
+ if (this.isVueComponent(guiId)) this._notifyVueGui(guiId, guiInstance.display(), nextData);
440
+ }
441
+ };
442
+ //#endregion
477
443
  export { RpgGui };
478
- //# sourceMappingURL=Gui.js.map
444
+
445
+ //# sourceMappingURL=Gui.js.map