@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.js","names":[],"sources":["../../src/services/interactions.ts"],"sourcesContent":["import { signal } from \"canvasengine\";\nimport type { RpgActionInput, RpgActionName } from \"@rpgjs/common\";\nimport type { RpgClientEngine } from \"../RpgClientEngine\";\n\nexport type RpgInteractionEventName =\n | \"pointerenter\"\n | \"pointerleave\"\n | \"pointerover\"\n | \"pointerout\"\n | \"pointerdown\"\n | \"pointerup\"\n | \"pointermove\"\n | \"click\"\n | \"dragstart\"\n | \"dragmove\"\n | \"drop\"\n | \"cancel\";\n\nexport type RpgInteractionPosition = {\n x: number;\n y: number;\n};\n\nexport type RpgInteractionBounds = {\n left: number;\n top: number;\n right: number;\n bottom: number;\n width: number;\n height: number;\n centerX: number;\n centerY: number;\n contains(point: RpgInteractionPosition | null | undefined): boolean;\n};\n\nexport type RpgInteractionBoundsSet = {\n bounds?: RpgInteractionBounds;\n hitbox?: RpgInteractionBounds;\n graphic?: RpgInteractionBounds;\n [key: string]: RpgInteractionBounds | undefined;\n};\n\nexport type RpgInteractionTile = {\n x: number;\n y: number;\n worldX: number;\n worldY: number;\n width: number;\n height: number;\n};\n\nexport type RpgInteractionState = {\n hovered: boolean;\n pressed: boolean;\n selected: boolean;\n dragging: boolean;\n data: Record<string, any>;\n overlays: Record<string, RpgInteractionOverlay>;\n};\n\nexport type RpgInteractionOverlay = {\n component: any;\n props?: Record<string, any>;\n};\n\nexport type RpgInteractionMatcher =\n | string\n | ((ctx: RpgInteractionMatcherContext) => boolean);\n\nexport type RpgInteractionMatcherContext = {\n client: RpgClientEngine;\n target: any;\n sprite: any;\n};\n\nexport type RpgInteractionHandler = (ctx: RpgInteractionContext) => void;\n\nexport type RpgInteractionBehavior = {\n component?: any;\n props?: Record<string, any> | ((ctx: RpgInteractionContext) => Record<string, any>);\n dependencies?: any[] | ((ctx: RpgInteractionContext) => any[]);\n cursor?: string | ((ctx: RpgInteractionContext) => string | undefined);\n hitTest?: (ctx: RpgInteractionContext) => boolean;\n pointerenter?: RpgInteractionHandler;\n pointerleave?: RpgInteractionHandler;\n pointerover?: RpgInteractionHandler;\n pointerout?: RpgInteractionHandler;\n pointerdown?: RpgInteractionHandler;\n pointerup?: RpgInteractionHandler;\n pointermove?: RpgInteractionHandler;\n click?: RpgInteractionHandler;\n dragstart?: RpgInteractionHandler;\n dragmove?: RpgInteractionHandler;\n drop?: RpgInteractionHandler;\n cancel?: RpgInteractionHandler;\n};\n\nexport type RpgInteractionComponentEntry = {\n component: any;\n props: Record<string, any>;\n dependencies: any[];\n};\n\nexport type RpgInteractionContext = {\n client: RpgClientEngine;\n target: any;\n sprite: any;\n event?: any;\n behavior: RpgInteractionBehavior;\n behaviorId: string;\n pointer: {\n screen(): RpgInteractionPosition | null;\n world(): RpgInteractionPosition | null;\n tile(): RpgInteractionTile | null;\n };\n bounds(kind?: string): RpgInteractionBounds;\n state: {\n value(): RpgInteractionState;\n get<T = any>(key: string): T | undefined;\n set(key: string, value: any): void;\n patch(patch: Partial<Omit<RpgInteractionState, \"data\" | \"overlays\">>): void;\n };\n overlay: {\n render(component: any, props?: Record<string, any>): void;\n update(props?: Record<string, any>): void;\n clear(): void;\n };\n select(selected?: boolean): void;\n action(action: RpgActionName, data?: any): void;\n action(input: RpgActionInput): void;\n cancel(): void;\n};\n\ntype InteractionRegistration = {\n id: string;\n matcher: RpgInteractionMatcher;\n behavior: RpgInteractionBehavior;\n};\n\ntype InteractionEventInput = {\n event?: any;\n bounds?: RpgInteractionBoundsSet;\n};\n\ntype ActiveDrag = {\n sprite: any;\n behavior: RpgInteractionBehavior;\n behaviorId: string;\n bounds?: RpgInteractionBoundsSet;\n cancelled: boolean;\n};\n\nconst DEFAULT_STATE: RpgInteractionState = {\n hovered: false,\n pressed: false,\n selected: false,\n dragging: false,\n data: {},\n overlays: {},\n};\n\nfunction readValue(value: any): any {\n return typeof value === \"function\" ? value() : value;\n}\n\nfunction createBounds(value: any): RpgInteractionBounds {\n const left = Number(value?.left ?? value?.x ?? 0);\n const top = Number(value?.top ?? value?.y ?? 0);\n const width = Number(value?.width ?? value?.w ?? 0);\n const height = Number(value?.height ?? value?.h ?? 0);\n const right = Number(value?.right ?? left + width);\n const bottom = Number(value?.bottom ?? top + height);\n const resolvedWidth = Number.isFinite(width) && width > 0 ? width : Math.max(0, right - left);\n const resolvedHeight = Number.isFinite(height) && height > 0 ? height : Math.max(0, bottom - top);\n\n return {\n left,\n top,\n right,\n bottom,\n width: resolvedWidth,\n height: resolvedHeight,\n centerX: Number(value?.centerX ?? left + resolvedWidth / 2),\n centerY: Number(value?.centerY ?? top + resolvedHeight / 2),\n contains(point) {\n if (!point) return false;\n return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom;\n },\n };\n}\n\nfunction normalizeBounds(bounds?: RpgInteractionBoundsSet): RpgInteractionBoundsSet {\n if (!bounds) return {};\n\n return Object.entries(bounds).reduce<RpgInteractionBoundsSet>((next, [key, value]) => {\n if (value) next[key] = createBounds(value);\n return next;\n }, {});\n}\n\nfunction readNumber(value: any, fallback = 0): number {\n const resolved = readValue(value);\n const number = Number(resolved);\n return Number.isFinite(number) ? number : fallback;\n}\n\nfunction offsetBounds(bounds: RpgInteractionBounds, x: number, y: number): RpgInteractionBounds {\n return createBounds({\n left: bounds.left + x,\n top: bounds.top + y,\n right: bounds.right + x,\n bottom: bounds.bottom + y,\n width: bounds.width,\n height: bounds.height,\n centerX: bounds.centerX + x,\n centerY: bounds.centerY + y,\n });\n}\n\nfunction toWorldBounds(sprite: any, bounds: RpgInteractionBoundsSet): RpgInteractionBoundsSet {\n const x = readNumber(sprite?.x);\n const y = readNumber(sprite?.y);\n\n return Object.entries(bounds).reduce<RpgInteractionBoundsSet>((next, [key, value]) => {\n if (value) next[key] = offsetBounds(value, x, y);\n return next;\n }, {});\n}\n\nfunction normalizeBehavior(behavior: RpgInteractionBehavior | any): RpgInteractionBehavior {\n if (typeof behavior === \"function\") {\n return { component: behavior };\n }\n return behavior ?? {};\n}\n\nexport class RpgClientInteractions {\n private registrations = signal<InteractionRegistration[]>([]);\n private states = signal<Record<string, RpgInteractionState>>({});\n private activeDrag?: ActiveDrag;\n private nextId = 0;\n\n constructor(private client: RpgClientEngine) {}\n\n use(matcher: RpgInteractionMatcher, behavior: RpgInteractionBehavior | any): () => void {\n const registration = {\n id: `interaction:${++this.nextId}`,\n matcher,\n behavior: normalizeBehavior(behavior),\n };\n\n this.registrations.update((registrations) => [...registrations, registration]);\n\n return () => {\n this.registrations.update((registrations) =>\n registrations.filter((entry) => entry.id !== registration.id)\n );\n };\n }\n\n getState(sprite: any): RpgInteractionState {\n const id = this.getSpriteId(sprite);\n return {\n ...DEFAULT_STATE,\n ...(id ? this.states()[id] : undefined),\n data: id ? { ...(this.states()[id]?.data ?? {}) } : {},\n overlays: id ? { ...(this.states()[id]?.overlays ?? {}) } : {},\n };\n }\n\n getRenderedComponents(sprite: any, bounds?: RpgInteractionBoundsSet): RpgInteractionComponentEntry[] {\n const normalizedBounds = normalizeBounds(bounds);\n const matched = this.getMatches(sprite);\n const state = this.getState(sprite);\n const entries: RpgInteractionComponentEntry[] = [];\n\n matched.forEach((registration) => {\n if (!registration.behavior.component) return;\n const ctx = this.createContext(sprite, registration, { bounds: normalizedBounds });\n entries.push({\n component: registration.behavior.component,\n props: {\n ...this.defaultComponentProps(sprite, state, normalizedBounds),\n ...this.resolveProps(registration.behavior.props, ctx),\n },\n dependencies: this.resolveDependencies(registration.behavior.dependencies, ctx),\n });\n });\n\n Object.entries(state.overlays).forEach(([id, overlay]) => {\n entries.push({\n component: overlay.component,\n props: {\n ...this.defaultComponentProps(sprite, state, normalizedBounds),\n ...(overlay.props ?? {}),\n overlayId: id,\n },\n dependencies: [],\n });\n });\n\n return entries;\n }\n\n cursorFor(sprite: any, bounds?: RpgInteractionBoundsSet): string | undefined {\n this.states();\n for (const registration of this.getMatches(sprite)) {\n const cursor = registration.behavior.cursor;\n if (!cursor) continue;\n const ctx = this.createContext(sprite, registration, { bounds: normalizeBounds(bounds) });\n if (!this.passesHitTest(registration.behavior, ctx, \"pointermove\")) continue;\n const resolved = typeof cursor === \"function\" ? cursor(ctx) : cursor;\n if (resolved) return resolved;\n }\n return undefined;\n }\n\n handle(sprite: any, type: RpgInteractionEventName, input: InteractionEventInput = {}): void {\n const matches = this.getMatches(sprite);\n const bounds = normalizeBounds(input.bounds);\n const entries = matches\n .map((registration) => ({\n registration,\n ctx: this.createContext(sprite, registration, {\n event: input.event,\n bounds,\n }),\n }))\n .filter(({ registration, ctx }) =>\n this.passesHitTest(registration.behavior, ctx, type)\n );\n\n if (type === \"pointerover\" || type === \"pointerenter\") {\n if (entries.length > 0) {\n this.patchState(sprite, { hovered: true });\n }\n }\n if (type === \"pointerout\" || type === \"pointerleave\") {\n if (matches.length > 0) {\n this.patchState(sprite, { hovered: false, pressed: false });\n }\n }\n if (type === \"pointerdown\") {\n if (entries.length > 0) {\n this.patchState(sprite, { pressed: true });\n }\n }\n if (type === \"pointermove\" && matches.length > 0) {\n this.patchState(sprite, { hovered: entries.length > 0 });\n }\n if (type === \"pointerup\") {\n if (matches.length > 0) {\n this.patchState(sprite, { pressed: false });\n }\n }\n\n entries.forEach(({ registration, ctx }) => {\n this.callHandler(registration.behavior, type, ctx);\n\n if (type === \"pointerdown\" && this.isDraggable(registration.behavior)) {\n this.activeDrag = {\n sprite,\n behavior: registration.behavior,\n behaviorId: registration.id,\n bounds,\n cancelled: false,\n };\n this.patchState(sprite, { dragging: true });\n this.callHandler(registration.behavior, \"dragstart\", ctx);\n }\n });\n }\n\n handlePointerMove(event?: any): void {\n if (!this.activeDrag) return;\n const drag = this.activeDrag;\n const registration = {\n id: drag.behaviorId,\n matcher: \"*\",\n behavior: drag.behavior,\n };\n const ctx = this.createContext(drag.sprite, registration, {\n event,\n bounds: drag.bounds,\n });\n this.callHandler(drag.behavior, \"dragmove\", ctx);\n }\n\n handlePointerUp(event?: any): void {\n if (!this.activeDrag) return;\n const drag = this.activeDrag;\n this.activeDrag = undefined;\n this.patchState(drag.sprite, { dragging: false, pressed: false });\n\n const registration = {\n id: drag.behaviorId,\n matcher: \"*\",\n behavior: drag.behavior,\n };\n const ctx = this.createContext(drag.sprite, registration, {\n event,\n bounds: drag.bounds,\n });\n\n this.callHandler(drag.behavior, drag.cancelled ? \"cancel\" : \"drop\", ctx);\n }\n\n cancelDrag(event?: any): void {\n if (!this.activeDrag) return;\n this.activeDrag.cancelled = true;\n this.handlePointerUp(event);\n }\n\n private getMatches(sprite: any): InteractionRegistration[] {\n return this.registrations().filter((registration) =>\n this.matches(registration.matcher, sprite)\n );\n }\n\n private matches(matcher: RpgInteractionMatcher, sprite: any): boolean {\n if (typeof matcher === \"function\") {\n return !!matcher({ client: this.client, target: sprite, sprite });\n }\n\n if (matcher === \"*\") return true;\n\n const candidates = [\n readValue(sprite?.id),\n readValue(sprite?.name),\n readValue(sprite?._name),\n readValue(sprite?.type),\n readValue(sprite?._type),\n sprite?.constructor?.name,\n ].filter(Boolean);\n\n return candidates.includes(matcher);\n }\n\n private createContext(\n sprite: any,\n registration: Pick<InteractionRegistration, \"id\" | \"behavior\">,\n input: InteractionEventInput = {},\n ): RpgInteractionContext {\n const bounds = toWorldBounds(sprite, normalizeBounds(input.bounds));\n\n const ctx = {\n client: this.client,\n target: sprite,\n sprite,\n event: input.event,\n behavior: registration.behavior,\n behaviorId: registration.id,\n pointer: {\n screen: () => this.client.pointer.screen(),\n world: () => this.client.pointer.world(),\n tile: () => this.getPointerTile(),\n },\n bounds: (kind = \"bounds\") =>\n bounds[kind] ?? bounds.graphic ?? bounds.hitbox ?? bounds.bounds ?? createBounds({}),\n state: {\n value: () => this.getState(sprite),\n get: <T = any>(key: string) => this.getState(sprite).data[key] as T | undefined,\n set: (key: string, value: any) => this.patchStateData(sprite, { [key]: value }),\n patch: (patch: Partial<Omit<RpgInteractionState, \"data\" | \"overlays\">>) =>\n this.patchState(sprite, patch),\n },\n overlay: {\n render: (component: any, props?: Record<string, any>) =>\n this.patchOverlay(sprite, registration.id, { component, props }),\n update: (props?: Record<string, any>) =>\n this.updateOverlay(sprite, registration.id, props),\n clear: () => this.clearOverlay(sprite, registration.id),\n },\n select: (selected = true) => this.patchState(sprite, { selected }),\n action: (action: RpgActionName | RpgActionInput, data?: any) =>\n this.client.processAction(action as any, data),\n cancel: () => {\n const activeDrag = this.activeDrag;\n if (activeDrag && activeDrag.sprite === sprite) {\n activeDrag.cancelled = true;\n }\n },\n };\n\n return ctx;\n }\n\n private callHandler(\n behavior: RpgInteractionBehavior,\n type: RpgInteractionEventName,\n ctx: RpgInteractionContext,\n ): void {\n const handler = behavior[type];\n handler?.(ctx);\n\n if (type === \"pointerover\") {\n behavior.pointerenter?.(ctx);\n }\n if (type === \"pointerout\") {\n behavior.pointerleave?.(ctx);\n }\n }\n\n private passesHitTest(\n behavior: RpgInteractionBehavior,\n ctx: RpgInteractionContext,\n type: RpgInteractionEventName,\n ): boolean {\n if (!behavior.hitTest) return true;\n if (type === \"pointerout\" || type === \"pointerleave\" || type === \"cancel\" || type === \"drop\") {\n return true;\n }\n return behavior.hitTest(ctx);\n }\n\n private isDraggable(behavior: RpgInteractionBehavior): boolean {\n return !!(behavior.dragstart || behavior.dragmove || behavior.drop || behavior.cancel);\n }\n\n private defaultComponentProps(\n sprite: any,\n state: RpgInteractionState,\n bounds: RpgInteractionBoundsSet,\n ): Record<string, any> {\n return {\n target: sprite,\n sprite,\n state,\n bounds: bounds.bounds ?? bounds.graphic ?? bounds.hitbox ?? createBounds({}),\n hitboxBounds: bounds.hitbox,\n graphicBounds: bounds.graphic,\n pointer: this.client.pointer,\n client: this.client,\n };\n }\n\n private resolveProps(\n props: RpgInteractionBehavior[\"props\"],\n ctx: RpgInteractionContext,\n ): Record<string, any> {\n if (!props) return {};\n return typeof props === \"function\" ? props(ctx) : props;\n }\n\n private resolveDependencies(\n dependencies: RpgInteractionBehavior[\"dependencies\"],\n ctx: RpgInteractionContext,\n ): any[] {\n if (!dependencies) return [];\n return typeof dependencies === \"function\" ? dependencies(ctx) : dependencies;\n }\n\n private getSpriteId(sprite: any): string | undefined {\n const id = readValue(sprite?.id);\n return id == null ? undefined : String(id);\n }\n\n private patchState(\n sprite: any,\n patch: Partial<Omit<RpgInteractionState, \"data\" | \"overlays\">>,\n ): void {\n const id = this.getSpriteId(sprite);\n if (!id) return;\n this.states.update((states) => ({\n ...states,\n [id]: {\n ...DEFAULT_STATE,\n ...(states[id] ?? {}),\n ...patch,\n data: states[id]?.data ?? {},\n overlays: states[id]?.overlays ?? {},\n },\n }));\n }\n\n private patchStateData(sprite: any, patch: Record<string, any>): void {\n const id = this.getSpriteId(sprite);\n if (!id) return;\n const current = this.getState(sprite);\n this.states.update((states) => ({\n ...states,\n [id]: {\n ...current,\n data: {\n ...current.data,\n ...patch,\n },\n },\n }));\n }\n\n private patchOverlay(sprite: any, id: string, overlay: RpgInteractionOverlay): void {\n const spriteId = this.getSpriteId(sprite);\n if (!spriteId) return;\n const current = this.getState(sprite);\n this.states.update((states) => ({\n ...states,\n [spriteId]: {\n ...current,\n overlays: {\n ...current.overlays,\n [id]: overlay,\n },\n },\n }));\n }\n\n private updateOverlay(sprite: any, id: string, props?: Record<string, any>): void {\n const current = this.getState(sprite);\n const overlay = current.overlays[id];\n if (!overlay) return;\n this.patchOverlay(sprite, id, {\n ...overlay,\n props: {\n ...(overlay.props ?? {}),\n ...(props ?? {}),\n },\n });\n }\n\n private clearOverlay(sprite: any, id: string): void {\n const spriteId = this.getSpriteId(sprite);\n if (!spriteId) return;\n const current = this.getState(sprite);\n const { [id]: _removed, ...overlays } = current.overlays;\n this.states.update((states) => ({\n ...states,\n [spriteId]: {\n ...current,\n overlays,\n },\n }));\n }\n\n private getPointerTile(): RpgInteractionTile | null {\n const world = this.client.pointer.world();\n if (!world) return null;\n\n const map = (this.client as any).sceneMap;\n const width = Number(map?.tileWidth ?? 32);\n const height = Number(map?.tileHeight ?? 32);\n const tileX = Math.floor(world.x / width);\n const tileY = Math.floor(world.y / height);\n\n return {\n x: tileX,\n y: tileY,\n worldX: tileX * width,\n worldY: tileY * height,\n width,\n height,\n };\n }\n}\n\nexport function hoverPopover(component: any, props?: Record<string, any>): RpgInteractionBehavior {\n return {\n component,\n props,\n cursor: \"pointer\",\n };\n}\n\nexport function selectable(options: {\n cursor?: string;\n onSelect?: RpgInteractionHandler;\n} = {}): RpgInteractionBehavior {\n return {\n cursor: options.cursor ?? \"pointer\",\n click(ctx) {\n ctx.select();\n options.onSelect?.(ctx);\n },\n };\n}\n\nexport function draggable(options: {\n cursor?: string;\n start?: RpgInteractionHandler;\n move?: RpgInteractionHandler;\n drop?: RpgInteractionHandler;\n cancel?: RpgInteractionHandler;\n} = {}): RpgInteractionBehavior {\n return {\n cursor: options.cursor ?? \"grab\",\n dragstart(ctx) {\n ctx.state.patch({ dragging: true });\n options.start?.(ctx);\n },\n dragmove: options.move,\n drop(ctx) {\n ctx.state.patch({ dragging: false, pressed: false });\n options.drop?.(ctx);\n },\n cancel(ctx) {\n ctx.state.patch({ dragging: false, pressed: false });\n options.cancel?.(ctx);\n },\n };\n}\n\nexport function dragToTile(options: {\n action?: RpgActionName;\n data?: (ctx: RpgInteractionContext) => any;\n onDrop?: RpgInteractionHandler;\n cursor?: string;\n}): RpgInteractionBehavior {\n return draggable({\n cursor: options.cursor,\n drop(ctx) {\n if (options.onDrop) {\n options.onDrop(ctx);\n return;\n }\n if (!options.action) return;\n ctx.action(options.action, options.data ? options.data(ctx) : {\n eventId: ctx.target.id,\n position: ctx.pointer.tile(),\n });\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ export declare enum Control {
3
3
  Attack = "attack",
4
4
  Defense = "defense",
5
5
  Skill = "skill",
6
+ Dash = "dash",
6
7
  Back = "back",
7
8
  Up = 1,
8
9
  Down = 3,
@@ -4,6 +4,7 @@ var Control = /* @__PURE__ */ function(Control) {
4
4
  Control["Attack"] = "attack";
5
5
  Control["Defense"] = "defense";
6
6
  Control["Skill"] = "skill";
7
+ Control["Dash"] = "dash";
7
8
  Control["Back"] = "back";
8
9
  Control[Control["Up"] = 1] = "Up";
9
10
  Control[Control["Down"] = 3] = "Down";
@@ -1 +1 @@
1
- {"version":3,"file":"keyboardControls.js","names":[],"sources":["../../src/services/keyboardControls.ts"],"sourcesContent":["import { KeyboardControls } from \"canvasengine\";\n\nexport enum Control {\n Action = 'action',\n Attack = 'attack',\n Defense = 'defense',\n Skill = 'skill',\n Back = 'back',\n Up = 1,\n Down = 3,\n Right = 2,\n Left = 4\n}\n\nexport function provideKeyboardControls() {\n return {\n provide: 'KeyboardControls',\n useValue: null,\n };\n}"],"mappings":";AAEA,IAAY,UAAL,yBAAA,SAAA;AACL,SAAA,YAAA;AACA,SAAA,YAAA;AACA,SAAA,aAAA;AACA,SAAA,WAAA;AACA,SAAA,UAAA;AACA,SAAA,QAAA,QAAA,KAAA;AACA,SAAA,QAAA,UAAA,KAAA;AACA,SAAA,QAAA,WAAA,KAAA;AACA,SAAA,QAAA,UAAA,KAAA;;KACD;AAED,SAAgB,0BAA0B;AACxC,QAAO;EACL,SAAS;EACT,UAAU;EACX"}
1
+ {"version":3,"file":"keyboardControls.js","names":[],"sources":["../../src/services/keyboardControls.ts"],"sourcesContent":["import { KeyboardControls } from \"canvasengine\";\n\nexport enum Control {\n Action = 'action',\n Attack = 'attack',\n Defense = 'defense',\n Skill = 'skill',\n Dash = 'dash',\n Back = 'back',\n Up = 1,\n Down = 3,\n Right = 2,\n Left = 4\n}\n\nexport function provideKeyboardControls() {\n return {\n provide: 'KeyboardControls',\n useValue: null,\n };\n}\n"],"mappings":";AAEA,IAAY,UAAL,yBAAA,SAAA;CACL,QAAA,YAAA;CACA,QAAA,YAAA;CACA,QAAA,aAAA;CACA,QAAA,WAAA;CACA,QAAA,UAAA;CACA,QAAA,UAAA;CACA,QAAA,QAAA,QAAA,KAAA;CACA,QAAA,QAAA,UAAA,KAAA;CACA,QAAA,QAAA,WAAA,KAAA;CACA,QAAA,QAAA,UAAA,KAAA;;AACF,EAAA,CAAA,CAAA;AAEA,SAAgB,0BAA0B;CACxC,OAAO;EACL,SAAS;EACT,UAAU;CACZ;AACF"}
@@ -1,4 +1,5 @@
1
1
  import { Context } from '@signe/di';
2
+ import { LightingState } from '@rpgjs/common';
2
3
  export declare const LoadMapToken = "LoadMapToken";
3
4
  /**
4
5
  * Represents the structure of map data that should be returned by the load map callback.
@@ -17,8 +18,16 @@ type MapData = {
17
18
  height?: number;
18
19
  /** Optional map events data (NPCs, interactive objects, etc.) */
19
20
  events?: any;
21
+ /** Optional named positions, for example Tiled point objects used by changeMap("map", "name") */
22
+ positions?: Record<string, {
23
+ x: number;
24
+ y: number;
25
+ z?: number;
26
+ }>;
20
27
  /** Optional map identifier, defaults to the mapId parameter if not provided */
21
28
  id?: string;
29
+ /** Optional initial lighting state for the loaded map */
30
+ lighting?: LightingState | null;
22
31
  };
23
32
  /**
24
33
  * Callback function type for loading map data.
@@ -1,4 +1,4 @@
1
- import { inject } from "../node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js";
1
+ import { inject } from "../node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js";
2
2
  import { UpdateMapToken } from "@rpgjs/common";
3
3
  //#region src/services/loadMap.ts
4
4
  var LoadMapToken = "LoadMapToken";
@@ -1 +1 @@
1
- {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService } from \"@rpgjs/common\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\ntype MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: Context, private options: LoadMapOptions) {\n if (context['side'] === 'server') {\n return\n }\n this.updateMapService = inject(context, UpdateMapToken);\n }\n\n async load(mapId: string) {\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions) {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => {\n if (context['side'] === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: Context) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAGA,IAAa,eAAe;AAkC5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA0B,SAAiC;AAAnD,OAAA,UAAA;AAA0B,OAAA,UAAA;AAC5C,MAAI,QAAQ,YAAY,SACtB;AAEF,OAAK,mBAAmB,OAAO,SAAS,eAAe;;CAGzD,MAAM,KAAK,OAAe;EACxB,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,GAAG,CAAC;AACzD,QAAM,KAAK,iBAAiB,OAAO,IAAI;AACvC,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+FX,SAAgB,eAAe,SAAyB;AACtD,QAAO,CACL;EACE,SAAS;EACT,aAAa,YAAqB;AAChC,OAAI,QAAQ,YAAY,SACtB,SAAQ,KAAK,mCAAmC;;EAIrD,EACD;EACE,SAAS;EACT,aAAa,YAAqB,IAAI,eAAe,SAAS,QAAQ;EACvE,CACF"}
1
+ {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService, type LightingState } from \"@rpgjs/common\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\ntype MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional named positions, for example Tiled point objects used by changeMap(\"map\", \"name\") */\n positions?: Record<string, { x: number; y: number; z?: number }>;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n /** Optional initial lighting state for the loaded map */\n lighting?: LightingState | null;\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: Context, private options: LoadMapOptions) {\n if (context['side'] === 'server') {\n return\n }\n this.updateMapService = inject(context, UpdateMapToken);\n }\n\n async load(mapId: string) {\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions) {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => {\n if (context['side'] === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: Context) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAGA,IAAa,eAAe;AAsC5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA0B,SAAiC;EAAnD,KAAA,UAAA;EAA0B,KAAA,UAAA;EAC5C,IAAI,QAAQ,YAAY,UACtB;EAEF,KAAK,mBAAmB,OAAO,SAAS,cAAc;CACxD;CAEA,MAAM,KAAK,OAAe;EACxB,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE,CAAC;EACxD,MAAM,KAAK,iBAAiB,OAAO,GAAG;EACtC,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FA,SAAgB,eAAe,SAAyB;CACtD,OAAO,CACL;EACE,SAAS;EACT,aAAa,YAAqB;GAChC,IAAI,QAAQ,YAAY,UACtB,QAAQ,KAAK,kCAAkC;EAGnD;CACF,GACA;EACE,SAAS;EACT,aAAa,YAAqB,IAAI,eAAe,SAAS,OAAO;CACvE,CACF;AACF"}
@@ -0,0 +1,5 @@
1
+ export declare function parseSocketMessage(data: any): any;
2
+ export declare function isNativeSocketEvent(event: string): event is "error" | "open" | "close";
3
+ export declare function waitForRpgjsConnected(conn: any, timeoutMs?: number, options?: {
4
+ ignoreCleanClose?: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,50 @@
1
+ //#region src/services/mmorpg-connection.ts
2
+ function parseSocketMessage(data) {
3
+ if (typeof data !== "string") return data;
4
+ try {
5
+ return JSON.parse(data);
6
+ } catch {
7
+ return;
8
+ }
9
+ }
10
+ function isNativeSocketEvent(event) {
11
+ return event === "open" || event === "close" || event === "error";
12
+ }
13
+ function waitForRpgjsConnected(conn, timeoutMs = 1e4, options = {}) {
14
+ return new Promise((resolve, reject) => {
15
+ let timeoutId;
16
+ const cleanup = () => {
17
+ conn.removeEventListener("message", onMessage);
18
+ conn.removeEventListener("close", onClose);
19
+ conn.removeEventListener("error", onError);
20
+ if (timeoutId !== void 0) window.clearTimeout(timeoutId);
21
+ };
22
+ const rejectWith = (error) => {
23
+ cleanup();
24
+ reject(error);
25
+ };
26
+ const onMessage = (event) => {
27
+ if (parseSocketMessage(event.data)?.type !== "connected") return;
28
+ cleanup();
29
+ resolve();
30
+ };
31
+ const onClose = (event) => {
32
+ const rawReason = event.reason;
33
+ if (options.ignoreCleanClose && (event.code === 1e3 || rawReason instanceof Event || typeof rawReason !== "string")) return;
34
+ rejectWith(new Error(typeof rawReason === "string" && rawReason ? rawReason : "WebSocket closed before RPGJS connection was accepted"));
35
+ };
36
+ const onError = (event) => {
37
+ rejectWith(new Error(event.message || "WebSocket connection failed"));
38
+ };
39
+ conn.addEventListener("message", onMessage);
40
+ conn.addEventListener("close", onClose);
41
+ conn.addEventListener("error", onError);
42
+ timeoutId = window.setTimeout(() => {
43
+ rejectWith(/* @__PURE__ */ new Error("RPGJS connection timeout"));
44
+ }, timeoutMs);
45
+ });
46
+ }
47
+ //#endregion
48
+ export { isNativeSocketEvent, waitForRpgjsConnected };
49
+
50
+ //# sourceMappingURL=mmorpg-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mmorpg-connection.js","names":[],"sources":["../../src/services/mmorpg-connection.ts"],"sourcesContent":["export function parseSocketMessage(data: any) {\n if (typeof data !== \"string\") {\n return data;\n }\n try {\n return JSON.parse(data);\n }\n catch {\n return undefined;\n }\n}\n\nexport function isNativeSocketEvent(event: string) {\n return event === \"open\" || event === \"close\" || event === \"error\";\n}\n\nexport function waitForRpgjsConnected(\n conn: any,\n timeoutMs = 10000,\n options: { ignoreCleanClose?: boolean } = {},\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let timeoutId: number | undefined;\n\n const cleanup = () => {\n conn.removeEventListener(\"message\", onMessage);\n conn.removeEventListener(\"close\", onClose);\n conn.removeEventListener(\"error\", onError);\n if (timeoutId !== undefined) {\n window.clearTimeout(timeoutId);\n }\n };\n const rejectWith = (error: Error) => {\n cleanup();\n reject(error);\n };\n const onMessage = (event: MessageEvent) => {\n const data = parseSocketMessage(event.data);\n if (data?.type !== \"connected\") {\n return;\n }\n cleanup();\n resolve();\n };\n const onClose = (event: CloseEvent) => {\n const rawReason: unknown = (event as any).reason;\n if (\n options.ignoreCleanClose\n && (event.code === 1000 || rawReason instanceof Event || typeof rawReason !== \"string\")\n ) {\n return;\n }\n const reason = typeof rawReason === \"string\" && rawReason\n ? rawReason\n : \"WebSocket closed before RPGJS connection was accepted\";\n rejectWith(new Error(reason));\n };\n const onError = (event: ErrorEvent) => {\n rejectWith(new Error(event.message || \"WebSocket connection failed\"));\n };\n\n conn.addEventListener(\"message\", onMessage);\n conn.addEventListener(\"close\", onClose);\n conn.addEventListener(\"error\", onError);\n timeoutId = window.setTimeout(() => {\n rejectWith(new Error(\"RPGJS connection timeout\"));\n }, timeoutMs);\n });\n}\n"],"mappings":";AAAA,SAAgB,mBAAmB,MAAW;CAC5C,IAAI,OAAO,SAAS,UAClB,OAAO;CAET,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QACM;EACJ;CACF;AACF;AAEA,SAAgB,oBAAoB,OAAe;CACjD,OAAO,UAAU,UAAU,UAAU,WAAW,UAAU;AAC5D;AAEA,SAAgB,sBACd,MACA,YAAY,KACZ,UAA0C,CAAC,GAC5B;CACf,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI;EAEJ,MAAM,gBAAgB;GACpB,KAAK,oBAAoB,WAAW,SAAS;GAC7C,KAAK,oBAAoB,SAAS,OAAO;GACzC,KAAK,oBAAoB,SAAS,OAAO;GACzC,IAAI,cAAc,KAAA,GAChB,OAAO,aAAa,SAAS;EAEjC;EACA,MAAM,cAAc,UAAiB;GACnC,QAAQ;GACR,OAAO,KAAK;EACd;EACA,MAAM,aAAa,UAAwB;GAEzC,IADa,mBAAmB,MAAM,IAClC,GAAM,SAAS,aACjB;GAEF,QAAQ;GACR,QAAQ;EACV;EACA,MAAM,WAAW,UAAsB;GACrC,MAAM,YAAsB,MAAc;GAC1C,IACE,QAAQ,qBACJ,MAAM,SAAS,OAAQ,qBAAqB,SAAS,OAAO,cAAc,WAE9E;GAKF,WAAW,IAAI,MAHA,OAAO,cAAc,YAAY,YAC5C,YACA,uDACuB,CAAC;EAC9B;EACA,MAAM,WAAW,UAAsB;GACrC,WAAW,IAAI,MAAM,MAAM,WAAW,6BAA6B,CAAC;EACtE;EAEA,KAAK,iBAAiB,WAAW,SAAS;EAC1C,KAAK,iBAAiB,SAAS,OAAO;EACtC,KAAK,iBAAiB,SAAS,OAAO;EACtC,YAAY,OAAO,iBAAiB;GAClC,2BAAW,IAAI,MAAM,0BAA0B,CAAC;EAClD,GAAG,SAAS;CACd,CAAC;AACH"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,28 +1,35 @@
1
1
  import { Context } from '@signe/di';
2
2
  import { RpgGui } from '../Gui/Gui';
3
3
  import { RpgClientEngine } from '../RpgClientEngine';
4
- import { AbstractWebsocket, SocketUpdateProperties } from './AbstractSocket';
4
+ import { AbstractWebsocket, SocketQuery, SocketUpdateProperties } from './AbstractSocket';
5
5
  import { UpdateMapService } from '@rpgjs/common';
6
- interface MmorpgOptions {
6
+ export interface MmorpgOptions {
7
7
  host?: string;
8
8
  connectionId?: string;
9
9
  connectionIdScope?: "local" | "session" | "ephemeral";
10
+ query?: SocketQuery | (() => SocketQuery | undefined);
11
+ socketOptions?: Record<string, any>;
10
12
  }
11
- declare class BridgeWebsocket extends AbstractWebsocket {
13
+ export declare class BridgeWebsocket extends AbstractWebsocket {
12
14
  protected context: Context;
13
15
  private options;
16
+ readonly mode: "mmorpg";
14
17
  private socket;
15
18
  private privateId;
16
19
  private pendingOn;
20
+ private acceptedOpenListeners;
17
21
  private targetRoom;
18
22
  constructor(context: Context, options?: MmorpgOptions);
19
23
  private resolveConnectionId;
24
+ private resolveQuery;
20
25
  connection(listeners?: (data: any) => void): Promise<void>;
21
26
  on(key: string, callback: (data: any) => void): void;
22
27
  off(event: string, callback: (data: any) => void): void;
23
28
  emit(event: string, data: any): void;
29
+ private attachEvent;
30
+ private emitAcceptedOpen;
24
31
  updateProperties({ room, host, query }: SocketUpdateProperties): void;
25
- private waitForNextOpen;
32
+ private isNativeSocketEvent;
26
33
  reconnect(_listeners?: (data: any) => void): Promise<void>;
27
34
  getCurrentRoom(): string;
28
35
  }
@@ -3,7 +3,8 @@ import { provideSaveClient } from "./save.js";
3
3
  import { RpgGui } from "../Gui/Gui.js";
4
4
  import { RpgClientEngine } from "../RpgClientEngine.js";
5
5
  import { provideKeyboardControls } from "./keyboardControls.js";
6
- import { connectionRoom } from "../node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js";
6
+ import { connectionRoom } from "../node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/client/index.js";
7
+ import { isNativeSocketEvent, waitForRpgjsConnected } from "./mmorpg-connection.js";
7
8
  import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
8
9
  //#region src/services/mmorpg.ts
9
10
  var BridgeWebsocket = class extends AbstractWebsocket {
@@ -11,7 +12,9 @@ var BridgeWebsocket = class extends AbstractWebsocket {
11
12
  super(context);
12
13
  this.context = context;
13
14
  this.options = options;
15
+ this.mode = "mmorpg";
14
16
  this.pendingOn = [];
17
+ this.acceptedOpenListeners = /* @__PURE__ */ new Set();
15
18
  this.targetRoom = "lobby-1";
16
19
  this.privateId = this.resolveConnectionId();
17
20
  }
@@ -27,17 +30,31 @@ var BridgeWebsocket = class extends AbstractWebsocket {
27
30
  storage.setItem(key, id);
28
31
  return id;
29
32
  }
33
+ resolveQuery() {
34
+ return (typeof this.options.query === "function" ? this.options.query() : this.options.query) ?? {};
35
+ }
30
36
  async connection(listeners) {
31
37
  class Room {}
32
38
  const instance = new Room();
39
+ const host = this.options.host || window.location.host;
33
40
  this.socket = await connectionRoom({
34
- host: this.options.host || window.location.host,
41
+ maxRetries: 0,
42
+ ...this.options.socketOptions,
43
+ host,
35
44
  room: this.targetRoom,
36
- id: this.privateId
45
+ id: this.privateId,
46
+ query: {
47
+ ...this.resolveQuery(),
48
+ id: this.privateId
49
+ }
37
50
  }, instance);
38
- listeners?.(this.socket);
39
- this.pendingOn.forEach(({ event, callback }) => this.socket.on(event, callback));
51
+ const pendingOn = this.pendingOn;
40
52
  this.pendingOn = [];
53
+ pendingOn.filter(({ event }) => !this.isNativeSocketEvent(event)).forEach(({ event, callback }) => this.attachEvent(event, callback));
54
+ await waitForRpgjsConnected(this.socket.conn);
55
+ pendingOn.filter(({ event }) => this.isNativeSocketEvent(event)).forEach(({ event, callback }) => this.attachEvent(event, callback));
56
+ this.emitAcceptedOpen();
57
+ listeners?.(this.socket);
41
58
  }
42
59
  on(key, callback) {
43
60
  if (!this.socket) {
@@ -47,15 +64,38 @@ var BridgeWebsocket = class extends AbstractWebsocket {
47
64
  });
48
65
  return;
49
66
  }
50
- this.socket.on(key, callback);
67
+ this.attachEvent(key, callback);
51
68
  }
52
69
  off(event, callback) {
53
70
  if (!this.socket) return;
71
+ if (event === "open") {
72
+ this.acceptedOpenListeners.delete(callback);
73
+ return;
74
+ }
75
+ if (this.isNativeSocketEvent(event)) {
76
+ this.socket.conn.removeEventListener(event, callback);
77
+ return;
78
+ }
54
79
  this.socket.off(event, callback);
55
80
  }
56
81
  emit(event, data) {
57
82
  this.socket.emit(event, data);
58
83
  }
84
+ attachEvent(event, callback) {
85
+ if (event === "open") {
86
+ this.acceptedOpenListeners.add(callback);
87
+ return;
88
+ }
89
+ if (this.isNativeSocketEvent(event)) {
90
+ this.socket.conn.addEventListener(event, callback);
91
+ return;
92
+ }
93
+ this.socket.on(event, callback);
94
+ }
95
+ emitAcceptedOpen() {
96
+ const event = new Event("open");
97
+ this.acceptedOpenListeners.forEach((callback) => callback(event));
98
+ }
59
99
  updateProperties({ room, host, query }) {
60
100
  if (!this.socket?.conn) return;
61
101
  this.targetRoom = room;
@@ -63,39 +103,23 @@ var BridgeWebsocket = class extends AbstractWebsocket {
63
103
  room,
64
104
  id: this.privateId,
65
105
  host: host || this.options.host || window.location.host,
66
- query
106
+ query: {
107
+ ...this.resolveQuery(),
108
+ ...query,
109
+ id: this.privateId
110
+ }
67
111
  });
68
112
  }
69
- waitForNextOpen(conn, timeoutMs = 1e4) {
70
- return new Promise((resolve, reject) => {
71
- let timeoutId;
72
- const onOpen = () => {
73
- cleanup();
74
- resolve();
75
- };
76
- const onError = () => {
77
- cleanup();
78
- reject(/* @__PURE__ */ new Error("WebSocket reconnect failed"));
79
- };
80
- const cleanup = () => {
81
- conn.removeEventListener("open", onOpen);
82
- conn.removeEventListener("error", onError);
83
- if (timeoutId !== void 0) window.clearTimeout(timeoutId);
84
- };
85
- conn.addEventListener("open", onOpen);
86
- conn.addEventListener("error", onError);
87
- timeoutId = window.setTimeout(() => {
88
- cleanup();
89
- reject(/* @__PURE__ */ new Error("WebSocket reconnect timeout"));
90
- }, timeoutMs);
91
- });
113
+ isNativeSocketEvent(event) {
114
+ return isNativeSocketEvent(event);
92
115
  }
93
116
  async reconnect(_listeners) {
94
117
  if (!this.socket?.conn) return;
95
118
  const conn = this.socket.conn;
96
- const opened = this.waitForNextOpen(conn);
119
+ const connected = waitForRpgjsConnected(conn, 1e4, { ignoreCleanClose: true });
97
120
  conn.reconnect();
98
- await opened;
121
+ await connected;
122
+ this.emitAcceptedOpen();
99
123
  }
100
124
  getCurrentRoom() {
101
125
  return this.targetRoom || this.socket?.conn?.room || "lobby-1";
@@ -126,6 +150,6 @@ function provideMmorpg(options) {
126
150
  ];
127
151
  }
128
152
  //#endregion
129
- export { provideMmorpg };
153
+ export { BridgeWebsocket, provideMmorpg };
130
154
 
131
155
  //# sourceMappingURL=mmorpg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mmorpg.js","names":[],"sources":["../../src/services/mmorpg.ts"],"sourcesContent":["import { Context } from \"@signe/di\";\nimport { connectionRoom } from \"@signe/sync/client\";\nimport { RpgGui } from \"../Gui/Gui\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from \"./AbstractSocket\";\nimport { UpdateMapService, UpdateMapToken } from \"@rpgjs/common\";\nimport { provideKeyboardControls } from \"./keyboardControls\";\nimport { provideSaveClient } from \"./save\";\n\ninterface MmorpgOptions {\n host?: string;\n connectionId?: string;\n connectionIdScope?: \"local\" | \"session\" | \"ephemeral\";\n}\n\nclass BridgeWebsocket extends AbstractWebsocket {\n private socket: any;\n private privateId: string;\n private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];\n private targetRoom = \"lobby-1\";\n\n constructor(protected context: Context, private options: MmorpgOptions = {}) {\n super(context);\n this.privateId = this.resolveConnectionId();\n }\n\n private resolveConnectionId(): string {\n if (this.options.connectionId) {\n return this.options.connectionId;\n }\n\n const scope = this.options.connectionIdScope ?? \"local\";\n const key = \"rpgjs-user-id\";\n\n if (scope === \"ephemeral\") {\n return crypto.randomUUID();\n }\n\n const storage =\n scope === \"session\"\n ? window.sessionStorage\n : window.localStorage;\n\n const existing = storage.getItem(key);\n if (existing) {\n return existing;\n }\n\n const id = crypto.randomUUID();\n storage.setItem(key, id);\n return id;\n }\n\n async connection(listeners?: (data: any) => void) {\n // tmp\n class Room {\n \n }\n const instance = new Room()\n const host = this.options.host || window.location.host;\n this.socket = await connectionRoom({\n host,\n room: this.targetRoom,\n id: this.privateId\n }, instance)\n\n listeners?.(this.socket)\n this.pendingOn.forEach(({ event, callback }) => this.socket.on(event, callback));\n this.pendingOn = [];\n }\n\n on(key: string, callback: (data: any) => void) {\n if (!this.socket) {\n this.pendingOn.push({ event: key, callback });\n return;\n }\n this.socket.on(key, callback);\n }\n\n off(event: string, callback: (data: any) => void) {\n if (!this.socket) return;\n this.socket.off(event, callback);\n }\n\n emit(event: string, data: any) {\n this.socket.emit(event, data);\n }\n\n updateProperties({ room, host, query }: SocketUpdateProperties) {\n if (!this.socket?.conn) return;\n this.targetRoom = room;\n this.socket.conn.updateProperties({\n room,\n id: this.privateId,\n host: host || this.options.host || window.location.host,\n query,\n })\n }\n\n private waitForNextOpen(conn: any, timeoutMs = 10000): Promise<void> {\n return new Promise((resolve, reject) => {\n let timeoutId: number | undefined;\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"WebSocket reconnect failed\"));\n };\n const cleanup = () => {\n conn.removeEventListener(\"open\", onOpen);\n conn.removeEventListener(\"error\", onError);\n if (timeoutId !== undefined) {\n window.clearTimeout(timeoutId);\n }\n };\n\n conn.addEventListener(\"open\", onOpen);\n conn.addEventListener(\"error\", onError);\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"WebSocket reconnect timeout\"));\n }, timeoutMs);\n });\n }\n\n async reconnect(_listeners?: (data: any) => void): Promise<void> {\n if (!this.socket?.conn) return;\n const conn = this.socket.conn;\n const opened = this.waitForNextOpen(conn);\n conn.reconnect();\n await opened;\n }\n\n getCurrentRoom(): string {\n return this.targetRoom || this.socket?.conn?.room || \"lobby-1\";\n }\n}\n\nclass UpdateMapStandaloneService extends UpdateMapService {\n constructor(protected context: Context, private _options: MmorpgOptions) {\n super(context);\n }\n\n async update(_map: any) {\n // In MMORPG mode, clients are untrusted and must not push map definitions.\n // Map bootstrap/update is handled server-side by @rpgjs/vite.\n return;\n }\n}\n\nexport function provideMmorpg(options: MmorpgOptions) {\n return [\n {\n provide: WebSocketToken,\n useFactory: (context: Context) => new BridgeWebsocket(context, options),\n },\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => new UpdateMapStandaloneService(context, options),\n },\n provideKeyboardControls(),\n provideSaveClient(),\n RpgGui,\n RpgClientEngine,\n ];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mmorpg.js","names":[],"sources":["../../src/services/mmorpg.ts"],"sourcesContent":["import { Context } from \"@signe/di\";\nimport { connectionRoom } from \"@signe/sync/client\";\nimport { RpgGui } from \"../Gui/Gui\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { AbstractWebsocket, SocketQuery, SocketUpdateProperties, WebSocketToken } from \"./AbstractSocket\";\nimport { UpdateMapService, UpdateMapToken } from \"@rpgjs/common\";\nimport { provideKeyboardControls } from \"./keyboardControls\";\nimport { provideSaveClient } from \"./save\";\nimport { isNativeSocketEvent, waitForRpgjsConnected } from \"./mmorpg-connection\";\n\nexport interface MmorpgOptions {\n host?: string;\n connectionId?: string;\n connectionIdScope?: \"local\" | \"session\" | \"ephemeral\";\n query?: SocketQuery | (() => SocketQuery | undefined);\n socketOptions?: Record<string, any>;\n}\n\nexport class BridgeWebsocket extends AbstractWebsocket {\n readonly mode = \"mmorpg\" as const;\n\n private socket: any;\n private privateId: string;\n private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];\n private acceptedOpenListeners = new Set<(data: any) => void>();\n private targetRoom = \"lobby-1\";\n\n constructor(protected context: Context, private options: MmorpgOptions = {}) {\n super(context);\n this.privateId = this.resolveConnectionId();\n }\n\n private resolveConnectionId(): string {\n if (this.options.connectionId) {\n return this.options.connectionId;\n }\n\n const scope = this.options.connectionIdScope ?? \"local\";\n const key = \"rpgjs-user-id\";\n\n if (scope === \"ephemeral\") {\n return crypto.randomUUID();\n }\n\n const storage =\n scope === \"session\"\n ? window.sessionStorage\n : window.localStorage;\n\n const existing = storage.getItem(key);\n if (existing) {\n return existing;\n }\n\n const id = crypto.randomUUID();\n storage.setItem(key, id);\n return id;\n }\n\n private resolveQuery(): SocketQuery {\n const query = typeof this.options.query === \"function\"\n ? this.options.query()\n : this.options.query;\n\n return query ?? {};\n }\n\n async connection(listeners?: (data: any) => void) {\n // tmp\n class Room {\n \n }\n const instance = new Room()\n const host = this.options.host || window.location.host;\n this.socket = await connectionRoom({\n maxRetries: 0,\n ...this.options.socketOptions,\n host,\n room: this.targetRoom,\n id: this.privateId,\n query: {\n ...this.resolveQuery(),\n id: this.privateId,\n },\n }, instance)\n\n const pendingOn = this.pendingOn;\n this.pendingOn = [];\n pendingOn\n .filter(({ event }) => !this.isNativeSocketEvent(event))\n .forEach(({ event, callback }) => this.attachEvent(event, callback));\n await waitForRpgjsConnected(this.socket.conn);\n pendingOn\n .filter(({ event }) => this.isNativeSocketEvent(event))\n .forEach(({ event, callback }) => this.attachEvent(event, callback));\n this.emitAcceptedOpen();\n listeners?.(this.socket)\n }\n\n on(key: string, callback: (data: any) => void) {\n if (!this.socket) {\n this.pendingOn.push({ event: key, callback });\n return;\n }\n this.attachEvent(key, callback);\n }\n\n off(event: string, callback: (data: any) => void) {\n if (!this.socket) return;\n if (event === \"open\") {\n this.acceptedOpenListeners.delete(callback);\n return;\n }\n if (this.isNativeSocketEvent(event)) {\n this.socket.conn.removeEventListener(event, callback);\n return;\n }\n this.socket.off(event, callback);\n }\n\n emit(event: string, data: any) {\n this.socket.emit(event, data);\n }\n\n private attachEvent(event: string, callback: (data: any) => void) {\n if (event === \"open\") {\n this.acceptedOpenListeners.add(callback);\n return;\n }\n if (this.isNativeSocketEvent(event)) {\n this.socket.conn.addEventListener(event, callback);\n return;\n }\n this.socket.on(event, callback);\n }\n\n private emitAcceptedOpen() {\n const event = new Event(\"open\");\n this.acceptedOpenListeners.forEach((callback) => callback(event));\n }\n\n updateProperties({ room, host, query }: SocketUpdateProperties) {\n if (!this.socket?.conn) return;\n this.targetRoom = room;\n this.socket.conn.updateProperties({\n room,\n id: this.privateId,\n host: host || this.options.host || window.location.host,\n query: {\n ...this.resolveQuery(),\n ...query,\n id: this.privateId,\n },\n })\n }\n\n private isNativeSocketEvent(event: string) {\n return isNativeSocketEvent(event);\n }\n\n async reconnect(_listeners?: (data: any) => void): Promise<void> {\n if (!this.socket?.conn) return;\n const conn = this.socket.conn;\n const connected = waitForRpgjsConnected(conn, 10000, { ignoreCleanClose: true });\n conn.reconnect();\n await connected;\n this.emitAcceptedOpen();\n }\n\n getCurrentRoom(): string {\n return this.targetRoom || this.socket?.conn?.room || \"lobby-1\";\n }\n}\n\nclass UpdateMapStandaloneService extends UpdateMapService {\n constructor(protected context: Context, private _options: MmorpgOptions) {\n super(context);\n }\n\n async update(_map: any) {\n // In MMORPG mode, clients are untrusted and must not push map definitions.\n // Map bootstrap/update is handled server-side by @rpgjs/vite.\n return;\n }\n}\n\nexport function provideMmorpg(options: MmorpgOptions) {\n return [\n {\n provide: WebSocketToken,\n useFactory: (context: Context) => new BridgeWebsocket(context, options),\n },\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => new UpdateMapStandaloneService(context, options),\n },\n provideKeyboardControls(),\n provideSaveClient(),\n RpgGui,\n RpgClientEngine,\n ];\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface ClientPointerPosition {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface ClientPointerContext {
6
+ screen(): ClientPointerPosition | null;
7
+ world(): ClientPointerPosition | null;
8
+ update(screen: ClientPointerPosition | null, world?: ClientPointerPosition | null): ClientPointerPosition | null;
9
+ updateFromEvent(event: any): ClientPointerPosition | null;
10
+ }
11
+ export declare function createClientPointerContext(): ClientPointerContext;
@@ -0,0 +1,48 @@
1
+ //#region src/services/pointerContext.ts
2
+ function toPosition(point) {
3
+ const x = Number(point?.x);
4
+ const y = Number(point?.y);
5
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
6
+ return {
7
+ x,
8
+ y
9
+ };
10
+ }
11
+ function extractGlobalPoint(event) {
12
+ return toPosition(event?.global) ?? toPosition(event?.data?.global) ?? toPosition(event);
13
+ }
14
+ function extractWorldPoint(event, global) {
15
+ const target = event?.currentTarget ?? event?.target;
16
+ if (target && global && typeof target.toLocal === "function") return toPosition(target.toLocal(global));
17
+ if (typeof event?.getLocalPosition === "function") return toPosition(event.getLocalPosition(target));
18
+ if (typeof event?.data?.getLocalPosition === "function") return toPosition(event.data.getLocalPosition(target));
19
+ return global;
20
+ }
21
+ function createClientPointerContext() {
22
+ let lastScreen = null;
23
+ let lastWorld = null;
24
+ const update = (screen, world) => {
25
+ const nextScreen = toPosition(screen);
26
+ const nextWorld = toPosition(world) ?? nextScreen;
27
+ if (nextScreen) lastScreen = nextScreen;
28
+ if (nextWorld) lastWorld = nextWorld;
29
+ return lastWorld ? { ...lastWorld } : null;
30
+ };
31
+ return {
32
+ screen() {
33
+ return lastScreen ? { ...lastScreen } : null;
34
+ },
35
+ world() {
36
+ return lastWorld ? { ...lastWorld } : null;
37
+ },
38
+ update,
39
+ updateFromEvent(event) {
40
+ const global = extractGlobalPoint(event);
41
+ return update(global, extractWorldPoint(event, global));
42
+ }
43
+ };
44
+ }
45
+ //#endregion
46
+ export { createClientPointerContext };
47
+
48
+ //# sourceMappingURL=pointerContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointerContext.js","names":[],"sources":["../../src/services/pointerContext.ts"],"sourcesContent":["export interface ClientPointerPosition {\n x: number;\n y: number;\n}\n\nexport interface ClientPointerContext {\n screen(): ClientPointerPosition | null;\n world(): ClientPointerPosition | null;\n update(screen: ClientPointerPosition | null, world?: ClientPointerPosition | null): ClientPointerPosition | null;\n updateFromEvent(event: any): ClientPointerPosition | null;\n}\n\nfunction toPosition(point: any): ClientPointerPosition | null {\n const x = Number(point?.x);\n const y = Number(point?.y);\n\n if (!Number.isFinite(x) || !Number.isFinite(y)) {\n return null;\n }\n\n return { x, y };\n}\n\nfunction extractGlobalPoint(event: any): ClientPointerPosition | null {\n return toPosition(event?.global)\n ?? toPosition(event?.data?.global)\n ?? toPosition(event);\n}\n\nfunction extractWorldPoint(event: any, global: ClientPointerPosition | null): ClientPointerPosition | null {\n const target = event?.currentTarget ?? event?.target;\n\n if (target && global && typeof target.toLocal === \"function\") {\n return toPosition(target.toLocal(global));\n }\n\n if (typeof event?.getLocalPosition === \"function\") {\n return toPosition(event.getLocalPosition(target));\n }\n\n if (typeof event?.data?.getLocalPosition === \"function\") {\n return toPosition(event.data.getLocalPosition(target));\n }\n\n return global;\n}\n\nexport function createClientPointerContext(): ClientPointerContext {\n let lastScreen: ClientPointerPosition | null = null;\n let lastWorld: ClientPointerPosition | null = null;\n\n const update = (screen: ClientPointerPosition | null, world?: ClientPointerPosition | null) => {\n const nextScreen = toPosition(screen);\n const nextWorld = toPosition(world) ?? nextScreen;\n\n if (nextScreen) {\n lastScreen = nextScreen;\n }\n\n if (nextWorld) {\n lastWorld = nextWorld;\n }\n\n return lastWorld ? { ...lastWorld } : null;\n };\n\n return {\n screen() {\n return lastScreen ? { ...lastScreen } : null;\n },\n\n world() {\n return lastWorld ? { ...lastWorld } : null;\n },\n\n update,\n\n updateFromEvent(event: any) {\n const global = extractGlobalPoint(event);\n const world = extractWorldPoint(event, global);\n return update(global, world);\n },\n };\n}\n"],"mappings":";AAYA,SAAS,WAAW,OAA0C;CAC5D,MAAM,IAAI,OAAO,OAAO,CAAC;CACzB,MAAM,IAAI,OAAO,OAAO,CAAC;CAEzB,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,GAC3C,OAAO;CAGT,OAAO;EAAE;EAAG;CAAE;AAChB;AAEA,SAAS,mBAAmB,OAA0C;CACpE,OAAO,WAAW,OAAO,MAAM,KAC1B,WAAW,OAAO,MAAM,MAAM,KAC9B,WAAW,KAAK;AACvB;AAEA,SAAS,kBAAkB,OAAY,QAAoE;CACzG,MAAM,SAAS,OAAO,iBAAiB,OAAO;CAE9C,IAAI,UAAU,UAAU,OAAO,OAAO,YAAY,YAChD,OAAO,WAAW,OAAO,QAAQ,MAAM,CAAC;CAG1C,IAAI,OAAO,OAAO,qBAAqB,YACrC,OAAO,WAAW,MAAM,iBAAiB,MAAM,CAAC;CAGlD,IAAI,OAAO,OAAO,MAAM,qBAAqB,YAC3C,OAAO,WAAW,MAAM,KAAK,iBAAiB,MAAM,CAAC;CAGvD,OAAO;AACT;AAEA,SAAgB,6BAAmD;CACjE,IAAI,aAA2C;CAC/C,IAAI,YAA0C;CAE9C,MAAM,UAAU,QAAsC,UAAyC;EAC7F,MAAM,aAAa,WAAW,MAAM;EACpC,MAAM,YAAY,WAAW,KAAK,KAAK;EAEvC,IAAI,YACF,aAAa;EAGf,IAAI,WACF,YAAY;EAGd,OAAO,YAAY,EAAE,GAAG,UAAU,IAAI;CACxC;CAEA,OAAO;EACL,SAAS;GACP,OAAO,aAAa,EAAE,GAAG,WAAW,IAAI;EAC1C;EAEA,QAAQ;GACN,OAAO,YAAY,EAAE,GAAG,UAAU,IAAI;EACxC;EAEA;EAEA,gBAAgB,OAAY;GAC1B,MAAM,SAAS,mBAAmB,KAAK;GAEvC,OAAO,OAAO,QADA,kBAAkB,OAAO,MACjB,CAAK;EAC7B;CACF;AACF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"save.js","names":[],"sources":["../../src/services/save.ts"],"sourcesContent":["import type { SaveSlotList, SaveSlotMeta } from \"@rpgjs/common\";\nimport { inject } from \"../core/inject\";\nimport { AbstractWebsocket, WebSocketToken } from \"./AbstractSocket\";\n\nexport const SaveClientToken = \"SaveClientToken\";\n\ntype SaveListResult = {\n requestId: string;\n slots: SaveSlotList;\n};\n\ntype SaveSaveResult = {\n requestId: string;\n index: number;\n slots: SaveSlotList;\n};\n\ntype SaveLoadResult = {\n requestId: string;\n index: number;\n ok: boolean;\n slot?: SaveSlotMeta;\n};\n\ntype SaveErrorResult = {\n requestId: string;\n message: string;\n};\n\ntype PendingRequest = {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n};\n\nexport class SaveClientService {\n private webSocket: AbstractWebsocket = inject(WebSocketToken);\n private pending: Map<string, PendingRequest> = new Map();\n private requestCounter = 0;\n\n initialize() {\n const saveListResult = (data: SaveListResult) => this.resolveRequest(data.requestId, data);\n const saveSaveResult = (data: SaveSaveResult) => this.resolveRequest(data.requestId, data);\n const saveLoadResult = (data: SaveLoadResult) => this.resolveRequest(data.requestId, data);\n const saveErrorResult = (data: SaveErrorResult) => this.rejectRequest(data.requestId, data.message);\n this.webSocket.off(\"save.list.result\", saveListResult);\n this.webSocket.off(\"save.save.result\", saveSaveResult);\n this.webSocket.off(\"save.load.result\", saveLoadResult);\n this.webSocket.off(\"save.error\", saveErrorResult);\n this.webSocket.on(\"save.list.result\", saveListResult);\n this.webSocket.on(\"save.save.result\", saveSaveResult);\n this.webSocket.on(\"save.load.result\", saveLoadResult);\n this.webSocket.on(\"save.error\", saveErrorResult);\n }\n\n listSlots(): Promise<SaveSlotList> {\n return this.request<SaveListResult>(\"save.list\", {}).then((result) => result.slots);\n }\n\n saveSlot(index: number, meta: SaveSlotMeta = {}): Promise<SaveSlotList> {\n return this.request<SaveSaveResult>(\"save.save\", { index, meta }).then((result) => result.slots);\n }\n\n loadSlot(index: number): Promise<boolean> {\n return this.request<SaveLoadResult>(\"save.load\", { index }).then((result) => result.ok);\n }\n\n private request<T>(event: string, payload: Record<string, any>): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.webSocket) {\n this.initialize();\n }\n const requestId = this.nextRequestId();\n this.pending.set(requestId, { resolve, reject });\n this.webSocket?.emit(event, { requestId, ...payload });\n });\n }\n\n private resolveRequest(requestId: string, result: any) {\n const pending = this.pending.get(requestId);\n if (!pending) return;\n this.pending.delete(requestId);\n pending.resolve(result);\n }\n\n private rejectRequest(requestId: string, message: string) {\n const pending = this.pending.get(requestId);\n if (!pending) return;\n this.pending.delete(requestId);\n pending.reject(new Error(message));\n }\n\n private nextRequestId(): string {\n this.requestCounter += 1;\n return `${Date.now()}-${this.requestCounter}`;\n }\n}\n\nexport function provideSaveClient() {\n return {\n provide: SaveClientService,\n useClass: SaveClientService,\n };\n}\n"],"mappings":";;;AAIA,IAAa,kBAAkB;AA8B/B,IAAa,oBAAb,MAA+B;;mBACU,OAAO,eAAe;iCACd,IAAI,KAAK;wBAC/B;;CAEzB,aAAa;EACX,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,KAAK;EAC1F,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,KAAK;EAC1F,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,KAAK;EAC1F,MAAM,mBAAmB,SAA0B,KAAK,cAAc,KAAK,WAAW,KAAK,QAAQ;AACnG,OAAK,UAAU,IAAI,oBAAoB,eAAe;AACtD,OAAK,UAAU,IAAI,oBAAoB,eAAe;AACtD,OAAK,UAAU,IAAI,oBAAoB,eAAe;AACtD,OAAK,UAAU,IAAI,cAAc,gBAAgB;AACjD,OAAK,UAAU,GAAG,oBAAoB,eAAe;AACrD,OAAK,UAAU,GAAG,oBAAoB,eAAe;AACrD,OAAK,UAAU,GAAG,oBAAoB,eAAe;AACrD,OAAK,UAAU,GAAG,cAAc,gBAAgB;;CAGlD,YAAmC;AACjC,SAAO,KAAK,QAAwB,aAAa,EAAE,CAAC,CAAC,MAAM,WAAW,OAAO,MAAM;;CAGrF,SAAS,OAAe,OAAqB,EAAE,EAAyB;AACtE,SAAO,KAAK,QAAwB,aAAa;GAAE;GAAO;GAAM,CAAC,CAAC,MAAM,WAAW,OAAO,MAAM;;CAGlG,SAAS,OAAiC;AACxC,SAAO,KAAK,QAAwB,aAAa,EAAE,OAAO,CAAC,CAAC,MAAM,WAAW,OAAO,GAAG;;CAGzF,QAAmB,OAAe,SAA0C;AAC1E,SAAO,IAAI,SAAS,SAAS,WAAW;AACtC,OAAI,CAAC,KAAK,UACR,MAAK,YAAY;GAEnB,MAAM,YAAY,KAAK,eAAe;AACtC,QAAK,QAAQ,IAAI,WAAW;IAAE;IAAS;IAAQ,CAAC;AAChD,QAAK,WAAW,KAAK,OAAO;IAAE;IAAW,GAAG;IAAS,CAAC;IACtD;;CAGJ,eAAuB,WAAmB,QAAa;EACrD,MAAM,UAAU,KAAK,QAAQ,IAAI,UAAU;AAC3C,MAAI,CAAC,QAAS;AACd,OAAK,QAAQ,OAAO,UAAU;AAC9B,UAAQ,QAAQ,OAAO;;CAGzB,cAAsB,WAAmB,SAAiB;EACxD,MAAM,UAAU,KAAK,QAAQ,IAAI,UAAU;AAC3C,MAAI,CAAC,QAAS;AACd,OAAK,QAAQ,OAAO,UAAU;AAC9B,UAAQ,OAAO,IAAI,MAAM,QAAQ,CAAC;;CAGpC,gBAAgC;AAC9B,OAAK,kBAAkB;AACvB,SAAO,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK;;;AAIjC,SAAgB,oBAAoB;AAClC,QAAO;EACL,SAAS;EACT,UAAU;EACX"}
1
+ {"version":3,"file":"save.js","names":[],"sources":["../../src/services/save.ts"],"sourcesContent":["import type { SaveSlotList, SaveSlotMeta } from \"@rpgjs/common\";\nimport { inject } from \"../core/inject\";\nimport { AbstractWebsocket, WebSocketToken } from \"./AbstractSocket\";\n\nexport const SaveClientToken = \"SaveClientToken\";\n\ntype SaveListResult = {\n requestId: string;\n slots: SaveSlotList;\n};\n\ntype SaveSaveResult = {\n requestId: string;\n index: number;\n slots: SaveSlotList;\n};\n\ntype SaveLoadResult = {\n requestId: string;\n index: number;\n ok: boolean;\n slot?: SaveSlotMeta;\n};\n\ntype SaveErrorResult = {\n requestId: string;\n message: string;\n};\n\ntype PendingRequest = {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n};\n\nexport class SaveClientService {\n private webSocket: AbstractWebsocket = inject(WebSocketToken);\n private pending: Map<string, PendingRequest> = new Map();\n private requestCounter = 0;\n\n initialize() {\n const saveListResult = (data: SaveListResult) => this.resolveRequest(data.requestId, data);\n const saveSaveResult = (data: SaveSaveResult) => this.resolveRequest(data.requestId, data);\n const saveLoadResult = (data: SaveLoadResult) => this.resolveRequest(data.requestId, data);\n const saveErrorResult = (data: SaveErrorResult) => this.rejectRequest(data.requestId, data.message);\n this.webSocket.off(\"save.list.result\", saveListResult);\n this.webSocket.off(\"save.save.result\", saveSaveResult);\n this.webSocket.off(\"save.load.result\", saveLoadResult);\n this.webSocket.off(\"save.error\", saveErrorResult);\n this.webSocket.on(\"save.list.result\", saveListResult);\n this.webSocket.on(\"save.save.result\", saveSaveResult);\n this.webSocket.on(\"save.load.result\", saveLoadResult);\n this.webSocket.on(\"save.error\", saveErrorResult);\n }\n\n listSlots(): Promise<SaveSlotList> {\n return this.request<SaveListResult>(\"save.list\", {}).then((result) => result.slots);\n }\n\n saveSlot(index: number, meta: SaveSlotMeta = {}): Promise<SaveSlotList> {\n return this.request<SaveSaveResult>(\"save.save\", { index, meta }).then((result) => result.slots);\n }\n\n loadSlot(index: number): Promise<boolean> {\n return this.request<SaveLoadResult>(\"save.load\", { index }).then((result) => result.ok);\n }\n\n private request<T>(event: string, payload: Record<string, any>): Promise<T> {\n return new Promise((resolve, reject) => {\n if (!this.webSocket) {\n this.initialize();\n }\n const requestId = this.nextRequestId();\n this.pending.set(requestId, { resolve, reject });\n this.webSocket?.emit(event, { requestId, ...payload });\n });\n }\n\n private resolveRequest(requestId: string, result: any) {\n const pending = this.pending.get(requestId);\n if (!pending) return;\n this.pending.delete(requestId);\n pending.resolve(result);\n }\n\n private rejectRequest(requestId: string, message: string) {\n const pending = this.pending.get(requestId);\n if (!pending) return;\n this.pending.delete(requestId);\n pending.reject(new Error(message));\n }\n\n private nextRequestId(): string {\n this.requestCounter += 1;\n return `${Date.now()}-${this.requestCounter}`;\n }\n}\n\nexport function provideSaveClient() {\n return {\n provide: SaveClientService,\n useClass: SaveClientService,\n };\n}\n"],"mappings":";;;AAIA,IAAa,kBAAkB;AA8B/B,IAAa,oBAAb,MAA+B;;mBACU,OAAO,cAAc;iCACb,IAAI,IAAI;wBAC9B;;CAEzB,aAAa;EACX,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,IAAI;EACzF,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,IAAI;EACzF,MAAM,kBAAkB,SAAyB,KAAK,eAAe,KAAK,WAAW,IAAI;EACzF,MAAM,mBAAmB,SAA0B,KAAK,cAAc,KAAK,WAAW,KAAK,OAAO;EAClG,KAAK,UAAU,IAAI,oBAAoB,cAAc;EACrD,KAAK,UAAU,IAAI,oBAAoB,cAAc;EACrD,KAAK,UAAU,IAAI,oBAAoB,cAAc;EACrD,KAAK,UAAU,IAAI,cAAc,eAAe;EAChD,KAAK,UAAU,GAAG,oBAAoB,cAAc;EACpD,KAAK,UAAU,GAAG,oBAAoB,cAAc;EACpD,KAAK,UAAU,GAAG,oBAAoB,cAAc;EACpD,KAAK,UAAU,GAAG,cAAc,eAAe;CACjD;CAEA,YAAmC;EACjC,OAAO,KAAK,QAAwB,aAAa,CAAC,CAAC,EAAE,MAAM,WAAW,OAAO,KAAK;CACpF;CAEA,SAAS,OAAe,OAAqB,CAAC,GAA0B;EACtE,OAAO,KAAK,QAAwB,aAAa;GAAE;GAAO;EAAK,CAAC,EAAE,MAAM,WAAW,OAAO,KAAK;CACjG;CAEA,SAAS,OAAiC;EACxC,OAAO,KAAK,QAAwB,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,WAAW,OAAO,EAAE;CACxF;CAEA,QAAmB,OAAe,SAA0C;EAC1E,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,IAAI,CAAC,KAAK,WACR,KAAK,WAAW;GAElB,MAAM,YAAY,KAAK,cAAc;GACrC,KAAK,QAAQ,IAAI,WAAW;IAAE;IAAS;GAAO,CAAC;GAC/C,KAAK,WAAW,KAAK,OAAO;IAAE;IAAW,GAAG;GAAQ,CAAC;EACvD,CAAC;CACH;CAEA,eAAuB,WAAmB,QAAa;EACrD,MAAM,UAAU,KAAK,QAAQ,IAAI,SAAS;EAC1C,IAAI,CAAC,SAAS;EACd,KAAK,QAAQ,OAAO,SAAS;EAC7B,QAAQ,QAAQ,MAAM;CACxB;CAEA,cAAsB,WAAmB,SAAiB;EACxD,MAAM,UAAU,KAAK,QAAQ,IAAI,SAAS;EAC1C,IAAI,CAAC,SAAS;EACd,KAAK,QAAQ,OAAO,SAAS;EAC7B,QAAQ,OAAO,IAAI,MAAM,OAAO,CAAC;CACnC;CAEA,gBAAgC;EAC9B,KAAK,kBAAkB;EACvB,OAAO,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK;CAC/B;AACF;AAEA,SAAgB,oBAAoB;CAClC,OAAO;EACL,SAAS;EACT,UAAU;CACZ;AACF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function normalizeStandaloneMessage(event: unknown): any;
@@ -0,0 +1,9 @@
1
+ //#region src/services/standalone-message.ts
2
+ function normalizeStandaloneMessage(event) {
3
+ const raw = event && typeof event === "object" && "data" in event ? event.data : event;
4
+ return typeof raw === "string" ? JSON.parse(raw) : raw;
5
+ }
6
+ //#endregion
7
+ export { normalizeStandaloneMessage };
8
+
9
+ //# sourceMappingURL=standalone-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone-message.js","names":[],"sources":["../../src/services/standalone-message.ts"],"sourcesContent":["export function normalizeStandaloneMessage(event: unknown): any {\n const raw = event && typeof event === \"object\" && \"data\" in event\n ? (event as MessageEvent).data\n : event;\n\n return typeof raw === \"string\" ? JSON.parse(raw) : raw;\n}\n"],"mappings":";AAAA,SAAgB,2BAA2B,OAAqB;CAC9D,MAAM,MAAM,SAAS,OAAO,UAAU,YAAY,UAAU,QACvD,MAAuB,OACxB;CAEJ,OAAO,OAAO,QAAQ,WAAW,KAAK,MAAM,GAAG,IAAI;AACrD"}