@rpgjs/client 5.0.0-alpha.9 → 5.0.0-beta.10

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 (347) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +19 -0
  3. package/dist/Game/AnimationManager.d.ts +8 -0
  4. package/dist/Game/AnimationManager.js +35 -0
  5. package/dist/Game/AnimationManager.js.map +1 -0
  6. package/dist/Game/AnimationManager.spec.d.ts +1 -0
  7. package/dist/Game/Event.d.ts +1 -1
  8. package/dist/Game/Event.js +12 -0
  9. package/dist/Game/Event.js.map +1 -0
  10. package/dist/Game/Map.d.ts +31 -2
  11. package/dist/Game/Map.js +138 -0
  12. package/dist/Game/Map.js.map +1 -0
  13. package/dist/Game/Object.d.ts +189 -0
  14. package/dist/Game/Object.js +255 -0
  15. package/dist/Game/Object.js.map +1 -0
  16. package/dist/Game/Player.d.ts +1 -1
  17. package/dist/Game/Player.js +12 -0
  18. package/dist/Game/Player.js.map +1 -0
  19. package/dist/Gui/Gui.d.ts +192 -7
  20. package/dist/Gui/Gui.js +475 -0
  21. package/dist/Gui/Gui.js.map +1 -0
  22. package/dist/Gui/Gui.spec.d.ts +1 -0
  23. package/dist/Gui/NotificationManager.d.ts +23 -0
  24. package/dist/Gui/NotificationManager.js +49 -0
  25. package/dist/Gui/NotificationManager.js.map +1 -0
  26. package/dist/Resource.d.ts +97 -0
  27. package/dist/Resource.js +133 -0
  28. package/dist/Resource.js.map +1 -0
  29. package/dist/RpgClient.d.ts +295 -13
  30. package/dist/RpgClientEngine.d.ts +671 -15
  31. package/dist/RpgClientEngine.js +1442 -0
  32. package/dist/RpgClientEngine.js.map +1 -0
  33. package/dist/Sound.d.ts +199 -0
  34. package/dist/Sound.js +167 -0
  35. package/dist/Sound.js.map +1 -0
  36. package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js +9 -0
  37. package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js +6 -0
  38. package/dist/components/animations/animation.ce.js +23 -0
  39. package/dist/components/animations/animation.ce.js.map +1 -0
  40. package/dist/components/animations/hit.ce.js +64 -0
  41. package/dist/components/animations/hit.ce.js.map +1 -0
  42. package/dist/components/animations/index.d.ts +4 -0
  43. package/dist/components/animations/index.js +11 -0
  44. package/dist/components/animations/index.js.map +1 -0
  45. package/dist/components/character.ce.js +572 -0
  46. package/dist/components/character.ce.js.map +1 -0
  47. package/dist/components/dynamics/bar.ce.js +96 -0
  48. package/dist/components/dynamics/bar.ce.js.map +1 -0
  49. package/dist/components/dynamics/image.ce.js +23 -0
  50. package/dist/components/dynamics/image.ce.js.map +1 -0
  51. package/dist/components/dynamics/parse-value.d.ts +4 -0
  52. package/dist/components/dynamics/parse-value.js +63 -0
  53. package/dist/components/dynamics/parse-value.js.map +1 -0
  54. package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
  55. package/dist/components/dynamics/shape-utils.d.ts +16 -0
  56. package/dist/components/dynamics/shape-utils.js +73 -0
  57. package/dist/components/dynamics/shape-utils.js.map +1 -0
  58. package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
  59. package/dist/components/dynamics/shape.ce.js +83 -0
  60. package/dist/components/dynamics/shape.ce.js.map +1 -0
  61. package/dist/components/dynamics/text.ce.js +50 -0
  62. package/dist/components/dynamics/text.ce.js.map +1 -0
  63. package/dist/components/gui/box.ce.js +26 -0
  64. package/dist/components/gui/box.ce.js.map +1 -0
  65. package/dist/components/gui/dialogbox/index.ce.js +198 -0
  66. package/dist/components/gui/dialogbox/index.ce.js.map +1 -0
  67. package/dist/components/gui/gameover.ce.js +169 -0
  68. package/dist/components/gui/gameover.ce.js.map +1 -0
  69. package/dist/components/gui/hud/hud.ce.js +83 -0
  70. package/dist/components/gui/hud/hud.ce.js.map +1 -0
  71. package/dist/components/gui/index.d.ts +15 -3
  72. package/dist/components/gui/index.js +14 -0
  73. package/dist/components/gui/menu/equip-menu.ce.js +427 -0
  74. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -0
  75. package/dist/components/gui/menu/exit-menu.ce.js +55 -0
  76. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -0
  77. package/dist/components/gui/menu/items-menu.ce.js +326 -0
  78. package/dist/components/gui/menu/items-menu.ce.js.map +1 -0
  79. package/dist/components/gui/menu/main-menu.ce.js +399 -0
  80. package/dist/components/gui/menu/main-menu.ce.js.map +1 -0
  81. package/dist/components/gui/menu/options-menu.ce.js +49 -0
  82. package/dist/components/gui/menu/options-menu.ce.js.map +1 -0
  83. package/dist/components/gui/menu/skills-menu.ce.js +102 -0
  84. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -0
  85. package/dist/components/gui/mobile/index.d.ts +8 -0
  86. package/dist/components/gui/mobile/index.js +21 -0
  87. package/dist/components/gui/mobile/index.js.map +1 -0
  88. package/dist/components/gui/mobile/mobile.ce.js +79 -0
  89. package/dist/components/gui/mobile/mobile.ce.js.map +1 -0
  90. package/dist/components/gui/notification/notification.ce.js +62 -0
  91. package/dist/components/gui/notification/notification.ce.js.map +1 -0
  92. package/dist/components/gui/save-load.ce.js +211 -0
  93. package/dist/components/gui/save-load.ce.js.map +1 -0
  94. package/dist/components/gui/shop/shop.ce.js +614 -0
  95. package/dist/components/gui/shop/shop.ce.js.map +1 -0
  96. package/dist/components/gui/title-screen.ce.js +164 -0
  97. package/dist/components/gui/title-screen.ce.js.map +1 -0
  98. package/dist/components/index.d.ts +1 -0
  99. package/dist/components/index.js +4 -0
  100. package/dist/components/player-components-utils.d.ts +67 -0
  101. package/dist/components/player-components-utils.js +162 -0
  102. package/dist/components/player-components-utils.js.map +1 -0
  103. package/dist/components/player-components-utils.spec.d.ts +1 -0
  104. package/dist/components/player-components.ce.js +188 -0
  105. package/dist/components/player-components.ce.js.map +1 -0
  106. package/dist/components/prebuilt/hp-bar.ce.js +113 -0
  107. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -0
  108. package/dist/components/prebuilt/index.d.ts +19 -0
  109. package/dist/components/prebuilt/index.js +2 -0
  110. package/dist/components/prebuilt/light-halo.ce.js +70 -0
  111. package/dist/components/prebuilt/light-halo.ce.js.map +1 -0
  112. package/dist/components/scenes/canvas.ce.js +196 -0
  113. package/dist/components/scenes/canvas.ce.js.map +1 -0
  114. package/dist/components/scenes/draw-map.ce.js +79 -0
  115. package/dist/components/scenes/draw-map.ce.js.map +1 -0
  116. package/dist/components/scenes/event-layer.ce.js +29 -0
  117. package/dist/components/scenes/event-layer.ce.js.map +1 -0
  118. package/dist/core/inject.js +18 -0
  119. package/dist/core/inject.js.map +1 -0
  120. package/dist/core/setup.js +16 -0
  121. package/dist/core/setup.js.map +1 -0
  122. package/dist/decorators/spritesheet.d.ts +1 -0
  123. package/dist/decorators/spritesheet.js +11 -0
  124. package/dist/decorators/spritesheet.js.map +1 -0
  125. package/dist/index.d.ts +16 -1
  126. package/dist/index.js +45 -14
  127. package/dist/module.d.ts +43 -4
  128. package/dist/module.js +179 -0
  129. package/dist/module.js.map +1 -0
  130. package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js +167 -0
  131. package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
  132. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
  133. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
  134. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
  135. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
  136. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
  137. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
  138. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
  139. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  140. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js +241 -0
  141. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
  142. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js +115 -0
  143. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -0
  144. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js +401 -0
  145. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -0
  146. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +2 -0
  147. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +3756 -0
  148. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -0
  149. package/dist/presets/animation.d.ts +31 -0
  150. package/dist/presets/animation.js +39 -0
  151. package/dist/presets/animation.js.map +1 -0
  152. package/dist/presets/faceset.d.ts +30 -0
  153. package/dist/presets/faceset.js +51 -0
  154. package/dist/presets/faceset.js.map +1 -0
  155. package/dist/presets/icon.d.ts +20 -0
  156. package/dist/presets/icon.js +15 -0
  157. package/dist/presets/icon.js.map +1 -0
  158. package/dist/presets/index.d.ts +123 -0
  159. package/dist/presets/index.js +17 -0
  160. package/dist/presets/index.js.map +1 -0
  161. package/dist/presets/lpc.d.ts +89 -0
  162. package/dist/presets/lpc.js +98 -0
  163. package/dist/presets/lpc.js.map +1 -0
  164. package/dist/presets/rmspritesheet.js +42 -0
  165. package/dist/presets/rmspritesheet.js.map +1 -0
  166. package/dist/services/AbstractSocket.d.ts +9 -5
  167. package/dist/services/AbstractSocket.js +11 -0
  168. package/dist/services/AbstractSocket.js.map +1 -0
  169. package/dist/services/keyboardControls.d.ts +15 -0
  170. package/dist/services/keyboardControls.js +23 -0
  171. package/dist/services/keyboardControls.js.map +1 -0
  172. package/dist/services/loadMap.d.ts +6 -0
  173. package/dist/services/loadMap.js +123 -0
  174. package/dist/services/loadMap.js.map +1 -0
  175. package/dist/services/mmorpg.d.ts +21 -9
  176. package/dist/services/mmorpg.js +136 -0
  177. package/dist/services/mmorpg.js.map +1 -0
  178. package/dist/services/save.d.ts +19 -0
  179. package/dist/services/save.js +77 -0
  180. package/dist/services/save.js.map +1 -0
  181. package/dist/services/save.spec.d.ts +1 -0
  182. package/dist/services/standalone.d.ts +67 -7
  183. package/dist/services/standalone.js +168 -0
  184. package/dist/services/standalone.js.map +1 -0
  185. package/dist/utils/getEntityProp.d.ts +39 -0
  186. package/dist/utils/getEntityProp.js +53 -0
  187. package/dist/utils/getEntityProp.js.map +1 -0
  188. package/dist/utils/getEntityProp.spec.d.ts +1 -0
  189. package/dist/utils/readPropValue.d.ts +2 -0
  190. package/dist/utils/readPropValue.js +13 -0
  191. package/dist/utils/readPropValue.js.map +1 -0
  192. package/package.json +14 -11
  193. package/src/Game/AnimationManager.spec.ts +30 -0
  194. package/src/Game/AnimationManager.ts +33 -0
  195. package/src/Game/Event.ts +1 -1
  196. package/src/Game/Map.ts +184 -3
  197. package/src/Game/Object.ts +409 -14
  198. package/src/Game/Player.ts +1 -1
  199. package/src/Gui/Gui.spec.ts +273 -0
  200. package/src/Gui/Gui.ts +566 -23
  201. package/src/Gui/NotificationManager.ts +69 -0
  202. package/src/Resource.ts +149 -0
  203. package/src/RpgClient.ts +309 -14
  204. package/src/RpgClientEngine.ts +1790 -63
  205. package/src/Sound.ts +253 -0
  206. package/src/components/{effects → animations}/animation.ce +3 -6
  207. package/src/components/{effects → animations}/index.ts +1 -1
  208. package/src/components/character.ce +801 -59
  209. package/src/components/dynamics/bar.ce +87 -0
  210. package/src/components/dynamics/image.ce +20 -0
  211. package/src/components/dynamics/parse-value.spec.ts +83 -0
  212. package/src/components/dynamics/parse-value.ts +154 -0
  213. package/src/components/dynamics/shape-utils.spec.ts +46 -0
  214. package/src/components/dynamics/shape-utils.ts +61 -0
  215. package/src/components/dynamics/shape.ce +89 -0
  216. package/src/components/dynamics/text.ce +68 -0
  217. package/src/components/gui/box.ce +17 -0
  218. package/src/components/gui/dialogbox/index.ce +213 -187
  219. package/src/components/gui/gameover.ce +158 -0
  220. package/src/components/gui/hud/hud.ce +61 -0
  221. package/src/components/gui/index.ts +30 -4
  222. package/src/components/gui/menu/equip-menu.ce +410 -0
  223. package/src/components/gui/menu/exit-menu.ce +41 -0
  224. package/src/components/gui/menu/items-menu.ce +317 -0
  225. package/src/components/gui/menu/main-menu.ce +294 -0
  226. package/src/components/gui/menu/options-menu.ce +35 -0
  227. package/src/components/gui/menu/skills-menu.ce +83 -0
  228. package/src/components/gui/mobile/index.ts +24 -0
  229. package/src/components/gui/mobile/mobile.ce +80 -0
  230. package/src/components/gui/notification/notification.ce +51 -0
  231. package/src/components/gui/save-load.ce +208 -0
  232. package/src/components/gui/shop/shop.ce +493 -0
  233. package/src/components/gui/title-screen.ce +163 -0
  234. package/src/components/index.ts +3 -0
  235. package/src/components/player-components-utils.spec.ts +109 -0
  236. package/src/components/player-components-utils.ts +205 -0
  237. package/src/components/player-components.ce +221 -0
  238. package/src/components/prebuilt/hp-bar.ce +255 -0
  239. package/src/components/prebuilt/index.ts +24 -0
  240. package/src/components/prebuilt/light-halo.ce +148 -0
  241. package/src/components/scenes/canvas.ce +185 -21
  242. package/src/components/scenes/draw-map.ce +55 -21
  243. package/src/components/scenes/event-layer.ce +8 -2
  244. package/src/components/scenes/transition.ce +60 -0
  245. package/src/core/setup.ts +2 -2
  246. package/src/decorators/spritesheet.ts +8 -0
  247. package/src/index.ts +17 -2
  248. package/src/module.ts +132 -10
  249. package/src/presets/animation.ts +46 -0
  250. package/src/presets/faceset.ts +60 -0
  251. package/src/presets/icon.ts +17 -0
  252. package/src/presets/index.ts +9 -1
  253. package/src/presets/lpc.ts +108 -0
  254. package/src/services/AbstractSocket.ts +10 -2
  255. package/src/services/keyboardControls.ts +20 -0
  256. package/src/services/loadMap.ts +3 -1
  257. package/src/services/mmorpg.ts +106 -12
  258. package/src/services/save.spec.ts +127 -0
  259. package/src/services/save.ts +103 -0
  260. package/src/services/standalone.ts +110 -18
  261. package/src/utils/getEntityProp.spec.ts +96 -0
  262. package/src/utils/getEntityProp.ts +88 -0
  263. package/src/utils/readPropValue.ts +16 -0
  264. package/vite.config.ts +4 -2
  265. package/dist/Game/EffectManager.d.ts +0 -5
  266. package/dist/components/effects/index.d.ts +0 -4
  267. package/dist/index.js.map +0 -1
  268. package/dist/index10.js +0 -8
  269. package/dist/index10.js.map +0 -1
  270. package/dist/index11.js +0 -10
  271. package/dist/index11.js.map +0 -1
  272. package/dist/index12.js +0 -8
  273. package/dist/index12.js.map +0 -1
  274. package/dist/index13.js +0 -17
  275. package/dist/index13.js.map +0 -1
  276. package/dist/index14.js +0 -107
  277. package/dist/index14.js.map +0 -1
  278. package/dist/index15.js +0 -50
  279. package/dist/index15.js.map +0 -1
  280. package/dist/index16.js +0 -191
  281. package/dist/index16.js.map +0 -1
  282. package/dist/index17.js +0 -9
  283. package/dist/index17.js.map +0 -1
  284. package/dist/index18.js +0 -387
  285. package/dist/index18.js.map +0 -1
  286. package/dist/index19.js +0 -31
  287. package/dist/index19.js.map +0 -1
  288. package/dist/index2.js +0 -181
  289. package/dist/index2.js.map +0 -1
  290. package/dist/index20.js +0 -24
  291. package/dist/index20.js.map +0 -1
  292. package/dist/index21.js +0 -2421
  293. package/dist/index21.js.map +0 -1
  294. package/dist/index22.js +0 -114
  295. package/dist/index22.js.map +0 -1
  296. package/dist/index23.js +0 -109
  297. package/dist/index23.js.map +0 -1
  298. package/dist/index24.js +0 -71
  299. package/dist/index24.js.map +0 -1
  300. package/dist/index25.js +0 -21
  301. package/dist/index25.js.map +0 -1
  302. package/dist/index26.js +0 -41
  303. package/dist/index26.js.map +0 -1
  304. package/dist/index27.js +0 -5
  305. package/dist/index27.js.map +0 -1
  306. package/dist/index28.js +0 -322
  307. package/dist/index28.js.map +0 -1
  308. package/dist/index29.js +0 -27
  309. package/dist/index29.js.map +0 -1
  310. package/dist/index3.js +0 -87
  311. package/dist/index3.js.map +0 -1
  312. package/dist/index30.js +0 -11
  313. package/dist/index30.js.map +0 -1
  314. package/dist/index31.js +0 -11
  315. package/dist/index31.js.map +0 -1
  316. package/dist/index32.js +0 -174
  317. package/dist/index32.js.map +0 -1
  318. package/dist/index33.js +0 -501
  319. package/dist/index33.js.map +0 -1
  320. package/dist/index34.js +0 -12
  321. package/dist/index34.js.map +0 -1
  322. package/dist/index35.js +0 -4403
  323. package/dist/index35.js.map +0 -1
  324. package/dist/index36.js +0 -316
  325. package/dist/index36.js.map +0 -1
  326. package/dist/index37.js +0 -61
  327. package/dist/index37.js.map +0 -1
  328. package/dist/index38.js +0 -20
  329. package/dist/index38.js.map +0 -1
  330. package/dist/index39.js +0 -20
  331. package/dist/index39.js.map +0 -1
  332. package/dist/index4.js +0 -67
  333. package/dist/index4.js.map +0 -1
  334. package/dist/index5.js +0 -16
  335. package/dist/index5.js.map +0 -1
  336. package/dist/index6.js +0 -17
  337. package/dist/index6.js.map +0 -1
  338. package/dist/index7.js +0 -39
  339. package/dist/index7.js.map +0 -1
  340. package/dist/index8.js +0 -108
  341. package/dist/index8.js.map +0 -1
  342. package/dist/index9.js +0 -76
  343. package/dist/index9.js.map +0 -1
  344. package/src/Game/EffectManager.ts +0 -20
  345. package/src/components/gui/dialogbox/itemMenu.ce +0 -23
  346. package/src/components/gui/dialogbox/selection.ce +0 -67
  347. /package/src/components/{effects → animations}/hit.ce +0 -0
package/src/Gui/Gui.ts CHANGED
@@ -1,46 +1,258 @@
1
1
  import { Context, inject } from "@signe/di";
2
- import { signal } from "canvasengine";
2
+ import { signal, Signal, WritableSignal } from "canvasengine";
3
3
  import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
4
- import { PrebuiltGui } from "../components/gui";
4
+ import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent } from "../components/gui";
5
+ import { combineLatest, Subscription } from "rxjs";
6
+ import { PrebuiltGui } from "@rpgjs/common";
5
7
 
6
8
  interface GuiOptions {
7
- name: string;
8
- component: any;
9
+ name?: string;
10
+ id?: string;
11
+ component?: any;
9
12
  display?: boolean;
10
13
  data?: any;
14
+ /**
15
+ * Auto display the GUI when added to the system
16
+ * @default false
17
+ */
18
+ autoDisplay?: boolean;
19
+ /**
20
+ * Function that returns an array of Signal dependencies
21
+ * The GUI will only display when all dependencies are resolved (!= undefined)
22
+ * @returns Array of Signal dependencies
23
+ */
24
+ dependencies?: () => Signal[];
25
+ /**
26
+ * Attach the GUI to sprites instead of displaying globally
27
+ * When true, the GUI will be rendered in character.ce for each sprite
28
+ * @default false
29
+ */
30
+ attachToSprite?: boolean;
31
+ /**
32
+ * Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.
33
+ */
34
+ rpgAttachToSprite?: boolean;
35
+ }
36
+
37
+ export interface GuiInstance {
38
+ name: string;
39
+ component: any;
40
+ display: WritableSignal<boolean>;
41
+ data: WritableSignal<any>;
42
+ autoDisplay: boolean;
43
+ dependencies?: Signal[];
44
+ subscription?: Subscription;
45
+ attachToSprite?: boolean;
46
+ }
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
+
61
+ interface GuiAction {
62
+ guiId: string;
63
+ name: string;
64
+ data: any;
65
+ clientActionId: string;
11
66
  }
12
67
 
68
+ type OptimisticReducer = (data: any, action: GuiAction) => any;
69
+
13
70
  const throwError = (id: string) => {
14
71
  throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;
15
72
  };
16
73
 
74
+ const updateItemQuantity = (items: any[], id: string) => {
75
+ const index = items.findIndex((item) => item?.id === id);
76
+ if (index === -1) return items;
77
+ const item = items[index];
78
+ if (item?.usable === false) return items;
79
+ if (item?.consumable === false) return items;
80
+ const quantity = typeof item?.quantity === "number" ? item.quantity : 1;
81
+ const nextQuantity = Math.max(0, quantity - 1);
82
+ if (nextQuantity === quantity) return items;
83
+ if (nextQuantity <= 0) {
84
+ return items.filter((_, idx) => idx !== index);
85
+ }
86
+ const nextItems = items.slice();
87
+ nextItems[index] = { ...item, quantity: nextQuantity };
88
+ return nextItems;
89
+ };
90
+
91
+ const updateEquippedFlag = (items: any[], id: string, equip: boolean) => {
92
+ const index = items.findIndex((item) => item?.id === id);
93
+ if (index === -1) return items;
94
+ const item = items[index];
95
+ if (item?.equipped === equip) return items;
96
+ const nextItems = items.slice();
97
+ nextItems[index] = { ...item, equipped: equip };
98
+ return nextItems;
99
+ };
100
+
101
+ const mainMenuOptimisticReducer: OptimisticReducer = (data, action) => {
102
+ if (!data || typeof data !== "object") return data;
103
+ if (action.name === "useItem") {
104
+ if (!Array.isArray(data.items)) return data;
105
+ const id = action.data?.id;
106
+ if (!id) return data;
107
+ const nextItems = updateItemQuantity(data.items, id);
108
+ if (nextItems === data.items) return data;
109
+ return { ...data, items: nextItems };
110
+ }
111
+ if (action.name === "equipItem") {
112
+ const id = action.data?.id;
113
+ if (!id || typeof action.data?.equip !== "boolean") return data;
114
+ const equip = action.data.equip;
115
+ let nextItems = data.items;
116
+ let nextEquips = data.equips;
117
+ if (Array.isArray(data.items)) {
118
+ nextItems = updateEquippedFlag(data.items, id, equip);
119
+ }
120
+ if (Array.isArray(data.equips)) {
121
+ nextEquips = updateEquippedFlag(data.equips, id, equip);
122
+ }
123
+ if (nextItems === data.items && nextEquips === data.equips) return data;
124
+ return {
125
+ ...data,
126
+ ...(nextItems !== data.items ? { items: nextItems } : {}),
127
+ ...(nextEquips !== data.equips ? { equips: nextEquips } : {})
128
+ };
129
+ }
130
+ return data;
131
+ };
132
+
17
133
  export class RpgGui {
18
134
  private webSocket: AbstractWebsocket;
19
- gui = signal<any>({});
135
+ gui = signal<Record<string, GuiInstance>>({});
136
+ extraGuis: GuiInstance[] = [];
137
+ private vueGuiInstance: VueGuiBridge | null = null;
138
+ private optimisticReducers = new Map<string, OptimisticReducer[]>();
139
+ private pendingActions = new Map<string, GuiAction[]>();
140
+ /**
141
+ * Signal tracking which player IDs should display attached GUIs
142
+ * Key: player ID, Value: boolean (true = show, false = hide)
143
+ */
144
+ attachedGuiDisplayState = signal<Record<string, boolean>>({});
20
145
 
21
146
  constructor(private context: Context) {
22
147
  this.webSocket = inject(context, WebSocketToken);
23
148
  this.add({
24
149
  name: "rpg-dialog",
25
- component: PrebuiltGui.Dialogbox,
150
+ component: DialogboxComponent,
151
+ });
152
+ this.add({
153
+ name: PrebuiltGui.MainMenu,
154
+ component: MainMenuComponent,
155
+ });
156
+ this.add({
157
+ name: PrebuiltGui.Shop,
158
+ component: ShopComponent,
159
+ });
160
+ this.add({
161
+ name: PrebuiltGui.Notification,
162
+ component: NotificationComponent,
163
+ autoDisplay: true,
164
+ });
165
+ this.add({
166
+ name: PrebuiltGui.Save,
167
+ component: SaveLoadComponent,
168
+ });
169
+ this.add({
170
+ name: PrebuiltGui.TitleScreen,
171
+ component: TitleScreenComponent,
172
+ });
173
+ this.add({
174
+ name: PrebuiltGui.Gameover,
175
+ component: GameoverComponent,
26
176
  });
177
+
178
+ this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
27
179
  }
28
180
 
29
181
  async _initialize() {
30
182
  this.webSocket.on("gui.open", (data: { guiId: string; data: any }) => {
183
+ this.clearPendingActions(data.guiId);
31
184
  this.display(data.guiId, data.data);
32
185
  });
33
186
 
34
187
  this.webSocket.on("gui.exit", (guiId: string) => {
35
188
  this.hide(guiId);
36
189
  });
190
+
191
+ this.webSocket.on("gui.update", (payload: { guiId: string; data: any; clientActionId?: string }) => {
192
+ this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);
193
+ });
194
+
195
+ /**
196
+ * Listen for tooltip display state changes from server
197
+ * This is triggered by showAttachedGui/hideAttachedGui on the server
198
+ */
199
+ this.webSocket.on("gui.tooltip", (data: { players: string[]; display: boolean }) => {
200
+ const currentState = { ...this.attachedGuiDisplayState() };
201
+ data.players.forEach((playerId) => {
202
+ currentState[playerId] = data.display;
203
+ });
204
+ this.attachedGuiDisplayState.set(currentState);
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Set the VueGui instance reference for Vue component management
210
+ * This is called by VueGui when it's initialized
211
+ *
212
+ * @param vueGuiInstance - The VueGui instance
213
+ */
214
+ _setVueGuiInstance(vueGuiInstance: any) {
215
+ this.vueGuiInstance = vueGuiInstance;
216
+ this._initializeVueComponents();
217
+ }
218
+
219
+ /**
220
+ * Notify VueGui about GUI state changes
221
+ * This synchronizes the Vue component display state
222
+ *
223
+ * @param guiId - The GUI component ID
224
+ * @param display - Display state
225
+ * @param data - Component data
226
+ */
227
+ private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {
228
+ const extraGui = this.extraGuis.find(gui => gui.name === guiId);
229
+ if (!extraGui) return;
230
+ this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));
231
+ }
232
+
233
+ /**
234
+ * Initialize Vue components in the VueGui instance
235
+ * This should be called after VueGui is mounted
236
+ */
237
+ _initializeVueComponents() {
238
+ this.vueGuiInstance?.initializeGuiStates?.(
239
+ this.extraGuis.map(gui => this.toGuiState(gui))
240
+ );
37
241
  }
38
242
 
39
243
  guiInteraction(guiId: string, name: string, data: any) {
244
+ const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;
245
+ const actionData = { ...(data || {}), clientActionId };
246
+ this.applyOptimisticAction({
247
+ guiId,
248
+ name,
249
+ data: actionData,
250
+ clientActionId
251
+ });
40
252
  this.webSocket.emit("gui.interaction", {
41
253
  guiId,
42
254
  name,
43
- data,
255
+ data: actionData,
44
256
  });
45
257
  }
46
258
 
@@ -51,42 +263,373 @@ export class RpgGui {
51
263
  });
52
264
  }
53
265
 
54
- add(gui: GuiOptions) {
55
- this.gui()[gui.name] = {
56
- name: gui.name,
57
- component: gui.component,
58
- display: signal(gui.display || false),
59
- data: signal(gui.data || {}),
266
+ /**
267
+ * Add a GUI component to the system
268
+ *
269
+ * By default, only CanvasEngine components (.ce files) are accepted.
270
+ * Vue components should be handled by the @rpgjs/vue package.
271
+ *
272
+ * @param gui - GUI configuration options
273
+ * @param gui.name - Name or ID of the GUI component
274
+ * @param gui.id - Alternative ID if name is not provided
275
+ * @param gui.component - The component to render (must be a CanvasEngine component)
276
+ * @param gui.display - Initial display state (default: false)
277
+ * @param gui.data - Initial data for the component
278
+ * @param gui.autoDisplay - Auto display when added (default: false)
279
+ * @param gui.dependencies - Function returning Signal dependencies
280
+ * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * gui.add({
285
+ * name: 'inventory',
286
+ * component: InventoryComponent, // Must be a .ce component
287
+ * autoDisplay: true,
288
+ * dependencies: () => [playerSignal, inventorySignal]
289
+ * });
290
+ *
291
+ * // Attach to sprites
292
+ * gui.add({
293
+ * name: 'tooltip',
294
+ * component: TooltipComponent,
295
+ * attachToSprite: true
296
+ * });
297
+ * ```
298
+ */
299
+ add(gui: GuiOptions | any) {
300
+ const component = this.resolveComponent(gui);
301
+ const guiId = this.resolveGuiId(gui, component);
302
+ if (!guiId) {
303
+ throw new Error("GUI must have a name or id");
304
+ }
305
+ const attachToSprite = this.resolveAttachToSprite(gui, component);
306
+ const guiInstance: GuiInstance = {
307
+ name: guiId,
308
+ component,
309
+ display: signal<boolean>(gui.display || false),
310
+ data: signal<any>(gui.data || {}),
311
+ autoDisplay: gui.autoDisplay || false,
312
+ dependencies: gui.dependencies ? gui.dependencies() : [],
313
+ attachToSprite,
60
314
  };
315
+
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();
327
+
328
+ if (guiInstance.autoDisplay) {
329
+ this.display(guiId, gui.data);
330
+ } else {
331
+ this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());
332
+ }
333
+ return;
334
+ }
335
+
336
+ this.removeVueGui(guiId);
337
+ this.gui()[guiId] = guiInstance;
338
+ this._initializeVueComponents();
339
+
340
+ // Auto display if enabled and it's a CanvasEngine component
341
+ if (guiInstance.autoDisplay && typeof gui.component === 'function') {
342
+ this.display(guiId, gui.data);
343
+ }
344
+ }
345
+
346
+ registerOptimisticReducer(guiId: string, reducer: OptimisticReducer) {
347
+ const existing = this.optimisticReducers.get(guiId) || [];
348
+ this.optimisticReducers.set(guiId, existing.concat(reducer));
349
+ }
350
+
351
+ /**
352
+ * Get all attached GUI components (attachToSprite: true)
353
+ *
354
+ * Returns all GUI instances that are configured to be attached to sprites.
355
+ * These GUIs should be rendered in character.ce instead of canvas.ce.
356
+ *
357
+ * @returns Array of GUI instances with attachToSprite: true
358
+ *
359
+ * @example
360
+ * ```ts
361
+ * const attachedGuis = gui.getAttachedGuis();
362
+ * // Use in character.ce to render tooltips
363
+ * ```
364
+ */
365
+ getAttachedGuis(): GuiInstance[] {
366
+ return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);
367
+ }
368
+
369
+ getVueGuis(): GuiInstance[] {
370
+ return [...this.extraGuis];
61
371
  }
62
372
 
63
- get(id: string | GuiOptions) {
64
- if (typeof id != "string") {
65
- id = id.name;
373
+ getAttachedVueGuis(): GuiInstance[] {
374
+ return this.extraGuis.filter(gui => gui.attachToSprite === true);
375
+ }
376
+
377
+ /**
378
+ * Check if a player should display attached GUIs
379
+ *
380
+ * @param playerId - The player ID to check
381
+ * @returns true if attached GUIs should be displayed for this player
382
+ */
383
+ shouldDisplayAttachedGui(playerId: string): boolean {
384
+ return this.attachedGuiDisplayState()[playerId] === true;
385
+ }
386
+
387
+ get(id: string): GuiInstance | undefined {
388
+ // Check CanvasEngine GUIs first
389
+ const canvasGui = this.gui()[id];
390
+ if (canvasGui) {
391
+ return canvasGui;
66
392
  }
67
- return this.gui()[id];
393
+
394
+ // Check Vue GUIs in extraGuis
395
+ return this.extraGuis.find(gui => gui.name === id);
68
396
  }
69
397
 
70
398
  exists(id: string): boolean {
71
399
  return !!this.get(id);
72
400
  }
73
401
 
74
- getAll() {
75
- return this.gui();
402
+ getAll(): Record<string, GuiInstance> {
403
+ const allGuis = { ...this.gui() };
404
+
405
+ // Add extraGuis to the result
406
+ this.extraGuis.forEach(gui => {
407
+ allGuis[gui.name] = gui;
408
+ });
409
+
410
+ return allGuis;
76
411
  }
77
412
 
78
- display(id: string, data = {}) {
413
+ /**
414
+ * Display a GUI component
415
+ *
416
+ * Displays the GUI immediately if no dependencies are configured,
417
+ * or waits for all dependencies to be resolved if dependencies are present.
418
+ * Automatically manages subscriptions to prevent memory leaks.
419
+ * Works with both CanvasEngine components and Vue components.
420
+ *
421
+ * @param id - The GUI component ID
422
+ * @param data - Data to pass to the component
423
+ * @param dependencies - Optional runtime dependencies (overrides config dependencies)
424
+ *
425
+ * @example
426
+ * ```ts
427
+ * // Display immediately
428
+ * gui.display('inventory', { items: [] });
429
+ *
430
+ * // Display with runtime dependencies
431
+ * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
432
+ * ```
433
+ */
434
+ display(id: string, data = {}, dependencies: Signal[] = []) {
79
435
  if (!this.exists(id)) {
80
436
  throw throwError(id);
81
437
  }
82
- this.get(id).data.set(data);
83
- this.get(id).display.set(true);
438
+
439
+ const guiInstance = this.get(id)!;
440
+
441
+ // Check if it's a Vue component (in extraGuis)
442
+ const isVueComponent = this.extraGuis.some(gui => gui.name === id);
443
+
444
+ if (isVueComponent) {
445
+ // Handle Vue component display
446
+ this._handleVueComponentDisplay(id, data, dependencies, guiInstance);
447
+ } else {
448
+ guiInstance.data.set(data);
449
+ guiInstance.display.set(true);
450
+ }
84
451
  }
85
452
 
453
+ isDisplaying(id: string): boolean {
454
+ const guiInstance = this.get(id);
455
+ if (!guiInstance) return false;
456
+ return guiInstance.display();
457
+ }
458
+
459
+ /**
460
+ * Handle Vue component display logic
461
+ *
462
+ * @param id - GUI component ID
463
+ * @param data - Component data
464
+ * @param dependencies - Runtime dependencies
465
+ * @param guiInstance - GUI instance
466
+ */
467
+ private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance) {
468
+ // Unsubscribe from previous subscription if exists
469
+ if (guiInstance.subscription) {
470
+ guiInstance.subscription.unsubscribe();
471
+ guiInstance.subscription = undefined;
472
+ }
473
+
474
+ // Use runtime dependencies or config dependencies
475
+ const deps = dependencies.length > 0
476
+ ? dependencies
477
+ : (guiInstance.dependencies ?? []);
478
+
479
+ if (deps.length > 0) {
480
+ // Subscribe to dependencies
481
+ guiInstance.subscription = combineLatest(
482
+ deps.map(dependency => dependency.observable)
483
+ ).subscribe((values) => {
484
+ if (values.every(value => value !== undefined)) {
485
+ guiInstance.data.set(data);
486
+ guiInstance.display.set(true);
487
+ this._notifyVueGui(id, true, data);
488
+ }
489
+ });
490
+ return;
491
+ }
492
+
493
+ // No dependencies, display immediately
494
+ guiInstance.data.set(data);
495
+ guiInstance.display.set(true);
496
+ this._notifyVueGui(id, true, data);
497
+ }
498
+
499
+ /**
500
+ * Hide a GUI component
501
+ *
502
+ * Hides the GUI and cleans up any active subscriptions.
503
+ * Works with both CanvasEngine components and Vue components.
504
+ *
505
+ * @param id - The GUI component ID
506
+ *
507
+ * @example
508
+ * ```ts
509
+ * gui.hide('inventory');
510
+ * ```
511
+ */
86
512
  hide(id: string) {
87
513
  if (!this.exists(id)) {
88
514
  throw throwError(id);
89
515
  }
90
- this.get(id).display.set(false);
516
+
517
+ const guiInstance = this.get(id)!;
518
+
519
+ // Unsubscribe if there's an active subscription
520
+ if (guiInstance.subscription) {
521
+ guiInstance.subscription.unsubscribe();
522
+ guiInstance.subscription = undefined;
523
+ }
524
+
525
+ guiInstance.display.set(false)
526
+
527
+ // Check if it's a Vue component and notify VueGui
528
+ const isVueComponent = this.extraGuis.some(gui => gui.name === id);
529
+ if (isVueComponent) {
530
+ this._notifyVueGui(id, false);
531
+ }
532
+ }
533
+
534
+ private isVueComponent(id: string) {
535
+ return this.extraGuis.some(gui => gui.name === id);
536
+ }
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
+
580
+ private clearPendingActions(guiId: string) {
581
+ this.pendingActions.delete(guiId);
582
+ }
583
+
584
+ private applyReducers(guiId: string, data: any, actions: GuiAction[]) {
585
+ const reducers = this.optimisticReducers.get(guiId);
586
+ if (!reducers || reducers.length === 0) return data;
587
+ let next = data;
588
+ for (const action of actions) {
589
+ for (const reducer of reducers) {
590
+ const updated = reducer(next, action);
591
+ if (updated !== undefined && updated !== null && updated !== next) {
592
+ next = updated;
593
+ }
594
+ }
595
+ }
596
+ return next;
597
+ }
598
+
599
+ private applyOptimisticAction(action: GuiAction) {
600
+ const guiInstance = this.get(action.guiId);
601
+ if (!guiInstance) return;
602
+ const reducers = this.optimisticReducers.get(action.guiId);
603
+ if (!reducers || reducers.length === 0) return;
604
+ const currentData = guiInstance.data();
605
+ const nextData = this.applyReducers(action.guiId, currentData, [action]);
606
+ if (nextData === currentData) return;
607
+ guiInstance.data.set(nextData);
608
+ const pending = this.pendingActions.get(action.guiId) || [];
609
+ pending.push(action);
610
+ this.pendingActions.set(action.guiId, pending);
611
+ if (this.isVueComponent(action.guiId)) {
612
+ this._notifyVueGui(action.guiId, guiInstance.display(), nextData);
613
+ }
614
+ }
615
+
616
+ private applyServerUpdate(guiId: string, data: any, clientActionId?: string) {
617
+ const guiInstance = this.get(guiId);
618
+ if (!guiInstance) return;
619
+ let pending = this.pendingActions.get(guiId) || [];
620
+ if (clientActionId) {
621
+ pending = pending.filter(action => action.clientActionId !== clientActionId);
622
+ } else {
623
+ pending = [];
624
+ }
625
+ let nextData = data;
626
+ if (pending.length) {
627
+ nextData = this.applyReducers(guiId, nextData, pending);
628
+ }
629
+ guiInstance.data.set(nextData);
630
+ this.pendingActions.set(guiId, pending);
631
+ if (this.isVueComponent(guiId)) {
632
+ this._notifyVueGui(guiId, guiInstance.display(), nextData);
633
+ }
91
634
  }
92
635
  }