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

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 (314) hide show
  1. package/CHANGELOG.md +123 -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 +96 -54
  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 +176 -11
  36. package/dist/RpgClientEngine.js +669 -68
  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 +606 -248
  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.js +2 -2
  90. package/dist/components/gui/mobile/index.js.map +1 -1
  91. package/dist/components/gui/mobile/mobile.ce.js +7 -5
  92. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  93. package/dist/components/gui/notification/notification.ce.js +28 -26
  94. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  95. package/dist/components/gui/save-load.ce.js +81 -256
  96. package/dist/components/gui/save-load.ce.js.map +1 -1
  97. package/dist/components/gui/shop/shop.ce.js +107 -142
  98. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  99. package/dist/components/gui/title-screen.ce.js +50 -73
  100. package/dist/components/gui/title-screen.ce.js.map +1 -1
  101. package/dist/components/index.d.ts +2 -1
  102. package/dist/components/index.js +1 -0
  103. package/dist/components/interaction-components.ce.js +22 -0
  104. package/dist/components/interaction-components.ce.js.map +1 -0
  105. package/dist/components/player-components-utils.d.ts +67 -0
  106. package/dist/components/player-components-utils.js +162 -0
  107. package/dist/components/player-components-utils.js.map +1 -0
  108. package/dist/components/player-components-utils.spec.d.ts +1 -0
  109. package/dist/components/player-components.ce.js +191 -0
  110. package/dist/components/player-components.ce.js.map +1 -0
  111. package/dist/components/prebuilt/hp-bar.ce.js +45 -45
  112. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  113. package/dist/components/prebuilt/light-halo.ce.js +39 -60
  114. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  115. package/dist/components/scenes/canvas.ce.js +205 -27
  116. package/dist/components/scenes/canvas.ce.js.map +1 -1
  117. package/dist/components/scenes/draw-map.ce.js +39 -39
  118. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  119. package/dist/components/scenes/event-layer.ce.js +51 -10
  120. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  121. package/dist/core/inject.js +1 -1
  122. package/dist/core/inject.js.map +1 -1
  123. package/dist/core/setup.js +1 -1
  124. package/dist/core/setup.js.map +1 -1
  125. package/dist/decorators/spritesheet.d.ts +1 -0
  126. package/dist/decorators/spritesheet.js +11 -0
  127. package/dist/decorators/spritesheet.js.map +1 -0
  128. package/dist/i18n.d.ts +55 -0
  129. package/dist/i18n.js +60 -0
  130. package/dist/i18n.js.map +1 -0
  131. package/dist/i18n.spec.d.ts +1 -0
  132. package/dist/index.d.ts +7 -0
  133. package/dist/index.js +29 -21
  134. package/dist/module.js +45 -4
  135. package/dist/module.js.map +1 -1
  136. package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@3.1.0}/node_modules/@signe/di/dist/index.js +7 -117
  137. package/dist/node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js.map +1 -0
  138. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js +239 -0
  139. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js.map +1 -0
  140. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
  141. 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
  142. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js +698 -0
  143. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js.map +1 -0
  144. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/client/index.js +43 -0
  145. 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
  146. 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
  147. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/index.js.map +1 -0
  148. 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
  149. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/index.js.map +1 -0
  150. 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
  151. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/ws.js.map +1 -0
  152. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/index.js +13 -0
  153. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/index.js.map +1 -0
  154. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js +106 -0
  155. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js.map +1 -0
  156. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js +13 -0
  157. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js.map +1 -0
  158. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js +121 -0
  159. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js.map +1 -0
  160. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js +10 -0
  161. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js.map +1 -0
  162. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js +110 -0
  163. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js.map +1 -0
  164. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js +99 -0
  165. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js.map +1 -0
  166. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js +82 -0
  167. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js.map +1 -0
  168. 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
  169. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js.map +1 -0
  170. package/dist/presets/animation.js.map +1 -1
  171. package/dist/presets/faceset.js.map +1 -1
  172. package/dist/presets/icon.js.map +1 -1
  173. package/dist/presets/index.js.map +1 -1
  174. package/dist/presets/lpc.js.map +1 -1
  175. package/dist/presets/rmspritesheet.js.map +1 -1
  176. package/dist/services/AbstractSocket.d.ts +2 -0
  177. package/dist/services/AbstractSocket.js.map +1 -1
  178. package/dist/services/actionInput.d.ts +14 -0
  179. package/dist/services/actionInput.js +59 -0
  180. package/dist/services/actionInput.js.map +1 -0
  181. package/dist/services/actionInput.spec.d.ts +1 -0
  182. package/dist/services/interactions.d.ts +159 -0
  183. package/dist/services/interactions.js +460 -0
  184. package/dist/services/interactions.js.map +1 -0
  185. package/dist/services/interactions.spec.d.ts +1 -0
  186. package/dist/services/keyboardControls.d.ts +1 -0
  187. package/dist/services/keyboardControls.js +1 -0
  188. package/dist/services/keyboardControls.js.map +1 -1
  189. package/dist/services/loadMap.d.ts +9 -0
  190. package/dist/services/loadMap.js +1 -1
  191. package/dist/services/loadMap.js.map +1 -1
  192. package/dist/services/mmorpg-connection.d.ts +5 -0
  193. package/dist/services/mmorpg-connection.js +50 -0
  194. package/dist/services/mmorpg-connection.js.map +1 -0
  195. package/dist/services/mmorpg-connection.spec.d.ts +1 -0
  196. package/dist/services/mmorpg.d.ts +11 -4
  197. package/dist/services/mmorpg.js +57 -33
  198. package/dist/services/mmorpg.js.map +1 -1
  199. package/dist/services/pointerContext.d.ts +11 -0
  200. package/dist/services/pointerContext.js +48 -0
  201. package/dist/services/pointerContext.js.map +1 -0
  202. package/dist/services/pointerContext.spec.d.ts +1 -0
  203. package/dist/services/save.js.map +1 -1
  204. package/dist/services/save.spec.d.ts +1 -0
  205. package/dist/services/standalone-message.d.ts +1 -0
  206. package/dist/services/standalone-message.js +9 -0
  207. package/dist/services/standalone-message.js.map +1 -0
  208. package/dist/services/standalone.d.ts +4 -1
  209. package/dist/services/standalone.js +36 -16
  210. package/dist/services/standalone.js.map +1 -1
  211. package/dist/services/standalone.spec.d.ts +1 -0
  212. package/dist/utils/getEntityProp.js +4 -3
  213. package/dist/utils/getEntityProp.js.map +1 -1
  214. package/dist/utils/getEntityProp.spec.d.ts +1 -0
  215. package/dist/utils/mapId.d.ts +1 -0
  216. package/dist/utils/mapId.js +6 -0
  217. package/dist/utils/mapId.js.map +1 -0
  218. package/dist/utils/readPropValue.d.ts +2 -0
  219. package/dist/utils/readPropValue.js +13 -0
  220. package/dist/utils/readPropValue.js.map +1 -0
  221. package/package.json +13 -13
  222. package/src/Game/AnimationManager.spec.ts +30 -0
  223. package/src/Game/AnimationManager.ts +26 -10
  224. package/src/Game/ClientVisuals.spec.ts +56 -0
  225. package/src/Game/ClientVisuals.ts +184 -0
  226. package/src/Game/EventComponentResolver.spec.ts +84 -0
  227. package/src/Game/EventComponentResolver.ts +74 -0
  228. package/src/Game/Map.ts +134 -2
  229. package/src/Game/Object.spec.ts +59 -0
  230. package/src/Game/Object.ts +181 -77
  231. package/src/Game/ProjectileManager.spec.ts +449 -0
  232. package/src/Game/ProjectileManager.ts +346 -0
  233. package/src/Gui/Gui.spec.ts +340 -0
  234. package/src/Gui/Gui.ts +129 -57
  235. package/src/Resource.ts +1 -2
  236. package/src/RpgClient.ts +220 -17
  237. package/src/RpgClientEngine.ts +959 -116
  238. package/src/components/animations/fx.ce +101 -0
  239. package/src/components/animations/index.ts +4 -2
  240. package/src/components/character.ce +681 -45
  241. package/src/components/dynamics/bar.ce +88 -0
  242. package/src/components/dynamics/image.ce +21 -0
  243. package/src/components/dynamics/parse-value.spec.ts +83 -0
  244. package/src/components/dynamics/parse-value.ts +111 -37
  245. package/src/components/dynamics/shape-utils.spec.ts +46 -0
  246. package/src/components/dynamics/shape-utils.ts +61 -0
  247. package/src/components/dynamics/shape.ce +90 -0
  248. package/src/components/dynamics/text.ce +35 -149
  249. package/src/components/gui/dialogbox/index.ce +52 -21
  250. package/src/components/gui/gameover.ce +6 -4
  251. package/src/components/gui/menu/equip-menu.ce +11 -9
  252. package/src/components/gui/menu/exit-menu.ce +6 -4
  253. package/src/components/gui/menu/items-menu.ce +11 -9
  254. package/src/components/gui/menu/main-menu.ce +14 -12
  255. package/src/components/gui/menu/options-menu.ce +4 -3
  256. package/src/components/gui/menu/skills-menu.ce +2 -1
  257. package/src/components/gui/notification/notification.ce +7 -1
  258. package/src/components/gui/save-load.ce +13 -11
  259. package/src/components/gui/shop/shop.ce +20 -18
  260. package/src/components/gui/title-screen.ce +6 -4
  261. package/src/components/index.ts +2 -1
  262. package/src/components/interaction-components.ce +23 -0
  263. package/src/components/player-components-utils.spec.ts +109 -0
  264. package/src/components/player-components-utils.ts +205 -0
  265. package/src/components/player-components.ce +222 -0
  266. package/src/components/prebuilt/hp-bar.ce +4 -3
  267. package/src/components/prebuilt/light-halo.ce +2 -2
  268. package/src/components/scenes/canvas.ce +219 -15
  269. package/src/components/scenes/draw-map.ce +34 -22
  270. package/src/components/scenes/event-layer.ce +55 -4
  271. package/src/core/setup.ts +2 -2
  272. package/src/decorators/spritesheet.ts +8 -0
  273. package/src/i18n.spec.ts +39 -0
  274. package/src/i18n.ts +59 -0
  275. package/src/index.ts +7 -0
  276. package/src/module.ts +61 -11
  277. package/src/services/AbstractSocket.ts +3 -0
  278. package/src/services/actionInput.spec.ts +155 -0
  279. package/src/services/actionInput.ts +120 -0
  280. package/src/services/interactions.spec.ts +175 -0
  281. package/src/services/interactions.ts +722 -0
  282. package/src/services/keyboardControls.ts +2 -1
  283. package/src/services/loadMap.ts +5 -1
  284. package/src/services/mmorpg-connection.spec.ts +99 -0
  285. package/src/services/mmorpg-connection.ts +69 -0
  286. package/src/services/mmorpg.ts +70 -36
  287. package/src/services/pointerContext.spec.ts +36 -0
  288. package/src/services/pointerContext.ts +84 -0
  289. package/src/services/save.spec.ts +127 -0
  290. package/src/services/standalone-message.ts +7 -0
  291. package/src/services/standalone.spec.ts +54 -0
  292. package/src/services/standalone.ts +44 -12
  293. package/src/utils/getEntityProp.spec.ts +96 -0
  294. package/src/utils/getEntityProp.ts +4 -3
  295. package/src/utils/mapId.ts +2 -0
  296. package/src/utils/readPropValue.ts +16 -0
  297. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
  298. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +0 -457
  299. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +0 -1
  300. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
  301. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
  302. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
  303. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
  304. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
  305. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
  306. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
  307. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
  308. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
  309. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
  310. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
  311. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +0 -1
  312. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +0 -1
  313. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +0 -2
  314. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +0 -1
@@ -9,9 +9,11 @@ interface StandaloneOptions {
9
9
  declare class BridgeWebsocket extends AbstractWebsocket {
10
10
  protected context: Context;
11
11
  private server;
12
+ readonly mode: "standalone";
12
13
  private room;
13
14
  private socket;
14
- private pendingOn;
15
+ private socketRoom?;
16
+ private listeners;
15
17
  private rooms;
16
18
  private serverInstance;
17
19
  constructor(context: Context, server: any, options?: StandaloneOptions);
@@ -55,6 +57,7 @@ declare class BridgeWebsocket extends AbstractWebsocket {
55
57
  * ```
56
58
  */
57
59
  reconnect(listeners?: (data: any) => void): Promise<void>;
60
+ private detachCurrentSocket;
58
61
  getServer(): any;
59
62
  getSocket(): any;
60
63
  }
@@ -2,8 +2,9 @@ import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket.js";
2
2
  import { provideSaveClient } from "./save.js";
3
3
  import { RpgGui } from "../Gui/Gui.js";
4
4
  import { RpgClientEngine } from "../RpgClientEngine.js";
5
- import { ClientIo, ServerIo } from "../node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js";
5
+ import { ClientIo, ServerIo } from "../node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js";
6
6
  import { provideKeyboardControls } from "./keyboardControls.js";
7
+ import { normalizeStandaloneMessage } from "./standalone-message.js";
7
8
  import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
8
9
  //#region src/services/standalone.ts
9
10
  var BridgeWebsocket = class extends AbstractWebsocket {
@@ -11,7 +12,8 @@ var BridgeWebsocket = class extends AbstractWebsocket {
11
12
  super(context);
12
13
  this.context = context;
13
14
  this.server = server;
14
- this.pendingOn = [];
15
+ this.mode = "standalone";
16
+ this.listeners = [];
15
17
  this.rooms = {
16
18
  partyFn: async (roomId) => {
17
19
  this.room = new ServerIo(roomId, this.rooms);
@@ -34,6 +36,7 @@ var BridgeWebsocket = class extends AbstractWebsocket {
34
36
  return this._connection(listeners);
35
37
  }
36
38
  async _connection(listeners) {
39
+ this.detachCurrentSocket();
37
40
  this.serverInstance = this.context.get("server");
38
41
  this.socket = new ClientIo(this.serverInstance, "player-client-id");
39
42
  const url = new URL("http://localhost");
@@ -42,29 +45,37 @@ var BridgeWebsocket = class extends AbstractWebsocket {
42
45
  headers: { "Content-Type": "application/json" }
43
46
  });
44
47
  listeners?.(this.socket);
45
- await this.serverInstance.onConnect(this.socket.conn, { request });
46
48
  this.room.clients.set(this.socket.id, this.socket);
47
- this.pendingOn.forEach(({ event, callback }) => this.socket.addEventListener(event, callback));
48
- this.pendingOn = [];
49
+ this.socketRoom = this.room;
50
+ this.listeners.forEach(({ handler }) => {
51
+ this.socket.addEventListener("message", handler);
52
+ });
53
+ await this.serverInstance.onConnect(this.socket.conn, { request });
49
54
  return this.socket;
50
55
  }
51
56
  on(key, callback) {
57
+ if (this.listeners.some((listener) => listener.event === key && listener.callback === callback)) return;
52
58
  const handler = (event) => {
53
- const object = JSON.parse(event);
59
+ const object = normalizeStandaloneMessage(event);
54
60
  if (object.type === key) callback(object.value);
55
61
  };
56
- if (!this.socket) {
57
- this.pendingOn.push({
58
- event: "message",
59
- callback: handler
60
- });
61
- return;
62
- }
63
- this.socket.addEventListener("message", handler);
62
+ this.listeners.push({
63
+ event: key,
64
+ callback,
65
+ handler
66
+ });
67
+ this.socket?.addEventListener("message", handler);
64
68
  }
65
69
  off(event, callback) {
66
- if (!this.socket) return;
67
- this.socket.removeEventListener(event, callback);
70
+ const remaining = [];
71
+ for (const listener of this.listeners) {
72
+ if (listener.event === event && listener.callback === callback) {
73
+ this.socket?.removeEventListener("message", listener.handler);
74
+ continue;
75
+ }
76
+ remaining.push(listener);
77
+ }
78
+ this.listeners = remaining;
68
79
  }
69
80
  emit(event, data) {
70
81
  this.socket.send({
@@ -111,6 +122,15 @@ var BridgeWebsocket = class extends AbstractWebsocket {
111
122
  listeners?.(socket);
112
123
  });
113
124
  }
125
+ detachCurrentSocket() {
126
+ if (!this.socket) return;
127
+ this.listeners.forEach(({ handler }) => {
128
+ this.socket.removeEventListener("message", handler);
129
+ });
130
+ this.socketRoom?.clients?.delete?.(this.socket.id);
131
+ this.socket = void 0;
132
+ this.socketRoom = void 0;
133
+ }
114
134
  getServer() {
115
135
  return this.serverInstance;
116
136
  }
@@ -1 +1 @@
1
- {"version":3,"file":"standalone.js","names":[],"sources":["../../src/services/standalone.ts"],"sourcesContent":["import { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from \"./AbstractSocket\";\nimport { ClientIo, ServerIo } from \"@signe/room\";\nimport { Context } from \"@signe/di\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { UpdateMapService, UpdateMapToken } from \"@rpgjs/common\";\nimport { LoadMapToken } from \"./loadMap\";\nimport { RpgGui } from \"../Gui/Gui\";\nimport { provideKeyboardControls } from \"./keyboardControls\";\nimport { provideSaveClient } from \"./save\";\n\ntype ServerIo = any;\ntype ClientIo = any;\n\ninterface StandaloneOptions {\n env?: Record<string, any>;\n}\n\nclass BridgeWebsocket extends AbstractWebsocket {\n private room: ServerIo;\n private socket: ClientIo;\n private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];\n private rooms = {\n partyFn: async (roomId: string) => {\n this.room = new ServerIo(roomId, this.rooms);\n const server = new this.server(this.room)\n await server.onStart();\n await server.subRoom.onStart()\n this.context.set('server', server)\n return server\n },\n env: {}\n }\n private serverInstance: any;\n\n constructor(protected context: Context, private server: any, options: StandaloneOptions = {}) {\n super(context);\n // fake room\n this.rooms.env = options.env || {};\n this.room = new ServerIo(\"lobby-1\", this.rooms);\n }\n\n async connection(listeners?: (data: any) => void) {\n this.serverInstance = new this.server(this.room);\n await this.serverInstance.onStart();\n await this.serverInstance.subRoom.onStart()\n this.context.set('server', this.serverInstance)\n return this._connection(listeners)\n }\n\n private async _connection(listeners?: (data: any) => void) {\n this.serverInstance = this.context.get('server')\n this.socket = new ClientIo(this.serverInstance, 'player-client-id');\n const url = new URL('http://localhost')\n const request = new Request(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n listeners?.(this.socket)\n await this.serverInstance.onConnect(this.socket.conn as any, { request } as any);\n this.room.clients.set(this.socket.id, this.socket);\n this.pendingOn.forEach(({ event, callback }) => this.socket.addEventListener(event, callback));\n this.pendingOn = [];\n return this.socket\n }\n\n on(key: string, callback: (data: any) => void) {\n const handler = (event) => {\n const object = JSON.parse(event);\n if (object.type === key) {\n callback(object.value);\n }\n };\n if (!this.socket) {\n this.pendingOn.push({ event: \"message\", callback: handler });\n return;\n }\n this.socket.addEventListener(\"message\", handler);\n }\n\n off(event: string, callback: (data: any) => void) {\n if (!this.socket) return;\n this.socket.removeEventListener(event, callback);\n }\n\n emit(event: string, data: any) {\n this.socket.send({\n action: event,\n value: data,\n });\n }\n\n /**\n * Update underlying connection properties before a reconnect\n *\n * Design\n * - Dynamically register a factory for the requested room to ensure a fresh server instance\n * - Swap the internal ServerIo to target the new room\n *\n * @param params - Properties to update\n * @param params.room - The target room id (e.g. `map-simplemap2`)\n *\n * @example\n * ```ts\n * websocket.updateProperties({ room: 'map-simplemap2' })\n * await websocket.reconnect()\n * ```\n */\n updateProperties(_params: SocketUpdateProperties) {\n // empty\n }\n\n /**\n * Reconnect the client to the current Party room\n *\n * Design\n * - Must be called after `updateProperties()` when switching rooms\n * - Rebuilds the client <-> server bridge and re-triggers connection listeners\n *\n * @param listeners - Optional callback to re-bind event handlers on the new socket\n *\n * @example\n * ```ts\n * websocket.updateProperties({ room: 'map-dungeon' })\n * await websocket.reconnect((socket) => {\n * // re-bind events here\n * })\n * ```\n */\n async reconnect(listeners?: (data: any) => void): Promise<void> {\n await this._connection((socket) => {\n listeners?.(socket)\n })\n }\n\n getServer() {\n return this.serverInstance\n }\n\n getSocket() {\n return this.socket\n }\n}\n\nclass UpdateMapStandaloneService extends UpdateMapService {\n private server: any;\n\n /**\n * Update the current room map data on the server side\n *\n * Design\n * - Uses the in-memory server instance stored in context (standalone mode)\n * - Builds a local HTTP-like request to the current Party room endpoint\n *\n * @param map - The map payload to apply on the server\n *\n * @example\n * ```ts\n * await updateMapService.update({ width: 1024, height: 768, events: [] })\n * ```\n */\n async update(map: any) {\n this.server = this.context.get('server')\n const roomId = this.server?.room?.id ?? 'lobby-1'\n const req = {\n url: `http://localhost/parties/main/${roomId}/map/update`,\n method: 'POST',\n headers: new Headers({}),\n json: async () => {\n return map;\n }\n };\n await this.server.onRequest(req)\n }\n}\n\nexport function provideRpg(server: any, options: StandaloneOptions = {}) {\n return [\n {\n provide: WebSocketToken,\n useFactory: (context: Context) => new BridgeWebsocket(context, server, options),\n },\n {\n provide: UpdateMapToken,\n useClass: UpdateMapStandaloneService,\n },\n provideKeyboardControls(),\n provideSaveClient(),\n RpgGui,\n RpgClientEngine,\n ];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"standalone.js","names":[],"sources":["../../src/services/standalone.ts"],"sourcesContent":["import { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from \"./AbstractSocket\";\nimport { ClientIo, ServerIo } from \"@signe/room\";\nimport { Context } from \"@signe/di\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { UpdateMapService, UpdateMapToken } from \"@rpgjs/common\";\nimport { LoadMapToken } from \"./loadMap\";\nimport { RpgGui } from \"../Gui/Gui\";\nimport { provideKeyboardControls } from \"./keyboardControls\";\nimport { provideSaveClient } from \"./save\";\nimport { normalizeStandaloneMessage } from \"./standalone-message\";\n\ntype ServerIo = any;\ntype ClientIo = any;\n\ninterface StandaloneOptions {\n env?: Record<string, any>;\n}\n\nclass BridgeWebsocket extends AbstractWebsocket {\n readonly mode = \"standalone\" as const;\n\n private room: ServerIo;\n private socket: ClientIo;\n private socketRoom?: ServerIo;\n private listeners: Array<{\n event: string;\n callback: (data: any) => void;\n handler: (event: any) => void;\n }> = [];\n private rooms = {\n partyFn: async (roomId: string) => {\n this.room = new ServerIo(roomId, this.rooms);\n const server = new this.server(this.room)\n await server.onStart();\n await server.subRoom.onStart()\n this.context.set('server', server)\n return server\n },\n env: {}\n }\n private serverInstance: any;\n\n constructor(protected context: Context, private server: any, options: StandaloneOptions = {}) {\n super(context);\n // fake room\n this.rooms.env = options.env || {};\n this.room = new ServerIo(\"lobby-1\", this.rooms);\n }\n\n async connection(listeners?: (data: any) => void) {\n this.serverInstance = new this.server(this.room);\n await this.serverInstance.onStart();\n await this.serverInstance.subRoom.onStart()\n this.context.set('server', this.serverInstance)\n return this._connection(listeners)\n }\n\n private async _connection(listeners?: (data: any) => void) {\n this.detachCurrentSocket();\n this.serverInstance = this.context.get('server')\n this.socket = new ClientIo(this.serverInstance, 'player-client-id');\n const url = new URL('http://localhost')\n const request = new Request(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n listeners?.(this.socket)\n this.room.clients.set(this.socket.id, this.socket);\n this.socketRoom = this.room;\n this.listeners.forEach(({ handler }) => {\n this.socket.addEventListener(\"message\", handler);\n });\n await this.serverInstance.onConnect(this.socket.conn as any, { request } as any);\n return this.socket\n }\n\n on(key: string, callback: (data: any) => void) {\n if (\n this.listeners.some(\n (listener) => listener.event === key && listener.callback === callback\n )\n ) {\n return;\n }\n const handler = (event) => {\n const object = normalizeStandaloneMessage(event);\n if (object.type === key) {\n callback(object.value);\n }\n };\n this.listeners.push({ event: key, callback, handler });\n this.socket?.addEventListener(\"message\", handler);\n }\n\n off(event: string, callback: (data: any) => void) {\n const remaining: typeof this.listeners = [];\n for (const listener of this.listeners) {\n if (listener.event === event && listener.callback === callback) {\n this.socket?.removeEventListener(\"message\", listener.handler);\n continue;\n }\n remaining.push(listener);\n }\n this.listeners = remaining;\n }\n\n emit(event: string, data: any) {\n this.socket.send({\n action: event,\n value: data,\n });\n }\n\n /**\n * Update underlying connection properties before a reconnect\n *\n * Design\n * - Dynamically register a factory for the requested room to ensure a fresh server instance\n * - Swap the internal ServerIo to target the new room\n *\n * @param params - Properties to update\n * @param params.room - The target room id (e.g. `map-simplemap2`)\n *\n * @example\n * ```ts\n * websocket.updateProperties({ room: 'map-simplemap2' })\n * await websocket.reconnect()\n * ```\n */\n updateProperties(_params: SocketUpdateProperties) {\n // empty\n }\n\n /**\n * Reconnect the client to the current Party room\n *\n * Design\n * - Must be called after `updateProperties()` when switching rooms\n * - Rebuilds the client <-> server bridge and re-triggers connection listeners\n *\n * @param listeners - Optional callback to re-bind event handlers on the new socket\n *\n * @example\n * ```ts\n * websocket.updateProperties({ room: 'map-dungeon' })\n * await websocket.reconnect((socket) => {\n * // re-bind events here\n * })\n * ```\n */\n async reconnect(listeners?: (data: any) => void): Promise<void> {\n await this._connection((socket) => {\n listeners?.(socket)\n })\n }\n\n private detachCurrentSocket() {\n if (!this.socket) return;\n this.listeners.forEach(({ handler }) => {\n this.socket.removeEventListener(\"message\", handler);\n });\n this.socketRoom?.clients?.delete?.(this.socket.id);\n this.socket = undefined as any;\n this.socketRoom = undefined;\n }\n\n getServer() {\n return this.serverInstance\n }\n\n getSocket() {\n return this.socket\n }\n}\n\nclass UpdateMapStandaloneService extends UpdateMapService {\n private server: any;\n\n /**\n * Update the current room map data on the server side\n *\n * Design\n * - Uses the in-memory server instance stored in context (standalone mode)\n * - Builds a local HTTP-like request to the current Party room endpoint\n *\n * @param map - The map payload to apply on the server\n *\n * @example\n * ```ts\n * await updateMapService.update({ width: 1024, height: 768, events: [] })\n * ```\n */\n async update(map: any) {\n this.server = this.context.get('server')\n const roomId = this.server?.room?.id ?? 'lobby-1'\n const req = {\n url: `http://localhost/parties/main/${roomId}/map/update`,\n method: 'POST',\n headers: new Headers({}),\n json: async () => {\n return map;\n }\n };\n await this.server.onRequest(req)\n }\n}\n\nexport function provideRpg(server: any, options: StandaloneOptions = {}) {\n return [\n {\n provide: WebSocketToken,\n useFactory: (context: Context) => new BridgeWebsocket(context, server, options),\n },\n {\n provide: UpdateMapToken,\n useClass: UpdateMapStandaloneService,\n },\n provideKeyboardControls(),\n provideSaveClient(),\n RpgGui,\n RpgClientEngine,\n ];\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import { readPropValue } from "./readPropValue.js";
1
2
  import { computed } from "canvasengine";
2
3
  //#region src/utils/getEntityProp.ts
3
4
  var entityPropMap = {
@@ -6,17 +7,17 @@ var entityPropMap = {
6
7
  gold: (entity) => entity._gold(),
7
8
  hp: (entity) => entity.hpSignal(),
8
9
  sp: (entity) => entity.spSignal(),
9
- name: (entity) => entity.name(),
10
+ name: (entity) => entity.name,
10
11
  type: (entity) => entity.type(),
11
12
  x: (entity) => entity.x(),
12
13
  y: (entity) => entity.y(),
13
14
  z: (entity) => entity.z(),
14
15
  tint: (entity) => entity.tint(),
15
16
  direction: (entity) => entity.direction(),
16
- speed: (entity) => entity.speed(),
17
+ speed: (entity) => entity.speed,
17
18
  hitbox: (entity) => entity.hitbox(),
18
19
  animation: (entity) => entity.animationName(),
19
- canMove: (entity) => entity.canMove(),
20
+ canMove: (entity) => readPropValue(entity._canMove ?? entity.canMove),
20
21
  graphics: (entity) => entity.graphics(),
21
22
  items: (entity) => entity.items(),
22
23
  equipments: (entity) => entity.equipments(),
@@ -1 +1 @@
1
- {"version":3,"file":"getEntityProp.js","names":[],"sources":["../../src/utils/getEntityProp.ts"],"sourcesContent":["import { computed, Signal } from \"canvasengine\";\nimport { RpgClientObject } from \"../Game/Object\";\n\nconst BUILTIN_PARAM_KEYS = [\n \"maxHp\",\n \"maxSp\",\n \"atk\",\n \"pdef\",\n \"sdef\",\n \"str\",\n \"agi\",\n \"int\",\n \"dex\",\n] as const;\n\ntype BuiltInParamKey = typeof BUILTIN_PARAM_KEYS[number];\ntype ParamPropKey = `params.${BuiltInParamKey}` | `params.${string}`;\n\nconst entityPropMap = {\n level: (entity: RpgClientObject) => entity._level(),\n exp: (entity: RpgClientObject) => entity._exp(),\n gold: (entity: RpgClientObject) => entity._gold(),\n hp: (entity: RpgClientObject) => entity.hpSignal(),\n sp: (entity: RpgClientObject) => entity.spSignal(),\n name: (entity: RpgClientObject) => entity.name(),\n type: (entity: RpgClientObject) => entity.type(),\n x: (entity: RpgClientObject) => entity.x(),\n y: (entity: RpgClientObject) => entity.y(),\n z: (entity: RpgClientObject) => entity.z(),\n tint: (entity: RpgClientObject) => entity.tint(),\n direction: (entity: RpgClientObject) => entity.direction(),\n speed: (entity: RpgClientObject) => entity.speed(),\n hitbox: (entity: RpgClientObject) => entity.hitbox(),\n animation: (entity: RpgClientObject) => entity.animationName(),\n canMove: (entity: RpgClientObject) => entity.canMove(),\n graphics: (entity: RpgClientObject) => entity.graphics(),\n items: (entity: RpgClientObject) => entity.items(),\n equipments: (entity: RpgClientObject) => entity.equipments(),\n states: (entity: RpgClientObject) => entity.states(),\n skills: (entity: RpgClientObject) => entity.skills(),\n effects: (entity: RpgClientObject) => entity._effects(),\n componentsTop: (entity: RpgClientObject) => entity.componentsTop(),\n componentsBottom: (entity: RpgClientObject) => entity.componentsBottom(),\n componentsCenter: (entity: RpgClientObject) => entity.componentsCenter(),\n componentsLeft: (entity: RpgClientObject) => entity.componentsLeft(),\n componentsRight: (entity: RpgClientObject) => entity.componentsRight(),\n} as const;\n\ntype EntityPropMap = typeof entityPropMap;\ntype EntityPropKey = keyof EntityPropMap | ParamPropKey;\ntype EntityPropValue<K extends EntityPropKey> = K extends keyof EntityPropMap\n ? ReturnType<EntityPropMap[K]>\n : K extends `params.${string}`\n ? number | undefined\n : never;\n\nconst isSignal = <T>(value: unknown): value is Signal<T> =>\n typeof value === \"function\";\n\nconst toSignal = <T>(value: Signal<T> | T): Signal<T> =>\n isSignal<T>(value) ? value : computed(() => value);\n\nexport const getEntityProp = <K extends EntityPropKey>(\n entity: Signal<RpgClientObject | undefined> | RpgClientObject | undefined,\n key: K\n): Signal<EntityPropValue<K>> => {\n const entitySignal = toSignal(entity);\n\n return computed(() => {\n const current = entitySignal();\n if (!current) {\n return undefined as EntityPropValue<K>;\n }\n\n if (Object.prototype.hasOwnProperty.call(entityPropMap, key)) {\n const getter = entityPropMap[key as keyof EntityPropMap];\n return getter(current) as EntityPropValue<K>;\n }\n\n if (key.startsWith(\"params.\")) {\n const paramKey = key.slice(\"params.\".length);\n return current._param?.()?.[paramKey] as EntityPropValue<K>;\n }\n\n return undefined as EntityPropValue<K>;\n });\n};\n"],"mappings":";;AAkBA,IAAM,gBAAgB;CACpB,QAAQ,WAA4B,OAAO,QAAQ;CACnD,MAAM,WAA4B,OAAO,MAAM;CAC/C,OAAO,WAA4B,OAAO,OAAO;CACjD,KAAK,WAA4B,OAAO,UAAU;CAClD,KAAK,WAA4B,OAAO,UAAU;CAClD,OAAO,WAA4B,OAAO,MAAM;CAChD,OAAO,WAA4B,OAAO,MAAM;CAChD,IAAI,WAA4B,OAAO,GAAG;CAC1C,IAAI,WAA4B,OAAO,GAAG;CAC1C,IAAI,WAA4B,OAAO,GAAG;CAC1C,OAAO,WAA4B,OAAO,MAAM;CAChD,YAAY,WAA4B,OAAO,WAAW;CAC1D,QAAQ,WAA4B,OAAO,OAAO;CAClD,SAAS,WAA4B,OAAO,QAAQ;CACpD,YAAY,WAA4B,OAAO,eAAe;CAC9D,UAAU,WAA4B,OAAO,SAAS;CACtD,WAAW,WAA4B,OAAO,UAAU;CACxD,QAAQ,WAA4B,OAAO,OAAO;CAClD,aAAa,WAA4B,OAAO,YAAY;CAC5D,SAAS,WAA4B,OAAO,QAAQ;CACpD,SAAS,WAA4B,OAAO,QAAQ;CACpD,UAAU,WAA4B,OAAO,UAAU;CACvD,gBAAgB,WAA4B,OAAO,eAAe;CAClE,mBAAmB,WAA4B,OAAO,kBAAkB;CACxE,mBAAmB,WAA4B,OAAO,kBAAkB;CACxE,iBAAiB,WAA4B,OAAO,gBAAgB;CACpE,kBAAkB,WAA4B,OAAO,iBAAiB;CACvE;AAUD,IAAM,YAAe,UACnB,OAAO,UAAU;AAEnB,IAAM,YAAe,UACnB,SAAY,MAAM,GAAG,QAAQ,eAAe,MAAM;AAEpD,IAAa,iBACX,QACA,QAC+B;CAC/B,MAAM,eAAe,SAAS,OAAO;AAErC,QAAO,eAAe;EACpB,MAAM,UAAU,cAAc;AAC9B,MAAI,CAAC,QACH;AAGF,MAAI,OAAO,UAAU,eAAe,KAAK,eAAe,IAAI,EAAE;GAC5D,MAAM,SAAS,cAAc;AAC7B,UAAO,OAAO,QAAQ;;AAGxB,MAAI,IAAI,WAAW,UAAU,EAAE;GAC7B,MAAM,WAAW,IAAI,MAAM,EAAiB;AAC5C,UAAO,QAAQ,UAAU,GAAG;;GAI9B"}
1
+ {"version":3,"file":"getEntityProp.js","names":[],"sources":["../../src/utils/getEntityProp.ts"],"sourcesContent":["import { computed, Signal } from \"canvasengine\";\nimport { RpgClientObject } from \"../Game/Object\";\nimport { readPropValue } from \"./readPropValue\";\n\nconst BUILTIN_PARAM_KEYS = [\n \"maxHp\",\n \"maxSp\",\n \"atk\",\n \"pdef\",\n \"sdef\",\n \"str\",\n \"agi\",\n \"int\",\n \"dex\",\n] as const;\n\ntype BuiltInParamKey = typeof BUILTIN_PARAM_KEYS[number];\ntype ParamPropKey = `params.${BuiltInParamKey}` | `params.${string}`;\n\nconst entityPropMap = {\n level: (entity: RpgClientObject) => entity._level(),\n exp: (entity: RpgClientObject) => entity._exp(),\n gold: (entity: RpgClientObject) => entity._gold(),\n hp: (entity: RpgClientObject) => entity.hpSignal(),\n sp: (entity: RpgClientObject) => entity.spSignal(),\n name: (entity: RpgClientObject) => entity.name,\n type: (entity: RpgClientObject) => entity.type(),\n x: (entity: RpgClientObject) => entity.x(),\n y: (entity: RpgClientObject) => entity.y(),\n z: (entity: RpgClientObject) => entity.z(),\n tint: (entity: RpgClientObject) => entity.tint(),\n direction: (entity: RpgClientObject) => entity.direction(),\n speed: (entity: RpgClientObject) => entity.speed,\n hitbox: (entity: RpgClientObject) => entity.hitbox(),\n animation: (entity: RpgClientObject) => entity.animationName(),\n canMove: (entity: RpgClientObject) => readPropValue<boolean>((entity as any)._canMove ?? entity.canMove),\n graphics: (entity: RpgClientObject) => entity.graphics(),\n items: (entity: RpgClientObject) => entity.items(),\n equipments: (entity: RpgClientObject) => entity.equipments(),\n states: (entity: RpgClientObject) => entity.states(),\n skills: (entity: RpgClientObject) => entity.skills(),\n effects: (entity: RpgClientObject) => entity._effects(),\n componentsTop: (entity: RpgClientObject) => entity.componentsTop(),\n componentsBottom: (entity: RpgClientObject) => entity.componentsBottom(),\n componentsCenter: (entity: RpgClientObject) => entity.componentsCenter(),\n componentsLeft: (entity: RpgClientObject) => entity.componentsLeft(),\n componentsRight: (entity: RpgClientObject) => entity.componentsRight(),\n} as const;\n\ntype EntityPropMap = typeof entityPropMap;\ntype EntityPropKey = keyof EntityPropMap | ParamPropKey;\ntype EntityPropValue<K extends EntityPropKey> = K extends keyof EntityPropMap\n ? ReturnType<EntityPropMap[K]>\n : K extends `params.${string}`\n ? number | undefined\n : never;\n\nconst isSignal = <T>(value: unknown): value is Signal<T> =>\n typeof value === \"function\";\n\nconst toSignal = <T>(value: Signal<T> | T): Signal<T> =>\n isSignal<T>(value) ? value : computed(() => value);\n\nexport const getEntityProp = <K extends EntityPropKey>(\n entity: Signal<RpgClientObject | undefined> | RpgClientObject | undefined,\n key: K\n): Signal<EntityPropValue<K>> => {\n const entitySignal = toSignal(entity);\n\n return computed(() => {\n const current = entitySignal();\n if (!current) {\n return undefined as EntityPropValue<K>;\n }\n\n if (Object.prototype.hasOwnProperty.call(entityPropMap, key)) {\n const getter = entityPropMap[key as keyof EntityPropMap];\n return getter(current) as EntityPropValue<K>;\n }\n\n if (key.startsWith(\"params.\")) {\n const paramKey = key.slice(\"params.\".length);\n return current._param?.()?.[paramKey] as EntityPropValue<K>;\n }\n\n return undefined as EntityPropValue<K>;\n });\n};\n"],"mappings":";;;AAmBA,IAAM,gBAAgB;CACpB,QAAQ,WAA4B,OAAO,OAAO;CAClD,MAAM,WAA4B,OAAO,KAAK;CAC9C,OAAO,WAA4B,OAAO,MAAM;CAChD,KAAK,WAA4B,OAAO,SAAS;CACjD,KAAK,WAA4B,OAAO,SAAS;CACjD,OAAO,WAA4B,OAAO;CAC1C,OAAO,WAA4B,OAAO,KAAK;CAC/C,IAAI,WAA4B,OAAO,EAAE;CACzC,IAAI,WAA4B,OAAO,EAAE;CACzC,IAAI,WAA4B,OAAO,EAAE;CACzC,OAAO,WAA4B,OAAO,KAAK;CAC/C,YAAY,WAA4B,OAAO,UAAU;CACzD,QAAQ,WAA4B,OAAO;CAC3C,SAAS,WAA4B,OAAO,OAAO;CACnD,YAAY,WAA4B,OAAO,cAAc;CAC7D,UAAU,WAA4B,cAAwB,OAAe,YAAY,OAAO,OAAO;CACvG,WAAW,WAA4B,OAAO,SAAS;CACvD,QAAQ,WAA4B,OAAO,MAAM;CACjD,aAAa,WAA4B,OAAO,WAAW;CAC3D,SAAS,WAA4B,OAAO,OAAO;CACnD,SAAS,WAA4B,OAAO,OAAO;CACnD,UAAU,WAA4B,OAAO,SAAS;CACtD,gBAAgB,WAA4B,OAAO,cAAc;CACjE,mBAAmB,WAA4B,OAAO,iBAAiB;CACvE,mBAAmB,WAA4B,OAAO,iBAAiB;CACvE,iBAAiB,WAA4B,OAAO,eAAe;CACnE,kBAAkB,WAA4B,OAAO,gBAAgB;AACvE;AAUA,IAAM,YAAe,UACnB,OAAO,UAAU;AAEnB,IAAM,YAAe,UACnB,SAAY,KAAK,IAAI,QAAQ,eAAe,KAAK;AAEnD,IAAa,iBACX,QACA,QAC+B;CAC/B,MAAM,eAAe,SAAS,MAAM;CAEpC,OAAO,eAAe;EACpB,MAAM,UAAU,aAAa;EAC7B,IAAI,CAAC,SACH;EAGF,IAAI,OAAO,UAAU,eAAe,KAAK,eAAe,GAAG,GAAG;GAC5D,MAAM,SAAS,cAAc;GAC7B,OAAO,OAAO,OAAO;EACvB;EAEA,IAAI,IAAI,WAAW,SAAS,GAAG;GAC7B,MAAM,WAAW,IAAI,MAAM,CAAgB;GAC3C,OAAO,QAAQ,SAAS,IAAI;EAC9B;CAGF,CAAC;AACH"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const normalizeRoomMapId: (mapId: string | undefined) => string | undefined;
@@ -0,0 +1,6 @@
1
+ //#region src/utils/mapId.ts
2
+ var normalizeRoomMapId = (mapId) => typeof mapId === "string" ? mapId.replace(/^map-/, "") : void 0;
3
+ //#endregion
4
+ export { normalizeRoomMapId };
5
+
6
+ //# sourceMappingURL=mapId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapId.js","names":[],"sources":["../../src/utils/mapId.ts"],"sourcesContent":["export const normalizeRoomMapId = (mapId: string | undefined): string | undefined =>\n typeof mapId === \"string\" ? mapId.replace(/^map-/, \"\") : undefined;\n"],"mappings":";AAAA,IAAa,sBAAsB,UACjC,OAAO,UAAU,WAAW,MAAM,QAAQ,SAAS,EAAE,IAAI,KAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const readPropValue: <T = unknown>(value: unknown) => T;
2
+ export declare const getCanMoveValue: (entity: any) => boolean;
@@ -0,0 +1,13 @@
1
+ //#region src/utils/readPropValue.ts
2
+ var readPropValue = (value) => {
3
+ if (typeof value === "function") return readPropValue(value());
4
+ if (value && typeof value === "object" && "value" in value) return readPropValue(value.value);
5
+ return value;
6
+ };
7
+ var getCanMoveValue = (entity) => {
8
+ return readPropValue(entity?._canMove ?? entity?.canMove) !== false;
9
+ };
10
+ //#endregion
11
+ export { getCanMoveValue, readPropValue };
12
+
13
+ //# sourceMappingURL=readPropValue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readPropValue.js","names":[],"sources":["../../src/utils/readPropValue.ts"],"sourcesContent":["export const readPropValue = <T = unknown>(value: unknown): T => {\n if (typeof value === \"function\") {\n return readPropValue((value as () => unknown)());\n }\n\n if (value && typeof value === \"object\" && \"value\" in value) {\n return readPropValue((value as { value: unknown }).value);\n }\n\n return value as T;\n};\n\nexport const getCanMoveValue = (entity: any): boolean => {\n const value = readPropValue(entity?._canMove ?? entity?.canMove);\n return value !== false;\n};\n"],"mappings":";AAAA,IAAa,iBAA8B,UAAsB;CAC/D,IAAI,OAAO,UAAU,YACnB,OAAO,cAAe,MAAwB,CAAC;CAGjD,IAAI,SAAS,OAAO,UAAU,YAAY,WAAW,OACnD,OAAO,cAAe,MAA6B,KAAK;CAG1D,OAAO;AACT;AAEA,IAAa,mBAAmB,WAAyB;CAEvD,OADc,cAAc,QAAQ,YAAY,QAAQ,OACjD,MAAU;AACnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/client",
3
- "version": "5.0.0-beta.2",
3
+ "version": "5.0.0-beta.20",
4
4
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -17,25 +17,25 @@
17
17
  "author": "Samuel Ronce",
18
18
  "license": "MIT",
19
19
  "peerDependencies": {
20
- "@canvasengine/presets": "*",
21
- "canvasengine": "*",
20
+ "@canvasengine/presets": "^2.0.0",
21
+ "canvasengine": "^2.0.0",
22
22
  "pixi.js": "^8.9.2"
23
23
  },
24
24
  "dependencies": {
25
- "@rpgjs/common": "5.0.0-beta.2",
26
- "@rpgjs/server": "5.0.0-beta.2",
27
- "@rpgjs/ui-css": "5.0.0-beta.2",
28
- "@signe/di": "^2.9.0",
29
- "@signe/room": "^2.9.0",
30
- "@signe/sync": "^2.9.0",
25
+ "@rpgjs/common": "5.0.0-beta.20",
26
+ "@rpgjs/server": "5.0.0-beta.20",
27
+ "@rpgjs/ui-css": "5.0.0-beta.20",
28
+ "@signe/di": "3.1.0",
29
+ "@signe/room": "3.1.0",
30
+ "@signe/sync": "3.1.0",
31
31
  "pixi-filters": "^6.1.5",
32
32
  "rxjs": "^7.8.2"
33
33
  },
34
34
  "devDependencies": {
35
- "@canvasengine/compiler": "2.0.0-beta.56",
36
- "vite": "^8.0.3",
37
- "vite-plugin-dts": "^4.5.4",
38
- "vitest": "^4.1.2"
35
+ "@canvasengine/compiler": "2.0.0",
36
+ "vite": "^8.0.16",
37
+ "vite-plugin-dts": "^5.0.2",
38
+ "vitest": "^4.1.9"
39
39
  },
40
40
  "type": "module",
41
41
  "scripts": {
@@ -0,0 +1,30 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { AnimationManager } from "./AnimationManager";
3
+
4
+ describe("AnimationManager", () => {
5
+ test("resolves displayEffect when the component calls onFinish", async () => {
6
+ const manager = new AnimationManager();
7
+ const done = manager.displayEffect({}, { x: 10, y: 20 });
8
+
9
+ expect(manager.current()).toHaveLength(1);
10
+ manager.current()[0].onFinish();
11
+
12
+ await expect(done).resolves.toBeUndefined();
13
+ expect(manager.current()).toHaveLength(0);
14
+ });
15
+
16
+ test("keeps onFinish idempotent and forwards user callback data", async () => {
17
+ const manager = new AnimationManager();
18
+ const onFinish = vi.fn();
19
+ const done = manager.displayEffect({ onFinish }, { x: 10, y: 20 });
20
+ const finish = manager.current()[0].onFinish;
21
+
22
+ finish({ ok: true });
23
+ finish({ ok: false });
24
+
25
+ await done;
26
+ expect(onFinish).toHaveBeenCalledTimes(1);
27
+ expect(onFinish).toHaveBeenCalledWith({ ok: true });
28
+ expect(manager.current()).toHaveLength(0);
29
+ });
30
+ });
@@ -4,18 +4,34 @@ import { signal } from "canvasengine";
4
4
  export class AnimationManager {
5
5
  current = signal<any[]>([]);
6
6
 
7
- displayEffect(params: any, player: RpgCommonPlayer | { x: number, y: number }) {
7
+ clear(): void {
8
+ this.current.set([]);
9
+ }
10
+
11
+ displayEffect(params: any, player: RpgCommonPlayer | { x: number, y: number }): Promise<void> {
8
12
  const id = generateUID();
9
- this.current().push({
10
- ...params,
11
- id,
12
- x: player.x,
13
- y: player.y,
14
- object: player,
15
- onFinish: () => {
13
+ const effectParams = params ?? {};
14
+ return new Promise<void>((resolve) => {
15
+ let finished = false;
16
+ const finish = (data?: any) => {
17
+ if (finished) return;
18
+ finished = true;
16
19
  const index = this.current().findIndex((value) => value.id === id);
17
- this.current().splice(index, 1);
18
- },
20
+ if (index !== -1) {
21
+ this.current().splice(index, 1);
22
+ }
23
+ effectParams.onFinish?.(data);
24
+ resolve();
25
+ };
26
+
27
+ this.current().push({
28
+ ...effectParams,
29
+ id,
30
+ x: player.x,
31
+ y: player.y,
32
+ object: player,
33
+ onFinish: finish,
34
+ });
19
35
  });
20
36
  }
21
37
  }
@@ -0,0 +1,56 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { ClientVisualRegistry } from "./ClientVisuals";
3
+
4
+ describe("ClientVisualRegistry", () => {
5
+ test("plays registered visual handlers with resolved objects and helpers", async () => {
6
+ const target = {
7
+ flash: vi.fn(),
8
+ showHit: vi.fn(),
9
+ setAnimation: vi.fn(),
10
+ };
11
+ const componentAnimation = {
12
+ displayEffect: vi.fn(),
13
+ };
14
+ const engine = {
15
+ scene: {},
16
+ getObjectById: vi.fn(() => target),
17
+ getComponentAnimation: vi.fn(() => componentAnimation),
18
+ playSound: vi.fn().mockResolvedValue(undefined),
19
+ mapShakeTrigger: {
20
+ start: vi.fn(),
21
+ },
22
+ } as any;
23
+ const registry = new ClientVisualRegistry();
24
+
25
+ registry.register("hit", ({ target, data }, helpers) => {
26
+ helpers.flash(target, { type: "tint", tint: "red" });
27
+ helpers.showHit(target, `-${data.damage}`);
28
+ helpers.component("hit-spark", target, { scale: 2 });
29
+ helpers.sound("hit");
30
+ helpers.animation(target, "hurt");
31
+ helpers.shake({ intensity: 2 });
32
+ });
33
+
34
+ await registry.play(
35
+ {
36
+ name: "hit",
37
+ data: {
38
+ targetId: "enemy-1",
39
+ damage: 12,
40
+ },
41
+ },
42
+ engine
43
+ );
44
+
45
+ expect(engine.getObjectById).toHaveBeenCalledWith("enemy-1");
46
+ expect(target.flash).toHaveBeenCalledWith({ type: "tint", tint: "red" });
47
+ expect(target.showHit).toHaveBeenCalledWith("-12");
48
+ expect(componentAnimation.displayEffect).toHaveBeenCalledWith(
49
+ { scale: 2 },
50
+ target
51
+ );
52
+ expect(engine.playSound).toHaveBeenCalledWith("hit", undefined);
53
+ expect(target.setAnimation).toHaveBeenCalledWith("hurt", 1);
54
+ expect(engine.mapShakeTrigger.start).toHaveBeenCalledWith({ intensity: 2 });
55
+ });
56
+ });
@@ -0,0 +1,184 @@
1
+ import type { RpgClientEngine } from "../RpgClientEngine";
2
+ import type { RpgClientMap } from "./Map";
3
+
4
+ export type ClientVisualPosition = {
5
+ x: number;
6
+ y: number;
7
+ z?: number;
8
+ };
9
+
10
+ export type ClientVisualPayload = Record<string, any> & {
11
+ source?: string;
12
+ sourceId?: string;
13
+ target?: string;
14
+ targetId?: string;
15
+ object?: string;
16
+ objectId?: string;
17
+ position?: ClientVisualPosition;
18
+ };
19
+
20
+ export type ClientVisualPacket = {
21
+ name: string;
22
+ data?: ClientVisualPayload;
23
+ };
24
+
25
+ export type ClientVisualObjectTarget = string | Record<string, any> | undefined | null;
26
+ export type ClientVisualComponentTarget =
27
+ | ClientVisualObjectTarget
28
+ | ClientVisualPosition;
29
+
30
+ export type ClientVisualContext = {
31
+ name: string;
32
+ data: ClientVisualPayload;
33
+ engine: RpgClientEngine;
34
+ scene: RpgClientMap;
35
+ source?: any;
36
+ target?: any;
37
+ object?: any;
38
+ position?: ClientVisualPosition;
39
+ };
40
+
41
+ export type ClientVisualHelpers = {
42
+ getObject(id?: string | null): any;
43
+ flash(target?: ClientVisualObjectTarget, options?: Record<string, any>): void;
44
+ showHit(target: ClientVisualObjectTarget, text: string): void;
45
+ component(
46
+ id: string,
47
+ target?: ClientVisualComponentTarget,
48
+ params?: Record<string, any>
49
+ ): void;
50
+ sound(id: string, options?: { volume?: number; loop?: boolean }): Promise<void>;
51
+ animation(
52
+ target: ClientVisualObjectTarget,
53
+ animationName: string,
54
+ options?: { graphic?: string | string[]; repeat?: number }
55
+ ): void;
56
+ shake(options?: {
57
+ intensity?: number;
58
+ duration?: number;
59
+ frequency?: number;
60
+ direction?: string;
61
+ }): void;
62
+ };
63
+
64
+ export type ClientVisualHandler = (
65
+ context: ClientVisualContext,
66
+ helpers: ClientVisualHelpers
67
+ ) => void | Promise<void>;
68
+
69
+ export type ClientVisualMap = Record<string, ClientVisualHandler>;
70
+
71
+ const isPosition = (value: any): value is ClientVisualPosition =>
72
+ value &&
73
+ typeof value === "object" &&
74
+ typeof value.x === "number" &&
75
+ typeof value.y === "number";
76
+
77
+ const resolvePosition = (data: ClientVisualPayload): ClientVisualPosition | undefined => {
78
+ if (isPosition(data.position)) return data.position;
79
+ if (typeof data.x === "number" && typeof data.y === "number") {
80
+ return { x: data.x, y: data.y, z: data.z };
81
+ }
82
+ return undefined;
83
+ };
84
+
85
+ const resolveObject = (engine: RpgClientEngine, target: ClientVisualObjectTarget) => {
86
+ if (!target) return undefined;
87
+ if (typeof target === "string") return engine.getObjectById(target);
88
+ return target;
89
+ };
90
+
91
+ const resolvePayloadObject = (
92
+ engine: RpgClientEngine,
93
+ data: ClientVisualPayload,
94
+ keys: string[]
95
+ ) => {
96
+ const id = keys
97
+ .map((key) => data[key])
98
+ .find((value) => typeof value === "string");
99
+ return resolveObject(engine, id);
100
+ };
101
+
102
+ const createClientVisualHelpers = (
103
+ engine: RpgClientEngine
104
+ ): ClientVisualHelpers => ({
105
+ getObject(id) {
106
+ if (!id) return undefined;
107
+ return engine.getObjectById(id);
108
+ },
109
+ flash(target, options = {}) {
110
+ const object = resolveObject(engine, target);
111
+ object?.flash?.(options);
112
+ },
113
+ showHit(target, text) {
114
+ const object = resolveObject(engine, target);
115
+ object?.showHit?.(text);
116
+ },
117
+ component(id, target, params = {}) {
118
+ const object = isPosition(target) ? undefined : resolveObject(engine, target);
119
+ const position = isPosition(target) ? target : undefined;
120
+ const anchor = object ?? position;
121
+ if (!anchor) return;
122
+ engine.getComponentAnimation(id).displayEffect(params, anchor);
123
+ },
124
+ sound(id, options) {
125
+ return engine.playSound(id, options);
126
+ },
127
+ animation(target, animationName, options = {}) {
128
+ const object = resolveObject(engine, target);
129
+ if (!object?.setAnimation) return;
130
+ if (options.graphic !== undefined) {
131
+ object.setAnimation(animationName, options.graphic, options.repeat ?? 1);
132
+ return;
133
+ }
134
+ object.setAnimation(animationName, options.repeat ?? 1);
135
+ },
136
+ shake(options = {}) {
137
+ engine.mapShakeTrigger.start(options);
138
+ },
139
+ });
140
+
141
+ export class ClientVisualRegistry {
142
+ private readonly handlers = new Map<string, ClientVisualHandler>();
143
+
144
+ register(name: string, handler: ClientVisualHandler) {
145
+ this.handlers.set(name, handler);
146
+ return handler;
147
+ }
148
+
149
+ registerMany(visuals: ClientVisualMap) {
150
+ Object.entries(visuals).forEach(([name, handler]) => {
151
+ this.register(name, handler);
152
+ });
153
+ }
154
+
155
+ get(name: string) {
156
+ return this.handlers.get(name);
157
+ }
158
+
159
+ async play(packet: ClientVisualPacket, engine: RpgClientEngine) {
160
+ const handler = this.handlers.get(packet.name);
161
+ if (!handler) {
162
+ console.warn(`Client visual "${packet.name}" is not registered`);
163
+ return;
164
+ }
165
+
166
+ const data = packet.data ?? {};
167
+ const context: ClientVisualContext = {
168
+ name: packet.name,
169
+ data,
170
+ engine,
171
+ scene: engine.scene,
172
+ source: resolvePayloadObject(engine, data, ["sourceId", "source"]),
173
+ target: resolvePayloadObject(engine, data, ["targetId", "target"]),
174
+ object: resolvePayloadObject(engine, data, ["objectId", "object"]),
175
+ position: resolvePosition(data),
176
+ };
177
+
178
+ try {
179
+ await handler(context, createClientVisualHelpers(engine));
180
+ } catch (error) {
181
+ console.error(`Client visual "${packet.name}" failed`, error);
182
+ }
183
+ }
184
+ }