@rpgjs/client 5.0.0-beta.1 → 5.0.0-beta.11

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 (245) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +19 -0
  3. package/dist/Game/AnimationManager.d.ts +1 -1
  4. package/dist/Game/AnimationManager.js +18 -9
  5. package/dist/Game/AnimationManager.js.map +1 -1
  6. package/dist/Game/AnimationManager.spec.d.ts +1 -0
  7. package/dist/Game/Event.js.map +1 -1
  8. package/dist/Game/Map.d.ts +9 -1
  9. package/dist/Game/Map.js +63 -5
  10. package/dist/Game/Map.js.map +1 -1
  11. package/dist/Game/Object.d.ts +47 -15
  12. package/dist/Game/Object.js +82 -38
  13. package/dist/Game/Object.js.map +1 -1
  14. package/dist/Game/Player.js.map +1 -1
  15. package/dist/Game/ProjectileManager.d.ts +89 -0
  16. package/dist/Game/ProjectileManager.js +179 -0
  17. package/dist/Game/ProjectileManager.js.map +1 -0
  18. package/dist/Game/ProjectileManager.spec.d.ts +1 -0
  19. package/dist/Gui/Gui.d.ts +17 -4
  20. package/dist/Gui/Gui.js +78 -48
  21. package/dist/Gui/Gui.js.map +1 -1
  22. package/dist/Gui/Gui.spec.d.ts +1 -0
  23. package/dist/Gui/NotificationManager.js.map +1 -1
  24. package/dist/Resource.js +1 -1
  25. package/dist/Resource.js.map +1 -1
  26. package/dist/RpgClient.d.ts +110 -15
  27. package/dist/RpgClientEngine.d.ts +86 -10
  28. package/dist/RpgClientEngine.js +306 -49
  29. package/dist/RpgClientEngine.js.map +1 -1
  30. package/dist/Sound.js.map +1 -1
  31. package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorate.js +1 -1
  32. package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorateMetadata.js +1 -1
  33. package/dist/components/animations/animation.ce.js +4 -5
  34. package/dist/components/animations/animation.ce.js.map +1 -1
  35. package/dist/components/animations/hit.ce.js +19 -25
  36. package/dist/components/animations/hit.ce.js.map +1 -1
  37. package/dist/components/animations/index.js +4 -4
  38. package/dist/components/animations/index.js.map +1 -1
  39. package/dist/components/character.ce.js +422 -240
  40. package/dist/components/character.ce.js.map +1 -1
  41. package/dist/components/dynamics/bar.ce.js +97 -0
  42. package/dist/components/dynamics/bar.ce.js.map +1 -0
  43. package/dist/components/dynamics/image.ce.js +24 -0
  44. package/dist/components/dynamics/image.ce.js.map +1 -0
  45. package/dist/components/dynamics/parse-value.d.ts +3 -0
  46. package/dist/components/dynamics/parse-value.js +54 -35
  47. package/dist/components/dynamics/parse-value.js.map +1 -1
  48. package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
  49. package/dist/components/dynamics/shape-utils.d.ts +16 -0
  50. package/dist/components/dynamics/shape-utils.js +73 -0
  51. package/dist/components/dynamics/shape-utils.js.map +1 -0
  52. package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
  53. package/dist/components/dynamics/shape.ce.js +84 -0
  54. package/dist/components/dynamics/shape.ce.js.map +1 -0
  55. package/dist/components/dynamics/text.ce.js +34 -56
  56. package/dist/components/dynamics/text.ce.js.map +1 -1
  57. package/dist/components/gui/box.ce.js +6 -8
  58. package/dist/components/gui/box.ce.js.map +1 -1
  59. package/dist/components/gui/dialogbox/index.ce.js +56 -62
  60. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  61. package/dist/components/gui/gameover.ce.js +42 -65
  62. package/dist/components/gui/gameover.ce.js.map +1 -1
  63. package/dist/components/gui/hud/hud.ce.js +21 -30
  64. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  65. package/dist/components/gui/menu/equip-menu.ce.js +112 -165
  66. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
  67. package/dist/components/gui/menu/exit-menu.ce.js +8 -6
  68. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
  69. package/dist/components/gui/menu/items-menu.ce.js +52 -69
  70. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  71. package/dist/components/gui/menu/main-menu.ce.js +75 -92
  72. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  73. package/dist/components/gui/menu/options-menu.ce.js +5 -4
  74. package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
  75. package/dist/components/gui/menu/skills-menu.ce.js +12 -17
  76. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
  77. package/dist/components/gui/mobile/index.js +2 -2
  78. package/dist/components/gui/mobile/index.js.map +1 -1
  79. package/dist/components/gui/mobile/mobile.ce.js +5 -4
  80. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  81. package/dist/components/gui/notification/notification.ce.js +22 -24
  82. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  83. package/dist/components/gui/save-load.ce.js +72 -249
  84. package/dist/components/gui/save-load.ce.js.map +1 -1
  85. package/dist/components/gui/shop/shop.ce.js +90 -127
  86. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  87. package/dist/components/gui/title-screen.ce.js +45 -70
  88. package/dist/components/gui/title-screen.ce.js.map +1 -1
  89. package/dist/components/index.d.ts +2 -1
  90. package/dist/components/index.js +1 -0
  91. package/dist/components/player-components-utils.d.ts +67 -0
  92. package/dist/components/player-components-utils.js +162 -0
  93. package/dist/components/player-components-utils.js.map +1 -0
  94. package/dist/components/player-components-utils.spec.d.ts +1 -0
  95. package/dist/components/player-components.ce.js +189 -0
  96. package/dist/components/player-components.ce.js.map +1 -0
  97. package/dist/components/prebuilt/hp-bar.ce.js +42 -44
  98. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  99. package/dist/components/prebuilt/light-halo.ce.js +36 -59
  100. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  101. package/dist/components/scenes/canvas.ce.js +165 -21
  102. package/dist/components/scenes/canvas.ce.js.map +1 -1
  103. package/dist/components/scenes/draw-map.ce.js +25 -32
  104. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  105. package/dist/components/scenes/event-layer.ce.js +9 -8
  106. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  107. package/dist/core/inject.js +1 -1
  108. package/dist/core/inject.js.map +1 -1
  109. package/dist/core/setup.js +1 -1
  110. package/dist/core/setup.js.map +1 -1
  111. package/dist/decorators/spritesheet.d.ts +1 -0
  112. package/dist/decorators/spritesheet.js +11 -0
  113. package/dist/decorators/spritesheet.js.map +1 -0
  114. package/dist/index.d.ts +4 -0
  115. package/dist/index.js +26 -21
  116. package/dist/module.js +15 -1
  117. package/dist/module.js.map +1 -1
  118. package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@3.0.1}/node_modules/@signe/di/dist/index.js +7 -117
  119. package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
  120. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
  121. package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
  122. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
  123. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
  124. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
  125. package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
  126. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
  127. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
  128. package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@3.0.1}/node_modules/@signe/sync/dist/index.js +57 -141
  129. package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
  130. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
  131. package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
  132. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +27 -27
  133. package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
  134. package/dist/presets/animation.js.map +1 -1
  135. package/dist/presets/faceset.js.map +1 -1
  136. package/dist/presets/icon.js.map +1 -1
  137. package/dist/presets/index.js.map +1 -1
  138. package/dist/presets/lpc.js.map +1 -1
  139. package/dist/presets/rmspritesheet.js.map +1 -1
  140. package/dist/services/AbstractSocket.js.map +1 -1
  141. package/dist/services/actionInput.d.ts +12 -0
  142. package/dist/services/actionInput.js +27 -0
  143. package/dist/services/actionInput.js.map +1 -0
  144. package/dist/services/actionInput.spec.d.ts +1 -0
  145. package/dist/services/keyboardControls.js.map +1 -1
  146. package/dist/services/loadMap.d.ts +6 -0
  147. package/dist/services/loadMap.js +1 -1
  148. package/dist/services/loadMap.js.map +1 -1
  149. package/dist/services/mmorpg-connection.d.ts +5 -0
  150. package/dist/services/mmorpg-connection.js +50 -0
  151. package/dist/services/mmorpg-connection.js.map +1 -0
  152. package/dist/services/mmorpg-connection.spec.d.ts +1 -0
  153. package/dist/services/mmorpg.d.ts +10 -4
  154. package/dist/services/mmorpg.js +56 -33
  155. package/dist/services/mmorpg.js.map +1 -1
  156. package/dist/services/pointerContext.d.ts +11 -0
  157. package/dist/services/pointerContext.js +48 -0
  158. package/dist/services/pointerContext.js.map +1 -0
  159. package/dist/services/pointerContext.spec.d.ts +1 -0
  160. package/dist/services/save.js.map +1 -1
  161. package/dist/services/save.spec.d.ts +1 -0
  162. package/dist/services/standalone-message.d.ts +1 -0
  163. package/dist/services/standalone-message.js +9 -0
  164. package/dist/services/standalone-message.js.map +1 -0
  165. package/dist/services/standalone.js +4 -3
  166. package/dist/services/standalone.js.map +1 -1
  167. package/dist/services/standalone.spec.d.ts +1 -0
  168. package/dist/utils/getEntityProp.js +4 -3
  169. package/dist/utils/getEntityProp.js.map +1 -1
  170. package/dist/utils/getEntityProp.spec.d.ts +1 -0
  171. package/dist/utils/readPropValue.d.ts +2 -0
  172. package/dist/utils/readPropValue.js +13 -0
  173. package/dist/utils/readPropValue.js.map +1 -0
  174. package/package.json +13 -14
  175. package/src/Game/AnimationManager.spec.ts +30 -0
  176. package/src/Game/AnimationManager.ts +22 -10
  177. package/src/Game/Map.ts +91 -2
  178. package/src/Game/Object.ts +148 -69
  179. package/src/Game/ProjectileManager.spec.ts +338 -0
  180. package/src/Game/ProjectileManager.ts +324 -0
  181. package/src/Gui/Gui.spec.ts +273 -0
  182. package/src/Gui/Gui.ts +105 -50
  183. package/src/Resource.ts +1 -2
  184. package/src/RpgClient.ts +125 -17
  185. package/src/RpgClientEngine.ts +457 -87
  186. package/src/components/character.ce +441 -32
  187. package/src/components/dynamics/bar.ce +88 -0
  188. package/src/components/dynamics/image.ce +21 -0
  189. package/src/components/dynamics/parse-value.spec.ts +83 -0
  190. package/src/components/dynamics/parse-value.ts +111 -37
  191. package/src/components/dynamics/shape-utils.spec.ts +46 -0
  192. package/src/components/dynamics/shape-utils.ts +61 -0
  193. package/src/components/dynamics/shape.ce +90 -0
  194. package/src/components/dynamics/text.ce +35 -149
  195. package/src/components/gui/dialogbox/index.ce +18 -8
  196. package/src/components/gui/gameover.ce +2 -1
  197. package/src/components/gui/menu/equip-menu.ce +2 -1
  198. package/src/components/gui/menu/exit-menu.ce +2 -1
  199. package/src/components/gui/menu/items-menu.ce +3 -2
  200. package/src/components/gui/menu/main-menu.ce +2 -1
  201. package/src/components/gui/save-load.ce +2 -1
  202. package/src/components/gui/shop/shop.ce +3 -2
  203. package/src/components/gui/title-screen.ce +2 -1
  204. package/src/components/index.ts +2 -1
  205. package/src/components/player-components-utils.spec.ts +109 -0
  206. package/src/components/player-components-utils.ts +205 -0
  207. package/src/components/player-components.ce +222 -0
  208. package/src/components/prebuilt/hp-bar.ce +4 -3
  209. package/src/components/prebuilt/light-halo.ce +2 -2
  210. package/src/components/scenes/canvas.ce +175 -8
  211. package/src/components/scenes/draw-map.ce +18 -17
  212. package/src/components/scenes/event-layer.ce +1 -2
  213. package/src/core/setup.ts +2 -2
  214. package/src/decorators/spritesheet.ts +8 -0
  215. package/src/index.ts +4 -0
  216. package/src/module.ts +18 -1
  217. package/src/services/actionInput.spec.ts +101 -0
  218. package/src/services/actionInput.ts +53 -0
  219. package/src/services/loadMap.ts +2 -0
  220. package/src/services/mmorpg-connection.spec.ts +99 -0
  221. package/src/services/mmorpg-connection.ts +69 -0
  222. package/src/services/mmorpg.ts +68 -36
  223. package/src/services/pointerContext.spec.ts +36 -0
  224. package/src/services/pointerContext.ts +84 -0
  225. package/src/services/save.spec.ts +127 -0
  226. package/src/services/standalone-message.ts +7 -0
  227. package/src/services/standalone.spec.ts +34 -0
  228. package/src/services/standalone.ts +3 -2
  229. package/src/utils/getEntityProp.spec.ts +96 -0
  230. package/src/utils/getEntityProp.ts +4 -3
  231. package/src/utils/readPropValue.ts +16 -0
  232. package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
  233. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +0 -457
  234. package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +0 -1
  235. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
  236. package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
  237. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
  238. package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
  239. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
  240. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
  241. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
  242. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
  243. package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
  244. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
  245. package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
@@ -29,10 +29,15 @@
29
29
  </div>
30
30
  </Navigation>
31
31
  }
32
- </div>
32
+ </div>
33
33
  @if (hasFace()) {
34
34
  <div class="rpg-ui-dialog-face">
35
- <DOMSprite sheet={faceSheet(face.id, face.expression)} />
35
+ <DOMSprite
36
+ sheet={faceSheet(dialogFace())}
37
+ width="100%"
38
+ height="100%"
39
+ objectFit="contain"
40
+ />
36
41
  </div>
37
42
  }
38
43
  </div>
@@ -48,6 +53,7 @@
48
53
  import { inject } from "../../../core/inject";
49
54
  import { RpgClientEngine } from "../../../RpgClientEngine";
50
55
  import { delay } from "@rpgjs/common";
56
+ import { getKeyboardControlBind } from "../../../services/actionInput";
51
57
 
52
58
  const engine = inject(RpgClientEngine);
53
59
  const currentPlayer = engine.scene.currentPlayer
@@ -76,7 +82,11 @@
76
82
 
77
83
  const dialogPosition = computed(() => resolveProp(position) || "bottom");
78
84
  const isFullWidth = computed(() => resolveProp(fullWidth) !== false);
79
- const hasFace = computed(() => !!resolveProp(face));
85
+ const dialogFace = computed(() => resolveProp(face));
86
+ const hasFace = computed(() => {
87
+ const value = dialogFace();
88
+ return !!(value && value.id);
89
+ });
80
90
 
81
91
  const displayMessage = signal("");
82
92
  const fullMessage = signal("");
@@ -163,7 +173,7 @@
163
173
  }
164
174
  },
165
175
  action: {
166
- bind: keyboardControls.action,
176
+ bind: getKeyboardControlBind(keyboardControls.action),
167
177
  keyDown() {
168
178
  if (isTyping()) {
169
179
  finishTyping();
@@ -180,7 +190,7 @@
180
190
 
181
191
  const dialogControls = signal({
182
192
  action: {
183
- bind: keyboardControls.action,
193
+ bind: getKeyboardControlBind(keyboardControls.action),
184
194
  keyDown() {
185
195
  if (isTyping()) {
186
196
  finishTyping();
@@ -192,10 +202,10 @@
192
202
  },
193
203
  })
194
204
 
195
- const faceSheet = (graphicId, animationName) => {
205
+ const faceSheet = (faceValue) => {
196
206
  return {
197
- definition: engine.getSpriteSheet(graphicId),
198
- playing: animationName,
207
+ definition: engine.getSpriteSheet(faceValue.id),
208
+ playing: faceValue.expression || "default",
199
209
  };
200
210
  }
201
211
 
@@ -31,6 +31,7 @@
31
31
  import { inject } from "../../core/inject";
32
32
  import { RpgClientEngine } from "../../RpgClientEngine";
33
33
  import { RpgGui } from "../../Gui/Gui";
34
+ import { getKeyboardControlBind } from "../../services/actionInput";
34
35
 
35
36
  const engine = inject(RpgClientEngine);
36
37
  const guiService = inject(RpgGui);
@@ -146,7 +147,7 @@
146
147
  }
147
148
  },
148
149
  action: {
149
- bind: keyboardControls.action,
150
+ bind: getKeyboardControlBind(keyboardControls.action),
150
151
  keyDown() {
151
152
  triggerSelect(selectedEntry());
152
153
  }
@@ -90,6 +90,7 @@
90
90
  import { signal, computed, createTabindexNavigator, effect } from "canvasengine";
91
91
  import { inject } from "../../../core/inject";
92
92
  import { RpgClientEngine } from "../../../RpgClientEngine";
93
+ import { getKeyboardControlBind } from "../../../services/actionInput";
93
94
 
94
95
  const engine = inject(RpgClientEngine);
95
96
  const keyboardControls = engine.globalConfig.keyboardControls;
@@ -380,7 +381,7 @@
380
381
  }
381
382
  },
382
383
  action: {
383
- bind: keyboardControls.action,
384
+ bind: getKeyboardControlBind(keyboardControls.action),
384
385
  keyDown() {
385
386
  if (!listEntries().length) return;
386
387
  commitSelection(selectedItem());
@@ -16,6 +16,7 @@
16
16
  import { signal } from "canvasengine";
17
17
  import { inject } from "../../../core/inject";
18
18
  import { RpgClientEngine } from "../../../RpgClientEngine";
19
+ import { getKeyboardControlBind } from "../../../services/actionInput";
19
20
 
20
21
  const engine = inject(RpgClientEngine);
21
22
  const keyboardControls = engine.globalConfig.keyboardControls;
@@ -23,7 +24,7 @@
23
24
 
24
25
  const controls = signal({
25
26
  action: {
26
- bind: keyboardControls.action,
27
+ bind: getKeyboardControlBind(keyboardControls.action),
27
28
  keyDown() {
28
29
  if (onConfirm) onConfirm();
29
30
  }
@@ -90,6 +90,7 @@
90
90
  import { inject } from "../../../core/inject";
91
91
  import { RpgClientEngine } from "../../../RpgClientEngine";
92
92
  import { delay } from "@rpgjs/common";
93
+ import { getKeyboardControlBind } from "../../../services/actionInput";
93
94
 
94
95
  const engine = inject(RpgClientEngine);
95
96
  const keyboardControls = engine.globalConfig.keyboardControls;
@@ -226,7 +227,7 @@
226
227
  }
227
228
  },
228
229
  action: {
229
- bind: keyboardControls.action,
230
+ bind: getKeyboardControlBind(keyboardControls.action),
230
231
  keyDown() {
231
232
  if (!confirmOpen()) return;
232
233
  confirmSelect(selectedConfirm())();
@@ -289,7 +290,7 @@
289
290
  }
290
291
  },
291
292
  action: {
292
- bind: keyboardControls.action,
293
+ bind: getKeyboardControlBind(keyboardControls.action),
293
294
  keyDown() {
294
295
  if (confirmOpen()) {
295
296
  confirmSelect(selectedConfirm())();
@@ -101,6 +101,7 @@
101
101
  import ExitMenu from "./exit-menu.ce";
102
102
  import { getEntityProp } from "../../../utils/getEntityProp";
103
103
  import { delay } from "@rpgjs/common";
104
+ import { getKeyboardControlBind } from "../../../services/actionInput";
104
105
 
105
106
  const engine = inject(RpgClientEngine);
106
107
  const currentPlayer = engine.scene.currentPlayer;
@@ -259,7 +260,7 @@
259
260
  }
260
261
  },
261
262
  action: {
262
- bind: keyboardControls.action,
263
+ bind: getKeyboardControlBind(keyboardControls.action),
263
264
  keyDown() {
264
265
  if (saveOverlay()) return;
265
266
  if (view() !== "menu") return;
@@ -40,6 +40,7 @@
40
40
  import { SaveClientService } from "../../services/save";
41
41
  import { PrebuiltGui } from "@rpgjs/common";
42
42
  import { RpgGui } from "../../Gui/Gui";
43
+ import { getKeyboardControlBind } from "../../services/actionInput";
43
44
 
44
45
  const engine = inject(RpgClientEngine);
45
46
  const saveClient = inject(SaveClientService);
@@ -185,7 +186,7 @@
185
186
  }
186
187
  },
187
188
  action: {
188
- bind: keyboardControls.action,
189
+ bind: getKeyboardControlBind(keyboardControls.action),
189
190
  keyDown() {
190
191
  triggerSelect(selectedSlot());
191
192
  }
@@ -180,6 +180,7 @@
180
180
  import { mount, signal, computed, createTabindexNavigator, effect } from "canvasengine";
181
181
  import { inject } from "../../../core/inject";
182
182
  import { RpgClientEngine } from "../../../RpgClientEngine";
183
+ import { getKeyboardControlBind } from "../../../services/actionInput";
183
184
 
184
185
  const engine = inject(RpgClientEngine)
185
186
  const currentPlayer = engine.scene.currentPlayer
@@ -387,7 +388,7 @@
387
388
  }
388
389
  },
389
390
  action: {
390
- bind: keyboardControls.action,
391
+ bind: getKeyboardControlBind(keyboardControls.action),
391
392
  keyDown() {
392
393
  const mode = selectedModeIndex() === 0 ? 'buy' : 'sell'
393
394
  tradeMode.set(mode)
@@ -433,7 +434,7 @@
433
434
  }
434
435
  },
435
436
  action: {
436
- bind: keyboardControls.action,
437
+ bind: getKeyboardControlBind(keyboardControls.action),
437
438
  keyDown() {
438
439
  if (quantityDialogOpen()) {
439
440
  const item = currentItem()
@@ -31,6 +31,7 @@
31
31
  import { inject } from "../../core/inject";
32
32
  import { RpgClientEngine } from "../../RpgClientEngine";
33
33
  import { RpgGui } from "../../Gui/Gui";
34
+ import { getKeyboardControlBind } from "../../services/actionInput";
34
35
 
35
36
  const engine = inject(RpgClientEngine);
36
37
  const guiService = inject(RpgGui);
@@ -150,7 +151,7 @@
150
151
  }
151
152
  },
152
153
  action: {
153
- bind: keyboardControls.action,
154
+ bind: getKeyboardControlBind(keyboardControls.action),
154
155
  keyDown() {
155
156
  if (guiService.isDisplaying(PrebuiltGui.Save)) return;
156
157
  triggerSelect(selectedEntry());
@@ -1,7 +1,8 @@
1
1
  import EventLayerComponent from "./scenes/event-layer.ce";
2
+ import SceneMap from "./scenes/draw-map.ce";
2
3
  import CharacterComponent from "./character.ce";
3
4
 
4
5
  // Prebuilt sprite components
5
6
  export { HpBar } from "./prebuilt";
6
7
 
7
- export { EventLayerComponent, CharacterComponent }
8
+ export { EventLayerComponent, SceneMap, CharacterComponent }
@@ -0,0 +1,109 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import {
3
+ computeBlockPosition,
4
+ computeBlockSize,
5
+ estimateComponentSize,
6
+ getComponentProps
7
+ } from "./player-components-utils";
8
+
9
+ const graphic = {
10
+ left: -8,
11
+ top: -24,
12
+ right: 40,
13
+ bottom: 48,
14
+ width: 48,
15
+ height: 72,
16
+ centerX: 16,
17
+ centerY: 12
18
+ };
19
+
20
+ describe("player component layout utilities", () => {
21
+ test("bottom layout uses the hitbox as positioning rectangle", () => {
22
+ const rowMetrics = [
23
+ {
24
+ cells: [{ width: 32, height: 32 }],
25
+ width: 32,
26
+ height: 32
27
+ }
28
+ ];
29
+
30
+ const size = computeBlockSize({
31
+ position: "bottom",
32
+ rowMetrics,
33
+ graphic,
34
+ hitbox: { w: 32, h: 32 }
35
+ });
36
+
37
+ expect(size).toEqual({ width: 32, height: 32 });
38
+ expect(
39
+ computeBlockPosition({
40
+ position: "bottom",
41
+ size,
42
+ graphic,
43
+ hitbox: { w: 32, h: 32 }
44
+ })
45
+ ).toEqual({ x: 0, y: 0 });
46
+ });
47
+
48
+ test("bottom layout centers smaller content in the hitbox rectangle", () => {
49
+ const size = computeBlockSize({
50
+ position: "bottom",
51
+ rowMetrics: [
52
+ {
53
+ cells: [{ width: 16, height: 8 }],
54
+ width: 16,
55
+ height: 8
56
+ }
57
+ ],
58
+ graphic,
59
+ hitbox: { w: 32, h: 32 }
60
+ });
61
+
62
+ expect(size).toEqual({ width: 32, height: 32 });
63
+ expect(
64
+ computeBlockPosition({
65
+ position: "bottom",
66
+ size,
67
+ graphic,
68
+ hitbox: { w: 32, h: 32 }
69
+ })
70
+ ).toEqual({ x: 0, y: 0 });
71
+ });
72
+
73
+ test("bottom margins move from the hitbox-centered position", () => {
74
+ const position = computeBlockPosition({
75
+ position: "bottom",
76
+ size: { width: 32, height: 32 },
77
+ layout: { marginLeft: 3, marginRight: 1, marginBottom: 16, marginTop: 4 },
78
+ graphic,
79
+ hitbox: { w: 32, h: 32 }
80
+ });
81
+
82
+ expect(position).toEqual({ x: 2, y: 12 });
83
+ });
84
+
85
+ test("top layout remains anchored above graphic bounds", () => {
86
+ const position = computeBlockPosition({
87
+ position: "top",
88
+ size: { width: 50, height: 12 },
89
+ graphic,
90
+ hitbox: { w: 32, h: 32 }
91
+ });
92
+
93
+ expect(position).toEqual({ x: -9, y: -36 });
94
+ });
95
+
96
+ test("estimates bar labels, shape aliases, circles, lines and polygons", () => {
97
+ expect(estimateComponentSize({ type: "hpBar", style: { width: 60, height: 6 }, text: "{$percent}%" })).toEqual({ width: 60, height: 18 });
98
+ expect(estimateComponentSize({ type: "shape", value: { type: "rect", width: 32, height: 24 } })).toEqual({ width: 32, height: 24 });
99
+ expect(estimateComponentSize({ type: "shape", value: { type: "circle", radius: 7 } })).toEqual({ width: 14, height: 14 });
100
+ expect(estimateComponentSize({ type: "shape", value: { type: "line", x1: -4, y1: 2, x2: 6, y2: 2 } })).toEqual({ width: 10, height: 1 });
101
+ expect(estimateComponentSize({ type: "shape", value: { type: "polygon", points: [-5, 2, 15, 2, 5, 12] } })).toEqual({ width: 20, height: 10 });
102
+ });
103
+
104
+ test("legacy hp and sp bars receive default fill colors", () => {
105
+ expect(getComponentProps({ type: "hpBar" }).style.fillColor).toBe("#ef4444");
106
+ expect(getComponentProps({ type: "spBar" }).style.fillColor).toBe("#3b82f6");
107
+ expect(getComponentProps({ type: "hpBar", style: { fillColor: "#111111" } }).style.fillColor).toBe("#111111");
108
+ });
109
+ });
@@ -0,0 +1,205 @@
1
+ export const DEFAULT_HP_BAR_STYLE = { fillColor: '#ef4444' };
2
+ export const DEFAULT_SP_BAR_STYLE = { fillColor: '#3b82f6' };
3
+
4
+ const DEFAULT_CELL_HEIGHT = 16;
5
+ const DEFAULT_CELL_WIDTH = 32;
6
+
7
+ type NumberResolver = (value: any, fallback?: number) => number;
8
+
9
+ const defaultToNumber: NumberResolver = (value, fallback = 0) => {
10
+ const num = typeof value === 'number' ? value : parseFloat(value);
11
+ return Number.isFinite(num) ? num : fallback;
12
+ };
13
+
14
+ export function getPointBounds(points: any[] = [], toNumber: NumberResolver = defaultToNumber) {
15
+ if (!Array.isArray(points) || points.length < 2) {
16
+ return { width: 1, height: 1 };
17
+ }
18
+
19
+ let minX = Infinity;
20
+ let minY = Infinity;
21
+ let maxX = -Infinity;
22
+ let maxY = -Infinity;
23
+
24
+ for (let i = 0; i < points.length; i += 2) {
25
+ const x = toNumber(points[i], 0);
26
+ const y = toNumber(points[i + 1], 0);
27
+ minX = Math.min(minX, x);
28
+ minY = Math.min(minY, y);
29
+ maxX = Math.max(maxX, x);
30
+ maxY = Math.max(maxY, y);
31
+ }
32
+
33
+ return {
34
+ width: Math.max(1, maxX - minX),
35
+ height: Math.max(1, maxY - minY)
36
+ };
37
+ }
38
+
39
+ export function getComponentId(definition: any) {
40
+ if (!definition) return undefined;
41
+ if (definition.id) return definition.id;
42
+ if (definition.type === 'text') return 'rpg:text';
43
+ if (definition.type === 'hpBar') return 'rpg:hpBar';
44
+ if (definition.type === 'spBar') return 'rpg:spBar';
45
+ if (definition.type === 'bar') return 'rpg:bar';
46
+ if (definition.type === 'shape') return 'rpg:shape';
47
+ if (definition.type === 'image') return 'rpg:image';
48
+ if (definition.type === 'tile') return 'rpg:tile';
49
+ return definition.type;
50
+ }
51
+
52
+ export function getComponentProps(definition: any) {
53
+ if (definition.props) return definition.props;
54
+
55
+ if (definition.type === 'text') {
56
+ return { value: definition.value, style: definition.style };
57
+ }
58
+ if (definition.type === 'hpBar') {
59
+ return { current: '{hp}', max: '{param.maxHp}', style: { ...DEFAULT_HP_BAR_STYLE, ...definition.style }, text: definition.text };
60
+ }
61
+ if (definition.type === 'spBar') {
62
+ return { current: '{sp}', max: '{param.maxSp}', style: { ...DEFAULT_SP_BAR_STYLE, ...definition.style }, text: definition.text };
63
+ }
64
+ if (definition.type === 'bar') {
65
+ return { current: `{${definition.current}}`, max: `{${definition.max}}`, style: definition.style, text: definition.text };
66
+ }
67
+ if (definition.type === 'shape') {
68
+ return definition.value;
69
+ }
70
+ if (definition.type === 'image' || definition.type === 'tile') {
71
+ return { value: definition.value };
72
+ }
73
+
74
+ return {};
75
+ }
76
+
77
+ export function estimateComponentSize(
78
+ definition: any,
79
+ {
80
+ toNumber = defaultToNumber,
81
+ estimateTextWidth = (value: any) => String(value ?? '').length * 8
82
+ }: {
83
+ toNumber?: NumberResolver;
84
+ estimateTextWidth?: (value: any, style?: any) => number;
85
+ } = {}
86
+ ) {
87
+ const props = getComponentProps(definition);
88
+ const style = props?.style ?? definition?.style ?? {};
89
+
90
+ if (definition?.type === 'text' || definition?.id === 'rpg:text') {
91
+ return {
92
+ width: estimateTextWidth(props.value ?? definition.value, style),
93
+ height: toNumber(style.fontSize, 12)
94
+ };
95
+ }
96
+
97
+ if (definition?.type === 'hpBar' || definition?.type === 'spBar' || definition?.type === 'bar' || definition?.id === 'rpg:hpBar' || definition?.id === 'rpg:spBar' || definition?.id === 'rpg:bar') {
98
+ const barHeight = toNumber(style.height, 8);
99
+ const labelHeight = props.text != null && props.text !== '' ? toNumber(style.fontSize, 10) + 2 : 0;
100
+ return {
101
+ width: toNumber(style.width, 50),
102
+ height: barHeight + labelHeight
103
+ };
104
+ }
105
+
106
+ if (definition?.type === 'shape' || definition?.id === 'rpg:shape') {
107
+ const shape = props ?? {};
108
+ if (shape.type === 'circle') {
109
+ const radius = toNumber(shape.radius, 8);
110
+ return { width: radius * 2, height: radius * 2 };
111
+ }
112
+ if (shape.type === 'line') {
113
+ return {
114
+ width: Math.max(1, Math.abs(toNumber(shape.x2, 16) - toNumber(shape.x1, 0))),
115
+ height: Math.max(1, Math.abs(toNumber(shape.y2, 0) - toNumber(shape.y1, 0)))
116
+ };
117
+ }
118
+ if (shape.type === 'polygon') {
119
+ return getPointBounds(shape.points, toNumber);
120
+ }
121
+ return {
122
+ width: toNumber(shape.width, 16),
123
+ height: toNumber(shape.height, 16)
124
+ };
125
+ }
126
+
127
+ return {
128
+ width: toNumber(definition?.props?.width, DEFAULT_CELL_WIDTH),
129
+ height: toNumber(definition?.props?.height, DEFAULT_CELL_HEIGHT)
130
+ };
131
+ }
132
+
133
+ export function computeBlockSize({
134
+ position,
135
+ layout = {},
136
+ rowMetrics,
137
+ gap = { row: 0, column: 0 },
138
+ graphic,
139
+ hitbox
140
+ }: {
141
+ position: string;
142
+ layout?: any;
143
+ rowMetrics: Array<{ width: number; height: number; cells: any[] }>;
144
+ gap?: { row: number; column: number };
145
+ graphic: { width: number; height: number };
146
+ hitbox: { w: number; h: number };
147
+ }) {
148
+ const rowGapTotal = Math.max(0, rowMetrics.length - 1) * gap.row;
149
+ const maxColumns = rowMetrics.reduce((max, row) => Math.max(max, row.cells.length), 0);
150
+ const columnGapTotal = Math.max(0, maxColumns - 1) * gap.column;
151
+ const contentWidth = rowMetrics.reduce((max, row) => Math.max(max, row.width), 0) + columnGapTotal;
152
+ const contentHeight = rowMetrics.reduce((sum, row) => sum + row.height, 0) + rowGapTotal;
153
+ const width = layout.width ?? Math.max(contentWidth, position === 'bottom' ? hitbox.w : position === 'top' || position === 'center' ? graphic.width : contentWidth);
154
+ const height = layout.height ?? Math.max(contentHeight, position === 'bottom' ? hitbox.h : position === 'left' || position === 'right' || position === 'center' ? graphic.height : contentHeight);
155
+
156
+ return { width, height };
157
+ }
158
+
159
+ export function computeBlockPosition({
160
+ position,
161
+ size,
162
+ layout = {},
163
+ graphic,
164
+ hitbox
165
+ }: {
166
+ position: string;
167
+ size: { width: number; height: number };
168
+ layout?: any;
169
+ graphic: { left: number; top: number; right: number; centerX: number; centerY: number };
170
+ hitbox: { w: number; h: number };
171
+ }) {
172
+ const marginLeft = layout.marginLeft ?? 0;
173
+ const marginRight = layout.marginRight ?? 0;
174
+ const marginTop = layout.marginTop ?? 0;
175
+ const marginBottom = layout.marginBottom ?? 0;
176
+
177
+ switch (position) {
178
+ case 'bottom':
179
+ return {
180
+ x: (hitbox.w / 2) - (size.width / 2) + marginLeft - marginRight,
181
+ y: (hitbox.h / 2) - (size.height / 2) + marginBottom - marginTop
182
+ };
183
+ case 'center':
184
+ return {
185
+ x: graphic.centerX - (size.width / 2) + marginLeft - marginRight,
186
+ y: graphic.centerY - (size.height / 2) + marginTop - marginBottom
187
+ };
188
+ case 'left':
189
+ return {
190
+ x: graphic.left - size.width + marginLeft - marginRight,
191
+ y: graphic.centerY - (size.height / 2) + marginTop - marginBottom
192
+ };
193
+ case 'right':
194
+ return {
195
+ x: graphic.right + marginLeft - marginRight,
196
+ y: graphic.centerY - (size.height / 2) + marginTop - marginBottom
197
+ };
198
+ case 'top':
199
+ default:
200
+ return {
201
+ x: graphic.centerX - (size.width / 2) + marginLeft - marginRight,
202
+ y: graphic.top - size.height + marginTop - marginBottom
203
+ };
204
+ }
205
+ }