@rpgjs/client 5.0.0-alpha.8 → 5.0.0-beta.1

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 (305) hide show
  1. package/dist/Game/AnimationManager.d.ts +8 -0
  2. package/dist/Game/AnimationManager.js +26 -0
  3. package/dist/Game/AnimationManager.js.map +1 -0
  4. package/dist/Game/Event.d.ts +1 -1
  5. package/dist/Game/Event.js +12 -0
  6. package/dist/Game/Event.js.map +1 -0
  7. package/dist/Game/Map.d.ts +23 -2
  8. package/dist/Game/Map.js +80 -0
  9. package/dist/Game/Map.js.map +1 -0
  10. package/dist/Game/Object.d.ts +157 -0
  11. package/dist/Game/Object.js +211 -0
  12. package/dist/Game/Object.js.map +1 -0
  13. package/dist/Game/Player.d.ts +1 -1
  14. package/dist/Game/Player.js +12 -0
  15. package/dist/Game/Player.js.map +1 -0
  16. package/dist/Gui/Gui.d.ts +177 -5
  17. package/dist/Gui/Gui.js +445 -0
  18. package/dist/Gui/Gui.js.map +1 -0
  19. package/dist/Gui/NotificationManager.d.ts +23 -0
  20. package/dist/Gui/NotificationManager.js +49 -0
  21. package/dist/Gui/NotificationManager.js.map +1 -0
  22. package/dist/Resource.d.ts +97 -0
  23. package/dist/Resource.js +133 -0
  24. package/dist/Resource.js.map +1 -0
  25. package/dist/RpgClient.d.ts +290 -58
  26. package/dist/RpgClientEngine.d.ts +649 -9
  27. package/dist/RpgClientEngine.js +1334 -0
  28. package/dist/RpgClientEngine.js.map +1 -0
  29. package/dist/Sound.d.ts +199 -0
  30. package/dist/Sound.js +167 -0
  31. package/dist/Sound.js.map +1 -0
  32. package/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js +9 -0
  33. package/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js +6 -0
  34. package/dist/components/animations/animation.ce.js +24 -0
  35. package/dist/components/animations/animation.ce.js.map +1 -0
  36. package/dist/components/animations/hit.ce.js +70 -0
  37. package/dist/components/animations/hit.ce.js.map +1 -0
  38. package/dist/components/animations/index.d.ts +4 -0
  39. package/dist/components/animations/index.js +11 -0
  40. package/dist/components/animations/index.js.map +1 -0
  41. package/dist/components/character.ce.js +392 -0
  42. package/dist/components/character.ce.js.map +1 -0
  43. package/dist/components/dynamics/parse-value.d.ts +1 -0
  44. package/dist/components/dynamics/parse-value.js +44 -0
  45. package/dist/components/dynamics/parse-value.js.map +1 -0
  46. package/dist/components/dynamics/text.ce.js +73 -0
  47. package/dist/components/dynamics/text.ce.js.map +1 -0
  48. package/dist/components/gui/box.ce.js +28 -0
  49. package/dist/components/gui/box.ce.js.map +1 -0
  50. package/dist/components/gui/dialogbox/index.ce.js +205 -0
  51. package/dist/components/gui/dialogbox/index.ce.js.map +1 -0
  52. package/dist/components/gui/gameover.ce.js +193 -0
  53. package/dist/components/gui/gameover.ce.js.map +1 -0
  54. package/dist/components/gui/hud/hud.ce.js +92 -0
  55. package/dist/components/gui/hud/hud.ce.js.map +1 -0
  56. package/dist/components/gui/index.d.ts +15 -3
  57. package/dist/components/gui/index.js +14 -0
  58. package/dist/components/gui/menu/equip-menu.ce.js +481 -0
  59. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -0
  60. package/dist/components/gui/menu/exit-menu.ce.js +54 -0
  61. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -0
  62. package/dist/components/gui/menu/items-menu.ce.js +344 -0
  63. package/dist/components/gui/menu/items-menu.ce.js.map +1 -0
  64. package/dist/components/gui/menu/main-menu.ce.js +417 -0
  65. package/dist/components/gui/menu/main-menu.ce.js.map +1 -0
  66. package/dist/components/gui/menu/options-menu.ce.js +48 -0
  67. package/dist/components/gui/menu/options-menu.ce.js.map +1 -0
  68. package/dist/components/gui/menu/skills-menu.ce.js +107 -0
  69. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -0
  70. package/dist/components/gui/mobile/index.d.ts +8 -0
  71. package/dist/components/gui/mobile/index.js +21 -0
  72. package/dist/components/gui/mobile/index.js.map +1 -0
  73. package/dist/components/gui/mobile/mobile.ce.js +78 -0
  74. package/dist/components/gui/mobile/mobile.ce.js.map +1 -0
  75. package/dist/components/gui/notification/notification.ce.js +64 -0
  76. package/dist/components/gui/notification/notification.ce.js.map +1 -0
  77. package/dist/components/gui/save-load.ce.js +389 -0
  78. package/dist/components/gui/save-load.ce.js.map +1 -0
  79. package/dist/components/gui/shop/shop.ce.js +652 -0
  80. package/dist/components/gui/shop/shop.ce.js.map +1 -0
  81. package/dist/components/gui/title-screen.ce.js +190 -0
  82. package/dist/components/gui/title-screen.ce.js.map +1 -0
  83. package/dist/components/index.d.ts +1 -0
  84. package/dist/components/index.js +4 -0
  85. package/dist/components/prebuilt/hp-bar.ce.js +116 -0
  86. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -0
  87. package/dist/components/prebuilt/index.d.ts +19 -0
  88. package/dist/components/prebuilt/index.js +2 -0
  89. package/dist/components/prebuilt/light-halo.ce.js +94 -0
  90. package/dist/components/prebuilt/light-halo.ce.js.map +1 -0
  91. package/dist/components/scenes/canvas.ce.js +60 -0
  92. package/dist/components/scenes/canvas.ce.js.map +1 -0
  93. package/dist/components/scenes/draw-map.ce.js +89 -0
  94. package/dist/components/scenes/draw-map.ce.js.map +1 -0
  95. package/dist/components/scenes/event-layer.ce.js +28 -0
  96. package/dist/components/scenes/event-layer.ce.js.map +1 -0
  97. package/dist/core/inject.js +18 -0
  98. package/dist/core/inject.js.map +1 -0
  99. package/dist/core/setup.js +16 -0
  100. package/dist/core/setup.js.map +1 -0
  101. package/dist/index.d.ts +15 -1
  102. package/dist/index.js +44 -14
  103. package/dist/module.d.ts +43 -4
  104. package/dist/module.js +176 -0
  105. package/dist/module.js.map +1 -0
  106. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js +277 -0
  107. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +1 -0
  108. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +457 -0
  109. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +1 -0
  110. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +463 -0
  111. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +1 -0
  112. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +2191 -0
  113. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +1 -0
  114. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +10 -0
  115. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +1 -0
  116. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +91 -0
  117. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  118. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js +325 -0
  119. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +1 -0
  120. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +14 -0
  121. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +1 -0
  122. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js +115 -0
  123. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -0
  124. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js +401 -0
  125. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -0
  126. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +2 -0
  127. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +3756 -0
  128. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -0
  129. package/dist/presets/animation.d.ts +31 -0
  130. package/dist/presets/animation.js +39 -0
  131. package/dist/presets/animation.js.map +1 -0
  132. package/dist/presets/faceset.d.ts +30 -0
  133. package/dist/presets/faceset.js +51 -0
  134. package/dist/presets/faceset.js.map +1 -0
  135. package/dist/presets/icon.d.ts +20 -0
  136. package/dist/presets/icon.js +15 -0
  137. package/dist/presets/icon.js.map +1 -0
  138. package/dist/presets/index.d.ts +123 -0
  139. package/dist/presets/index.js +17 -0
  140. package/dist/presets/index.js.map +1 -0
  141. package/dist/presets/lpc.d.ts +89 -0
  142. package/dist/presets/lpc.js +98 -0
  143. package/dist/presets/lpc.js.map +1 -0
  144. package/dist/presets/rmspritesheet.js +42 -0
  145. package/dist/presets/rmspritesheet.js.map +1 -0
  146. package/dist/services/AbstractSocket.d.ts +9 -5
  147. package/dist/services/AbstractSocket.js +11 -0
  148. package/dist/services/AbstractSocket.js.map +1 -0
  149. package/dist/services/keyboardControls.d.ts +15 -0
  150. package/dist/services/keyboardControls.js +23 -0
  151. package/dist/services/keyboardControls.js.map +1 -0
  152. package/dist/services/loadMap.js +123 -0
  153. package/dist/services/loadMap.js.map +1 -0
  154. package/dist/services/mmorpg.d.ts +21 -9
  155. package/dist/services/mmorpg.js +131 -0
  156. package/dist/services/mmorpg.js.map +1 -0
  157. package/dist/services/save.d.ts +19 -0
  158. package/dist/services/save.js +77 -0
  159. package/dist/services/save.js.map +1 -0
  160. package/dist/services/standalone.d.ts +67 -7
  161. package/dist/services/standalone.js +168 -0
  162. package/dist/services/standalone.js.map +1 -0
  163. package/dist/utils/getEntityProp.d.ts +39 -0
  164. package/dist/utils/getEntityProp.js +52 -0
  165. package/dist/utils/getEntityProp.js.map +1 -0
  166. package/package.json +13 -9
  167. package/src/Game/{EffectManager.ts → AnimationManager.ts} +3 -2
  168. package/src/Game/Event.ts +1 -1
  169. package/src/Game/Map.ts +95 -3
  170. package/src/Game/Object.ts +330 -14
  171. package/src/Game/Player.ts +1 -1
  172. package/src/Gui/Gui.ts +506 -18
  173. package/src/Gui/NotificationManager.ts +69 -0
  174. package/src/Resource.ts +150 -0
  175. package/src/RpgClient.ts +300 -58
  176. package/src/RpgClientEngine.ts +1707 -48
  177. package/src/Sound.ts +253 -0
  178. package/src/components/{effects → animations}/animation.ce +3 -6
  179. package/src/components/{effects → animations}/index.ts +1 -1
  180. package/src/components/character.ce +406 -40
  181. package/src/components/dynamics/parse-value.ts +80 -0
  182. package/src/components/dynamics/text.ce +183 -0
  183. package/src/components/gui/box.ce +17 -0
  184. package/src/components/gui/dialogbox/index.ce +204 -187
  185. package/src/components/gui/gameover.ce +158 -0
  186. package/src/components/gui/hud/hud.ce +61 -0
  187. package/src/components/gui/index.ts +30 -4
  188. package/src/components/gui/menu/equip-menu.ce +410 -0
  189. package/src/components/gui/menu/exit-menu.ce +41 -0
  190. package/src/components/gui/menu/items-menu.ce +317 -0
  191. package/src/components/gui/menu/main-menu.ce +294 -0
  192. package/src/components/gui/menu/options-menu.ce +35 -0
  193. package/src/components/gui/menu/skills-menu.ce +83 -0
  194. package/src/components/gui/mobile/index.ts +24 -0
  195. package/src/components/gui/mobile/mobile.ce +80 -0
  196. package/src/components/gui/notification/notification.ce +51 -0
  197. package/src/components/gui/save-load.ce +208 -0
  198. package/src/components/gui/shop/shop.ce +493 -0
  199. package/src/components/gui/title-screen.ce +163 -0
  200. package/src/components/index.ts +3 -0
  201. package/src/components/prebuilt/hp-bar.ce +255 -0
  202. package/src/components/prebuilt/index.ts +24 -0
  203. package/src/components/prebuilt/light-halo.ce +148 -0
  204. package/src/components/scenes/canvas.ce +20 -15
  205. package/src/components/scenes/draw-map.ce +60 -13
  206. package/src/components/scenes/event-layer.ce +9 -2
  207. package/src/components/scenes/transition.ce +60 -0
  208. package/src/index.ts +16 -2
  209. package/src/module.ts +145 -9
  210. package/src/presets/animation.ts +46 -0
  211. package/src/presets/faceset.ts +60 -0
  212. package/src/presets/icon.ts +17 -0
  213. package/src/presets/index.ts +9 -1
  214. package/src/presets/lpc.ts +108 -0
  215. package/src/services/AbstractSocket.ts +10 -2
  216. package/src/services/keyboardControls.ts +20 -0
  217. package/src/services/loadMap.ts +1 -1
  218. package/src/services/mmorpg.ts +100 -12
  219. package/src/services/save.ts +103 -0
  220. package/src/services/standalone.ts +110 -18
  221. package/src/utils/getEntityProp.ts +87 -0
  222. package/tsconfig.json +1 -1
  223. package/vite.config.ts +4 -2
  224. package/dist/Game/EffectManager.d.ts +0 -5
  225. package/dist/components/effects/index.d.ts +0 -4
  226. package/dist/index.js.map +0 -1
  227. package/dist/index10.js +0 -8
  228. package/dist/index10.js.map +0 -1
  229. package/dist/index11.js +0 -10
  230. package/dist/index11.js.map +0 -1
  231. package/dist/index12.js +0 -8
  232. package/dist/index12.js.map +0 -1
  233. package/dist/index13.js +0 -17
  234. package/dist/index13.js.map +0 -1
  235. package/dist/index14.js +0 -91
  236. package/dist/index14.js.map +0 -1
  237. package/dist/index15.js +0 -50
  238. package/dist/index15.js.map +0 -1
  239. package/dist/index16.js +0 -191
  240. package/dist/index16.js.map +0 -1
  241. package/dist/index17.js +0 -9
  242. package/dist/index17.js.map +0 -1
  243. package/dist/index18.js +0 -387
  244. package/dist/index18.js.map +0 -1
  245. package/dist/index19.js +0 -31
  246. package/dist/index19.js.map +0 -1
  247. package/dist/index2.js +0 -112
  248. package/dist/index2.js.map +0 -1
  249. package/dist/index20.js +0 -24
  250. package/dist/index20.js.map +0 -1
  251. package/dist/index21.js +0 -2421
  252. package/dist/index21.js.map +0 -1
  253. package/dist/index22.js +0 -114
  254. package/dist/index22.js.map +0 -1
  255. package/dist/index23.js +0 -109
  256. package/dist/index23.js.map +0 -1
  257. package/dist/index24.js +0 -71
  258. package/dist/index24.js.map +0 -1
  259. package/dist/index25.js +0 -21
  260. package/dist/index25.js.map +0 -1
  261. package/dist/index26.js +0 -41
  262. package/dist/index26.js.map +0 -1
  263. package/dist/index27.js +0 -5
  264. package/dist/index27.js.map +0 -1
  265. package/dist/index28.js +0 -322
  266. package/dist/index28.js.map +0 -1
  267. package/dist/index29.js +0 -27
  268. package/dist/index29.js.map +0 -1
  269. package/dist/index3.js +0 -87
  270. package/dist/index3.js.map +0 -1
  271. package/dist/index30.js +0 -11
  272. package/dist/index30.js.map +0 -1
  273. package/dist/index31.js +0 -11
  274. package/dist/index31.js.map +0 -1
  275. package/dist/index32.js +0 -12
  276. package/dist/index32.js.map +0 -1
  277. package/dist/index33.js +0 -4403
  278. package/dist/index33.js.map +0 -1
  279. package/dist/index34.js +0 -316
  280. package/dist/index34.js.map +0 -1
  281. package/dist/index35.js +0 -174
  282. package/dist/index35.js.map +0 -1
  283. package/dist/index36.js +0 -501
  284. package/dist/index36.js.map +0 -1
  285. package/dist/index37.js +0 -61
  286. package/dist/index37.js.map +0 -1
  287. package/dist/index38.js +0 -20
  288. package/dist/index38.js.map +0 -1
  289. package/dist/index39.js +0 -20
  290. package/dist/index39.js.map +0 -1
  291. package/dist/index4.js +0 -67
  292. package/dist/index4.js.map +0 -1
  293. package/dist/index5.js +0 -16
  294. package/dist/index5.js.map +0 -1
  295. package/dist/index6.js +0 -17
  296. package/dist/index6.js.map +0 -1
  297. package/dist/index7.js +0 -39
  298. package/dist/index7.js.map +0 -1
  299. package/dist/index8.js +0 -90
  300. package/dist/index8.js.map +0 -1
  301. package/dist/index9.js +0 -76
  302. package/dist/index9.js.map +0 -1
  303. package/src/components/gui/dialogbox/itemMenu.ce +0 -23
  304. package/src/components/gui/dialogbox/selection.ce +0 -67
  305. /package/src/components/{effects → animations}/hit.ce +0 -0
package/src/module.ts CHANGED
@@ -1,14 +1,69 @@
1
1
  import { findModules, provideModules } from "@rpgjs/common";
2
+ import { FactoryProvider } from "@signe/di";
2
3
  import { RpgClientEngine } from "./RpgClientEngine";
3
4
  import { RpgClient } from "./RpgClient";
4
5
  import { inject } from "@signe/di";
5
6
  import { RpgGui } from "./Gui/Gui";
7
+ import { getSoundMetadata } from "./Sound";
6
8
 
7
- export function provideClientModules(modules: RpgClient[]) {
9
+ /**
10
+ * Type for client modules that can be either:
11
+ * - An object implementing RpgClient interface
12
+ * - A class decorated with @RpgModule decorator
13
+ */
14
+ export type RpgClientModule = RpgClient | (new () => any);
15
+
16
+ /**
17
+ * Provides client modules configuration to Dependency Injection
18
+ *
19
+ * This function accepts an array of client modules that can be either:
20
+ * - Objects implementing the RpgClient interface
21
+ * - Classes decorated with the @RpgModule decorator (which will be instantiated)
22
+ *
23
+ * @param modules - Array of client modules (objects or classes)
24
+ * @returns FactoryProvider configuration for DI
25
+ * @example
26
+ * ```ts
27
+ * // Using an object
28
+ * provideClientModules([
29
+ * {
30
+ * engine: {
31
+ * onConnected(engine) {
32
+ * console.log('Client connected')
33
+ * }
34
+ * }
35
+ * }
36
+ * ])
37
+ *
38
+ * // Using a decorated class
39
+ * @RpgModule<RpgClient>({
40
+ * engine: {
41
+ * onStart(engine) {
42
+ * console.log('Client started')
43
+ * }
44
+ * }
45
+ * })
46
+ * class MyClientModule {}
47
+ *
48
+ * provideClientModules([MyClientModule])
49
+ * ```
50
+ */
51
+ export function provideClientModules(modules: RpgClientModule[]): FactoryProvider {
8
52
  return provideModules(modules, "client", (modules, context) => {
9
53
  const mainModuleClient = findModules(context, 'Client')
10
54
  modules = [...mainModuleClient, ...modules]
11
55
  modules = modules.map((module) => {
56
+ // If module is a class (constructor function), instantiate it
57
+ // The RpgModule decorator adds properties to the prototype, which will be accessible via the instance
58
+ if (typeof module === 'function') {
59
+ const instance = new module() as any;
60
+ // Copy all enumerable properties (including from prototype) to a plain object
61
+ const moduleObj: any = {};
62
+ for (const key in instance) {
63
+ moduleObj[key] = instance[key];
64
+ }
65
+ module = moduleObj;
66
+ }
12
67
  if ('client' in module) {
13
68
  module = module.client as any;
14
69
  }
@@ -22,33 +77,95 @@ export function provideClientModules(modules: RpgClient[]) {
22
77
  },
23
78
  };
24
79
  }
80
+ if (module.spritesheetResolver) {
81
+ const resolver = module.spritesheetResolver;
82
+ module.spritesheetResolver = {
83
+ load: (engine: RpgClientEngine) => {
84
+ engine.setSpritesheetResolver(resolver);
85
+ },
86
+ };
87
+ }
25
88
  if (module.sounds) {
26
89
  const sounds = [...module.sounds];
27
90
  module.sounds = {
28
91
  load: (engine: RpgClientEngine) => {
29
92
  sounds.forEach((sound) => {
30
- engine.addSound(sound);
93
+ // Check if it's a class decorated with @Sound
94
+ if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {
95
+ const metadata = getSoundMetadata(sound);
96
+ if (metadata) {
97
+ // Handle single sound
98
+ if (metadata.id && metadata.sound) {
99
+ engine.addSound({
100
+ id: metadata.id,
101
+ src: metadata.sound,
102
+ loop: metadata.loop,
103
+ volume: metadata.volume,
104
+ });
105
+ }
106
+ // Handle multiple sounds
107
+ if (metadata.sounds) {
108
+ Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {
109
+ engine.addSound({
110
+ id: soundId,
111
+ src: soundSrc,
112
+ loop: metadata.loop,
113
+ volume: metadata.volume,
114
+ });
115
+ });
116
+ }
117
+ } else {
118
+ // Not a decorated class, treat as regular sound object
119
+ engine.addSound(sound);
120
+ }
121
+ } else {
122
+ // Regular sound object
123
+ engine.addSound(sound);
124
+ }
31
125
  });
32
126
  },
33
127
  };
34
128
  }
129
+ if (module.soundResolver) {
130
+ const resolver = module.soundResolver;
131
+ module.soundResolver = {
132
+ load: (engine: RpgClientEngine) => {
133
+ engine.setSoundResolver(resolver);
134
+ },
135
+ };
136
+ }
35
137
  if (module.gui) {
36
138
  const gui = [...module.gui];
37
139
  module.gui = {
38
140
  load: (engine: RpgClientEngine) => {
39
- const guiService = inject(engine.context, RpgGui);
141
+ const guiService = inject(engine.context, RpgGui) as RpgGui;
40
142
  gui.forEach((gui) => {
41
143
  guiService.add(gui);
42
144
  });
43
145
  },
44
146
  };
45
147
  }
46
- if (module.effects) {
47
- const effects = [...module.effects];
48
- module.effects = {
148
+ if (module.componentAnimations) {
149
+ const componentAnimations = [...module.componentAnimations];
150
+ module.componentAnimations = {
49
151
  load: (engine: RpgClientEngine) => {
50
- effects.forEach((effect) => {
51
- engine.addEffect(effect);
152
+ componentAnimations.forEach((componentAnimation) => {
153
+ engine.addComponentAnimation(componentAnimation);
154
+ });
155
+ },
156
+ };
157
+ }
158
+ if (module.transitions) {
159
+ const transitions = [...module.transitions];
160
+ module.transitions = {
161
+ load: (engine: RpgClientEngine) => {
162
+ const guiService = inject(engine.context, RpgGui) as RpgGui;
163
+ transitions.forEach((transition) => {
164
+ guiService.add({
165
+ name: transition.id,
166
+ component: transition.component,
167
+ data: transition.props || {}
168
+ });
52
169
  });
53
170
  },
54
171
  };
@@ -63,6 +180,24 @@ export function provideClientModules(modules: RpgClient[]) {
63
180
  },
64
181
  };
65
182
  }
183
+ if (module.sprite) {
184
+ const sprite = {...module.sprite};
185
+ module.sprite = {
186
+ ...sprite,
187
+ load: (engine: RpgClientEngine) => {
188
+ if (sprite.componentsBehind) {
189
+ sprite.componentsBehind.forEach((component) => {
190
+ engine.addSpriteComponentBehind(component);
191
+ });
192
+ }
193
+ if (sprite.componentsInFront) {
194
+ sprite.componentsInFront.forEach((component) => {
195
+ engine.addSpriteComponentInFront(component);
196
+ });
197
+ }
198
+ },
199
+ };
200
+ }
66
201
  return module;
67
202
  });
68
203
  return modules
@@ -85,7 +220,8 @@ export function provideClientGlobalConfig(config: any = {}) {
85
220
  down: 'down',
86
221
  left: 'left',
87
222
  right: 'right',
88
- action: 'space'
223
+ action: 'space',
224
+ escape: 'escape'
89
225
  }
90
226
  }
91
227
  return provideGlobalConfig(config)
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Creates an animation spritesheet preset with automatic frame generation
3
+ *
4
+ * This function generates animation frames based on the provided width and height dimensions.
5
+ * It creates a sequence of frames that progresses through the spritesheet from left to right,
6
+ * top to bottom, with each frame having a 10ms time increment.
7
+ *
8
+ * @param {number} framesWidth - The number of frames horizontally in the spritesheet
9
+ * @param {number} framesHeight - The number of frames vertically in the spritesheet
10
+ * @returns {Object} Animation preset configuration object
11
+ *
12
+ * @example
13
+ * ```javascript
14
+ * // For a 4x4 spritesheet
15
+ * const preset = AnimationSpritesheetPreset(4, 4);
16
+ * // This will generate 16 frames with coordinates from (0,0) to (3,3)
17
+ * ```
18
+ */
19
+ export const AnimationSpritesheetPreset = (framesWidth: number, framesHeight: number) => {
20
+
21
+ const animations: Array<{ time: number; frameX: number; frameY: number }> = [];
22
+
23
+ for (let y = 0; y < framesHeight; y++) {
24
+ for (let x = 0; x < framesWidth; x++) {
25
+ const frameIndex = y * framesWidth + x;
26
+ animations.push({
27
+ time: frameIndex * 10,
28
+ frameX: x,
29
+ frameY: y
30
+ });
31
+ }
32
+ }
33
+
34
+ return {
35
+ framesWidth,
36
+ framesHeight,
37
+ textures: {
38
+ default: {
39
+ animations: () => [
40
+ animations
41
+ ],
42
+ }
43
+ }
44
+ };
45
+ };
46
+
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Creates a faceset preset for character expressions
3
+ *
4
+ * This preset allows you to define multiple facial expressions for a character,
5
+ * where each expression corresponds to a specific frame position (frameX, frameY)
6
+ * within a single faceset texture. Each expression is defined by its position
7
+ * in the faceset grid.
8
+ *
9
+ * @param options - Object containing the faceset configuration
10
+ * @param framesWidth - Number of frames horizontally in the faceset texture
11
+ * @param framesHeight - Number of frames vertically in the faceset texture
12
+ * @param expressions - Object mapping expression names to their frame positions as tuples [frameX, frameY]
13
+ * @returns Faceset configuration with animations for each expression
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const faceset = FacesetPreset({
18
+ * id: "facesetId",
19
+ * image: "faceset.png",
20
+ * width: 1024,
21
+ * height: 1024,
22
+ * }, 4, 2, {
23
+ * happy: [0, 0],
24
+ * sad: [1, 0],
25
+ * angry: [2, 0],
26
+ * surprised: [3, 0]
27
+ * });
28
+ * ```
29
+ */
30
+ export const FacesetPreset = (
31
+ options: any,
32
+ framesWidth: number,
33
+ framesHeight: number,
34
+ expressions: Record<string, [number, number]>,
35
+ ) => {
36
+
37
+ const textures: Record<string, any> = {};
38
+
39
+ // Create texture configuration for each expression
40
+ Object.keys(expressions).forEach((expressionName) => {
41
+ const [frameX, frameY] = expressions[expressionName];
42
+ textures[expressionName] = {
43
+ animations: () => [
44
+ [{
45
+ time: 0,
46
+ frameX: frameX,
47
+ frameY: frameY
48
+ }]
49
+ ],
50
+ };
51
+ });
52
+
53
+ return {
54
+ ...options,
55
+ framesWidth,
56
+ framesHeight,
57
+ textures
58
+ };
59
+ };
60
+
@@ -0,0 +1,17 @@
1
+ export const IconPreset = (options: {
2
+ image: string;
3
+ framesWidth: number;
4
+ framesHeight: number;
5
+ id: string;
6
+ }) => {
7
+ return {
8
+ textures: {
9
+ default: {
10
+ animations: () => [
11
+ [{ time: 0, frameX: 0, frameY: 0 }]
12
+ ]
13
+ }
14
+ },
15
+ ...options
16
+ }
17
+ }
@@ -1,5 +1,13 @@
1
+ import { AnimationSpritesheetPreset } from "./animation";
2
+ import { LPCSpritesheetPreset } from "./lpc";
1
3
  import { RMSpritesheet } from "./rmspritesheet";
4
+ import { FacesetPreset } from "./faceset";
5
+ import { IconPreset } from "./icon";
2
6
 
3
7
  export const Presets = {
4
- RMSpritesheet
8
+ RMSpritesheet,
9
+ LPCSpritesheetPreset,
10
+ AnimationSpritesheetPreset,
11
+ FacesetPreset,
12
+ IconPreset
5
13
  }
@@ -0,0 +1,108 @@
1
+ import { Animation, Direction } from "@rpgjs/common";
2
+
3
+ export const LPCSpritesheetPreset = (options: {
4
+ id: string;
5
+ imageSource: string;
6
+ width: number;
7
+ height: number;
8
+ ratio?: number;
9
+ }) => {
10
+ const ratio = options.ratio ?? 1;
11
+
12
+ const frameY = (direction: Direction) => {
13
+ return {
14
+ [Direction.Down]: 2,
15
+ [Direction.Left]: 1,
16
+ [Direction.Right]: 3,
17
+ [Direction.Up]: 0,
18
+ }[direction];
19
+ };
20
+
21
+ const stand = (direction: Direction) => [
22
+ { time: 0, frameX: 0, frameY: frameY(direction) },
23
+ ];
24
+ const anim = (
25
+ direction: Direction,
26
+ framesWidth: number,
27
+ speed: number = 5
28
+ ) => {
29
+ const array: any = [];
30
+ for (let i = 0; i < framesWidth; i++) {
31
+ array.push({ time: i * speed, frameX: i, frameY: frameY(direction) });
32
+ }
33
+ return array;
34
+ };
35
+
36
+ return {
37
+ id: options.id,
38
+ image: options.imageSource,
39
+ width: options.width,
40
+ height: options.height,
41
+ opacity: 1,
42
+ rectWidth: 64 * ratio,
43
+ rectHeight: 64 * ratio,
44
+ framesWidth: 6,
45
+ framesHeight: 4,
46
+ spriteRealSize: {
47
+ width: 48 * ratio,
48
+ height: 52 * ratio,
49
+ },
50
+ textures: {
51
+ [Animation.Stand]: {
52
+ offset: {
53
+ x: 0,
54
+ y: 512 * ratio,
55
+ },
56
+ animations: ({ direction }) => [stand(direction)],
57
+ },
58
+ [Animation.Walk]: {
59
+ offset: {
60
+ x: 0,
61
+ y: 512 * ratio,
62
+ },
63
+ framesWidth: 9,
64
+ framesHeight: 4,
65
+ animations: ({ direction }) => [anim(direction, 9)],
66
+ },
67
+ [Animation.Attack]: {
68
+ offset: {
69
+ x: 0,
70
+ y: 768 * ratio,
71
+ },
72
+ framesWidth: 6,
73
+ framesHeight: 4,
74
+ animations: ({ direction }) => [anim(direction, 6, 3)],
75
+ },
76
+ [Animation.Skill]: {
77
+ framesWidth: 7,
78
+ framesHeight: 4,
79
+ animations: ({ direction }) => [anim(direction, 7, 3)],
80
+ },
81
+ attack2: {
82
+ offset: {
83
+ x: 0,
84
+ y: 256 * ratio,
85
+ },
86
+ framesWidth: 7,
87
+ framesHeight: 8,
88
+ animations: ({ direction }) => [anim(direction, 7, 3)],
89
+ },
90
+ ...(options.height > 3000
91
+ ? {
92
+ attack3: {
93
+ offset: {
94
+ x: 0,
95
+ y: 5568 - 288 * 4,
96
+ },
97
+ rectWidth: 288,
98
+ rectHeight: 288,
99
+ framesWidth: 6,
100
+ framesHeight: 4,
101
+ animations: ({ direction }) => [anim(direction, 6, 3)],
102
+ },
103
+ }
104
+ : {}),
105
+ },
106
+ };
107
+ };
108
+
@@ -2,6 +2,14 @@ import { Context } from "@signe/di";
2
2
 
3
3
  export const WebSocketToken = "websocket";
4
4
 
5
+ export type SocketQueryValue = string | null | undefined;
6
+ export type SocketQuery = Record<string, SocketQueryValue>;
7
+ export type SocketUpdateProperties = {
8
+ room: string;
9
+ host?: string;
10
+ query?: SocketQuery;
11
+ };
12
+
5
13
  export abstract class AbstractWebsocket {
6
14
  constructor(protected context: Context) {}
7
15
 
@@ -9,6 +17,6 @@ export abstract class AbstractWebsocket {
9
17
  abstract emit(event: string, data: any): void;
10
18
  abstract on(event: string, callback: (data: any) => void): void;
11
19
  abstract off(event: string, callback: (data: any) => void): void;
12
- abstract updateProperties(params: { room: string, host?: string }): void;
13
- abstract reconnect(listeners?: (data: any) => void): void;
20
+ abstract updateProperties(params: SocketUpdateProperties): void;
21
+ abstract reconnect(listeners?: (data: any) => void): Promise<void>;
14
22
  }
@@ -0,0 +1,20 @@
1
+ import { KeyboardControls } from "canvasengine";
2
+
3
+ export enum Control {
4
+ Action = 'action',
5
+ Attack = 'attack',
6
+ Defense = 'defense',
7
+ Skill = 'skill',
8
+ Back = 'back',
9
+ Up = 1,
10
+ Down = 3,
11
+ Right = 2,
12
+ Left = 4
13
+ }
14
+
15
+ export function provideKeyboardControls() {
16
+ return {
17
+ provide: 'KeyboardControls',
18
+ useValue: null,
19
+ };
20
+ }
@@ -47,7 +47,7 @@ export class LoadMapService {
47
47
 
48
48
  async load(mapId: string) {
49
49
  const map = await this.options(mapId.replace('map-', ''))
50
- await this.updateMapService.update(mapId, map);
50
+ await this.updateMapService.update(map);
51
51
  return map;
52
52
  }
53
53
  }
@@ -2,18 +2,53 @@ import { Context } from "@signe/di";
2
2
  import { connectionRoom } from "@signe/sync/client";
3
3
  import { RpgGui } from "../Gui/Gui";
4
4
  import { RpgClientEngine } from "../RpgClientEngine";
5
- import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
5
+ import { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from "./AbstractSocket";
6
6
  import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
7
+ import { provideKeyboardControls } from "./keyboardControls";
8
+ import { provideSaveClient } from "./save";
7
9
 
8
10
  interface MmorpgOptions {
9
11
  host?: string;
12
+ connectionId?: string;
13
+ connectionIdScope?: "local" | "session" | "ephemeral";
10
14
  }
11
15
 
12
16
  class BridgeWebsocket extends AbstractWebsocket {
13
- private socket: any;
17
+ private socket: any;
18
+ private privateId: string;
19
+ private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];
20
+ private targetRoom = "lobby-1";
14
21
 
15
22
  constructor(protected context: Context, private options: MmorpgOptions = {}) {
16
23
  super(context);
24
+ this.privateId = this.resolveConnectionId();
25
+ }
26
+
27
+ private resolveConnectionId(): string {
28
+ if (this.options.connectionId) {
29
+ return this.options.connectionId;
30
+ }
31
+
32
+ const scope = this.options.connectionIdScope ?? "local";
33
+ const key = "rpgjs-user-id";
34
+
35
+ if (scope === "ephemeral") {
36
+ return crypto.randomUUID();
37
+ }
38
+
39
+ const storage =
40
+ scope === "session"
41
+ ? window.sessionStorage
42
+ : window.localStorage;
43
+
44
+ const existing = storage.getItem(key);
45
+ if (existing) {
46
+ return existing;
47
+ }
48
+
49
+ const id = crypto.randomUUID();
50
+ storage.setItem(key, id);
51
+ return id;
17
52
  }
18
53
 
19
54
  async connection(listeners?: (data: any) => void) {
@@ -22,19 +57,28 @@ class BridgeWebsocket extends AbstractWebsocket {
22
57
 
23
58
  }
24
59
  const instance = new Room()
60
+ const host = this.options.host || window.location.host;
25
61
  this.socket = await connectionRoom({
26
- host: this.options.host || window.location.host,
27
- room: "lobby-1",
62
+ host,
63
+ room: this.targetRoom,
64
+ id: this.privateId
28
65
  }, instance)
29
66
 
30
67
  listeners?.(this.socket)
68
+ this.pendingOn.forEach(({ event, callback }) => this.socket.on(event, callback));
69
+ this.pendingOn = [];
31
70
  }
32
71
 
33
72
  on(key: string, callback: (data: any) => void) {
73
+ if (!this.socket) {
74
+ this.pendingOn.push({ event: key, callback });
75
+ return;
76
+ }
34
77
  this.socket.on(key, callback);
35
78
  }
36
79
 
37
80
  off(event: string, callback: (data: any) => void) {
81
+ if (!this.socket) return;
38
82
  this.socket.off(event, callback);
39
83
  }
40
84
 
@@ -42,25 +86,67 @@ class BridgeWebsocket extends AbstractWebsocket {
42
86
  this.socket.emit(event, data);
43
87
  }
44
88
 
45
- updateProperties({ room }: { room: any }) {
89
+ updateProperties({ room, host, query }: SocketUpdateProperties) {
90
+ if (!this.socket?.conn) return;
91
+ this.targetRoom = room;
46
92
  this.socket.conn.updateProperties({
47
- room: room,
48
- host: this.options.host
93
+ room,
94
+ id: this.privateId,
95
+ host: host || this.options.host || window.location.host,
96
+ query,
49
97
  })
50
98
  }
51
99
 
52
- async reconnect(listeners?: (data: any) => void) {
53
- this.socket.conn.reconnect()
100
+ private waitForNextOpen(conn: any, timeoutMs = 10000): Promise<void> {
101
+ return new Promise((resolve, reject) => {
102
+ let timeoutId: number | undefined;
103
+ const onOpen = () => {
104
+ cleanup();
105
+ resolve();
106
+ };
107
+ const onError = () => {
108
+ cleanup();
109
+ reject(new Error("WebSocket reconnect failed"));
110
+ };
111
+ const cleanup = () => {
112
+ conn.removeEventListener("open", onOpen);
113
+ conn.removeEventListener("error", onError);
114
+ if (timeoutId !== undefined) {
115
+ window.clearTimeout(timeoutId);
116
+ }
117
+ };
118
+
119
+ conn.addEventListener("open", onOpen);
120
+ conn.addEventListener("error", onError);
121
+ timeoutId = window.setTimeout(() => {
122
+ cleanup();
123
+ reject(new Error("WebSocket reconnect timeout"));
124
+ }, timeoutMs);
125
+ });
126
+ }
127
+
128
+ async reconnect(_listeners?: (data: any) => void): Promise<void> {
129
+ if (!this.socket?.conn) return;
130
+ const conn = this.socket.conn;
131
+ const opened = this.waitForNextOpen(conn);
132
+ conn.reconnect();
133
+ await opened;
134
+ }
135
+
136
+ getCurrentRoom(): string {
137
+ return this.targetRoom || this.socket?.conn?.room || "lobby-1";
54
138
  }
55
139
  }
56
140
 
57
141
  class UpdateMapStandaloneService extends UpdateMapService {
58
- constructor(protected context: Context, private options: MmorpgOptions) {
142
+ constructor(protected context: Context, private _options: MmorpgOptions) {
59
143
  super(context);
60
144
  }
61
145
 
62
- async update(map: any) {
63
- // nothing
146
+ async update(_map: any) {
147
+ // In MMORPG mode, clients are untrusted and must not push map definitions.
148
+ // Map bootstrap/update is handled server-side by @rpgjs/vite.
149
+ return;
64
150
  }
65
151
  }
66
152
 
@@ -74,6 +160,8 @@ export function provideMmorpg(options: MmorpgOptions) {
74
160
  provide: UpdateMapToken,
75
161
  useFactory: (context: Context) => new UpdateMapStandaloneService(context, options),
76
162
  },
163
+ provideKeyboardControls(),
164
+ provideSaveClient(),
77
165
  RpgGui,
78
166
  RpgClientEngine,
79
167
  ];