@rpgjs/client 5.0.0-beta.2 → 5.0.0-beta.22

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 (325) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/dist/Game/AnimationManager.d.ts +2 -1
  3. package/dist/Game/AnimationManager.js +21 -9
  4. package/dist/Game/AnimationManager.js.map +1 -1
  5. package/dist/Game/AnimationManager.spec.d.ts +1 -0
  6. package/dist/Game/ClientVisuals.d.ts +61 -0
  7. package/dist/Game/ClientVisuals.js +96 -0
  8. package/dist/Game/ClientVisuals.js.map +1 -0
  9. package/dist/Game/ClientVisuals.spec.d.ts +1 -0
  10. package/dist/Game/Event.js.map +1 -1
  11. package/dist/Game/EventComponentResolver.d.ts +16 -0
  12. package/dist/Game/EventComponentResolver.js +52 -0
  13. package/dist/Game/EventComponentResolver.js.map +1 -0
  14. package/dist/Game/EventComponentResolver.spec.d.ts +1 -0
  15. package/dist/Game/Map.d.ts +9 -1
  16. package/dist/Game/Map.js +83 -5
  17. package/dist/Game/Map.js.map +1 -1
  18. package/dist/Game/Object.d.ts +49 -15
  19. package/dist/Game/Object.js +101 -42
  20. package/dist/Game/Object.js.map +1 -1
  21. package/dist/Game/Object.spec.d.ts +1 -0
  22. package/dist/Game/Player.js.map +1 -1
  23. package/dist/Game/ProjectileManager.d.ts +98 -0
  24. package/dist/Game/ProjectileManager.js +196 -0
  25. package/dist/Game/ProjectileManager.js.map +1 -0
  26. package/dist/Game/ProjectileManager.spec.d.ts +1 -0
  27. package/dist/Gui/Gui.d.ts +20 -6
  28. package/dist/Gui/Gui.js +132 -57
  29. package/dist/Gui/Gui.js.map +1 -1
  30. package/dist/Gui/Gui.spec.d.ts +1 -0
  31. package/dist/Gui/NotificationManager.js.map +1 -1
  32. package/dist/Resource.js +1 -1
  33. package/dist/Resource.js.map +1 -1
  34. package/dist/RpgClient.d.ts +194 -15
  35. package/dist/RpgClientEngine.d.ts +191 -19
  36. package/dist/RpgClientEngine.js +693 -74
  37. package/dist/RpgClientEngine.js.map +1 -1
  38. package/dist/Sound.js.map +1 -1
  39. package/dist/_virtual/{_@oxc-project_runtime@0.122.0/helpers → _@oxc-project_runtime@0.133.0/helpers/esm}/decorate.js +1 -1
  40. package/dist/_virtual/{_@oxc-project_runtime@0.122.0/helpers → _@oxc-project_runtime@0.133.0/helpers/esm}/decorateMetadata.js +1 -1
  41. package/dist/_virtual/_rolldown/runtime.js +13 -0
  42. package/dist/components/animations/animation.ce.js +6 -5
  43. package/dist/components/animations/animation.ce.js.map +1 -1
  44. package/dist/components/animations/fx.ce.js +60 -0
  45. package/dist/components/animations/fx.ce.js.map +1 -0
  46. package/dist/components/animations/hit.ce.js +24 -28
  47. package/dist/components/animations/hit.ce.js.map +1 -1
  48. package/dist/components/animations/index.d.ts +1 -0
  49. package/dist/components/animations/index.js +6 -4
  50. package/dist/components/animations/index.js.map +1 -1
  51. package/dist/components/character.ce.js +748 -249
  52. package/dist/components/character.ce.js.map +1 -1
  53. package/dist/components/dynamics/bar.ce.js +99 -0
  54. package/dist/components/dynamics/bar.ce.js.map +1 -0
  55. package/dist/components/dynamics/image.ce.js +26 -0
  56. package/dist/components/dynamics/image.ce.js.map +1 -0
  57. package/dist/components/dynamics/parse-value.d.ts +3 -0
  58. package/dist/components/dynamics/parse-value.js +54 -35
  59. package/dist/components/dynamics/parse-value.js.map +1 -1
  60. package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
  61. package/dist/components/dynamics/shape-utils.d.ts +16 -0
  62. package/dist/components/dynamics/shape-utils.js +73 -0
  63. package/dist/components/dynamics/shape-utils.js.map +1 -0
  64. package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
  65. package/dist/components/dynamics/shape.ce.js +86 -0
  66. package/dist/components/dynamics/shape.ce.js.map +1 -0
  67. package/dist/components/dynamics/text.ce.js +36 -56
  68. package/dist/components/dynamics/text.ce.js.map +1 -1
  69. package/dist/components/gui/box.ce.js +9 -9
  70. package/dist/components/gui/box.ce.js.map +1 -1
  71. package/dist/components/gui/dialogbox/index.ce.js +75 -65
  72. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  73. package/dist/components/gui/gameover.ce.js +47 -68
  74. package/dist/components/gui/gameover.ce.js.map +1 -1
  75. package/dist/components/gui/hud/hud.ce.js +23 -31
  76. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  77. package/dist/components/gui/menu/equip-menu.ce.js +121 -172
  78. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
  79. package/dist/components/gui/menu/exit-menu.ce.js +15 -10
  80. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
  81. package/dist/components/gui/menu/items-menu.ce.js +62 -77
  82. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  83. package/dist/components/gui/menu/main-menu.ce.js +89 -104
  84. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  85. package/dist/components/gui/menu/options-menu.ce.js +12 -8
  86. package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
  87. package/dist/components/gui/menu/skills-menu.ce.js +17 -19
  88. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
  89. package/dist/components/gui/mobile/index.d.ts +51 -2
  90. package/dist/components/gui/mobile/index.js +14 -6
  91. package/dist/components/gui/mobile/index.js.map +1 -1
  92. package/dist/components/gui/mobile/index.spec.d.ts +1 -0
  93. package/dist/components/gui/mobile/mobile.ce.js +309 -59
  94. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  95. package/dist/components/gui/notification/notification.ce.js +28 -26
  96. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  97. package/dist/components/gui/save-load.ce.js +81 -256
  98. package/dist/components/gui/save-load.ce.js.map +1 -1
  99. package/dist/components/gui/shop/shop.ce.js +107 -142
  100. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  101. package/dist/components/gui/title-screen.ce.js +50 -73
  102. package/dist/components/gui/title-screen.ce.js.map +1 -1
  103. package/dist/components/index.d.ts +2 -1
  104. package/dist/components/index.js +1 -0
  105. package/dist/components/interaction-components.ce.js +22 -0
  106. package/dist/components/interaction-components.ce.js.map +1 -0
  107. package/dist/components/player-components-utils.d.ts +67 -0
  108. package/dist/components/player-components-utils.js +162 -0
  109. package/dist/components/player-components-utils.js.map +1 -0
  110. package/dist/components/player-components-utils.spec.d.ts +1 -0
  111. package/dist/components/player-components.ce.js +191 -0
  112. package/dist/components/player-components.ce.js.map +1 -0
  113. package/dist/components/prebuilt/hp-bar.ce.js +45 -45
  114. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  115. package/dist/components/prebuilt/light-halo.ce.js +39 -60
  116. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  117. package/dist/components/scenes/canvas.ce.js +205 -27
  118. package/dist/components/scenes/canvas.ce.js.map +1 -1
  119. package/dist/components/scenes/draw-map.ce.js +39 -39
  120. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  121. package/dist/components/scenes/event-layer.ce.js +51 -10
  122. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  123. package/dist/core/inject.js +1 -1
  124. package/dist/core/inject.js.map +1 -1
  125. package/dist/core/setup.js +1 -1
  126. package/dist/core/setup.js.map +1 -1
  127. package/dist/decorators/spritesheet.d.ts +1 -0
  128. package/dist/decorators/spritesheet.js +11 -0
  129. package/dist/decorators/spritesheet.js.map +1 -0
  130. package/dist/i18n.d.ts +55 -0
  131. package/dist/i18n.js +60 -0
  132. package/dist/i18n.js.map +1 -0
  133. package/dist/i18n.spec.d.ts +1 -0
  134. package/dist/index.d.ts +8 -0
  135. package/dist/index.js +29 -21
  136. package/dist/module.js +45 -4
  137. package/dist/module.js.map +1 -1
  138. package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@3.1.0}/node_modules/@signe/di/dist/index.js +7 -117
  139. package/dist/node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js.map +1 -0
  140. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js +239 -0
  141. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js.map +1 -0
  142. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
  143. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
  144. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js +698 -0
  145. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js.map +1 -0
  146. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/client/index.js +43 -0
  147. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  148. package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@3.1.0_react@19.2.7}/node_modules/@signe/sync/dist/index.js +57 -141
  149. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/index.js.map +1 -0
  150. package/dist/node_modules/.pnpm/{partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js → partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/index.js} +30 -17
  151. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/index.js.map +1 -0
  152. package/dist/node_modules/.pnpm/{partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js → partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/ws.js} +65 -34
  153. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/ws.js.map +1 -0
  154. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/index.js +13 -0
  155. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/index.js.map +1 -0
  156. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js +106 -0
  157. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js.map +1 -0
  158. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js +13 -0
  159. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js.map +1 -0
  160. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js +121 -0
  161. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js.map +1 -0
  162. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js +10 -0
  163. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js.map +1 -0
  164. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js +110 -0
  165. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js.map +1 -0
  166. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js +99 -0
  167. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js.map +1 -0
  168. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js +82 -0
  169. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js.map +1 -0
  170. package/dist/node_modules/.pnpm/{zod@3.24.2/node_modules/zod/lib/index.js → zod@3.25.76/node_modules/zod/v3/types.js} +74 -611
  171. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js.map +1 -0
  172. package/dist/presets/animation.js.map +1 -1
  173. package/dist/presets/faceset.js.map +1 -1
  174. package/dist/presets/icon.js.map +1 -1
  175. package/dist/presets/index.js.map +1 -1
  176. package/dist/presets/lpc.js.map +1 -1
  177. package/dist/presets/rmspritesheet.js.map +1 -1
  178. package/dist/services/AbstractSocket.d.ts +2 -0
  179. package/dist/services/AbstractSocket.js.map +1 -1
  180. package/dist/services/actionInput.d.ts +14 -0
  181. package/dist/services/actionInput.js +59 -0
  182. package/dist/services/actionInput.js.map +1 -0
  183. package/dist/services/actionInput.spec.d.ts +1 -0
  184. package/dist/services/cameraFollow.d.ts +51 -0
  185. package/dist/services/cameraFollow.js +134 -0
  186. package/dist/services/cameraFollow.js.map +1 -0
  187. package/dist/services/cameraFollow.spec.d.ts +1 -0
  188. package/dist/services/interactions.d.ts +159 -0
  189. package/dist/services/interactions.js +460 -0
  190. package/dist/services/interactions.js.map +1 -0
  191. package/dist/services/interactions.spec.d.ts +1 -0
  192. package/dist/services/keyboardControls.d.ts +1 -0
  193. package/dist/services/keyboardControls.js +1 -0
  194. package/dist/services/keyboardControls.js.map +1 -1
  195. package/dist/services/loadMap.d.ts +9 -0
  196. package/dist/services/loadMap.js +1 -1
  197. package/dist/services/loadMap.js.map +1 -1
  198. package/dist/services/mmorpg-connection.d.ts +5 -0
  199. package/dist/services/mmorpg-connection.js +50 -0
  200. package/dist/services/mmorpg-connection.js.map +1 -0
  201. package/dist/services/mmorpg-connection.spec.d.ts +1 -0
  202. package/dist/services/mmorpg.d.ts +11 -4
  203. package/dist/services/mmorpg.js +57 -33
  204. package/dist/services/mmorpg.js.map +1 -1
  205. package/dist/services/pointerContext.d.ts +11 -0
  206. package/dist/services/pointerContext.js +48 -0
  207. package/dist/services/pointerContext.js.map +1 -0
  208. package/dist/services/pointerContext.spec.d.ts +1 -0
  209. package/dist/services/save.js.map +1 -1
  210. package/dist/services/save.spec.d.ts +1 -0
  211. package/dist/services/standalone-message.d.ts +1 -0
  212. package/dist/services/standalone-message.js +9 -0
  213. package/dist/services/standalone-message.js.map +1 -0
  214. package/dist/services/standalone.d.ts +4 -1
  215. package/dist/services/standalone.js +36 -16
  216. package/dist/services/standalone.js.map +1 -1
  217. package/dist/services/standalone.spec.d.ts +1 -0
  218. package/dist/utils/getEntityProp.js +4 -3
  219. package/dist/utils/getEntityProp.js.map +1 -1
  220. package/dist/utils/getEntityProp.spec.d.ts +1 -0
  221. package/dist/utils/mapId.d.ts +1 -0
  222. package/dist/utils/mapId.js +6 -0
  223. package/dist/utils/mapId.js.map +1 -0
  224. package/dist/utils/readPropValue.d.ts +2 -0
  225. package/dist/utils/readPropValue.js +13 -0
  226. package/dist/utils/readPropValue.js.map +1 -0
  227. package/package.json +13 -13
  228. package/src/Game/AnimationManager.spec.ts +30 -0
  229. package/src/Game/AnimationManager.ts +26 -10
  230. package/src/Game/ClientVisuals.spec.ts +56 -0
  231. package/src/Game/ClientVisuals.ts +184 -0
  232. package/src/Game/EventComponentResolver.spec.ts +84 -0
  233. package/src/Game/EventComponentResolver.ts +74 -0
  234. package/src/Game/Map.ts +134 -2
  235. package/src/Game/Object.spec.ts +59 -0
  236. package/src/Game/Object.ts +181 -77
  237. package/src/Game/ProjectileManager.spec.ts +449 -0
  238. package/src/Game/ProjectileManager.ts +346 -0
  239. package/src/Gui/Gui.spec.ts +359 -0
  240. package/src/Gui/Gui.ts +178 -67
  241. package/src/Resource.ts +1 -2
  242. package/src/RpgClient.ts +220 -17
  243. package/src/RpgClientEngine.ts +995 -133
  244. package/src/components/animations/fx.ce +101 -0
  245. package/src/components/animations/index.ts +4 -2
  246. package/src/components/character.ce +839 -45
  247. package/src/components/dynamics/bar.ce +88 -0
  248. package/src/components/dynamics/image.ce +21 -0
  249. package/src/components/dynamics/parse-value.spec.ts +83 -0
  250. package/src/components/dynamics/parse-value.ts +111 -37
  251. package/src/components/dynamics/shape-utils.spec.ts +46 -0
  252. package/src/components/dynamics/shape-utils.ts +61 -0
  253. package/src/components/dynamics/shape.ce +90 -0
  254. package/src/components/dynamics/text.ce +35 -149
  255. package/src/components/gui/dialogbox/index.ce +52 -21
  256. package/src/components/gui/gameover.ce +6 -4
  257. package/src/components/gui/menu/equip-menu.ce +11 -9
  258. package/src/components/gui/menu/exit-menu.ce +6 -4
  259. package/src/components/gui/menu/items-menu.ce +11 -9
  260. package/src/components/gui/menu/main-menu.ce +14 -12
  261. package/src/components/gui/menu/options-menu.ce +4 -3
  262. package/src/components/gui/menu/skills-menu.ce +2 -1
  263. package/src/components/gui/mobile/index.spec.ts +94 -0
  264. package/src/components/gui/mobile/index.ts +74 -6
  265. package/src/components/gui/mobile/mobile.ce +347 -65
  266. package/src/components/gui/notification/notification.ce +7 -1
  267. package/src/components/gui/save-load.ce +13 -11
  268. package/src/components/gui/shop/shop.ce +20 -18
  269. package/src/components/gui/title-screen.ce +6 -4
  270. package/src/components/index.ts +2 -1
  271. package/src/components/interaction-components.ce +23 -0
  272. package/src/components/player-components-utils.spec.ts +109 -0
  273. package/src/components/player-components-utils.ts +205 -0
  274. package/src/components/player-components.ce +222 -0
  275. package/src/components/prebuilt/hp-bar.ce +4 -3
  276. package/src/components/prebuilt/light-halo.ce +2 -2
  277. package/src/components/scenes/canvas.ce +219 -15
  278. package/src/components/scenes/draw-map.ce +34 -22
  279. package/src/components/scenes/event-layer.ce +55 -4
  280. package/src/core/setup.ts +2 -2
  281. package/src/decorators/spritesheet.ts +8 -0
  282. package/src/i18n.spec.ts +39 -0
  283. package/src/i18n.ts +59 -0
  284. package/src/index.ts +19 -0
  285. package/src/module.ts +61 -11
  286. package/src/services/AbstractSocket.ts +3 -0
  287. package/src/services/actionInput.spec.ts +155 -0
  288. package/src/services/actionInput.ts +120 -0
  289. package/src/services/cameraFollow.spec.ts +220 -0
  290. package/src/services/cameraFollow.ts +222 -0
  291. package/src/services/interactions.spec.ts +175 -0
  292. package/src/services/interactions.ts +722 -0
  293. package/src/services/keyboardControls.ts +2 -1
  294. package/src/services/loadMap.ts +5 -1
  295. package/src/services/mmorpg-connection.spec.ts +99 -0
  296. package/src/services/mmorpg-connection.ts +69 -0
  297. package/src/services/mmorpg.ts +70 -36
  298. package/src/services/pointerContext.spec.ts +36 -0
  299. package/src/services/pointerContext.ts +84 -0
  300. package/src/services/save.spec.ts +127 -0
  301. package/src/services/standalone-message.ts +7 -0
  302. package/src/services/standalone.spec.ts +54 -0
  303. package/src/services/standalone.ts +44 -12
  304. package/src/utils/getEntityProp.spec.ts +96 -0
  305. package/src/utils/getEntityProp.ts +4 -3
  306. package/src/utils/mapId.ts +2 -0
  307. package/src/utils/readPropValue.ts +16 -0
  308. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
  309. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +0 -457
  310. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +0 -1
  311. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
  312. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
  313. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
  314. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
  315. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
  316. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
  317. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
  318. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
  319. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
  320. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
  321. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
  322. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +0 -1
  323. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +0 -1
  324. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +0 -2
  325. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +0 -1
@@ -0,0 +1,346 @@
1
+ import { computed, signal } from "canvasengine";
2
+ import { Hooks } from "@rpgjs/common";
3
+ import { normalizeRoomMapId } from "../utils/mapId";
4
+
5
+ export interface ClientProjectileSpawn {
6
+ id: string;
7
+ type: string;
8
+ ownerId?: string;
9
+ origin: { x: number; y: number };
10
+ direction: { x: number; y: number };
11
+ speed: number;
12
+ range: number;
13
+ ttl: number;
14
+ spawnTick: number;
15
+ delay?: number;
16
+ index?: number;
17
+ count?: number;
18
+ params?: Record<string, unknown>;
19
+ collisionMask?: number;
20
+ ignoreOwner?: boolean;
21
+ predictImpact?: boolean;
22
+ }
23
+
24
+ export interface ClientProjectileImpact {
25
+ id: string;
26
+ targetId?: string;
27
+ x: number;
28
+ y: number;
29
+ distance?: number;
30
+ }
31
+
32
+ export interface ClientProjectileDestroy {
33
+ id: string;
34
+ reason?: string;
35
+ targetId?: string;
36
+ x?: number;
37
+ y?: number;
38
+ distance?: number;
39
+ }
40
+
41
+ export interface RenderedProjectileProps extends ClientProjectileSpawn {
42
+ x: number;
43
+ y: number;
44
+ angle: number;
45
+ distance: number;
46
+ elapsed: number;
47
+ progress: number;
48
+ impact?: ClientProjectileImpact;
49
+ impactElapsed?: number;
50
+ impactProgress?: number;
51
+ destroyed?: boolean;
52
+ }
53
+
54
+ export interface RenderedProjectile {
55
+ id: string;
56
+ type: string;
57
+ component: any;
58
+ props: RenderedProjectileProps;
59
+ }
60
+
61
+ export type ProjectilePredictionResolver = (
62
+ projectile: ClientProjectileSpawn,
63
+ ) => ClientProjectileImpact | null | undefined;
64
+
65
+ export interface ProjectileSpawnClock {
66
+ now?: number;
67
+ currentServerTick?: number;
68
+ tickDurationMs?: number;
69
+ mapId?: string;
70
+ }
71
+
72
+ interface RuntimeProjectile {
73
+ spawn: ClientProjectileSpawn;
74
+ component: any;
75
+ createdAt: number;
76
+ impact?: ClientProjectileImpact;
77
+ visualImpact?: ClientProjectileImpact;
78
+ predictedImpact?: ClientProjectileImpact;
79
+ impactStartedAt?: number;
80
+ destroyAt?: number;
81
+ destroyReason?: string;
82
+ }
83
+
84
+ export class ProjectileManager {
85
+ private readonly components = new Map<string, any>();
86
+ private readonly projectiles = new Map<string, RuntimeProjectile>();
87
+ private readonly version = signal(0);
88
+ private readonly impactDurationMs = 350;
89
+ private mapId?: string;
90
+
91
+ constructor(
92
+ private readonly hooks: Hooks,
93
+ private readonly predictionResolver?: ProjectilePredictionResolver,
94
+ ) {}
95
+
96
+ current = computed<RenderedProjectile[]>(() => {
97
+ this.version();
98
+ const now = Date.now();
99
+ const rendered: RenderedProjectile[] = [];
100
+ for (const projectile of this.projectiles.values()) {
101
+ const props = this.toProps(projectile, now);
102
+ if (!props) {
103
+ continue;
104
+ }
105
+ rendered.push({
106
+ id: projectile.spawn.id,
107
+ type: projectile.spawn.type,
108
+ component: projectile.component,
109
+ props,
110
+ });
111
+ }
112
+ return rendered;
113
+ });
114
+
115
+ register(type: string, component: any): any {
116
+ this.components.set(type, component);
117
+ return component;
118
+ }
119
+
120
+ get(type: string): any {
121
+ return this.components.get(type);
122
+ }
123
+
124
+ setMapId(mapId: string | undefined): void {
125
+ const normalizedMapId = normalizeRoomMapId(mapId);
126
+ if (this.mapId === normalizedMapId) return;
127
+ this.mapId = normalizedMapId;
128
+ this.clear();
129
+ }
130
+
131
+ getMapId(): string | undefined {
132
+ return this.mapId;
133
+ }
134
+
135
+ spawnBatch(projectiles: ClientProjectileSpawn[], clock: ProjectileSpawnClock = {}): void {
136
+ if (!this.acceptsMap(clock.mapId)) return;
137
+ const now = clock.now ?? Date.now();
138
+ for (const projectile of projectiles) {
139
+ const component = this.components.get(projectile.type);
140
+ if (!component) {
141
+ continue;
142
+ }
143
+ const runtime: RuntimeProjectile = {
144
+ spawn: {
145
+ ...projectile,
146
+ delay: projectile.delay ?? 0,
147
+ index: projectile.index ?? 0,
148
+ count: projectile.count ?? 1,
149
+ },
150
+ component,
151
+ createdAt: now,
152
+ };
153
+ this.setPredictedImpact(runtime);
154
+ this.projectiles.set(projectile.id, runtime);
155
+ this.hooks.callHooks("client-projectiles-onSpawn", runtime.spawn).subscribe();
156
+ }
157
+ this.touch();
158
+ }
159
+
160
+ impactBatch(impacts: ClientProjectileImpact[], context: { mapId?: string } = {}): void {
161
+ if (!this.acceptsMap(context.mapId)) return;
162
+ const now = Date.now();
163
+ for (const impact of impacts) {
164
+ const projectile = this.projectiles.get(impact.id);
165
+ if (!projectile) {
166
+ continue;
167
+ }
168
+ this.setImpact(projectile, impact, now);
169
+ this.hooks.callHooks("client-projectiles-onImpact", this.toProps(projectile, now)).subscribe();
170
+ }
171
+ this.touch();
172
+ }
173
+
174
+ destroyBatch(projectiles: ClientProjectileDestroy[], context: { mapId?: string } = {}): void {
175
+ if (!this.acceptsMap(context.mapId)) return;
176
+ const now = Date.now();
177
+ for (const destroyed of projectiles) {
178
+ const projectile = this.projectiles.get(destroyed.id);
179
+ if (!projectile) {
180
+ continue;
181
+ }
182
+ if (destroyed.reason === "hit") {
183
+ const current = this.toProps(projectile, now);
184
+ this.setImpact(projectile, {
185
+ id: destroyed.id,
186
+ targetId: destroyed.targetId ?? projectile.impact?.targetId,
187
+ x: destroyed.x ?? projectile.impact?.x ?? current?.x ?? projectile.spawn.origin.x,
188
+ y: destroyed.y ?? projectile.impact?.y ?? current?.y ?? projectile.spawn.origin.y,
189
+ distance: destroyed.distance ?? projectile.impact?.distance ?? current?.distance,
190
+ }, now);
191
+ }
192
+ projectile.destroyReason = destroyed.reason;
193
+ projectile.destroyAt = projectile.destroyAt ?? (
194
+ projectile.impact && projectile.impactStartedAt !== undefined
195
+ ? projectile.impactStartedAt + this.impactDurationMs
196
+ : now
197
+ );
198
+ this.hooks.callHooks("client-projectiles-onDestroy", this.toProps(projectile, now)).subscribe();
199
+ }
200
+ this.touch();
201
+ }
202
+
203
+ clear(): void {
204
+ this.projectiles.clear();
205
+ this.touch();
206
+ }
207
+
208
+ step(): void {
209
+ const now = Date.now();
210
+ let changed = false;
211
+ for (const [id, projectile] of this.projectiles) {
212
+ const props = this.toProps(projectile, now);
213
+ if (
214
+ (!props && !this.isWaitingForDelay(projectile, now)) ||
215
+ (projectile.destroyAt !== undefined && now >= projectile.destroyAt)
216
+ ) {
217
+ this.projectiles.delete(id);
218
+ changed = true;
219
+ }
220
+ }
221
+ this.touch(changed || this.projectiles.size > 0);
222
+ }
223
+
224
+ private toProps(projectile: RuntimeProjectile, now: number): RenderedProjectileProps | null {
225
+ const spawn = projectile.spawn;
226
+ const delayMs = (spawn.delay ?? 0) * 1000;
227
+ const elapsedMs = now - projectile.createdAt - delayMs;
228
+ if (elapsedMs < 0) {
229
+ return null;
230
+ }
231
+ const elapsed = elapsedMs / 1000;
232
+ const ttl = Math.max(0.001, spawn.ttl);
233
+ const rawDistance = Math.min(spawn.speed * elapsed, spawn.range);
234
+ const predictedImpact = this.getActivePredictedImpact(projectile, now, rawDistance);
235
+ const visualImpact = projectile.visualImpact ?? projectile.impact;
236
+ const distance = visualImpact?.distance ?? predictedImpact?.distance ?? rawDistance;
237
+ const progress = Math.min(1, distance / spawn.range);
238
+ const x = visualImpact?.x ?? predictedImpact?.x ?? spawn.origin.x + spawn.direction.x * distance;
239
+ const y = visualImpact?.y ?? predictedImpact?.y ?? spawn.origin.y + spawn.direction.y * distance;
240
+ const impactElapsedMs = projectile.impactStartedAt !== undefined
241
+ ? Math.max(0, now - projectile.impactStartedAt)
242
+ : undefined;
243
+ return {
244
+ ...spawn,
245
+ x,
246
+ y,
247
+ angle: Math.atan2(spawn.direction.y, spawn.direction.x),
248
+ distance,
249
+ elapsed,
250
+ progress,
251
+ impact: projectile.impact,
252
+ impactElapsed: impactElapsedMs === undefined ? undefined : impactElapsedMs / 1000,
253
+ impactProgress: impactElapsedMs === undefined
254
+ ? undefined
255
+ : Math.min(1, impactElapsedMs / this.impactDurationMs),
256
+ destroyed: projectile.destroyAt !== undefined,
257
+ ttl,
258
+ };
259
+ }
260
+
261
+ private acceptsMap(mapId: string | undefined): boolean {
262
+ const normalizedMapId = normalizeRoomMapId(mapId);
263
+ return !normalizedMapId || !this.mapId || normalizedMapId === this.mapId;
264
+ }
265
+
266
+ private isWaitingForDelay(projectile: RuntimeProjectile, now: number): boolean {
267
+ const delayMs = (projectile.spawn.delay ?? 0) * 1000;
268
+ return now - projectile.createdAt - delayMs < 0;
269
+ }
270
+
271
+ private setPredictedImpact(projectile: RuntimeProjectile): void {
272
+ if (projectile.spawn.predictImpact === false) {
273
+ return;
274
+ }
275
+ const impact = this.predictionResolver?.(projectile.spawn);
276
+ if (!impact || !Number.isFinite(impact.x) || !Number.isFinite(impact.y)) {
277
+ return;
278
+ }
279
+ const distance = typeof impact.distance === "number" && Number.isFinite(impact.distance)
280
+ ? impact.distance
281
+ : Math.hypot(impact.x - projectile.spawn.origin.x, impact.y - projectile.spawn.origin.y);
282
+ if (!Number.isFinite(distance) || distance < 0 || distance > projectile.spawn.range) {
283
+ return;
284
+ }
285
+ projectile.predictedImpact = {
286
+ ...impact,
287
+ distance,
288
+ };
289
+ }
290
+
291
+ private getActivePredictedImpact(
292
+ projectile: RuntimeProjectile,
293
+ now: number,
294
+ rawDistance: number,
295
+ ): ClientProjectileImpact | undefined {
296
+ if (!projectile.predictedImpact || projectile.impact) {
297
+ return undefined;
298
+ }
299
+ const distance = projectile.predictedImpact.distance;
300
+ if (distance === undefined || rawDistance < distance) {
301
+ return undefined;
302
+ }
303
+ return projectile.predictedImpact;
304
+ }
305
+
306
+ private setImpact(projectile: RuntimeProjectile, impact: ClientProjectileImpact, now: number): void {
307
+ projectile.visualImpact = this.resolveVisualImpact(projectile, impact, now);
308
+ projectile.impact = impact;
309
+ projectile.predictedImpact = undefined;
310
+ projectile.impactStartedAt = projectile.impactStartedAt ?? now;
311
+ const impactDestroyAt = projectile.impactStartedAt + this.impactDurationMs;
312
+ projectile.destroyAt = Math.max(projectile.destroyAt ?? 0, impactDestroyAt);
313
+ }
314
+
315
+ private resolveVisualImpact(
316
+ projectile: RuntimeProjectile,
317
+ impact: ClientProjectileImpact,
318
+ now: number,
319
+ ): ClientProjectileImpact {
320
+ const predicted = projectile.predictedImpact;
321
+ if (!predicted || !this.isSameTarget(predicted, impact)) {
322
+ return impact;
323
+ }
324
+ const distance = predicted.distance;
325
+ if (distance === undefined) {
326
+ return impact;
327
+ }
328
+ const delayMs = (projectile.spawn.delay ?? 0) * 1000;
329
+ const elapsedMs = now - projectile.createdAt - delayMs;
330
+ if (elapsedMs < 0) {
331
+ return impact;
332
+ }
333
+ const rawDistance = Math.min(projectile.spawn.speed * (elapsedMs / 1000), projectile.spawn.range);
334
+ return rawDistance >= distance ? predicted : impact;
335
+ }
336
+
337
+ private isSameTarget(a: ClientProjectileImpact, b: ClientProjectileImpact): boolean {
338
+ return a.targetId !== undefined && a.targetId === b.targetId;
339
+ }
340
+
341
+ private touch(force = true): void {
342
+ if (force) {
343
+ this.version.update((value) => value + 1);
344
+ }
345
+ }
346
+ }
@@ -0,0 +1,359 @@
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("tracks GUI open ids and sends them back when closing", async () => {
68
+ const { gui, socket } = await createGui();
69
+ await gui._initialize();
70
+ const openHandler = socket.on.mock.calls.find(([event]) => event === "gui.open")?.[1];
71
+
72
+ openHandler({
73
+ guiId: PrebuiltGui.Dialog,
74
+ guiOpenId: "open-1",
75
+ data: { message: "Hello" },
76
+ });
77
+
78
+ expect(gui.get(PrebuiltGui.Dialog)?.openId).toBe("open-1");
79
+
80
+ gui.guiClose(PrebuiltGui.Dialog, 0, gui.get(PrebuiltGui.Dialog)?.openId);
81
+
82
+ expect(socket.emit).toHaveBeenCalledWith("gui.exit", {
83
+ guiId: PrebuiltGui.Dialog,
84
+ guiOpenId: "open-1",
85
+ data: 0,
86
+ });
87
+ });
88
+
89
+ test("does not emit malformed GUI open ids", async () => {
90
+ const { gui, socket } = await createGui();
91
+
92
+ gui.guiClose(PrebuiltGui.Dialog, 0, (() => "open-1") as any);
93
+
94
+ expect(socket.emit).toHaveBeenCalledWith("gui.exit", {
95
+ guiId: PrebuiltGui.Dialog,
96
+ guiOpenId: undefined,
97
+ data: 0,
98
+ });
99
+ });
100
+
101
+ test("waits for CanvasEngine GUI dependencies before displaying", async () => {
102
+ const { gui } = await createGui();
103
+ const enabled = signal(true);
104
+ const ready = signal<boolean | undefined>(undefined);
105
+
106
+ gui.add({
107
+ id: "canvas-dependency",
108
+ component: CanvasGui,
109
+ autoDisplay: true,
110
+ dependencies: () => [enabled, ready],
111
+ });
112
+
113
+ expect(gui.isDisplaying("canvas-dependency")).toBe(false);
114
+
115
+ ready.set(true);
116
+
117
+ expect(gui.isDisplaying("canvas-dependency")).toBe(true);
118
+ });
119
+
120
+ test("ignores stale server close events for previous GUI opens", async () => {
121
+ const { gui, socket } = await createGui();
122
+ await gui._initialize();
123
+ const openHandler = socket.on.mock.calls.find(([event]) => event === "gui.open")?.[1];
124
+ const exitHandler = socket.on.mock.calls.find(([event]) => event === "gui.exit")?.[1];
125
+
126
+ openHandler({
127
+ guiId: PrebuiltGui.Dialog,
128
+ guiOpenId: "open-1",
129
+ data: { message: "First" },
130
+ });
131
+ openHandler({
132
+ guiId: PrebuiltGui.Dialog,
133
+ guiOpenId: "open-2",
134
+ data: { message: "Second" },
135
+ });
136
+
137
+ exitHandler({
138
+ guiId: PrebuiltGui.Dialog,
139
+ guiOpenId: "open-1",
140
+ });
141
+
142
+ expect(gui.isDisplaying(PrebuiltGui.Dialog)).toBe(true);
143
+ expect(gui.get(PrebuiltGui.Dialog)?.data()).toEqual({ message: "Second" });
144
+
145
+ exitHandler({
146
+ guiId: PrebuiltGui.Dialog,
147
+ guiOpenId: "open-2",
148
+ });
149
+
150
+ expect(gui.isDisplaying(PrebuiltGui.Dialog)).toBe(false);
151
+ });
152
+
153
+ test("separates CanvasEngine and Vue GUI registries", async () => {
154
+ const { gui } = await createGui();
155
+
156
+ gui.add({
157
+ id: "canvas-tooltip",
158
+ component: CanvasGui,
159
+ attachToSprite: true,
160
+ });
161
+ gui.add({
162
+ id: "inventory",
163
+ component: VueInventory,
164
+ });
165
+ gui.add(VueTooltip);
166
+
167
+ expect(gui.get("canvas-tooltip")?.component).toBe(CanvasGui);
168
+ expect(gui.get("inventory")?.component).toBe(VueInventory);
169
+ expect(gui.get("tooltip")?.component).toBe(VueTooltip);
170
+ expect(gui.getAttachedGuis().map(item => item.name)).toEqual(["canvas-tooltip"]);
171
+ expect(gui.getAttachedVueGuis().map(item => item.name)).toEqual(["tooltip"]);
172
+ });
173
+
174
+ test("synchronizes Vue GUI display and hide states through the Vue bridge", async () => {
175
+ const { gui } = await createGui();
176
+ const bridge = {
177
+ updateGuiState: vi.fn(),
178
+ initializeGuiStates: vi.fn(),
179
+ };
180
+
181
+ gui.add({
182
+ id: "inventory",
183
+ component: VueInventory,
184
+ });
185
+ gui._setVueGuiInstance(bridge);
186
+
187
+ expect(bridge.initializeGuiStates).toHaveBeenCalledWith([
188
+ expect.objectContaining({
189
+ name: "inventory",
190
+ display: false,
191
+ data: {},
192
+ attachToSprite: false,
193
+ }),
194
+ ]);
195
+
196
+ gui.display("inventory", { gold: 12 });
197
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
198
+ expect.objectContaining({
199
+ name: "inventory",
200
+ display: true,
201
+ data: { gold: 12 },
202
+ attachToSprite: false,
203
+ }),
204
+ );
205
+
206
+ gui.hide("inventory");
207
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
208
+ expect.objectContaining({
209
+ name: "inventory",
210
+ display: false,
211
+ }),
212
+ );
213
+ });
214
+
215
+ test("waits for Vue GUI dependencies before display", async () => {
216
+ const { gui } = await createGui();
217
+ const bridge = {
218
+ updateGuiState: vi.fn(),
219
+ initializeGuiStates: vi.fn(),
220
+ };
221
+ const dependency = signal<any>(undefined);
222
+
223
+ gui.add({
224
+ id: "inventory",
225
+ component: VueInventory,
226
+ dependencies: () => [dependency],
227
+ });
228
+ gui._setVueGuiInstance(bridge);
229
+ gui.display("inventory", { items: ["potion"] });
230
+
231
+ expect(gui.isDisplaying("inventory")).toBe(false);
232
+ expect(bridge.updateGuiState).not.toHaveBeenCalledWith(
233
+ expect.objectContaining({
234
+ display: true,
235
+ }),
236
+ );
237
+
238
+ dependency.set({ id: "player" });
239
+
240
+ expect(gui.isDisplaying("inventory")).toBe(true);
241
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
242
+ expect.objectContaining({
243
+ name: "inventory",
244
+ display: true,
245
+ data: { items: ["potion"] },
246
+ }),
247
+ );
248
+ });
249
+
250
+ test("allows Vue GUI entries to replace prebuilt CanvasEngine GUIs", async () => {
251
+ const { gui } = await createGui();
252
+ const bridge = {
253
+ updateGuiState: vi.fn(),
254
+ initializeGuiStates: vi.fn(),
255
+ };
256
+
257
+ gui._setVueGuiInstance(bridge);
258
+ gui.add({
259
+ id: PrebuiltGui.Dialog,
260
+ component: VueDialog,
261
+ });
262
+
263
+ expect(gui.get(PrebuiltGui.Dialog)?.component).toBe(VueDialog);
264
+ expect(gui.getAll()[PrebuiltGui.Dialog].component).toBe(VueDialog);
265
+ expect((gui as any).gui()[PrebuiltGui.Dialog]).toBeUndefined();
266
+ expect(gui.getVueGuis().filter(item => item.name === PrebuiltGui.Dialog)).toHaveLength(1);
267
+
268
+ gui.display(PrebuiltGui.Dialog, { text: "Hello" });
269
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
270
+ expect.objectContaining({
271
+ name: PrebuiltGui.Dialog,
272
+ display: true,
273
+ data: { text: "Hello" },
274
+ }),
275
+ );
276
+
277
+ gui.hide(PrebuiltGui.Dialog);
278
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
279
+ expect.objectContaining({
280
+ name: PrebuiltGui.Dialog,
281
+ display: false,
282
+ }),
283
+ );
284
+ });
285
+
286
+ test("allows CanvasEngine GUI entries to replace Vue GUI entries with the same id", async () => {
287
+ const { gui } = await createGui();
288
+ const bridge = {
289
+ updateGuiState: vi.fn(),
290
+ initializeGuiStates: vi.fn(),
291
+ };
292
+
293
+ gui._setVueGuiInstance(bridge);
294
+ gui.add({
295
+ id: PrebuiltGui.Dialog,
296
+ component: VueDialog,
297
+ });
298
+ gui.add({
299
+ id: PrebuiltGui.Dialog,
300
+ component: CanvasGui,
301
+ });
302
+
303
+ expect(gui.get(PrebuiltGui.Dialog)?.component).toBe(CanvasGui);
304
+ expect(gui.getVueGuis().some(item => item.name === PrebuiltGui.Dialog)).toBe(false);
305
+ expect((gui as any).gui()[PrebuiltGui.Dialog].component).toBe(CanvasGui);
306
+ expect(bridge.initializeGuiStates).toHaveBeenLastCalledWith([]);
307
+ });
308
+
309
+ test("keeps main menu optimistic reducers when a Vue GUI replaces the prebuilt component", async () => {
310
+ const { gui, socket } = await createGui();
311
+ const bridge = {
312
+ updateGuiState: vi.fn(),
313
+ initializeGuiStates: vi.fn(),
314
+ };
315
+
316
+ gui.add({
317
+ id: PrebuiltGui.MainMenu,
318
+ component: VueMainMenu,
319
+ });
320
+ gui._setVueGuiInstance(bridge);
321
+ gui.display(PrebuiltGui.MainMenu, {
322
+ items: [
323
+ {
324
+ id: "potion",
325
+ quantity: 2,
326
+ },
327
+ ],
328
+ });
329
+
330
+ gui.guiInteraction(PrebuiltGui.MainMenu, "useItem", { id: "potion" });
331
+
332
+ expect(gui.get(PrebuiltGui.MainMenu)?.data().items).toEqual([
333
+ {
334
+ id: "potion",
335
+ quantity: 1,
336
+ },
337
+ ]);
338
+ expect(bridge.updateGuiState).toHaveBeenLastCalledWith(
339
+ expect.objectContaining({
340
+ name: PrebuiltGui.MainMenu,
341
+ data: {
342
+ items: [
343
+ {
344
+ id: "potion",
345
+ quantity: 1,
346
+ },
347
+ ],
348
+ },
349
+ }),
350
+ );
351
+ expect(socket.emit).toHaveBeenCalledWith(
352
+ "gui.interaction",
353
+ expect.objectContaining({
354
+ guiId: PrebuiltGui.MainMenu,
355
+ name: "useItem",
356
+ }),
357
+ );
358
+ });
359
+ });