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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +19 -0
  3. package/dist/Game/AnimationManager.d.ts +8 -0
  4. package/dist/Game/AnimationManager.js +35 -0
  5. package/dist/Game/AnimationManager.js.map +1 -0
  6. package/dist/Game/AnimationManager.spec.d.ts +1 -0
  7. package/dist/Game/Event.d.ts +1 -1
  8. package/dist/Game/Event.js +12 -0
  9. package/dist/Game/Event.js.map +1 -0
  10. package/dist/Game/Map.d.ts +31 -2
  11. package/dist/Game/Map.js +138 -0
  12. package/dist/Game/Map.js.map +1 -0
  13. package/dist/Game/Object.d.ts +189 -0
  14. package/dist/Game/Object.js +255 -0
  15. package/dist/Game/Object.js.map +1 -0
  16. package/dist/Game/Player.d.ts +1 -1
  17. package/dist/Game/Player.js +12 -0
  18. package/dist/Game/Player.js.map +1 -0
  19. package/dist/Gui/Gui.d.ts +192 -7
  20. package/dist/Gui/Gui.js +475 -0
  21. package/dist/Gui/Gui.js.map +1 -0
  22. package/dist/Gui/Gui.spec.d.ts +1 -0
  23. package/dist/Gui/NotificationManager.d.ts +23 -0
  24. package/dist/Gui/NotificationManager.js +49 -0
  25. package/dist/Gui/NotificationManager.js.map +1 -0
  26. package/dist/Resource.d.ts +97 -0
  27. package/dist/Resource.js +133 -0
  28. package/dist/Resource.js.map +1 -0
  29. package/dist/RpgClient.d.ts +295 -13
  30. package/dist/RpgClientEngine.d.ts +671 -15
  31. package/dist/RpgClientEngine.js +1442 -0
  32. package/dist/RpgClientEngine.js.map +1 -0
  33. package/dist/Sound.d.ts +199 -0
  34. package/dist/Sound.js +167 -0
  35. package/dist/Sound.js.map +1 -0
  36. package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js +9 -0
  37. package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js +6 -0
  38. package/dist/components/animations/animation.ce.js +23 -0
  39. package/dist/components/animations/animation.ce.js.map +1 -0
  40. package/dist/components/animations/hit.ce.js +64 -0
  41. package/dist/components/animations/hit.ce.js.map +1 -0
  42. package/dist/components/animations/index.d.ts +4 -0
  43. package/dist/components/animations/index.js +11 -0
  44. package/dist/components/animations/index.js.map +1 -0
  45. package/dist/components/character.ce.js +572 -0
  46. package/dist/components/character.ce.js.map +1 -0
  47. package/dist/components/dynamics/bar.ce.js +96 -0
  48. package/dist/components/dynamics/bar.ce.js.map +1 -0
  49. package/dist/components/dynamics/image.ce.js +23 -0
  50. package/dist/components/dynamics/image.ce.js.map +1 -0
  51. package/dist/components/dynamics/parse-value.d.ts +4 -0
  52. package/dist/components/dynamics/parse-value.js +63 -0
  53. package/dist/components/dynamics/parse-value.js.map +1 -0
  54. package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
  55. package/dist/components/dynamics/shape-utils.d.ts +16 -0
  56. package/dist/components/dynamics/shape-utils.js +73 -0
  57. package/dist/components/dynamics/shape-utils.js.map +1 -0
  58. package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
  59. package/dist/components/dynamics/shape.ce.js +83 -0
  60. package/dist/components/dynamics/shape.ce.js.map +1 -0
  61. package/dist/components/dynamics/text.ce.js +50 -0
  62. package/dist/components/dynamics/text.ce.js.map +1 -0
  63. package/dist/components/gui/box.ce.js +26 -0
  64. package/dist/components/gui/box.ce.js.map +1 -0
  65. package/dist/components/gui/dialogbox/index.ce.js +198 -0
  66. package/dist/components/gui/dialogbox/index.ce.js.map +1 -0
  67. package/dist/components/gui/gameover.ce.js +169 -0
  68. package/dist/components/gui/gameover.ce.js.map +1 -0
  69. package/dist/components/gui/hud/hud.ce.js +83 -0
  70. package/dist/components/gui/hud/hud.ce.js.map +1 -0
  71. package/dist/components/gui/index.d.ts +15 -3
  72. package/dist/components/gui/index.js +14 -0
  73. package/dist/components/gui/menu/equip-menu.ce.js +427 -0
  74. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -0
  75. package/dist/components/gui/menu/exit-menu.ce.js +55 -0
  76. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -0
  77. package/dist/components/gui/menu/items-menu.ce.js +326 -0
  78. package/dist/components/gui/menu/items-menu.ce.js.map +1 -0
  79. package/dist/components/gui/menu/main-menu.ce.js +399 -0
  80. package/dist/components/gui/menu/main-menu.ce.js.map +1 -0
  81. package/dist/components/gui/menu/options-menu.ce.js +49 -0
  82. package/dist/components/gui/menu/options-menu.ce.js.map +1 -0
  83. package/dist/components/gui/menu/skills-menu.ce.js +102 -0
  84. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -0
  85. package/dist/components/gui/mobile/index.d.ts +8 -0
  86. package/dist/components/gui/mobile/index.js +21 -0
  87. package/dist/components/gui/mobile/index.js.map +1 -0
  88. package/dist/components/gui/mobile/mobile.ce.js +79 -0
  89. package/dist/components/gui/mobile/mobile.ce.js.map +1 -0
  90. package/dist/components/gui/notification/notification.ce.js +62 -0
  91. package/dist/components/gui/notification/notification.ce.js.map +1 -0
  92. package/dist/components/gui/save-load.ce.js +211 -0
  93. package/dist/components/gui/save-load.ce.js.map +1 -0
  94. package/dist/components/gui/shop/shop.ce.js +614 -0
  95. package/dist/components/gui/shop/shop.ce.js.map +1 -0
  96. package/dist/components/gui/title-screen.ce.js +164 -0
  97. package/dist/components/gui/title-screen.ce.js.map +1 -0
  98. package/dist/components/index.d.ts +1 -0
  99. package/dist/components/index.js +4 -0
  100. package/dist/components/player-components-utils.d.ts +67 -0
  101. package/dist/components/player-components-utils.js +162 -0
  102. package/dist/components/player-components-utils.js.map +1 -0
  103. package/dist/components/player-components-utils.spec.d.ts +1 -0
  104. package/dist/components/player-components.ce.js +188 -0
  105. package/dist/components/player-components.ce.js.map +1 -0
  106. package/dist/components/prebuilt/hp-bar.ce.js +113 -0
  107. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -0
  108. package/dist/components/prebuilt/index.d.ts +19 -0
  109. package/dist/components/prebuilt/index.js +2 -0
  110. package/dist/components/prebuilt/light-halo.ce.js +70 -0
  111. package/dist/components/prebuilt/light-halo.ce.js.map +1 -0
  112. package/dist/components/scenes/canvas.ce.js +196 -0
  113. package/dist/components/scenes/canvas.ce.js.map +1 -0
  114. package/dist/components/scenes/draw-map.ce.js +79 -0
  115. package/dist/components/scenes/draw-map.ce.js.map +1 -0
  116. package/dist/components/scenes/event-layer.ce.js +29 -0
  117. package/dist/components/scenes/event-layer.ce.js.map +1 -0
  118. package/dist/core/inject.js +18 -0
  119. package/dist/core/inject.js.map +1 -0
  120. package/dist/core/setup.js +16 -0
  121. package/dist/core/setup.js.map +1 -0
  122. package/dist/decorators/spritesheet.d.ts +1 -0
  123. package/dist/decorators/spritesheet.js +11 -0
  124. package/dist/decorators/spritesheet.js.map +1 -0
  125. package/dist/index.d.ts +16 -1
  126. package/dist/index.js +45 -14
  127. package/dist/module.d.ts +43 -4
  128. package/dist/module.js +179 -0
  129. package/dist/module.js.map +1 -0
  130. package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js +167 -0
  131. package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
  132. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
  133. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
  134. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
  135. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
  136. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
  137. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
  138. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
  139. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  140. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js +241 -0
  141. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
  142. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js +115 -0
  143. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -0
  144. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js +401 -0
  145. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -0
  146. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +2 -0
  147. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +3756 -0
  148. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -0
  149. package/dist/presets/animation.d.ts +31 -0
  150. package/dist/presets/animation.js +39 -0
  151. package/dist/presets/animation.js.map +1 -0
  152. package/dist/presets/faceset.d.ts +30 -0
  153. package/dist/presets/faceset.js +51 -0
  154. package/dist/presets/faceset.js.map +1 -0
  155. package/dist/presets/icon.d.ts +20 -0
  156. package/dist/presets/icon.js +15 -0
  157. package/dist/presets/icon.js.map +1 -0
  158. package/dist/presets/index.d.ts +123 -0
  159. package/dist/presets/index.js +17 -0
  160. package/dist/presets/index.js.map +1 -0
  161. package/dist/presets/lpc.d.ts +89 -0
  162. package/dist/presets/lpc.js +98 -0
  163. package/dist/presets/lpc.js.map +1 -0
  164. package/dist/presets/rmspritesheet.js +42 -0
  165. package/dist/presets/rmspritesheet.js.map +1 -0
  166. package/dist/services/AbstractSocket.d.ts +9 -5
  167. package/dist/services/AbstractSocket.js +11 -0
  168. package/dist/services/AbstractSocket.js.map +1 -0
  169. package/dist/services/keyboardControls.d.ts +15 -0
  170. package/dist/services/keyboardControls.js +23 -0
  171. package/dist/services/keyboardControls.js.map +1 -0
  172. package/dist/services/loadMap.d.ts +6 -0
  173. package/dist/services/loadMap.js +123 -0
  174. package/dist/services/loadMap.js.map +1 -0
  175. package/dist/services/mmorpg.d.ts +21 -9
  176. package/dist/services/mmorpg.js +136 -0
  177. package/dist/services/mmorpg.js.map +1 -0
  178. package/dist/services/save.d.ts +19 -0
  179. package/dist/services/save.js +77 -0
  180. package/dist/services/save.js.map +1 -0
  181. package/dist/services/save.spec.d.ts +1 -0
  182. package/dist/services/standalone.d.ts +67 -7
  183. package/dist/services/standalone.js +168 -0
  184. package/dist/services/standalone.js.map +1 -0
  185. package/dist/utils/getEntityProp.d.ts +39 -0
  186. package/dist/utils/getEntityProp.js +53 -0
  187. package/dist/utils/getEntityProp.js.map +1 -0
  188. package/dist/utils/getEntityProp.spec.d.ts +1 -0
  189. package/dist/utils/readPropValue.d.ts +2 -0
  190. package/dist/utils/readPropValue.js +13 -0
  191. package/dist/utils/readPropValue.js.map +1 -0
  192. package/package.json +14 -11
  193. package/src/Game/AnimationManager.spec.ts +30 -0
  194. package/src/Game/AnimationManager.ts +33 -0
  195. package/src/Game/Event.ts +1 -1
  196. package/src/Game/Map.ts +184 -3
  197. package/src/Game/Object.ts +409 -14
  198. package/src/Game/Player.ts +1 -1
  199. package/src/Gui/Gui.spec.ts +273 -0
  200. package/src/Gui/Gui.ts +566 -23
  201. package/src/Gui/NotificationManager.ts +69 -0
  202. package/src/Resource.ts +149 -0
  203. package/src/RpgClient.ts +309 -14
  204. package/src/RpgClientEngine.ts +1790 -63
  205. package/src/Sound.ts +253 -0
  206. package/src/components/{effects → animations}/animation.ce +3 -6
  207. package/src/components/{effects → animations}/index.ts +1 -1
  208. package/src/components/character.ce +801 -59
  209. package/src/components/dynamics/bar.ce +87 -0
  210. package/src/components/dynamics/image.ce +20 -0
  211. package/src/components/dynamics/parse-value.spec.ts +83 -0
  212. package/src/components/dynamics/parse-value.ts +154 -0
  213. package/src/components/dynamics/shape-utils.spec.ts +46 -0
  214. package/src/components/dynamics/shape-utils.ts +61 -0
  215. package/src/components/dynamics/shape.ce +89 -0
  216. package/src/components/dynamics/text.ce +68 -0
  217. package/src/components/gui/box.ce +17 -0
  218. package/src/components/gui/dialogbox/index.ce +213 -187
  219. package/src/components/gui/gameover.ce +158 -0
  220. package/src/components/gui/hud/hud.ce +61 -0
  221. package/src/components/gui/index.ts +30 -4
  222. package/src/components/gui/menu/equip-menu.ce +410 -0
  223. package/src/components/gui/menu/exit-menu.ce +41 -0
  224. package/src/components/gui/menu/items-menu.ce +317 -0
  225. package/src/components/gui/menu/main-menu.ce +294 -0
  226. package/src/components/gui/menu/options-menu.ce +35 -0
  227. package/src/components/gui/menu/skills-menu.ce +83 -0
  228. package/src/components/gui/mobile/index.ts +24 -0
  229. package/src/components/gui/mobile/mobile.ce +80 -0
  230. package/src/components/gui/notification/notification.ce +51 -0
  231. package/src/components/gui/save-load.ce +208 -0
  232. package/src/components/gui/shop/shop.ce +493 -0
  233. package/src/components/gui/title-screen.ce +163 -0
  234. package/src/components/index.ts +3 -0
  235. package/src/components/player-components-utils.spec.ts +109 -0
  236. package/src/components/player-components-utils.ts +205 -0
  237. package/src/components/player-components.ce +221 -0
  238. package/src/components/prebuilt/hp-bar.ce +255 -0
  239. package/src/components/prebuilt/index.ts +24 -0
  240. package/src/components/prebuilt/light-halo.ce +148 -0
  241. package/src/components/scenes/canvas.ce +185 -21
  242. package/src/components/scenes/draw-map.ce +55 -21
  243. package/src/components/scenes/event-layer.ce +8 -2
  244. package/src/components/scenes/transition.ce +60 -0
  245. package/src/core/setup.ts +2 -2
  246. package/src/decorators/spritesheet.ts +8 -0
  247. package/src/index.ts +17 -2
  248. package/src/module.ts +132 -10
  249. package/src/presets/animation.ts +46 -0
  250. package/src/presets/faceset.ts +60 -0
  251. package/src/presets/icon.ts +17 -0
  252. package/src/presets/index.ts +9 -1
  253. package/src/presets/lpc.ts +108 -0
  254. package/src/services/AbstractSocket.ts +10 -2
  255. package/src/services/keyboardControls.ts +20 -0
  256. package/src/services/loadMap.ts +3 -1
  257. package/src/services/mmorpg.ts +106 -12
  258. package/src/services/save.spec.ts +127 -0
  259. package/src/services/save.ts +103 -0
  260. package/src/services/standalone.ts +110 -18
  261. package/src/utils/getEntityProp.spec.ts +96 -0
  262. package/src/utils/getEntityProp.ts +88 -0
  263. package/src/utils/readPropValue.ts +16 -0
  264. package/vite.config.ts +4 -2
  265. package/dist/Game/EffectManager.d.ts +0 -5
  266. package/dist/components/effects/index.d.ts +0 -4
  267. package/dist/index.js.map +0 -1
  268. package/dist/index10.js +0 -8
  269. package/dist/index10.js.map +0 -1
  270. package/dist/index11.js +0 -10
  271. package/dist/index11.js.map +0 -1
  272. package/dist/index12.js +0 -8
  273. package/dist/index12.js.map +0 -1
  274. package/dist/index13.js +0 -17
  275. package/dist/index13.js.map +0 -1
  276. package/dist/index14.js +0 -107
  277. package/dist/index14.js.map +0 -1
  278. package/dist/index15.js +0 -50
  279. package/dist/index15.js.map +0 -1
  280. package/dist/index16.js +0 -191
  281. package/dist/index16.js.map +0 -1
  282. package/dist/index17.js +0 -9
  283. package/dist/index17.js.map +0 -1
  284. package/dist/index18.js +0 -387
  285. package/dist/index18.js.map +0 -1
  286. package/dist/index19.js +0 -31
  287. package/dist/index19.js.map +0 -1
  288. package/dist/index2.js +0 -181
  289. package/dist/index2.js.map +0 -1
  290. package/dist/index20.js +0 -24
  291. package/dist/index20.js.map +0 -1
  292. package/dist/index21.js +0 -2421
  293. package/dist/index21.js.map +0 -1
  294. package/dist/index22.js +0 -114
  295. package/dist/index22.js.map +0 -1
  296. package/dist/index23.js +0 -109
  297. package/dist/index23.js.map +0 -1
  298. package/dist/index24.js +0 -71
  299. package/dist/index24.js.map +0 -1
  300. package/dist/index25.js +0 -21
  301. package/dist/index25.js.map +0 -1
  302. package/dist/index26.js +0 -41
  303. package/dist/index26.js.map +0 -1
  304. package/dist/index27.js +0 -5
  305. package/dist/index27.js.map +0 -1
  306. package/dist/index28.js +0 -322
  307. package/dist/index28.js.map +0 -1
  308. package/dist/index29.js +0 -27
  309. package/dist/index29.js.map +0 -1
  310. package/dist/index3.js +0 -87
  311. package/dist/index3.js.map +0 -1
  312. package/dist/index30.js +0 -11
  313. package/dist/index30.js.map +0 -1
  314. package/dist/index31.js +0 -11
  315. package/dist/index31.js.map +0 -1
  316. package/dist/index32.js +0 -174
  317. package/dist/index32.js.map +0 -1
  318. package/dist/index33.js +0 -501
  319. package/dist/index33.js.map +0 -1
  320. package/dist/index34.js +0 -12
  321. package/dist/index34.js.map +0 -1
  322. package/dist/index35.js +0 -4403
  323. package/dist/index35.js.map +0 -1
  324. package/dist/index36.js +0 -316
  325. package/dist/index36.js.map +0 -1
  326. package/dist/index37.js +0 -61
  327. package/dist/index37.js.map +0 -1
  328. package/dist/index38.js +0 -20
  329. package/dist/index38.js.map +0 -1
  330. package/dist/index39.js +0 -20
  331. package/dist/index39.js.map +0 -1
  332. package/dist/index4.js +0 -67
  333. package/dist/index4.js.map +0 -1
  334. package/dist/index5.js +0 -16
  335. package/dist/index5.js.map +0 -1
  336. package/dist/index6.js +0 -17
  337. package/dist/index6.js.map +0 -1
  338. package/dist/index7.js +0 -39
  339. package/dist/index7.js.map +0 -1
  340. package/dist/index8.js +0 -108
  341. package/dist/index8.js.map +0 -1
  342. package/dist/index9.js +0 -76
  343. package/dist/index9.js.map +0 -1
  344. package/src/Game/EffectManager.ts +0 -20
  345. package/src/components/gui/dialogbox/itemMenu.ce +0 -23
  346. package/src/components/gui/dialogbox/selection.ce +0 -67
  347. /package/src/components/{effects → animations}/hit.ce +0 -0
@@ -0,0 +1,69 @@
1
+ import { signal, animatedSignal } from "canvasengine";
2
+
3
+ export type NotificationType = "info" | "warn" | "error";
4
+
5
+ export interface NotificationPayload {
6
+ message: string;
7
+ type?: NotificationType;
8
+ icon?: string;
9
+ time?: number;
10
+ sound?: string;
11
+ }
12
+
13
+ export interface NotificationItem extends NotificationPayload {
14
+ id: number;
15
+ opacity: any;
16
+ offset: any;
17
+ layoutY: any;
18
+ removing: boolean;
19
+ }
20
+
21
+ const DEFAULT_DURATION = 220;
22
+
23
+ export class NotificationManager {
24
+ stack = signal<NotificationItem[]>([]);
25
+ private _counter = 0;
26
+
27
+ add(payload: NotificationPayload, engine?: { playSound?: (id: string) => void }) {
28
+ const id = ++this._counter;
29
+ const opacity = animatedSignal(0, { duration: DEFAULT_DURATION });
30
+ const offset = animatedSignal(12, { duration: DEFAULT_DURATION });
31
+ const layoutY = animatedSignal(0, { duration: DEFAULT_DURATION });
32
+ const item: NotificationItem = {
33
+ id,
34
+ message: payload.message,
35
+ type: payload.type || "info",
36
+ icon: payload.icon,
37
+ time: payload.time,
38
+ sound: payload.sound,
39
+ opacity,
40
+ offset,
41
+ layoutY,
42
+ removing: false,
43
+ };
44
+ this.stack.update((list) => [...list, item]);
45
+ opacity.set(1);
46
+ offset.set(0);
47
+
48
+ if (payload.sound && engine?.playSound) {
49
+ engine.playSound(payload.sound);
50
+ }
51
+
52
+ const delay = typeof payload.time === "number" ? payload.time : 2000;
53
+ setTimeout(() => {
54
+ this.remove(id);
55
+ }, delay);
56
+ }
57
+
58
+ remove(id: number) {
59
+ const list = this.stack();
60
+ const item = list.find((it) => it.id === id);
61
+ if (!item || item.removing) return;
62
+ item.removing = true;
63
+ item.opacity.set(0);
64
+ item.offset.set(-8);
65
+ setTimeout(() => {
66
+ this.stack.update((items) => items.filter((it) => it.id !== id));
67
+ }, DEFAULT_DURATION);
68
+ }
69
+ }
@@ -0,0 +1,149 @@
1
+ import { RpgClientEngine } from './RpgClientEngine';
2
+
3
+ /**
4
+ * RpgResource class
5
+ *
6
+ * Provides a unified API to access resource file links (images and sounds) in the game.
7
+ * Resources are stored as Maps of resource IDs to file paths/URLs.
8
+ *
9
+ * ## Design
10
+ *
11
+ * RpgResource acts as a facade over the engine's resource storage, providing
12
+ * easy access to resource file links. It maintains Maps that are synchronized
13
+ * with the engine's internal storage, but only stores the file paths/URLs,
14
+ * not the full resource objects.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { RpgResource } from '@rpgjs/client'
19
+ *
20
+ * // Get spritesheet image link
21
+ * const imageLink = RpgResource.spritesheets.get('hero')
22
+ *
23
+ * // Get sound file link
24
+ * const soundLink = RpgResource.sounds.get('town-music')
25
+ *
26
+ * // Set a new resource link
27
+ * RpgResource.spritesheets.set('new-sprite', './assets/new-sprite.png')
28
+ * ```
29
+ */
30
+ export class RpgResource {
31
+ private static engine: RpgClientEngine | null = null;
32
+ private static _spritesheets: Map<string, string> = new Map();
33
+ private static _sounds: Map<string, string> = new Map();
34
+
35
+ /**
36
+ * Initialize RpgResource with the engine instance
37
+ *
38
+ * This is called automatically by the engine during initialization.
39
+ * It synchronizes the resource Maps with the engine's internal storage.
40
+ *
41
+ * @param engine - The RpgClientEngine instance
42
+ */
43
+ static init(engine: RpgClientEngine): void {
44
+ RpgResource.engine = engine;
45
+ RpgResource.syncResources();
46
+ }
47
+
48
+ /**
49
+ * Synchronize resource Maps with the engine's internal storage
50
+ *
51
+ * Extracts file links from spritesheets and sounds stored in the engine
52
+ * and updates the Maps accordingly.
53
+ *
54
+ * @private
55
+ */
56
+ private static syncResources(): void {
57
+ if (!RpgResource.engine) {
58
+ return;
59
+ }
60
+
61
+ // Sync spritesheets
62
+ RpgResource._spritesheets.clear();
63
+ RpgResource.engine.spritesheets.forEach((spritesheet, id) => {
64
+ // Extract image path from spritesheet
65
+ const imageLink = spritesheet?.image || spritesheet?.imageSource || undefined;
66
+ if (imageLink) {
67
+ RpgResource._spritesheets.set(String(id), imageLink);
68
+ }
69
+ });
70
+
71
+ // Sync sounds
72
+ RpgResource._sounds.clear();
73
+ RpgResource.engine.sounds.forEach((sound, id) => {
74
+ // Extract src path from sound
75
+ let soundLink: string | undefined;
76
+
77
+ // If it's a Howler instance, try to get src from _src or src property
78
+ if (sound && typeof sound === 'object') {
79
+ if (sound._src && Array.isArray(sound._src) && sound._src.length > 0) {
80
+ soundLink = sound._src[0];
81
+ } else if (sound.src && typeof sound.src === 'string') {
82
+ soundLink = sound.src;
83
+ } else if (sound.src && Array.isArray(sound.src) && sound.src.length > 0) {
84
+ soundLink = sound.src[0];
85
+ }
86
+ }
87
+
88
+ if (soundLink) {
89
+ RpgResource._sounds.set(id, soundLink);
90
+ }
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Get/Set image links for spritesheets
96
+ *
97
+ * Map of spritesheet IDs to their image file paths/URLs.
98
+ * This Map is synchronized with the engine's spritesheet storage.
99
+ *
100
+ * @type {Map<string, string>}
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * // Get an image link
105
+ * const imageLink = RpgResource.spritesheets.get('hero')
106
+ *
107
+ * // Set a new image link
108
+ * RpgResource.spritesheets.set('new-sprite', './assets/new-sprite.png')
109
+ *
110
+ * // Check if a spritesheet exists
111
+ * if (RpgResource.spritesheets.has('monster')) {
112
+ * const link = RpgResource.spritesheets.get('monster')
113
+ * }
114
+ * ```
115
+ */
116
+ static get spritesheets(): Map<string, string> {
117
+ // Sync before returning to ensure we have the latest data
118
+ RpgResource.syncResources();
119
+ return RpgResource._spritesheets;
120
+ }
121
+
122
+ /**
123
+ * Get/Set sound file links
124
+ *
125
+ * Map of sound IDs to their audio file paths/URLs.
126
+ * This Map is synchronized with the engine's sound storage.
127
+ *
128
+ * @type {Map<string, string>}
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * // Get a sound link
133
+ * const soundLink = RpgResource.sounds.get('town-music')
134
+ *
135
+ * // Set a new sound link
136
+ * RpgResource.sounds.set('new-sound', './assets/new-sound.ogg')
137
+ *
138
+ * // Iterate over all sounds
139
+ * RpgResource.sounds.forEach((link, id) => {
140
+ * console.log(`Sound ${id}: ${link}`)
141
+ * })
142
+ * ```
143
+ */
144
+ static get sounds(): Map<string, string> {
145
+ // Sync before returning to ensure we have the latest data
146
+ RpgResource.syncResources();
147
+ return RpgResource._sounds;
148
+ }
149
+ }
package/src/RpgClient.ts CHANGED
@@ -1,10 +1,30 @@
1
- import { ComponentFunction } from 'canvasengine'
1
+ import { ComponentFunction, Signal } from 'canvasengine'
2
2
  import { RpgClientEngine } from './RpgClientEngine'
3
3
  import { Loader, Container } from 'pixi.js'
4
+ import { RpgClientObject } from './Game/Object'
5
+ import { type MapPhysicsEntityContext, type MapPhysicsInitContext } from '@rpgjs/common'
4
6
 
5
7
  type RpgClass<T = any> = new (...args: any[]) => T
6
- type RpgComponent = Container
8
+ type RpgComponent = RpgClientObject
7
9
  type SceneMap = Container
10
+ export type SpriteComponentConfig = ComponentFunction | {
11
+ component: ComponentFunction
12
+ props?: Record<string, any> | ((object: RpgClientObject) => Record<string, any>)
13
+ data?: Record<string, any> | ((object: RpgClientObject) => Record<string, any>)
14
+ dependencies?: (object: RpgClientObject) => any[]
15
+ }
16
+
17
+ export interface RpgSpriteBeforeRemoveContext {
18
+ reason?: string
19
+ data?: any
20
+ transition?: {
21
+ animation?: string
22
+ graphic?: string | string[]
23
+ duration?: number
24
+ effect?: string
25
+ }
26
+ timeoutMs?: number
27
+ }
8
28
 
9
29
  export interface RpgClientEngineHooks {
10
30
  /**
@@ -79,7 +99,7 @@ export interface RpgSpriteHooks {
79
99
  * }
80
100
  * ```
81
101
  */
82
- componentsBehind?: ComponentFunction[]
102
+ componentsBehind?: SpriteComponentConfig[]
83
103
 
84
104
  /**
85
105
  * Array of components to render in front of the sprite
@@ -94,7 +114,28 @@ export interface RpgSpriteHooks {
94
114
  * }
95
115
  * ```
96
116
  */
97
- componentsInFront?: ComponentFunction[]
117
+ componentsInFront?: SpriteComponentConfig[]
118
+
119
+ /**
120
+ * Reusable sprite components addressable by server-side component definitions.
121
+ *
122
+ * The server sends only the component id and serializable props. The client
123
+ * registry maps that id to the CanvasEngine component that renders it.
124
+ *
125
+ * @prop {Record<string, ComponentFunction>} [components]
126
+ * @memberof RpgSpriteHooks
127
+ * @example
128
+ * ```ts
129
+ * import GuildBadge from './components/guild-badge.ce'
130
+ *
131
+ * const sprite: RpgSpriteHooks = {
132
+ * components: {
133
+ * guildBadge: GuildBadge
134
+ * }
135
+ * }
136
+ * ```
137
+ */
138
+ components?: Record<string, ComponentFunction>
98
139
 
99
140
  /**
100
141
  * As soon as the sprite is initialized
@@ -112,6 +153,21 @@ export interface RpgSpriteHooks {
112
153
  */
113
154
  onDestroy?: (sprite: RpgComponent) => any
114
155
 
156
+ /**
157
+ * Called when a sprite removal is requested, before it disappears from the scene.
158
+ *
159
+ * Return a promise to keep the sprite visible while an animation, effect, or
160
+ * sound transition is running. The server still owns gameplay removal and
161
+ * uses the timeout carried by the remove request as a safety limit.
162
+ *
163
+ * @prop { (sprite: RpgSprite, context: RpgSpriteBeforeRemoveContext) => any } [onBeforeRemove]
164
+ * @memberof RpgSpriteHooks
165
+ */
166
+ onBeforeRemove?: (
167
+ sprite: RpgComponent,
168
+ context: RpgSpriteBeforeRemoveContext
169
+ ) => any
170
+
115
171
  /**
116
172
  * As soon as a data is changed on the server side (the name for example), you are able to know the new data but also the old data.
117
173
  *
@@ -196,6 +252,41 @@ export interface RpgSceneMapHooks extends RpgSceneHooks<SceneMap> {
196
252
  * @memberof RpgSceneHooks
197
253
  */
198
254
  onMapLoading?: (scene: SceneMap, loader: Loader) => any
255
+
256
+ /**
257
+ * Called when client physics has been initialized for the current map.
258
+ *
259
+ * Use this hook to initialize shared physics extensions based on map data
260
+ * (for example, tile-based collision rules used by client prediction).
261
+ *
262
+ * @prop { (scene: SceneMap, context: MapPhysicsInitContext) => any } [onPhysicsInit]
263
+ * @memberof RpgSceneMapHooks
264
+ */
265
+ onPhysicsInit?: (scene: SceneMap, context: MapPhysicsInitContext) => any
266
+
267
+ /**
268
+ * Called when a character physics body is added to the map.
269
+ *
270
+ * @prop { (scene: SceneMap, context: MapPhysicsEntityContext) => any } [onPhysicsEntityAdd]
271
+ * @memberof RpgSceneMapHooks
272
+ */
273
+ onPhysicsEntityAdd?: (scene: SceneMap, context: MapPhysicsEntityContext) => any
274
+
275
+ /**
276
+ * Called when a character physics body is removed from the map.
277
+ *
278
+ * @prop { (scene: SceneMap, context: MapPhysicsEntityContext) => any } [onPhysicsEntityRemove]
279
+ * @memberof RpgSceneMapHooks
280
+ */
281
+ onPhysicsEntityRemove?: (scene: SceneMap, context: MapPhysicsEntityContext) => any
282
+
283
+ /**
284
+ * Called when the physics world is reset (e.g. before a map physics reload).
285
+ *
286
+ * @prop { (scene: SceneMap) => any } [onPhysicsReset]
287
+ * @memberof RpgSceneMapHooks
288
+ */
289
+ onPhysicsReset?: (scene: SceneMap) => any
199
290
  }
200
291
 
201
292
  export interface RpgClient {
@@ -305,34 +396,210 @@ export interface RpgClient {
305
396
  spritesheets?: any[],
306
397
 
307
398
  /**
308
- * Array containing the list of VueJS components
399
+ * Resolver function for dynamically creating spritesheets
309
400
  *
401
+ * This function is called when a spritesheet is requested but not found in the cache.
402
+ * It can be synchronous (returns directly) or asynchronous (returns a Promise).
403
+ * The resolved spritesheet is automatically cached for future use.
404
+ *
405
+ * ```ts
406
+ * import { defineModule, RpgClient } from '@rpgjs/client'
407
+ *
408
+ * defineModule<RpgClient>({
409
+ * spritesheetResolver: (id: string) => {
410
+ * // Synchronous resolver
411
+ * if (id === 'dynamic-sprite') {
412
+ * return {
413
+ * id: 'dynamic-sprite',
414
+ * image: 'path/to/image.png',
415
+ * framesWidth: 32,
416
+ * framesHeight: 32
417
+ * };
418
+ * }
419
+ * return undefined;
420
+ * }
421
+ * })
422
+ *
423
+ * // Or asynchronous resolver
424
+ * defineModule<RpgClient>({
425
+ * spritesheetResolver: async (id: string) => {
426
+ * const response = await fetch(`/api/spritesheets/${id}`);
427
+ * const data = await response.json();
428
+ * return data;
429
+ * }
430
+ * })
431
+ * ```
432
+ *
433
+ * @prop {(id: string) => any | Promise<any>} [spritesheetResolver]
434
+ * @memberof RpgClient
435
+ * */
436
+ spritesheetResolver?: (id: string) => any | Promise<any>,
437
+
438
+ /**
439
+ * Resolver function for dynamically loading sounds
440
+ *
441
+ * The resolver is called when a sound is requested but not found in the cache.
442
+ * It can be synchronous (returns directly) or asynchronous (returns a Promise).
443
+ * The resolved sound is automatically cached for future use.
444
+ *
445
+ * ```ts
446
+ * import { defineModule, RpgClient } from '@rpgjs/client'
447
+ *
448
+ * defineModule<RpgClient>({
449
+ * soundResolver: (id: string) => {
450
+ * if (id === 'dynamic-sound') {
451
+ * return { id: 'dynamic-sound', src: 'path/to/sound.mp3' };
452
+ * }
453
+ * return undefined;
454
+ * }
455
+ * })
456
+ * ```
457
+ *
458
+ * @prop {(id: string) => any | Promise<any>} [soundResolver]
459
+ * @memberof RpgClient
460
+ * */
461
+ soundResolver?: (id: string) => any | Promise<any>,
462
+
463
+ /**
464
+ * Array containing the list of GUI components
465
+ *
466
+ * ```ts
467
+ * import { defineModule, RpgClient } from '@rpgjs/client'
468
+ * import InventoryComponent from './inventory.ce'
469
+ *
470
+ * defineModule<RpgClient>({
471
+ * gui: [
472
+ * {
473
+ * id: 'inventory',
474
+ * component: InventoryComponent,
475
+ * autoDisplay: true,
476
+ * dependencies: () => [playerSignal, inventorySignal]
477
+ * }
478
+ * ]
479
+ * })
480
+ * ```
310
481
  *
311
482
  * [Guide: Create GUI](/guide/create-gui.html)
312
483
  *
313
- * @prop {Array<Component of CanvasEngine>} [gui]
484
+ * @prop {Array<GuiOptions>} [gui]
314
485
  * @memberof RpgClient
315
486
  * */
316
- gui?: ComponentFunction[],
487
+ gui?: ({
488
+ id: string,
489
+ component: ComponentFunction,
490
+ /**
491
+ * Auto display the GUI when added to the system
492
+ * @default false
493
+ */
494
+ autoDisplay?: boolean,
495
+ /**
496
+ * Function that returns an array of Signal dependencies
497
+ * The GUI will only display when all dependencies are resolved (!= undefined)
498
+ */
499
+ dependencies?: () => Signal[]
500
+ /**
501
+ * Attach the GUI to sprites instead of displaying globally
502
+ *
503
+ * When set to `true`, the GUI component will be rendered directly on each sprite
504
+ * in the game world, rather than being displayed as a fixed overlay on the screen.
505
+ * This is useful for tooltips, health bars, name tags, or any UI element that
506
+ * should follow a specific sprite.
507
+ *
508
+ * The GUI will be rendered in the `character.ce` component for each sprite.
509
+ * You can control the display state of attached GUIs from the server side using
510
+ * `player.showAttachedGui()` and `player.hideAttachedGui()` methods.
511
+ *
512
+ * @default false
513
+ * @example
514
+ * ```ts
515
+ * import { defineModule, RpgClient } from '@rpgjs/client'
516
+ * import TooltipComponent from './tooltip.ce'
517
+ *
518
+ * defineModule<RpgClient>({
519
+ * gui: [
520
+ * {
521
+ * id: "my-tooltip",
522
+ * component: TooltipComponent,
523
+ * attachToSprite: true
524
+ * }
525
+ * ]
526
+ * })
527
+ * ```
528
+ *
529
+ * On the server side, control the display:
530
+ * ```ts
531
+ * // Show the tooltip for this player
532
+ * player.showAttachedGui()
533
+ *
534
+ * // Hide the tooltip for this player
535
+ * player.hideAttachedGui()
536
+ *
537
+ * // Show tooltip for other players
538
+ * player.showAttachedGui([otherPlayer1, otherPlayer2])
539
+ * ```
540
+ */
541
+ attachToSprite?: boolean
542
+ /**
543
+ * Vue v4 compatibility alias for `attachToSprite`.
544
+ *
545
+ * Prefer `attachToSprite` in v5 projects. This is read by `@rpgjs/vue`
546
+ * for Vue GUI components migrated from the v4 GUI API.
547
+ */
548
+ rpgAttachToSprite?: boolean
549
+ } | any)[],
317
550
 
318
551
  /**
319
552
  * Array containing the list of sounds
320
- * Each element is a simple object containing sound definitions
553
+ * Each element can be:
554
+ * - A simple object containing sound definitions
555
+ * - A class decorated with @Sound
321
556
  *
322
557
  * ```ts
323
- * import { defineModule, RpgClient } from '@rpgjs/client'
558
+ * import { defineModule, RpgClient, Sound } from '@rpgjs/client'
324
559
  *
560
+ * // Using simple objects
325
561
  * defineModule<RpgClient>({
326
562
  * sounds: [
327
563
  * {
328
- * town: require('./assets/Town_Theme.ogg'),
329
- * battle: require('./assets/Battle_Theme.ogg')
564
+ * id: 'typewriter',
565
+ * src: 'typewriter.wav'
566
+ * },
567
+ * {
568
+ * id: 'cursor',
569
+ * src: 'cursor.wav'
330
570
  * }
331
571
  * ]
332
572
  * })
573
+ *
574
+ * // Using @Sound decorator
575
+ * @Sound({
576
+ * id: 'town-music',
577
+ * sound: require('./sound/town.ogg'),
578
+ * loop: true,
579
+ * volume: 0.5
580
+ * })
581
+ * export class TownMusic {}
582
+ *
583
+ * defineModule<RpgClient>({
584
+ * sounds: [TownMusic]
585
+ * })
586
+ *
587
+ * // Multiple sounds in one class
588
+ * @Sound({
589
+ * sounds: {
590
+ * hero: require('./assets/hero.ogg'),
591
+ * monster: require('./assets/monster.ogg')
592
+ * },
593
+ * loop: true
594
+ * })
595
+ * export class CharacterSounds {}
596
+ *
597
+ * defineModule<RpgClient>({
598
+ * sounds: [CharacterSounds]
599
+ * })
333
600
  * ```
334
601
  *
335
- * @prop {Array<Object>} [sounds]
602
+ * @prop {Array<Object | Class>} [sounds]
336
603
  * @memberof RpgClient
337
604
  * */
338
605
  sounds?: any[],
@@ -382,8 +649,36 @@ export interface RpgClient {
382
649
  map: RpgSceneMapHooks
383
650
  }
384
651
 
385
- effects?: {
652
+ sceneMap?: RpgSceneMapHooks
653
+
654
+ /**
655
+ * Array containing the list of component animations
656
+ * Each element defines a temporary component to display for animations like hits, effects, etc.
657
+ *
658
+ * ```ts
659
+ * import { defineModule, RpgClient } from '@rpgjs/client'
660
+ * import HitComponent from './hit.ce'
661
+ * import ExplosionComponent from './explosion.ce'
662
+ *
663
+ * defineModule<RpgClient>({
664
+ * componentAnimations: [
665
+ * {
666
+ * id: 'hit',
667
+ * component: HitComponent
668
+ * },
669
+ * {
670
+ * id: 'explosion',
671
+ * component: ExplosionComponent
672
+ * }
673
+ * ]
674
+ * })
675
+ * ```
676
+ *
677
+ * @prop {Array<{id: string, component: ComponentFunction}>} [componentAnimations]
678
+ * @memberof RpgClient
679
+ * */
680
+ componentAnimations?: {
386
681
  id: string,
387
682
  component: ComponentFunction
388
683
  }[]
389
- }
684
+ }