@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
@@ -0,0 +1,222 @@
1
+ <Container
2
+ x={blockPosition().x}
3
+ y={blockPosition().y}
4
+ width={blockSize().width}
5
+ height={blockSize().height}
6
+ minWidth={blockSize().width}
7
+ minHeight={blockSize().height}
8
+ display="flex"
9
+ flexDirection="column"
10
+ justifyContent="center"
11
+ alignItems="center"
12
+ rowGap={gap().row}
13
+ >
14
+ @for (row of renderedRows) {
15
+ <Container
16
+ width="100%"
17
+ height={row.height}
18
+ display="flex"
19
+ flexDirection="row"
20
+ justifyContent="center"
21
+ alignItems="center"
22
+ columnGap={gap().column}
23
+ >
24
+ @for (entry of row.entries) {
25
+ <Container
26
+ width={entry.width}
27
+ height={entry.height}
28
+ display="flex"
29
+ justifyContent="center"
30
+ alignItems="center"
31
+ >
32
+ <entry.component object={sprite} ...entry.props />
33
+ </Container>
34
+ }
35
+ </Container>
36
+ }
37
+ </Container>
38
+
39
+ <script>
40
+ import { computed } from "canvasengine";
41
+ import { RpgClientEngine } from "../RpgClientEngine";
42
+ import { inject } from "../core/inject";
43
+ import { resolveDynamicProps, resolveDynamicValue } from "./dynamics/parse-value";
44
+ import {
45
+ computeBlockPosition,
46
+ computeBlockSize,
47
+ estimateComponentSize,
48
+ getComponentId,
49
+ getComponentProps
50
+ } from "./player-components-utils";
51
+
52
+ const { object, position, graphicBounds } = defineProps({
53
+ position: {
54
+ default: 'top'
55
+ }
56
+ });
57
+ const sprite = object();
58
+
59
+ const client = inject(RpgClientEngine);
60
+ const warnedComponents = new Set();
61
+
62
+ const readPosition = computed(() => position?.() ?? 'top');
63
+
64
+ const componentSource = computed(() => {
65
+ switch (readPosition()) {
66
+ case 'bottom':
67
+ return sprite.componentsBottom?.();
68
+ case 'center':
69
+ return sprite.componentsCenter?.();
70
+ case 'left':
71
+ return sprite.componentsLeft?.();
72
+ case 'right':
73
+ return sprite.componentsRight?.();
74
+ case 'top':
75
+ default:
76
+ return sprite.componentsTop?.();
77
+ }
78
+ });
79
+
80
+ const componentData = computed(() => {
81
+ const value = componentSource();
82
+ if (!value) {
83
+ return { components: [], layout: {} };
84
+ }
85
+
86
+ if (typeof value !== 'string') {
87
+ return value;
88
+ }
89
+
90
+ try {
91
+ return JSON.parse(value);
92
+ } catch (error) {
93
+ if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {
94
+ console.warn('[RPGJS] Invalid server sprite component payload', error);
95
+ }
96
+ return { components: [], layout: {} };
97
+ }
98
+ });
99
+
100
+ const layout = computed(() => componentData()?.layout ?? {});
101
+ const rows = computed(() => componentData()?.components ?? []);
102
+ const hitbox = sprite.hitbox;
103
+
104
+ const toNumber = (value, fallback = 0) => {
105
+ const resolved = resolveDynamicValue(value, sprite);
106
+ const num = typeof resolved === 'number' ? resolved : parseFloat(resolved);
107
+ return Number.isFinite(num) ? num : fallback;
108
+ };
109
+
110
+ const estimateTextWidth = (value, style = {}) => {
111
+ const text = String(resolveDynamicValue(value ?? '', sprite) ?? '');
112
+ const fontSize = toNumber(style.fontSize, 12);
113
+ return Math.max(1, Math.ceil(text.length * fontSize * 0.6));
114
+ };
115
+
116
+ const rowMetrics = computed(() => {
117
+ return rows().map((row) => {
118
+ const cells = row.map((definition) => {
119
+ const intrinsic = estimateComponentSize(definition, { toNumber, estimateTextWidth });
120
+ return {
121
+ definition,
122
+ width: intrinsic.width,
123
+ height: intrinsic.height
124
+ };
125
+ });
126
+
127
+ return {
128
+ cells,
129
+ width: cells.reduce((sum, cell) => sum + cell.width, 0),
130
+ height: cells.reduce((max, cell) => Math.max(max, cell.height), 0)
131
+ };
132
+ });
133
+ });
134
+
135
+ const gap = computed(() => ({
136
+ row: toNumber(layout().rowGap ?? layout().gap, 0),
137
+ column: toNumber(layout().columnGap ?? layout().gap, 0)
138
+ }));
139
+
140
+ const fallbackBounds = computed(() => {
141
+ const box = hitbox();
142
+ const width = box?.w ?? 0;
143
+ const height = box?.h ?? 0;
144
+
145
+ return {
146
+ left: 0,
147
+ top: 0,
148
+ right: width,
149
+ bottom: height,
150
+ width,
151
+ height,
152
+ centerX: width / 2,
153
+ centerY: height / 2
154
+ };
155
+ });
156
+
157
+ const bounds = computed(() => {
158
+ const resolvedBounds = typeof graphicBounds === 'function' ? graphicBounds() : undefined;
159
+ return resolvedBounds ?? fallbackBounds();
160
+ });
161
+
162
+ const blockSize = computed(() => {
163
+ const box = hitbox() ?? { w: 0, h: 0 };
164
+ const graphic = bounds();
165
+ return computeBlockSize({
166
+ position: readPosition(),
167
+ layout: layout(),
168
+ rowMetrics: rowMetrics(),
169
+ gap: gap(),
170
+ graphic,
171
+ hitbox: box
172
+ });
173
+ });
174
+
175
+ const blockPosition = computed(() => {
176
+ const box = hitbox() ?? { w: 0, h: 0 };
177
+ const graphic = bounds();
178
+ return computeBlockPosition({
179
+ position: readPosition(),
180
+ size: blockSize(),
181
+ layout: layout(),
182
+ graphic,
183
+ hitbox: box
184
+ });
185
+ });
186
+
187
+ const warnMissingComponent = (id) => {
188
+ if (!id || warnedComponents.has(id)) return;
189
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') return;
190
+ warnedComponents.add(id);
191
+ console.warn(`[RPGJS] Server sprite component "${id}" is not registered on the client.`);
192
+ };
193
+
194
+ const renderedRows = computed(() => {
195
+ return rowMetrics().map((row) => {
196
+ const entries = [];
197
+
198
+ row.cells.forEach((cell) => {
199
+ const definition = cell.definition;
200
+ const id = getComponentId(definition);
201
+ const component = client.getSpriteComponent(id);
202
+
203
+ if (!component) {
204
+ warnMissingComponent(id);
205
+ return;
206
+ }
207
+
208
+ entries.push({
209
+ component,
210
+ props: resolveDynamicProps(getComponentProps(definition), sprite),
211
+ width: cell.width,
212
+ height: cell.height
213
+ });
214
+ });
215
+
216
+ return {
217
+ height: row.height,
218
+ entries
219
+ };
220
+ });
221
+ });
222
+ </script>
@@ -46,6 +46,7 @@
46
46
  import { computed, animatedSignal, effect } from "canvasengine";
47
47
 
48
48
  const { object } = defineProps();
49
+ const sprite = object();
49
50
 
50
51
  // ====================
51
52
  // Configuration
@@ -93,14 +94,14 @@ const highlightHeight = Math.floor(fillHeight / 2);
93
94
  // ====================
94
95
 
95
96
  /** Gets hitbox dimensions for positioning */
96
- const hitbox = object.hitbox;
97
+ const hitbox = sprite.hitbox;
97
98
 
98
99
  /**
99
100
  * Gets the current HP value from the player object
100
101
  * Uses hpSignal which is synchronized from the server
101
102
  */
102
103
  const currentHp = computed(() => {
103
- return object.hpSignal?.() ?? 0;
104
+ return sprite.hpSignal?.() ?? 0;
104
105
  });
105
106
 
106
107
  /**
@@ -108,7 +109,7 @@ const currentHp = computed(() => {
108
109
  * Reads from _param.maxHp which contains calculated stats
109
110
  */
110
111
  const maxHp = computed(() => {
111
- const params = object._param?.() ?? {};
112
+ const params = sprite._param?.() ?? {};
112
113
  return params.maxHp ?? 100;
113
114
  });
114
115
 
@@ -31,6 +31,7 @@ const {
31
31
  opacitySpeed,
32
32
  lightColor
33
33
  } = defineProps();
34
+ const sprite = object();
34
35
 
35
36
  // ====================
36
37
  // Props with default values
@@ -92,7 +93,7 @@ const currentOpacity = computed(() => {
92
93
  // Position calculations
93
94
  // ====================
94
95
 
95
- const hitbox = object.hitbox;
96
+ const hitbox = sprite.hitbox;
96
97
 
97
98
  const position = computed(() => ({
98
99
  x: hitbox().w / 2,
@@ -145,4 +146,3 @@ tick(() => {
145
146
  time.update(t => t + 1);
146
147
  });
147
148
  </script>
148
-
@@ -1,11 +1,37 @@
1
1
  <Canvas width={engine.width} height={engine.height}>
2
2
  <Viewport worldWidth worldHeight clamp sortableChildren={true}>
3
- @if (sceneData) {
4
- <SceneMap />
3
+ @if (lightingAmbientEnabled()) {
4
+ <NightAmbiant
5
+ spots={nightLights}
6
+ darkness={lightingDarknessProps}
7
+ haze={lightingHaze}
8
+ />
5
9
  }
10
+ @if (shadowEnabled()) {
11
+ <SpriteShadows
12
+ lights={shadowLights}
13
+ ambientLight={shadowAmbientLight}
14
+ minInfluence={shadowMinInfluence}
15
+ falloffPower={shadowFalloffPower}
16
+ mode={shadowMode}
17
+ updateHz={shadowUpdateHz}
18
+ scanHz={shadowScanHz}
19
+ cullToViewport={shadowCullToViewport}
20
+ shadowColor={shadowColor}
21
+ />
22
+ }
23
+ <SceneMap />
6
24
  </Viewport>
7
25
  @for (gui of guiList) {
8
- <Container display="flex">
26
+ <Container
27
+ positionType="absolute"
28
+ top={0}
29
+ left={0}
30
+ right={0}
31
+ bottom={0}
32
+ width={engine.width}
33
+ height={engine.height}
34
+ >
9
35
  @if (gui.display) {
10
36
  <gui.component data={gui.data} dependencies={gui.dependencies} onFinish={(data) => {
11
37
  onGuiFinish(gui, data)
@@ -18,21 +44,31 @@
18
44
  </Canvas>
19
45
 
20
46
  <script>
21
- import { signal, computed, effect } from "canvasengine";
47
+ import { computed, effect } from "canvasengine";
22
48
  import { inject } from "../../core/inject";
23
49
  import { RpgClientEngine } from "../../RpgClientEngine";
24
- import SceneMap from './draw-map.ce'
25
50
  import { RpgGui } from "../../Gui/Gui";
26
51
  import { delay } from "@rpgjs/common";
52
+ import { NightAmbiant, SpriteShadows } from '@canvasengine/presets'
27
53
 
28
54
  const engine = inject(RpgClientEngine);
55
+ const SceneMap = engine.sceneMapComponent;
29
56
  const guiService = inject(RpgGui);
30
57
  const sceneData = engine.sceneMap.data
58
+ const lighting = engine.sceneMap.lighting
31
59
  const guiList = computed(() => {
32
60
  return Object.values(guiService.gui()).filter((gui) => !gui.attachToSprite)
33
61
  })
34
- const worldWidth = computed(() => sceneData()?.width)
35
- const worldHeight = computed(() => sceneData()?.height)
62
+ const worldWidth = computed(() => {
63
+ const data = sceneData?.()
64
+ const scale = Number(data?.params?.scale ?? 1) || 1
65
+ return Number(data?.width ?? data?.params?.width ?? 2048) * scale
66
+ })
67
+ const worldHeight = computed(() => {
68
+ const data = sceneData?.()
69
+ const scale = Number(data?.params?.scale ?? 1) || 1
70
+ return Number(data?.height ?? data?.params?.height ?? 2048) * scale
71
+ })
36
72
 
37
73
  effect(() => {
38
74
  if (sceneData() && !sceneData().component) {
@@ -49,8 +85,139 @@
49
85
  const onGuiInteraction = (gui, name, data) => {
50
86
  guiService.guiInteraction(gui.name, name, data)
51
87
  }
52
-
88
+
53
89
  const clamp = {
54
90
  direction: "all"
55
91
  }
92
+ const NIGHT_SPOT_RADIUS_SCALE = 4.25
93
+ const NIGHT_SPOT_MIN_RADIUS = 170
94
+ const NIGHT_SPOT_MIN_INTENSITY = 1
95
+ const SHADOW_SPOT_RADIUS_SCALE = 12
96
+ const SHADOW_SPOT_MIN_RADIUS = 480
97
+ const SHADOW_SPOT_MIN_INTENSITY = 1.35
98
+
99
+ const nightSpotRadius = (radius) => Math.max(radius * NIGHT_SPOT_RADIUS_SCALE, NIGHT_SPOT_MIN_RADIUS)
100
+ const shadowSpotRadius = (radius) => Math.max(radius * SHADOW_SPOT_RADIUS_SCALE, SHADOW_SPOT_MIN_RADIUS)
101
+ const nightSpotIntensity = (intensity, fallback) => Math.max(intensity ?? fallback, NIGHT_SPOT_MIN_INTENSITY)
102
+ const shadowSpotIntensity = (intensity) => Math.max(intensity ?? 1.3, SHADOW_SPOT_MIN_INTENSITY)
103
+
104
+ const lightingAmbient = computed(() => {
105
+ const state = lighting?.()
106
+ return state?.ambient ?? {}
107
+ })
108
+
109
+ const nightLights = computed(() => {
110
+ const state = lighting?.()
111
+ return (state?.spots ?? []).map((spot, index) => {
112
+ const radius = spot.radius ?? 180
113
+ return {
114
+ x: spot.x,
115
+ y: spot.y,
116
+ radius: nightSpotRadius(radius),
117
+ intensity: nightSpotIntensity(spot.intensity, index === 0 ? 1 : 0.92),
118
+ flicker: spot.flicker,
119
+ flickerSpeed: spot.flickerSpeed ?? 14,
120
+ pulse: spot.pulse,
121
+ pulseSpeed: spot.pulseSpeed,
122
+ phase: spot.phase,
123
+ }
124
+ })
125
+ })
126
+ const hasLightSpots = computed(() => {
127
+ const state = lighting?.()
128
+ return (state?.spots?.length ?? 0) > 0
129
+ })
130
+
131
+ const lightingDarkness = computed(() => {
132
+ const darkness = lightingAmbient().darkness
133
+ return typeof darkness === "number" ? darkness : 0
134
+ })
135
+
136
+ const lightingAmbientEnabled = computed(() => {
137
+ const state = lighting?.()
138
+ return Boolean(state && lightingDarkness() > 0)
139
+ })
140
+
141
+ const lightingDarknessProps = computed(() => ({
142
+ opacity: lightingDarkness(),
143
+ color: lightingAmbient().darkColor ?? "#080a12",
144
+ }))
145
+ const lightingHaze = computed(() => ({
146
+ color: lightingAmbient().fogColor ?? "#12182a",
147
+ radius: lightingAmbient().fogRadius ?? 0.44,
148
+ softness: lightingAmbient().fogSoftness ?? 0.3,
149
+ opacity: lightingAmbient().fogOpacity ?? 0.35,
150
+ }))
151
+
152
+ const defaultSunLight = () => {
153
+ const data = sceneData?.()
154
+ const width = Number(data?.width ?? data?.params?.width ?? 2048)
155
+ const height = Number(data?.height ?? data?.params?.height ?? 2048)
156
+ const scale = Number(data?.params?.scale ?? 1) || 1
157
+ const mapWidth = width * scale
158
+ const mapHeight = height * scale
159
+
160
+ return {
161
+ x: -mapWidth * 0.35,
162
+ y: -mapHeight * 0.45,
163
+ z: 520,
164
+ radius: Math.max(mapWidth, mapHeight) * 2.5,
165
+ intensity: 0.85,
166
+ shadowWeight: lightingDarkness() > 0 ? 2.2 : 1,
167
+ enabled: true,
168
+ }
169
+ }
170
+
171
+ const shadowState = computed(() => {
172
+ const state = lighting?.()
173
+ return state?.shadows ?? null
174
+ })
175
+
176
+ const shadowLights = computed(() => {
177
+ const state = lighting?.()
178
+ const defaultSun = defaultSunLight()
179
+ const sun = {
180
+ ...defaultSun,
181
+ ...(state?.sun ?? {}),
182
+ shadowWeight: state?.sun?.shadowWeight ?? defaultSun.shadowWeight,
183
+ }
184
+ const spotLights = (state?.spots ?? []).map((spot) => {
185
+ const radius = spot.radius ?? 180
186
+ return {
187
+ x: spot.x,
188
+ y: spot.y,
189
+ z: 170,
190
+ radius: shadowSpotRadius(radius),
191
+ intensity: shadowSpotIntensity(spot.intensity),
192
+ shadowWeight: 2.4,
193
+ enabled: true,
194
+ }
195
+ })
196
+
197
+ return [
198
+ ...((sun.enabled === false || sun.intensity <= 0) ? [] : [sun]),
199
+ ...spotLights,
200
+ ]
201
+ })
202
+
203
+ const shadowAmbientLight = computed(() => {
204
+ const shadows = shadowState()
205
+ if (shadows?.ambientLight === null || shadows?.ambientLight?.enabled === false) {
206
+ return null
207
+ }
208
+ return shadows?.ambientLight ?? { x: -0.18, y: -1, z: 420, intensity: 0.32, shadowWeight: 1 }
209
+ })
210
+
211
+ const shadowEnabled = computed(() => {
212
+ const shadows = shadowState()
213
+ return Boolean((shadows?.enabled || hasLightSpots()) && (shadowLights().length > 0 || shadowAmbientLight()))
214
+ })
215
+
216
+ const shadowMode = computed(() => shadowState()?.mode ?? "strongest")
217
+ const shadowUpdateHz = computed(() => shadowState()?.updateHz ?? 60)
218
+ const shadowScanHz = computed(() => shadowState()?.scanHz ?? 8)
219
+ const shadowCullToViewport = computed(() => shadowState()?.cullToViewport ?? true)
220
+ const shadowMinInfluence = computed(() => shadowState()?.minInfluence ?? 0.24)
221
+ const shadowFalloffPower = computed(() => shadowState()?.falloffPower ?? 0.85)
222
+ const shadowColor = computed(() => shadowState()?.shadowColor ?? "#05070d")
56
223
  </script>
@@ -1,8 +1,14 @@
1
1
  <Container sound={backgroundMusic} shake={shakeConfig} freeze={engine.gamePause}>
2
2
  <Container sound={backgroundAmbientSound} />
3
3
 
4
- @if (map() && sceneComponent()) {
5
- <sceneComponent() data={map().data} params={map().params} />
4
+ <Container>
5
+ @if (map() && sceneComponent()) {
6
+ <sceneComponent() data={map().data} params={map().params} />
7
+ }
8
+ </Container>
9
+
10
+ @for (child of children) {
11
+ <child />
6
12
  }
7
13
 
8
14
  @for (componentAnimation of componentAnimations) {
@@ -13,39 +19,34 @@
13
19
  </Container>
14
20
  }
15
21
 
16
- @if (weatherProps) {
22
+ <Container sortableChildren={true}>
23
+ @for (projectile of projectiles() ; track projectile.props.id) {
24
+ <projectile.component ...projectile.props />
25
+ }
26
+ </Container>
27
+
28
+ @if (weatherProps()) {
17
29
  <Weather ...weatherProps() />
18
30
  }
19
31
  </Container>
20
32
 
21
33
  <script>
22
- import { NoiseFilter } from 'pixi-filters';
23
- import { effect, signal, computed, mount, on, tick } from 'canvasengine'
34
+ import { computed, effect } from 'canvasengine'
24
35
  import { inject } from "../../core/inject";
25
36
  import { RpgClientEngine } from "../../RpgClientEngine";
26
37
  import { Weather } from '@canvasengine/presets'
27
38
 
39
+ const { children } = defineProps()
28
40
  const engine = inject(RpgClientEngine);
29
41
  const componentAnimations = engine.componentAnimations
42
+ const projectiles = engine.projectiles.current
30
43
  const map = engine.sceneMap?.data
31
44
  const sceneComponent = computed(() => map()?.component)
32
- const sceneParams = map()?.params
33
45
  const mapParams = map()?.params
34
- const animations = engine.sceneMap.animations
35
46
  const weather = engine.sceneMap.weather
36
47
  const backgroundMusic = { src: mapParams?.backgroundMusic, autoplay: true, loop: true }
37
48
  const backgroundAmbientSound = { src: mapParams?.backgroundAmbientSound, autoplay: true, loop: true }
38
49
 
39
- const data = signal(map()?.data)
40
-
41
- const scale = mapParams?.scale || 1
42
- const worldWidth = (mapParams?.width || 2048) * scale
43
- const worldHeight = (mapParams?.height || 2048) * scale
44
-
45
- const clamp = {
46
- direction: "all"
47
- }
48
-
49
50
  const shakeConfig = {
50
51
  trigger: engine.mapShakeTrigger,
51
52
  intensity: 10,
@@ -13,7 +13,6 @@
13
13
  </Container>
14
14
 
15
15
  <script>
16
- import { effect, signal } from 'canvasengine'
17
16
  import { inject } from "../../core/inject";
18
17
  import { RpgClientEngine } from "../../RpgClientEngine";
19
18
  import Character from "../character.ce";
@@ -24,4 +23,4 @@
24
23
 
25
24
  const players = engine.sceneMap.players
26
25
  const events = engine.sceneMap.events
27
- </script>
26
+ </script>
package/src/core/setup.ts CHANGED
@@ -14,7 +14,7 @@ export async function startGame(options: SetupOptions) {
14
14
 
15
15
  await injector(context, options.providers);
16
16
 
17
- const engine = inject(context, RpgClientEngine);
17
+ const engine = inject<RpgClientEngine>(context, RpgClientEngine);
18
18
  await engine.start();
19
19
  return context;
20
- }
20
+ }
@@ -0,0 +1,8 @@
1
+ export function Spritesheet(options: Record<string, any> = {}) {
2
+ return (target: any) => {
3
+ Object.assign(target, options);
4
+ if (target.prototype) {
5
+ Object.assign(target.prototype, options);
6
+ }
7
+ };
8
+ }
package/src/index.ts CHANGED
@@ -6,6 +6,8 @@ export * from "./services/save";
6
6
  export * from "./core/setup";
7
7
  export * from "./core/inject";
8
8
  export * from "./services/loadMap";
9
+ export * from "./services/actionInput";
10
+ export * from "./services/pointerContext";
9
11
  export * from "./module";
10
12
  export * from "./Gui/Gui";
11
13
  export * from "./components/gui";
@@ -16,6 +18,7 @@ export * from "./components";
16
18
  export * from "./components/gui";
17
19
  export * from "./Sound";
18
20
  export * from "./Resource";
21
+ export * from "./decorators/spritesheet";
19
22
  export * from "./utils/getEntityProp";
20
23
  export { Context } from "@signe/di";
21
24
  export { KeyboardControls, Input } from "canvasengine";
@@ -23,5 +26,6 @@ export { Control } from "./services/keyboardControls";
23
26
  export { RpgClientObject } from "./Game/Object";
24
27
  export { RpgClientPlayer } from "./Game/Player";
25
28
  export { RpgClientEvent } from "./Game/Event";
29
+ export * from "./Game/ProjectileManager";
26
30
  export { withMobile } from "./components/gui/mobile";
27
31
  export * from "./services/AbstractSocket";
package/src/module.ts CHANGED
@@ -155,6 +155,19 @@ export function provideClientModules(modules: RpgClientModule[]): FactoryProvide
155
155
  },
156
156
  };
157
157
  }
158
+ if (module.projectiles) {
159
+ const projectiles = { ...module.projectiles };
160
+ module.projectiles = {
161
+ ...projectiles,
162
+ load: (engine: RpgClientEngine) => {
163
+ if (projectiles.components) {
164
+ Object.entries(projectiles.components).forEach(([type, component]) => {
165
+ engine.registerProjectileComponent(type, component);
166
+ });
167
+ }
168
+ },
169
+ };
170
+ }
158
171
  if (module.transitions) {
159
172
  const transitions = [...module.transitions];
160
173
  module.transitions = {
@@ -195,6 +208,11 @@ export function provideClientModules(modules: RpgClientModule[]): FactoryProvide
195
208
  engine.addSpriteComponentInFront(component);
196
209
  });
197
210
  }
211
+ if (sprite.components) {
212
+ Object.entries(sprite.components).forEach(([id, component]) => {
213
+ engine.registerSpriteComponent(id, component);
214
+ });
215
+ }
198
216
  },
199
217
  };
200
218
  }
@@ -226,4 +244,3 @@ export function provideClientGlobalConfig(config: any = {}) {
226
244
  }
227
245
  return provideGlobalConfig(config)
228
246
  }
229
-