@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
@@ -1,31 +1,65 @@
1
- <Container x={smoothX} y={smoothY} zIndex={z} viewportFollow={shouldFollowCamera} controls onBeforeDestroy visible>
1
+ <Container
2
+ x={smoothX}
3
+ y={smoothY}
4
+ zIndex={z}
5
+ viewportFollow={shouldFollowCamera}
6
+ controls
7
+ onBeforeDestroy
8
+ visible
9
+ cursor={interactionCursor}
10
+ pointerover={interactionPointerOver}
11
+ pointerout={interactionPointerOut}
12
+ pointerdown={interactionPointerDown}
13
+ pointerup={interactionPointerUp}
14
+ pointermove={interactionPointerMove}
15
+ click={interactionClick}
16
+ >
2
17
  @for (compConfig of normalizedComponentsBehind) {
3
18
  <Container>
4
- <compConfig.component object ...compConfig.props />
19
+ <compConfig.component object={sprite} ...compConfig.props />
5
20
  </Container>
6
21
  }
22
+ <PlayerComponents object={sprite} position="bottom" graphicBounds />
23
+ <PlayerComponents object={sprite} position="left" graphicBounds />
7
24
  <Particle emit={emitParticleTrigger} settings={particleSettings} zIndex={1000} name={particleName} />
8
25
  <Container>
9
- @for (graphicObj of graphicsSignals) {
10
- <Sprite
11
- sheet={sheet(graphicObj)}
12
- direction
13
- tint
14
- hitbox
15
- flash={flashConfig}
16
- />
26
+ @for (graphicObj of renderedGraphics) {
27
+ <Container scale={graphicScale(graphicObj)}>
28
+ <Sprite
29
+ sheet={sheet(graphicObj)}
30
+ direction
31
+ tint
32
+ hitbox
33
+ shadowCaster={shadowCaster(graphicObj)}
34
+ flash={flashConfig}
35
+ />
36
+ </Container>
37
+ }
38
+ @for (eventComponent of resolvedEventComponents) {
39
+ <Container dependencies={eventComponent.dependencies}>
40
+ <eventComponent.component ...eventComponent.props />
41
+ </Container>
17
42
  }
18
43
  </Container>
44
+ <PlayerComponents object={sprite} position="center" graphicBounds />
45
+ <PlayerComponents object={sprite} position="right" graphicBounds />
46
+ <PlayerComponents object={sprite} position="top" graphicBounds />
19
47
  @for (compConfig of normalizedComponentsInFront) {
20
48
  <Container dependencies={compConfig.dependencies}>
21
- <compConfig.component object ...compConfig.props />
49
+ <compConfig.component object={sprite} ...compConfig.props />
22
50
  </Container>
23
51
  }
52
+ <InteractionComponents
53
+ object={sprite}
54
+ bounds={graphicBounds}
55
+ hitboxBounds={hitboxBounds}
56
+ graphicBounds={graphicBounds}
57
+ />
24
58
  @for (attachedGui of attachedGuis) {
25
59
  @if (shouldDisplayAttachedGui) {
26
60
  <Container>
27
- <attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={object} onFinish={(data) => {
28
- onAttachedGuiFinish(attachedGui, data)
61
+ <attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={sprite} guiOpenId={attachedGui.openId} onFinish={(data, guiOpenId) => {
62
+ onAttachedGuiFinish(attachedGui, data, guiOpenId)
29
63
  }} onInteraction={(name, data) => {
30
64
  onAttachedGuiInteraction(attachedGui, name, data)
31
65
  }} />
@@ -36,28 +70,51 @@
36
70
 
37
71
  <script>
38
72
  import { signal, effect, mount, computed, tick, animatedSignal, on } from "canvasengine";
73
+ import { Assets } from "pixi.js";
39
74
 
40
75
  import { lastValueFrom, combineLatest, pairwise, filter, map, startWith } from "rxjs";
41
76
  import { Particle } from "@canvasengine/presets";
42
- import { GameEngineToken, ModulesToken } from "@rpgjs/common";
77
+ import { GameEngineToken, ModulesToken, shouldRenderLightingShadows } from "@rpgjs/common";
43
78
  import { RpgClientEngine } from "../RpgClientEngine";
44
79
  import { inject } from "../core/inject";
45
- import { Direction } from "@rpgjs/common";
80
+ import { Direction, Animation } from "@rpgjs/common";
81
+ import { normalizeEventComponent } from "../Game/EventComponentResolver";
46
82
  import Hit from "./effects/hit.ce";
47
83
  import PlayerComponents from "./player-components.ce";
84
+ import InteractionComponents from "./interaction-components.ce";
48
85
  import { RpgGui } from "../Gui/Gui";
86
+ import { getCanMoveValue } from "../utils/readPropValue";
87
+ import {
88
+ getKeyboardControlBind,
89
+ keyboardEventMatchesBind,
90
+ resolveKeyboardActionInput,
91
+ resolveKeyboardDirectionInput,
92
+ } from "../services/actionInput";
49
93
 
50
94
  const { object, id } = defineProps();
95
+ const sprite = object();
51
96
 
52
97
  const client = inject(RpgClientEngine);
53
98
  const hooks = inject(ModulesToken);
54
99
  const guiService = inject(RpgGui);
55
100
 
56
101
  const spritesheets = client.spritesheets;
57
- const playerId = client.playerId;
58
102
  const componentsBehind = client.spriteComponentsBehind;
59
103
  const componentsInFront = client.spriteComponentsInFront;
60
- const isMe = computed(() => id() === playerId);
104
+ const readProp = (value) => typeof value === 'function' ? value() : value;
105
+ const isCurrentPlayer = () => {
106
+ const playerId = client.playerIdSignal();
107
+ const currentPlayer = playerId ? client.sceneMap?.players?.()?.[playerId] : undefined;
108
+ return readProp(id) === playerId
109
+ || readProp(sprite?.id) === playerId
110
+ || sprite === currentPlayer
111
+ || sprite === client.sceneMap?.getCurrentPlayer?.();
112
+ };
113
+ const isMe = computed(isCurrentPlayer);
114
+ const shadowsEnabled = computed(() => {
115
+ const lighting = client.sceneMap?.lighting?.();
116
+ return shouldRenderLightingShadows(lighting);
117
+ });
61
118
 
62
119
  /**
63
120
  * Normalize a single sprite component configuration
@@ -128,8 +185,8 @@
128
185
  // The computed will be created in the template when needed
129
186
  return {
130
187
  component: componentRef,
131
- props: typeof propsValue === 'function' ? propsValue(object) : propsValue || {},
132
- dependencies: dependenciesFn ? dependenciesFn(object) : []
188
+ props: typeof propsValue === 'function' ? propsValue(sprite) : propsValue || {},
189
+ dependencies: dependenciesFn ? dependenciesFn(sprite) : []
133
190
  };
134
191
  };
135
192
 
@@ -189,6 +246,18 @@
189
246
  const normalizedComponentsInFront = computed(() => {
190
247
  return normalizeComponents(componentsInFront());
191
248
  });
249
+
250
+ const isEventSprite = () => {
251
+ return typeof sprite?.isEvent === 'function'
252
+ ? sprite.isEvent()
253
+ : sprite?._type === 'event';
254
+ };
255
+
256
+ const resolvedEventComponents = computed(() => {
257
+ if (!isEventSprite()) return [];
258
+ const eventComponent = normalizeEventComponent(client.resolveEventComponent(sprite), sprite);
259
+ return eventComponent ? [eventComponent] : [];
260
+ });
192
261
 
193
262
  /**
194
263
  * Determine if the camera should follow this sprite
@@ -237,7 +306,13 @@
237
306
  isConnected,
238
307
  graphicsSignals,
239
308
  flashTrigger
240
- } = object;
309
+ } = sprite;
310
+
311
+ const renderedGraphics = computed(() => {
312
+ const eventComponent = resolvedEventComponents()[0];
313
+ if (eventComponent && !eventComponent.renderGraphic) return [];
314
+ return graphicsSignals();
315
+ });
241
316
 
242
317
  /**
243
318
  * Flash configuration signals for dynamic options
@@ -282,53 +357,161 @@
282
357
 
283
358
  const particleSettings = client.particleSettings;
284
359
 
285
- const canControls = () => isMe() && object.canMove()
360
+ const canControls = () => isMe() && getCanMoveValue(sprite)
286
361
  const keyboardControls = client.globalConfig.keyboardControls;
362
+ const activeDirectionKeys = new Map();
363
+
364
+ const resolveHeldDirection = () => {
365
+ const directions = Array.from(activeDirectionKeys.values());
366
+ return directions[directions.length - 1];
367
+ };
368
+
369
+ const resolveSpriteDirection = () => {
370
+ const heldDirection = resolveHeldDirection();
371
+ if (heldDirection) return heldDirection;
372
+ if (typeof sprite.getDirection === 'function') return sprite.getDirection();
373
+ if (typeof sprite.direction === 'function') return sprite.direction();
374
+ return direction();
375
+ };
376
+
377
+ const directionToDashVector = (currentDirection) => {
378
+ switch (currentDirection) {
379
+ case Direction.Left:
380
+ return { x: -1, y: 0 };
381
+ case Direction.Right:
382
+ return { x: 1, y: 0 };
383
+ case Direction.Up:
384
+ return { x: 0, y: -1 };
385
+ case Direction.Down:
386
+ default:
387
+ return { x: 0, y: 1 };
388
+ }
389
+ };
390
+
391
+ const withCurrentDirection = (payload) => {
392
+ if (payload.action !== 'action') return payload;
393
+ const data = payload.data && typeof payload.data === 'object'
394
+ ? { ...payload.data }
395
+ : {};
396
+ return {
397
+ ...payload,
398
+ data: {
399
+ ...data,
400
+ direction: data.direction ?? resolveSpriteDirection(),
401
+ },
402
+ };
403
+ };
404
+
405
+ const resolveCurrentActionInput = () =>
406
+ withCurrentDirection(
407
+ resolveKeyboardActionInput(keyboardControls.action, client, sprite)
408
+ );
409
+
410
+ const playPredictedWalkAnimation = () => {
411
+ if (sprite.animationFixed) return;
412
+ if (sprite.animationIsPlaying && sprite.animationIsPlaying()) return;
413
+ realAnimationName.set('walk');
414
+ };
415
+
416
+ const resumeHeldDirectionWalkAnimation = () => {
417
+ if (!isCurrentPlayer()) return false;
418
+ if (activeDirectionKeys.size === 0) return false;
419
+ if (!canControls()) return false;
420
+ if (sprite.animationFixed) return false;
421
+ if (sprite.animationIsPlaying && sprite.animationIsPlaying()) return false;
422
+ realAnimationName.set('walk');
423
+ return true;
424
+ };
425
+
426
+ const processMovementInput = (input) => {
427
+ if (!canControls()) return;
428
+ client.processInput({ input });
429
+ playPredictedWalkAnimation();
430
+ };
431
+
432
+ const processDashInput = () => {
433
+ if (!canControls()) return;
434
+ client.processDash({
435
+ direction: directionToDashVector(resolveSpriteDirection()),
436
+ });
437
+ };
438
+
439
+ const actionBind = () => getKeyboardControlBind(keyboardControls.action);
440
+ const keyboardEventId = (event) => `${event.keyCode}:${event.code}:${event.key}`;
441
+
442
+ const handleNativeActionWhileMoving = (event) => {
443
+ const inputDirection = resolveKeyboardDirectionInput(event, keyboardControls);
444
+ if (inputDirection) {
445
+ const keyId = keyboardEventId(event);
446
+ if (event.type === 'keydown') {
447
+ activeDirectionKeys.delete(keyId);
448
+ activeDirectionKeys.set(keyId, inputDirection);
449
+ resumeHeldDirectionWalkAnimation();
450
+ }
451
+ else {
452
+ activeDirectionKeys.delete(keyId);
453
+ }
454
+ }
455
+
456
+ if (!isCurrentPlayer()) return;
457
+ if (event.type !== 'keydown' || event.repeat) return;
458
+ if (activeDirectionKeys.size === 0) return;
459
+ if (!keyboardEventMatchesBind(event, actionBind())) return;
460
+ if (!canControls()) return;
461
+
462
+ client.processAction(resolveCurrentActionInput());
463
+ };
287
464
 
288
465
  const visible = computed(() => {
289
- if (object.isEvent()) {
466
+ if (sprite.isEvent()) {
290
467
  return true
291
468
  }
292
469
  return isConnected()
293
470
  });
294
471
 
295
- const controls = signal({
472
+ const controls = {
296
473
  down: {
297
474
  repeat: true,
298
475
  bind: keyboardControls.down,
299
476
  keyDown() {
300
- if (canControls()) client.processInput({ input: Direction.Down })
477
+ processMovementInput(Direction.Down)
301
478
  },
302
479
  },
303
480
  up: {
304
481
  repeat: true,
305
482
  bind: keyboardControls.up,
306
483
  keyDown() {
307
- if (canControls()) client.processInput({ input: Direction.Up })
484
+ processMovementInput(Direction.Up)
308
485
  },
309
486
  },
310
487
  left: {
311
488
  repeat: true,
312
489
  bind: keyboardControls.left,
313
490
  keyDown() {
314
- if (canControls()) client.processInput({ input: Direction.Left })
491
+ processMovementInput(Direction.Left)
315
492
  },
316
493
  },
317
494
  right: {
318
495
  repeat: true,
319
496
  bind: keyboardControls.right,
320
497
  keyDown() {
321
- if (canControls()) client.processInput({ input: Direction.Right })
498
+ processMovementInput(Direction.Right)
322
499
  },
323
500
  },
324
501
  action: {
325
- bind: keyboardControls.action,
502
+ bind: getKeyboardControlBind(keyboardControls.action),
326
503
  keyDown() {
327
504
  if (canControls()) {
328
- client.processAction({ action: 'action' })
505
+ client.processAction(resolveCurrentActionInput())
329
506
  }
330
507
  },
331
508
  },
509
+ dash: {
510
+ bind: keyboardControls.dash,
511
+ keyDown() {
512
+ processDashInput()
513
+ },
514
+ },
332
515
  escape: {
333
516
  bind: keyboardControls.escape,
334
517
  keyDown() {
@@ -340,7 +523,7 @@
340
523
  gamepad: {
341
524
  enabled: true
342
525
  }
343
- });
526
+ };
344
527
 
345
528
  const smoothX = animatedSignal(x(), {
346
529
  duration: isMe() ? 0 : 0
@@ -351,7 +534,8 @@
351
534
  });
352
535
 
353
536
  const z = computed(() => {
354
- return object.y() + object.z()
537
+ const box = hitbox?.()
538
+ return sprite.y() + (box?.h ?? 0) + sprite.z()
355
539
  });
356
540
 
357
541
  const realAnimationName = signal(animationName());
@@ -377,6 +561,344 @@
377
561
  };
378
562
  }
379
563
 
564
+ const graphicScale = (graphicObject) => {
565
+ const scale = graphicObject?.displayScale ?? graphicObject?.scale;
566
+ if (Array.isArray(scale)) return scale;
567
+ if (typeof scale === 'number') return [scale, scale];
568
+ if (scale && typeof scale === 'object') {
569
+ const x = typeof scale.x === 'number' ? scale.x : 1;
570
+ const y = typeof scale.y === 'number' ? scale.y : x;
571
+ return [x, y];
572
+ }
573
+ return undefined;
574
+ }
575
+
576
+ const shadowCaster = (graphicObject) => {
577
+ const box = hitbox();
578
+ const bounds = graphicBounds();
579
+ const scale = graphicScale(graphicObject);
580
+ const scaleY = Array.isArray(scale) && typeof scale[1] === 'number' ? Math.abs(scale[1]) : 1;
581
+ const height = Math.max(bounds?.height ?? box?.h ?? 32, box?.h ?? 32) * scaleY;
582
+
583
+ return {
584
+ enabled: shadowsEnabled,
585
+ height,
586
+ footAnchor: { x: 0.5, y: 1 },
587
+ footOffset: { x: 0, y: 2 },
588
+ alpha: 0.5,
589
+ blur: 3.5,
590
+ gradientPower: 2,
591
+ hardness: 0.42,
592
+ minLength: Math.max(6, (box?.h ?? 32) * 0.25),
593
+ maxLength: Math.max(90, height * 1.8),
594
+ contactAlpha: 0.3,
595
+ contactScale: 0.3,
596
+ };
597
+ }
598
+
599
+ const imageDimensions = signal({});
600
+ const loadingImageDimensions = new Set();
601
+
602
+ const toPositiveNumber = (value) => {
603
+ const number = typeof value === 'number' ? value : parseFloat(value);
604
+ return Number.isFinite(number) && number > 0 ? number : undefined;
605
+ };
606
+
607
+ const toFiniteNumber = (value, fallback = 0) => {
608
+ const number = typeof value === 'number' ? value : parseFloat(value);
609
+ return Number.isFinite(number) ? number : fallback;
610
+ };
611
+
612
+ const clampRatio = (value) => Math.min(1, Math.max(0, value));
613
+
614
+ const normalizePair = (value, fallback = [1, 1]) => {
615
+ if (Array.isArray(value)) {
616
+ const x = toFiniteNumber(value[0], fallback[0]);
617
+ const y = toFiniteNumber(value[1] ?? value[0], x);
618
+ return [x, y];
619
+ }
620
+ if (typeof value === 'number') {
621
+ return [value, value];
622
+ }
623
+ if (value && typeof value === 'object') {
624
+ const x = toFiniteNumber(value.x, fallback[0]);
625
+ const y = toFiniteNumber(value.y ?? value.x, x);
626
+ return [x, y];
627
+ }
628
+ return fallback;
629
+ };
630
+
631
+ const normalizeAnchor = (value) => {
632
+ if (!Array.isArray(value)) return undefined;
633
+ const [x, y] = normalizePair(value, [0, 0]);
634
+ return [clampRatio(x), clampRatio(y)];
635
+ };
636
+
637
+ const resolveImageSource = (image) => {
638
+ if (typeof image === 'string') return image;
639
+ if (typeof image?.default === 'string') return image.default;
640
+ return undefined;
641
+ };
642
+
643
+ const parentTextureOptions = (graphicObject) => {
644
+ const props = [
645
+ 'width',
646
+ 'height',
647
+ 'framesHeight',
648
+ 'framesWidth',
649
+ 'rectWidth',
650
+ 'rectHeight',
651
+ 'offset',
652
+ 'image',
653
+ 'sound',
654
+ 'spriteRealSize',
655
+ 'scale',
656
+ 'anchor',
657
+ 'pivot',
658
+ 'x',
659
+ 'y',
660
+ 'opacity'
661
+ ];
662
+
663
+ return props.reduce((options, prop) => {
664
+ if (graphicObject?.[prop] !== undefined) {
665
+ options[prop] = graphicObject[prop];
666
+ }
667
+ return options;
668
+ }, {});
669
+ };
670
+
671
+ const resolveTextureOptions = (graphicObject) => {
672
+ const textures = graphicObject?.textures ?? {};
673
+ const texture =
674
+ textures[realAnimationName()] ??
675
+ textures[Animation.Stand] ??
676
+ Object.values(textures)[0] ??
677
+ {};
678
+
679
+ return {
680
+ ...parentTextureOptions(graphicObject),
681
+ ...texture
682
+ };
683
+ };
684
+
685
+ const resolveFirstAnimationFrame = (textureOptions) => {
686
+ const animations = textureOptions?.animations;
687
+ if (!animations) return {};
688
+
689
+ try {
690
+ const frames = typeof animations === 'function'
691
+ ? animations({ direction: direction() })
692
+ : animations;
693
+ if (!Array.isArray(frames)) return {};
694
+ const firstGroup = frames[0];
695
+ return Array.isArray(firstGroup) ? firstGroup[0] ?? {} : firstGroup ?? {};
696
+ }
697
+ catch {
698
+ return {};
699
+ }
700
+ };
701
+
702
+ const optionValue = (prop, frame, textureOptions, graphicObject) => {
703
+ return frame?.[prop] ?? textureOptions?.[prop] ?? graphicObject?.[prop];
704
+ };
705
+
706
+ const resolveFrameSize = (textureOptions, dimensions) => {
707
+ const framesWidth = toPositiveNumber(textureOptions?.framesWidth) ?? 1;
708
+ const framesHeight = toPositiveNumber(textureOptions?.framesHeight) ?? 1;
709
+ const imageSource = resolveImageSource(textureOptions?.image);
710
+ const loadedSize = imageSource ? dimensions[imageSource] : undefined;
711
+ const fullWidth = toPositiveNumber(textureOptions?.width) ?? loadedSize?.width;
712
+ const fullHeight = toPositiveNumber(textureOptions?.height) ?? loadedSize?.height;
713
+ const width = toPositiveNumber(textureOptions?.rectWidth) ??
714
+ toPositiveNumber(textureOptions?.spriteWidth) ??
715
+ (fullWidth ? fullWidth / framesWidth : undefined);
716
+ const height = toPositiveNumber(textureOptions?.rectHeight) ??
717
+ toPositiveNumber(textureOptions?.spriteHeight) ??
718
+ (fullHeight ? fullHeight / framesHeight : undefined);
719
+
720
+ return {
721
+ width,
722
+ height
723
+ };
724
+ };
725
+
726
+ const resolveHitboxAnchor = (spriteWidth, spriteHeight, realSize, box) => {
727
+ if (!spriteWidth || !spriteHeight || !box) {
728
+ return [0, 0];
729
+ }
730
+
731
+ const heightOfSprite = typeof realSize === 'number' ? realSize : realSize?.height;
732
+ const resolvedHeight = toPositiveNumber(heightOfSprite) ?? spriteHeight;
733
+ const gap = Math.max(0, (spriteHeight - resolvedHeight) / 2);
734
+ const hitboxTopLeftX = clampRatio((spriteWidth - box.w) / 2 / spriteWidth);
735
+ const hitboxTopLeftY = clampRatio((spriteHeight - box.h - gap) / spriteHeight);
736
+ const hitboxCenterX = clampRatio(hitboxTopLeftX + box.w / 2 / spriteWidth);
737
+ const hitboxCenterY = clampRatio(hitboxTopLeftY + box.h / 2 / spriteHeight);
738
+ const footY = clampRatio((spriteHeight - gap) / spriteHeight);
739
+
740
+ switch (box.anchorMode ?? 'top-left') {
741
+ case 'center':
742
+ return [hitboxCenterX, hitboxCenterY];
743
+ case 'foot':
744
+ return [hitboxCenterX, footY];
745
+ case 'top-left':
746
+ default:
747
+ return [hitboxTopLeftX, hitboxTopLeftY];
748
+ }
749
+ };
750
+
751
+ const loadImageDimensions = (image) => {
752
+ const source = resolveImageSource(image);
753
+ if (!source || imageDimensions()[source] || loadingImageDimensions.has(source)) {
754
+ return;
755
+ }
756
+
757
+ loadingImageDimensions.add(source);
758
+ Assets.load(source)
759
+ .then((texture) => {
760
+ const width = toPositiveNumber(texture?.width);
761
+ const height = toPositiveNumber(texture?.height);
762
+ if (!width || !height) return;
763
+
764
+ imageDimensions.update((dimensions) => ({
765
+ ...dimensions,
766
+ [source]: { width, height }
767
+ }));
768
+ })
769
+ .catch(() => {})
770
+ .finally(() => {
771
+ loadingImageDimensions.delete(source);
772
+ });
773
+ };
774
+
775
+ effect(() => {
776
+ const sources = new Set();
777
+
778
+ graphicsSignals().forEach((graphicObject) => {
779
+ const baseImage = resolveImageSource(graphicObject?.image);
780
+ if (baseImage) sources.add(baseImage);
781
+
782
+ Object.values(graphicObject?.textures ?? {}).forEach((textureOptions) => {
783
+ const image = resolveImageSource(textureOptions?.image ?? graphicObject?.image);
784
+ if (image) sources.add(image);
785
+ });
786
+ });
787
+
788
+ sources.forEach((source) => loadImageDimensions(source));
789
+ });
790
+
791
+ const hitboxBounds = computed(() => {
792
+ const box = hitbox();
793
+ const width = box?.w ?? 0;
794
+ const height = box?.h ?? 0;
795
+
796
+ return {
797
+ left: 0,
798
+ top: 0,
799
+ right: width,
800
+ bottom: height,
801
+ width,
802
+ height,
803
+ centerX: width / 2,
804
+ centerY: height / 2
805
+ };
806
+ });
807
+
808
+ const graphicBounds = computed(() => {
809
+ const box = hitbox();
810
+ const fallback = hitboxBounds();
811
+ const customEventComponent = resolvedEventComponents()[0];
812
+ if (customEventComponent && !customEventComponent.renderGraphic) {
813
+ return fallback;
814
+ }
815
+ const dimensions = imageDimensions();
816
+ const graphics = graphicsSignals();
817
+ let bounds = null;
818
+
819
+ graphics.forEach((graphicObject) => {
820
+ const textureOptions = resolveTextureOptions(graphicObject);
821
+ const frame = resolveFirstAnimationFrame(textureOptions);
822
+ const size = resolveFrameSize(textureOptions, dimensions);
823
+ const spriteWidth = size.width ?? box?.w;
824
+ const spriteHeight = size.height ?? box?.h;
825
+
826
+ if (!spriteWidth || !spriteHeight) {
827
+ return;
828
+ }
829
+
830
+ const explicitAnchor = normalizeAnchor(optionValue('anchor', frame, textureOptions, graphicObject));
831
+ const anchor = explicitAnchor ?? resolveHitboxAnchor(
832
+ spriteWidth,
833
+ spriteHeight,
834
+ optionValue('spriteRealSize', frame, textureOptions, graphicObject),
835
+ box
836
+ );
837
+ const scale = normalizePair(optionValue('scale', frame, textureOptions, graphicObject) ?? graphicScale(graphicObject));
838
+ const x = toFiniteNumber(optionValue('x', frame, textureOptions, graphicObject), 0);
839
+ const y = toFiniteNumber(optionValue('y', frame, textureOptions, graphicObject), 0);
840
+ const leftEdge = -anchor[0] * spriteWidth * scale[0];
841
+ const rightEdge = (1 - anchor[0]) * spriteWidth * scale[0];
842
+ const topEdge = -anchor[1] * spriteHeight * scale[1];
843
+ const bottomEdge = (1 - anchor[1]) * spriteHeight * scale[1];
844
+ const graphic = {
845
+ left: x + Math.min(leftEdge, rightEdge),
846
+ top: y + Math.min(topEdge, bottomEdge),
847
+ right: x + Math.max(leftEdge, rightEdge),
848
+ bottom: y + Math.max(topEdge, bottomEdge)
849
+ };
850
+
851
+ bounds = bounds
852
+ ? {
853
+ left: Math.min(bounds.left, graphic.left),
854
+ top: Math.min(bounds.top, graphic.top),
855
+ right: Math.max(bounds.right, graphic.right),
856
+ bottom: Math.max(bounds.bottom, graphic.bottom)
857
+ }
858
+ : graphic;
859
+ });
860
+
861
+ if (!bounds) {
862
+ return fallback;
863
+ }
864
+
865
+ const width = bounds.right - bounds.left;
866
+ const height = bounds.bottom - bounds.top;
867
+
868
+ return {
869
+ ...bounds,
870
+ width,
871
+ height,
872
+ centerX: bounds.left + width / 2,
873
+ centerY: bounds.top + height / 2
874
+ };
875
+ });
876
+
877
+ const interactionBounds = () => ({
878
+ bounds: graphicBounds(),
879
+ hitbox: hitboxBounds(),
880
+ graphic: graphicBounds()
881
+ });
882
+
883
+ const interactionCursor = computed(() =>
884
+ client.interactions.cursorFor(sprite, interactionBounds())
885
+ );
886
+
887
+ const handleInteraction = (type) => (event) => {
888
+ client.updatePointerFromInteractionEvent(event);
889
+ client.interactions.handle(sprite, type, {
890
+ event,
891
+ bounds: interactionBounds()
892
+ });
893
+ };
894
+
895
+ const interactionPointerOver = handleInteraction('pointerover');
896
+ const interactionPointerOut = handleInteraction('pointerout');
897
+ const interactionPointerDown = handleInteraction('pointerdown');
898
+ const interactionPointerUp = handleInteraction('pointerup');
899
+ const interactionPointerMove = handleInteraction('pointermove');
900
+ const interactionClick = handleInteraction('click');
901
+
380
902
  // Combine animation change detection with movement state from smoothX/smoothY
381
903
  const movementAnimations = ['walk', 'stand'];
382
904
  const epsilon = 0; // movement threshold to consider the easing still running
@@ -400,25 +922,91 @@
400
922
  filter(([prev, curr]) => prev !== curr)
401
923
  );
402
924
 
925
+ let beforeRemovePromise = null;
926
+ let beforeRemoveTransitionValue = null;
927
+ const resolveRemoveContext = () => {
928
+ if (!sprite._removeTransition) return null;
929
+ const value = sprite._removeTransition();
930
+ if (!value || typeof value !== 'string') return null;
931
+ try {
932
+ const context = JSON.parse(value);
933
+ if (!context || typeof context !== 'object' || !context.active) return null;
934
+ context.__transitionValue = value;
935
+ return context;
936
+ }
937
+ catch {
938
+ return null;
939
+ }
940
+ };
941
+
942
+ const withTimeout = (promise, timeoutMs = 0) => {
943
+ if (!timeoutMs || timeoutMs <= 0) return promise;
944
+ return Promise.race([
945
+ promise,
946
+ new Promise((resolve) => setTimeout(resolve, timeoutMs)),
947
+ ]);
948
+ };
949
+
950
+ const runBeforeRemove = () => {
951
+ const context = resolveRemoveContext();
952
+ if (!context) return Promise.resolve();
953
+ if (beforeRemovePromise && beforeRemoveTransitionValue === context.__transitionValue) {
954
+ return beforeRemovePromise;
955
+ }
956
+ beforeRemoveTransitionValue = context.__transitionValue;
957
+ beforeRemovePromise = withTimeout(
958
+ lastValueFrom(hooks.callHooks("client-sprite-onBeforeRemove", sprite, context)),
959
+ context.timeoutMs
960
+ );
961
+ return beforeRemovePromise;
962
+ };
963
+
964
+ const removeTransitionSubscription = sprite._removeTransition?.observable?.subscribe(() => {
965
+ if (resolveRemoveContext()) {
966
+ runBeforeRemove();
967
+ }
968
+ });
969
+
403
970
  const animationMovementSubscription = combineLatest([animationChange$, moving$]).subscribe(([[prev, curr], isMoving]) => {
404
- if (curr == 'stand' && !isMoving) {
971
+ const isMovementAnimation = movementAnimations.includes(curr);
972
+ const isTemporaryAnimationPlaying =
973
+ sprite.animationIsPlaying && sprite.animationIsPlaying();
974
+
975
+ if (sprite.animationFixed && isMovementAnimation) {
405
976
  realAnimationName.set(curr);
977
+ return;
978
+ }
979
+
980
+ if (curr == 'stand' && !isMoving) {
981
+ if (!resumeHeldDirectionWalkAnimation()) {
982
+ realAnimationName.set(curr);
983
+ }
406
984
  }
407
985
  else if (curr == 'walk' && isMoving) {
408
986
  realAnimationName.set(curr);
409
987
  }
410
- else if (!movementAnimations.includes(curr)) {
988
+ else if (!isMovementAnimation) {
411
989
  realAnimationName.set(curr);
412
990
  }
413
- if (!isMoving && object.animationIsPlaying && object.animationIsPlaying()) {
414
- if (movementAnimations.includes(curr)) {
415
- if (typeof object.resetAnimationState === 'function') {
416
- object.resetAnimationState();
991
+ if (!isMoving && isTemporaryAnimationPlaying) {
992
+ if (isMovementAnimation) {
993
+ if (typeof sprite.resetAnimationState === 'function') {
994
+ sprite.resetAnimationState();
417
995
  }
418
996
  }
419
997
  }
420
998
  });
421
999
 
1000
+ const resumeWalkSubscriptions = [
1001
+ sprite._canMove,
1002
+ sprite._animationFixed,
1003
+ sprite.animationIsPlaying,
1004
+ ]
1005
+ .filter(signal => signal?.observable)
1006
+ .map(signal => signal.observable.subscribe(() => {
1007
+ resumeHeldDirectionWalkAnimation();
1008
+ }));
1009
+
422
1010
  /**
423
1011
  * Cleanup subscriptions and call hooks before sprite destruction.
424
1012
  *
@@ -429,18 +1017,58 @@
429
1017
  * @example
430
1018
  * await onBeforeDestroy();
431
1019
  */
1020
+ const waitForTemporaryAnimationEnd = (maxDuration = 1200) => {
1021
+ if (!sprite.animationIsPlaying || !sprite.animationIsPlaying()) {
1022
+ return Promise.resolve();
1023
+ }
1024
+
1025
+ return new Promise((resolve) => {
1026
+ let finished = false;
1027
+ let timeout;
1028
+ let subscription;
1029
+ const finish = () => {
1030
+ if (finished) return;
1031
+ finished = true;
1032
+ clearTimeout(timeout);
1033
+ subscription?.unsubscribe();
1034
+ resolve();
1035
+ };
1036
+ timeout = setTimeout(finish, maxDuration);
1037
+ subscription = sprite.animationIsPlaying.observable.subscribe((isPlaying) => {
1038
+ if (!isPlaying) finish();
1039
+ });
1040
+ if (finished) subscription.unsubscribe();
1041
+ });
1042
+ };
1043
+
432
1044
  const onBeforeDestroy = async () => {
1045
+ await runBeforeRemove();
1046
+ await waitForTemporaryAnimationEnd();
1047
+ if (typeof document !== 'undefined') {
1048
+ document.removeEventListener('keydown', handleNativeActionWhileMoving);
1049
+ document.removeEventListener('keyup', handleNativeActionWhileMoving);
1050
+ }
1051
+ removeTransitionSubscription?.unsubscribe();
433
1052
  animationMovementSubscription.unsubscribe();
1053
+ resumeWalkSubscriptions.forEach(subscription => subscription.unsubscribe());
434
1054
  xSubscription.unsubscribe();
435
1055
  ySubscription.unsubscribe();
436
- await lastValueFrom(hooks.callHooks("client-sprite-onDestroy", object))
437
- await lastValueFrom(hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, object))
1056
+ await lastValueFrom(hooks.callHooks("client-sprite-onDestroy", sprite))
1057
+ await lastValueFrom(hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, sprite))
438
1058
  }
439
1059
 
440
1060
  mount((element) => {
441
- hooks.callHooks("client-sprite-onAdd", object).subscribe()
442
- hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, object).subscribe()
443
- if (isMe()) client.setKeyboardControls(element.directives.controls)
1061
+ if (typeof document !== 'undefined') {
1062
+ document.addEventListener('keydown', handleNativeActionWhileMoving);
1063
+ document.addEventListener('keyup', handleNativeActionWhileMoving);
1064
+ }
1065
+ hooks.callHooks("client-sprite-onAdd", sprite).subscribe()
1066
+ hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, sprite).subscribe()
1067
+ effect(() => {
1068
+ if (isCurrentPlayer()) {
1069
+ client.setKeyboardControls(element.directives.controls)
1070
+ }
1071
+ })
444
1072
  })
445
1073
 
446
1074
  /**
@@ -449,8 +1077,16 @@
449
1077
  * @param gui - The GUI instance
450
1078
  * @param data - Data passed from the GUI component
451
1079
  */
452
- const onAttachedGuiFinish = (gui, data) => {
453
- guiService.guiClose(gui.name, data);
1080
+ const normalizeOpenId = (value) => {
1081
+ const resolved = typeof value === "function" ? value() : value;
1082
+ return typeof resolved === "string" && resolved.length > 0 ? resolved : undefined;
1083
+ };
1084
+
1085
+ const onAttachedGuiFinish = (gui, data, guiOpenId) => {
1086
+ const completedOpenId = normalizeOpenId(guiOpenId);
1087
+ const currentOpenId = normalizeOpenId(gui.openId);
1088
+ if (completedOpenId && currentOpenId && completedOpenId !== currentOpenId) return;
1089
+ guiService.guiClose(gui.name, data, completedOpenId ?? currentOpenId);
454
1090
  };
455
1091
 
456
1092
  /**
@@ -467,4 +1103,4 @@
467
1103
  tick(() => {
468
1104
  hooks.callHooks("client-sprite-onUpdate").subscribe()
469
1105
  })
470
- </script>
1106
+ </script>