@rpgjs/client 5.0.0-beta.20 → 5.0.0-beta.23

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 (52) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/Game/Object.d.ts +1 -0
  3. package/dist/Game/Object.js +10 -0
  4. package/dist/Game/Object.js.map +1 -1
  5. package/dist/Gui/Gui.js +37 -4
  6. package/dist/Gui/Gui.js.map +1 -1
  7. package/dist/RpgClientEngine.d.ts +18 -9
  8. package/dist/RpgClientEngine.js +40 -13
  9. package/dist/RpgClientEngine.js.map +1 -1
  10. package/dist/components/character-hitbox.d.ts +13 -0
  11. package/dist/components/character-hitbox.js +38 -0
  12. package/dist/components/character-hitbox.js.map +1 -0
  13. package/dist/components/character-hitbox.spec.d.ts +1 -0
  14. package/dist/components/character.ce.js +179 -36
  15. package/dist/components/character.ce.js.map +1 -1
  16. package/dist/components/gui/mobile/index.d.ts +51 -2
  17. package/dist/components/gui/mobile/index.js +12 -4
  18. package/dist/components/gui/mobile/index.js.map +1 -1
  19. package/dist/components/gui/mobile/index.spec.d.ts +1 -0
  20. package/dist/components/gui/mobile/mobile.ce.js +303 -55
  21. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/services/cameraFollow.d.ts +51 -0
  24. package/dist/services/cameraFollow.js +134 -0
  25. package/dist/services/cameraFollow.js.map +1 -0
  26. package/dist/services/cameraFollow.spec.d.ts +1 -0
  27. package/dist/services/standalone.d.ts +6 -1
  28. package/dist/services/standalone.js +35 -17
  29. package/dist/services/standalone.js.map +1 -1
  30. package/dist/utils/syncHitbox.d.ts +1 -0
  31. package/dist/utils/syncHitbox.js +69 -0
  32. package/dist/utils/syncHitbox.js.map +1 -0
  33. package/dist/utils/syncHitbox.spec.d.ts +1 -0
  34. package/package.json +5 -5
  35. package/src/Game/Object.spec.ts +44 -4
  36. package/src/Game/Object.ts +19 -0
  37. package/src/Gui/Gui.spec.ts +19 -0
  38. package/src/Gui/Gui.ts +50 -11
  39. package/src/RpgClientEngine.ts +56 -24
  40. package/src/components/character-hitbox.spec.ts +33 -0
  41. package/src/components/character-hitbox.ts +72 -0
  42. package/src/components/character.ce +207 -48
  43. package/src/components/gui/mobile/index.spec.ts +94 -0
  44. package/src/components/gui/mobile/index.ts +74 -6
  45. package/src/components/gui/mobile/mobile.ce +347 -65
  46. package/src/index.ts +12 -0
  47. package/src/services/cameraFollow.spec.ts +220 -0
  48. package/src/services/cameraFollow.ts +222 -0
  49. package/src/services/standalone.spec.ts +47 -0
  50. package/src/services/standalone.ts +53 -20
  51. package/src/utils/syncHitbox.spec.ts +79 -0
  52. package/src/utils/syncHitbox.ts +104 -0
@@ -1,77 +1,325 @@
1
1
  import { inject } from "../../../core/inject.js";
2
- import { Button, Container, Joystick, h, mount, signal, useDefineEmits, useDefineProps, useProps } from "canvasengine";
2
+ import { Button, Container, Joystick, computed, cond, h, mount, signal, useDefineEmits, useDefineProps, useProps } from "canvasengine";
3
3
  //#region src/components/gui/mobile/mobile.ce
4
4
  function component($$props) {
5
5
  useProps($$props);
6
- useDefineProps($$props);
6
+ const defineProps = useDefineProps($$props);
7
7
  useDefineEmits($$props);
8
+ const { data } = defineProps();
8
9
  const controlsInstance = signal(null);
10
+ const options = computed(() => data?.() || {});
11
+ const layout = computed(() => options().layout || {});
12
+ const components = computed(() => options().components || {});
13
+ const buttons = computed(() => options().buttons || {});
14
+ const joystick = computed(() => options().joystick);
15
+ const readButtonOption = (name) => buttons()[name];
16
+ const buttonEnabled = (name, defaultValue) => {
17
+ const option = readButtonOption(name);
18
+ if (typeof option === "boolean") return option;
19
+ return option?.enabled ?? defaultValue;
20
+ };
21
+ const showActionButton = computed(() => buttonEnabled("action", true));
22
+ const showBackButton = computed(() => buttonEnabled("back", true));
23
+ const showDashButton = computed(() => buttonEnabled("dash", false));
24
+ const showJoystick = computed(() => joystick() !== false);
25
+ const joystickSide = computed(() => layout().joystickSide || "right");
26
+ const rootDirection = computed(() => joystickSide() === "left" ? "row-reverse" : "row");
27
+ const layoutGap = computed(() => layout().gap ?? 14);
28
+ const buttonsMargin = computed(() => layout().buttonsMargin ?? layout().margin ?? 50);
29
+ const joystickMargin = computed(() => layout().joystickMargin ?? layout().margin ?? (joystickSide() === "right" ? [
30
+ 30,
31
+ 54,
32
+ 30,
33
+ 30
34
+ ] : [
35
+ 30,
36
+ 30,
37
+ 30,
38
+ 54
39
+ ]));
40
+ function readObjectOption(option) {
41
+ return typeof option === "object" && option ? option : {};
42
+ }
43
+ function resolveButtonComponent(name) {
44
+ return readObjectOption(readButtonOption(name)).component || components().buttons?.[name] || Button;
45
+ }
46
+ const joystickComponent = computed(() => joystick()?.component || components().joystick || Joystick);
47
+ const actionButtonComponent = computed(() => resolveButtonComponent("action"));
48
+ const backButtonComponent = computed(() => resolveButtonComponent("back"));
49
+ const dashButtonComponent = computed(() => resolveButtonComponent("dash"));
50
+ let activeJoystickControl = null;
51
+ let activeJoystickPower = 0;
52
+ let joystickRepeatTimer = null;
53
+ const baseButtonTextStyle = {
54
+ fontSize: 24,
55
+ fontFamily: "Arial Bold",
56
+ color: "#ffffff"
57
+ };
58
+ const actionButtonStyle = {
59
+ backgroundColor: {
60
+ normal: "#f7fbff",
61
+ hover: "#ffffff",
62
+ pressed: "#8fd4ff",
63
+ disabled: "#7f8c8d"
64
+ },
65
+ text: {
66
+ ...baseButtonTextStyle,
67
+ color: "#14253a"
68
+ },
69
+ border: {
70
+ color: "#ffffff",
71
+ width: 2
72
+ }
73
+ };
74
+ const backButtonStyle = {
75
+ backgroundColor: {
76
+ normal: "#243041",
77
+ hover: "#2f4059",
78
+ pressed: "#152132",
79
+ disabled: "#7f8c8d"
80
+ },
81
+ text: baseButtonTextStyle,
82
+ border: {
83
+ color: "#ffffff",
84
+ width: 1
85
+ }
86
+ };
87
+ const dashButtonStyle = {
88
+ backgroundColor: {
89
+ normal: "#243041",
90
+ hover: "#2f4059",
91
+ pressed: "#152132",
92
+ disabled: "#7f8c8d"
93
+ },
94
+ text: {
95
+ ...baseButtonTextStyle,
96
+ fontSize: 20
97
+ },
98
+ border: {
99
+ color: "#ffffff",
100
+ width: 1
101
+ }
102
+ };
103
+ const defaultButtonProps = {
104
+ action: {
105
+ shape: "circle",
106
+ text: "A",
107
+ width: 68,
108
+ height: 68,
109
+ controls: controlsInstance,
110
+ controlName: "action",
111
+ style: actionButtonStyle,
112
+ alpha: .84
113
+ },
114
+ back: {
115
+ shape: "circle",
116
+ text: "B",
117
+ width: 56,
118
+ height: 56,
119
+ controls: controlsInstance,
120
+ controlName: "back",
121
+ style: backButtonStyle,
122
+ alpha: .78
123
+ },
124
+ dash: {
125
+ shape: "circle",
126
+ text: "R",
127
+ width: 56,
128
+ height: 56,
129
+ controls: controlsInstance,
130
+ controlName: "dash",
131
+ style: dashButtonStyle,
132
+ alpha: .78
133
+ }
134
+ };
135
+ const buttonProps = (name) => {
136
+ const { enabled, component, container, ...props } = readObjectOption(readButtonOption(name));
137
+ const defaultProps = defaultButtonProps[name];
138
+ return {
139
+ ...defaultProps,
140
+ ...props,
141
+ defaultProps
142
+ };
143
+ };
144
+ const buttonContainerProps = (name) => {
145
+ const props = buttonProps(name);
146
+ return {
147
+ width: props.width,
148
+ height: props.height,
149
+ ...readObjectOption(readButtonOption(name)).container
150
+ };
151
+ };
152
+ const joystickProps = () => {
153
+ const { component, moveInterval, threshold, ...props } = readObjectOption(joystick());
154
+ const defaultProps = {
155
+ outerColor: "#d7e7ff",
156
+ innerColor: "#ffffff",
157
+ scale: .82,
158
+ outerScale: {
159
+ x: .86,
160
+ y: .86
161
+ },
162
+ innerScale: {
163
+ x: .88,
164
+ y: .88
165
+ },
166
+ onStart: onJoystickStart,
167
+ onChange: onJoystickChange,
168
+ onEnd: onJoystickEnd
169
+ };
170
+ return {
171
+ ...defaultProps,
172
+ ...props,
173
+ defaultProps
174
+ };
175
+ };
176
+ const applyJoystickOptions = (control) => {
177
+ const joystickOptions = joystick();
178
+ if (!control || !joystickOptions || joystickOptions === false) return;
179
+ control.joystick?.updateJoystickConfig?.({
180
+ ...control.joystick?.getJoystickConfig?.(),
181
+ ...typeof joystickOptions.moveInterval === "number" ? { moveInterval: joystickOptions.moveInterval } : {},
182
+ ...typeof joystickOptions.threshold === "number" ? { threshold: joystickOptions.threshold } : {}
183
+ });
184
+ };
185
+ const getJoystickThreshold = () => {
186
+ const joystickOptions = joystick();
187
+ if (joystickOptions && joystickOptions !== false && typeof joystickOptions.threshold === "number") return joystickOptions.threshold;
188
+ return controlsInstance()?.joystick?.getJoystickConfig?.()?.threshold ?? .1;
189
+ };
190
+ const getJoystickMoveInterval = () => {
191
+ const joystickOptions = joystick();
192
+ if (joystickOptions && joystickOptions !== false && typeof joystickOptions.moveInterval === "number") return joystickOptions.moveInterval;
193
+ return controlsInstance()?.joystick?.getJoystickConfig?.()?.moveInterval ?? 50;
194
+ };
195
+ const controlForAxis = (negativeControl, positiveControl, negativeAngle, positiveAngle, angle) => {
196
+ return Math.abs(angle - negativeAngle) <= Math.abs(angle - positiveAngle) ? negativeControl : positiveControl;
197
+ };
198
+ const resolveJoystickControl = (event) => {
199
+ const direction = event?.direction;
200
+ if (direction === "top") return "up";
201
+ if (direction === "bottom") return "down";
202
+ if (direction === "left") return "left";
203
+ if (direction === "right") return "right";
204
+ const angle = Number(event?.angle);
205
+ if (!Number.isFinite(angle)) {
206
+ if (direction === "top_left") return "left";
207
+ if (direction === "top_right") return "right";
208
+ if (direction === "bottom_left") return "left";
209
+ if (direction === "bottom_right") return "right";
210
+ return null;
211
+ }
212
+ if (direction === "top_left") return controlForAxis("up", "left", 90, 180, angle);
213
+ if (direction === "top_right") return controlForAxis("right", "up", 0, 90, angle);
214
+ if (direction === "bottom_left") return controlForAxis("left", "down", 180, 270, angle);
215
+ if (direction === "bottom_right") return controlForAxis("right", "down", 360, 270, angle);
216
+ return null;
217
+ };
218
+ const applyJoystickControl = (name, isDown, power = activeJoystickPower) => {
219
+ const control = controlsInstance();
220
+ const joystickControls = control?.joystick;
221
+ if (joystickControls?.applyControl) return joystickControls.applyControl(name, isDown, { power });
222
+ const keyboard = control?.keyboard;
223
+ if (keyboard?.applyControl) return keyboard.applyControl(name, isDown);
224
+ return control?.applyControl?.(name, isDown, { power });
225
+ };
226
+ const stopJoystickRepeat = () => {
227
+ if (!joystickRepeatTimer) return;
228
+ clearInterval(joystickRepeatTimer);
229
+ joystickRepeatTimer = null;
230
+ };
231
+ const startJoystickRepeat = () => {
232
+ if (!activeJoystickControl || joystickRepeatTimer) return;
233
+ joystickRepeatTimer = setInterval(() => {
234
+ if (!activeJoystickControl) return;
235
+ applyJoystickControl(activeJoystickControl, true);
236
+ }, getJoystickMoveInterval());
237
+ };
238
+ const releaseJoystickControl = () => {
239
+ stopJoystickRepeat();
240
+ if (!activeJoystickControl) return;
241
+ const previous = activeJoystickControl;
242
+ activeJoystickControl = null;
243
+ activeJoystickPower = 0;
244
+ applyJoystickControl(previous, false, 0);
245
+ };
246
+ const activateJoystickControl = (controlName, power = 0) => {
247
+ if (!controlName) {
248
+ releaseJoystickControl();
249
+ return;
250
+ }
251
+ activeJoystickPower = power;
252
+ if (activeJoystickControl === controlName) {
253
+ startJoystickRepeat();
254
+ return;
255
+ }
256
+ releaseJoystickControl();
257
+ activeJoystickControl = controlName;
258
+ applyJoystickControl(controlName, true, power);
259
+ startJoystickRepeat();
260
+ };
261
+ const publishMobileDebug = (type, payload = {}) => {
262
+ const target = typeof window !== "undefined" ? window : globalThis;
263
+ if (!target.__RPGJS_MOBILE_DEBUG__) return;
264
+ const event = {
265
+ source: "mobile-gui",
266
+ type,
267
+ time: Date.now(),
268
+ ...payload
269
+ };
270
+ target.__RPGJS_MOBILE_DEBUG_EVENTS__ = [...(target.__RPGJS_MOBILE_DEBUG_EVENTS__ || []).slice(-199), event];
271
+ target.__RPGJS_MOBILE_DEBUG_LAST__ = event;
272
+ };
273
+ const onJoystickStart = () => {
274
+ publishMobileDebug("joystick:start", { config: controlsInstance()?.joystick?.getJoystickConfig?.() });
275
+ };
276
+ const onJoystickChange = (event) => {
277
+ const threshold = getJoystickThreshold();
278
+ const controlName = event?.power >= threshold ? resolveJoystickControl(event) : null;
279
+ publishMobileDebug("joystick:change", {
280
+ direction: event?.direction,
281
+ power: event?.power,
282
+ angle: event?.angle,
283
+ threshold,
284
+ controlName,
285
+ config: controlsInstance()?.joystick?.getJoystickConfig?.()
286
+ });
287
+ activateJoystickControl(controlName, event?.power ?? 0);
288
+ };
289
+ const onJoystickEnd = () => {
290
+ publishMobileDebug("joystick:end", { controlName: activeJoystickControl });
291
+ releaseJoystickControl();
292
+ };
9
293
  mount((element) => {
10
294
  const control = inject("KeyboardControls");
11
295
  controlsInstance.set(control);
296
+ applyJoystickOptions(control);
12
297
  });
13
298
  return h(Container, {
299
+ flexDirection: computed(() => rootDirection()),
14
300
  justifyContent: "space-between",
15
301
  alignItems: "flex-end",
16
302
  width: "100%",
17
- height: "100%"
303
+ height: "100%",
304
+ onBeforeDestroy: releaseJoystickControl
18
305
  }, [h(Container, {
19
- justifyContent: "flex-start",
306
+ justifyContent: "flex-end",
20
307
  alignItems: "flex-end",
21
- gap: 20
308
+ gap: computed(() => layoutGap()),
309
+ margin: computed(() => buttonsMargin())
22
310
  }, h(Container, {
23
311
  display: "flex",
24
- direction: "column",
25
- gap: 20,
26
- margin: 50
27
- }, [h(Button, {
28
- text: "A",
29
- shape: "circle",
30
- width: 70,
31
- height: 70,
32
- controls: controlsInstance,
33
- controlName: "action",
34
- style: {
35
- backgroundColor: {
36
- normal: "#2ecc71",
37
- hover: "#27ae60",
38
- pressed: "#229954",
39
- disabled: "#7f8c8d"
40
- },
41
- text: {
42
- fontSize: 24,
43
- fontFamily: "Arial Bold",
44
- color: "#ffffff"
45
- }
46
- }
47
- }), h(Button, {
48
- text: "B",
49
- shape: "circle",
50
- width: 70,
51
- height: 70,
52
- controls: controlsInstance,
53
- controlName: "back",
54
- style: {
55
- backgroundColor: {
56
- normal: "#e74c3c",
57
- hover: "#c0392b",
58
- pressed: "#a93226",
59
- disabled: "#7f8c8d"
60
- },
61
- text: {
62
- fontSize: 24,
63
- fontFamily: "Arial Bold",
64
- color: "#ffffff"
65
- }
66
- }
67
- })])), h(Container, {
68
- margin: 100,
312
+ flexDirection: "row",
313
+ alignItems: "flex-end",
314
+ gap: computed(() => layoutGap())
315
+ }, [
316
+ cond(computed(() => showDashButton()), () => h(Container, buttonContainerProps("dash"), h(dashButtonComponent(), buttonProps("dash")))),
317
+ cond(computed(() => showActionButton()), () => h(Container, buttonContainerProps("action"), h(actionButtonComponent(), buttonProps("action")))),
318
+ cond(computed(() => showBackButton()), () => h(Container, buttonContainerProps("back"), h(backButtonComponent(), buttonProps("back"))))
319
+ ])), h(Container, {
320
+ margin: computed(() => joystickMargin()),
69
321
  alignItems: "flex-end"
70
- }, h(Container, null, h(Joystick, {
71
- controls: controlsInstance,
72
- outerColor: "#34495e",
73
- innerColor: "#3498db"
74
- })))]);
322
+ }, cond(computed(() => showJoystick()), () => h(joystickComponent(), joystickProps())))]);
75
323
  }
76
324
  var __ce_component = component;
77
325
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"mobile.ce.js","names":[],"sources":["../../../../src/components/gui/mobile/mobile.ce"],"sourcesContent":["<Container justifyContent=\"space-between\" alignItems=\"flex-end\" width=\"100%\" height=\"100%\">\n <!-- Gamepad buttons A and B (left side) -->\n <Container justifyContent=\"flex-start\" alignItems=\"flex-end\" gap={20}>\n <Container display=\"flex\" direction=\"column\" gap={20} margin={50}>\n <!-- Button B (top) -->\n \n <!-- Button A (bottom) -->\n <Button \n text=\"A\"\n shape=\"circle\"\n width={70}\n height={70}\n controls={controlsInstance}\n controlName=\"action\"\n style={{\n backgroundColor: {\n normal: \"#2ecc71\",\n hover: \"#27ae60\",\n pressed: \"#229954\",\n disabled: \"#7f8c8d\"\n },\n text: {\n fontSize: 24,\n fontFamily: \"Arial Bold\",\n color: \"#ffffff\"\n }\n }}\n />\n\n <Button \n text=\"B\"\n shape=\"circle\"\n width={70}\n height={70}\n controls={controlsInstance}\n controlName=\"back\"\n style={{\n backgroundColor: {\n normal: \"#e74c3c\",\n hover: \"#c0392b\",\n pressed: \"#a93226\",\n disabled: \"#7f8c8d\"\n },\n text: {\n fontSize: 24,\n fontFamily: \"Arial Bold\",\n color: \"#ffffff\"\n }\n }}\n />\n\n </Container>\n </Container>\n \n <Container margin={100} alignItems=\"flex-end\">\n <Container>\n <Joystick \n controls={controlsInstance}\n outerColor=\"#34495e\"\n innerColor=\"#3498db\"\n />\n </Container>\n </Container>\n</Container>\n\n<script>\n import { signal, mount } from 'canvasengine'\n import { Button } from 'canvasengine'\n import { inject } from '../../../core/inject'\n import { RpgClientEngine } from '../../../RpgClientEngine'\n import { Direction } from '@rpgjs/common'\n\n\n const controlsInstance = signal(null)\n\n mount((element) => {\n const control = inject('KeyboardControls')\n controlsInstance.set(control)\n })\n</script>"],"mappings":";;;AASM,SAAS,UAAQ,SAAM;CACN,SAAE,OAAA;CACC,eAAA,OAAA;CACA,eAAgB,OAAA;CAClC,MAAM,mBAAe,OAAM,IAAA;CACnC,OAAO,YAAS;EACZ,MAAM,UAAU,OAAA,kBAAiB;EACjC,iBAAiB,IAAG,OAAQ;CAChC,CAAC;CAEO,OADY,EAAE,WAAW;EAAE,gBAAO;EAAA,YAAA;EAAA,OAAA;EAAA,QAAA;CAAA,GAAA,CAAA,EAAA,WAAA;EAAA,gBAAA;EAAA,YAAA;EAAA,KAAA;CAAA,GAAA,EAAA,WAAA;EAAA,SAAA;EAAA,WAAA;EAAA,KAAA;EAAA,QAAA;CAAA,GAAA,CAAA,EAAA,QAAA;EAAA,MAAA;EAAA,OAAA;EAAA,OAAA;EAAA,QAAA;EAAA,UAAA;EAAA,aAAA;EAAA,OAAA;GAAA,iBAAA;IAAA,QAAA;IAAA,OAAA;IAAA,SAAA;IAAA,UAAA;GAAA;GAAA,MAAA;IAAA,UAAA;IAAA,YAAA;IAAA,OAAA;GAAA;EAAA;CAAA,CAAA,GAAA,EAAA,QAAA;EAAA,MAAA;EAAA,OAAA;EAAA,OAAA;EAAA,QAAA;EAAA,UAAA;EAAA,aAAA;EAAA,OAAA;GAAA,iBAAA;IAAA,QAAA;IAAA,OAAA;IAAA,SAAA;IAAA,UAAA;GAAA;GAAA,MAAA;IAAA,UAAA;IAAA,YAAA;IAAA,OAAA;GAAA;EAAA;CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA,WAAA;EAAA,QAAA;EAAA,YAAA;CAAA,GAAA,EAAA,WAAA,MAAA,EAAA,UAAA;EAAA,UAAA;EAAA,YAAA;EAAA,YAAA;CAAA,CAAA,CAAA,CAAA,CAAA,CACvB;AACb;AAEA,IAAM,iBAAY"}
1
+ {"version":3,"file":"mobile.ce.js","names":[],"sources":["../../../../src/components/gui/mobile/mobile.ce"],"sourcesContent":["<Container flexDirection={rootDirection()} justifyContent=\"space-between\" alignItems=\"flex-end\" width=\"100%\" height=\"100%\" onBeforeDestroy={releaseJoystickControl}>\n <Container justifyContent=\"flex-end\" alignItems=\"flex-end\" gap={layoutGap()} margin={buttonsMargin()}>\n <Container display=\"flex\" flexDirection=\"row\" alignItems=\"flex-end\" gap={layoutGap()}>\n @if (showDashButton()) {\n <Container ...buttonContainerProps(\"dash\")>\n <dashButtonComponent() ...buttonProps(\"dash\") />\n </Container>\n }\n\n @if (showActionButton()) {\n <Container ...buttonContainerProps(\"action\")>\n <actionButtonComponent() ...buttonProps(\"action\") />\n </Container>\n }\n\n @if (showBackButton()) {\n <Container ...buttonContainerProps(\"back\")>\n <backButtonComponent() ...buttonProps(\"back\") />\n </Container>\n }\n\n </Container>\n </Container>\n\n <Container margin={joystickMargin()} alignItems=\"flex-end\">\n @if (showJoystick()) {\n <joystickComponent() ...joystickProps() />\n }\n </Container>\n</Container>\n\n<script>\n import { Button, Joystick, signal, mount, computed } from 'canvasengine'\n import { inject } from '../../../core/inject'\n\n const { data } = defineProps()\n const controlsInstance = signal(null)\n const options = computed(() => data?.() || {})\n const layout = computed(() => options().layout || {})\n const components = computed(() => options().components || {})\n const buttons = computed(() => options().buttons || {})\n const joystick = computed(() => options().joystick)\n\n const readButtonOption = (name) => buttons()[name]\n const buttonEnabled = (name, defaultValue) => {\n const option = readButtonOption(name)\n if (typeof option === \"boolean\") return option\n return option?.enabled ?? defaultValue\n }\n\n const showActionButton = computed(() => buttonEnabled(\"action\", true))\n const showBackButton = computed(() => buttonEnabled(\"back\", true))\n const showDashButton = computed(() => buttonEnabled(\"dash\", false))\n const showJoystick = computed(() => joystick() !== false)\n const joystickSide = computed(() => layout().joystickSide || \"right\")\n const rootDirection = computed(() => joystickSide() === \"left\" ? \"row-reverse\" : \"row\")\n const layoutGap = computed(() => layout().gap ?? 14)\n const buttonsMargin = computed(() => layout().buttonsMargin ?? layout().margin ?? 50)\n const joystickMargin = computed(() => (\n layout().joystickMargin\n ?? layout().margin\n ?? (joystickSide() === \"right\" ? [30, 54, 30, 30] : [30, 30, 30, 54])\n ))\n\n function readObjectOption(option) {\n return typeof option === \"object\" && option ? option : {}\n }\n\n function resolveButtonComponent(name) {\n const option = readObjectOption(readButtonOption(name))\n return option.component || components().buttons?.[name] || Button\n }\n\n const joystickComponent = computed(() => joystick()?.component || components().joystick || Joystick)\n const actionButtonComponent = computed(() => resolveButtonComponent(\"action\"))\n const backButtonComponent = computed(() => resolveButtonComponent(\"back\"))\n const dashButtonComponent = computed(() => resolveButtonComponent(\"dash\"))\n let activeJoystickControl = null\n let activeJoystickPower = 0\n let joystickRepeatTimer = null\n\n const baseButtonTextStyle = {\n fontSize: 24,\n fontFamily: \"Arial Bold\",\n color: \"#ffffff\"\n }\n\n const actionButtonStyle = {\n backgroundColor: {\n normal: \"#f7fbff\",\n hover: \"#ffffff\",\n pressed: \"#8fd4ff\",\n disabled: \"#7f8c8d\"\n },\n text: {\n ...baseButtonTextStyle,\n color: \"#14253a\"\n },\n border: {\n color: \"#ffffff\",\n width: 2\n }\n }\n\n const backButtonStyle = {\n backgroundColor: {\n normal: \"#243041\",\n hover: \"#2f4059\",\n pressed: \"#152132\",\n disabled: \"#7f8c8d\"\n },\n text: baseButtonTextStyle,\n border: {\n color: \"#ffffff\",\n width: 1\n }\n }\n\n const dashButtonStyle = {\n backgroundColor: {\n normal: \"#243041\",\n hover: \"#2f4059\",\n pressed: \"#152132\",\n disabled: \"#7f8c8d\"\n },\n text: {\n ...baseButtonTextStyle,\n fontSize: 20\n },\n border: {\n color: \"#ffffff\",\n width: 1\n }\n }\n\n const defaultButtonProps = {\n action: {\n shape: \"circle\",\n text: \"A\",\n width: 68,\n height: 68,\n controls: controlsInstance,\n controlName: \"action\",\n style: actionButtonStyle,\n alpha: 0.84\n },\n back: {\n shape: \"circle\",\n text: \"B\",\n width: 56,\n height: 56,\n controls: controlsInstance,\n controlName: \"back\",\n style: backButtonStyle,\n alpha: 0.78\n },\n dash: {\n shape: \"circle\",\n text: \"R\",\n width: 56,\n height: 56,\n controls: controlsInstance,\n controlName: \"dash\",\n style: dashButtonStyle,\n alpha: 0.78\n }\n }\n\n const buttonProps = (name) => {\n const option = readObjectOption(readButtonOption(name))\n const { enabled, component, container, ...props } = option\n const defaultProps = defaultButtonProps[name]\n return {\n ...defaultProps,\n ...props,\n defaultProps\n }\n }\n\n const buttonContainerProps = (name) => {\n const props = buttonProps(name)\n return {\n width: props.width,\n height: props.height,\n ...readObjectOption(readButtonOption(name)).container\n }\n }\n\n const joystickProps = () => {\n const joystickOptions = readObjectOption(joystick())\n const { component, moveInterval, threshold, ...props } = joystickOptions\n const defaultProps = {\n outerColor: \"#d7e7ff\",\n innerColor: \"#ffffff\",\n scale: 0.82,\n outerScale: { x: 0.86, y: 0.86 },\n innerScale: { x: 0.88, y: 0.88 },\n onStart: onJoystickStart,\n onChange: onJoystickChange,\n onEnd: onJoystickEnd\n }\n return {\n ...defaultProps,\n ...props,\n defaultProps\n }\n }\n\n const applyJoystickOptions = (control) => {\n const joystickOptions = joystick()\n if (!control || !joystickOptions || joystickOptions === false) return\n control.joystick?.updateJoystickConfig?.({\n ...control.joystick?.getJoystickConfig?.(),\n ...(typeof joystickOptions.moveInterval === \"number\" ? { moveInterval: joystickOptions.moveInterval } : {}),\n ...(typeof joystickOptions.threshold === \"number\" ? { threshold: joystickOptions.threshold } : {})\n })\n }\n\n const getJoystickThreshold = () => {\n const joystickOptions = joystick()\n if (joystickOptions && joystickOptions !== false && typeof joystickOptions.threshold === \"number\") {\n return joystickOptions.threshold\n }\n return controlsInstance()?.joystick?.getJoystickConfig?.()?.threshold ?? 0.1\n }\n\n const getJoystickMoveInterval = () => {\n const joystickOptions = joystick()\n if (joystickOptions && joystickOptions !== false && typeof joystickOptions.moveInterval === \"number\") {\n return joystickOptions.moveInterval\n }\n return controlsInstance()?.joystick?.getJoystickConfig?.()?.moveInterval ?? 50\n }\n\n const controlForAxis = (negativeControl, positiveControl, negativeAngle, positiveAngle, angle) => {\n const distanceToNegative = Math.abs(angle - negativeAngle)\n const distanceToPositive = Math.abs(angle - positiveAngle)\n return distanceToNegative <= distanceToPositive ? negativeControl : positiveControl\n }\n\n const resolveJoystickControl = (event) => {\n const direction = event?.direction\n if (direction === \"top\") return \"up\"\n if (direction === \"bottom\") return \"down\"\n if (direction === \"left\") return \"left\"\n if (direction === \"right\") return \"right\"\n\n const angle = Number(event?.angle)\n if (!Number.isFinite(angle)) {\n if (direction === \"top_left\") return \"left\"\n if (direction === \"top_right\") return \"right\"\n if (direction === \"bottom_left\") return \"left\"\n if (direction === \"bottom_right\") return \"right\"\n return null\n }\n\n if (direction === \"top_left\") return controlForAxis(\"up\", \"left\", 90, 180, angle)\n if (direction === \"top_right\") return controlForAxis(\"right\", \"up\", 0, 90, angle)\n if (direction === \"bottom_left\") return controlForAxis(\"left\", \"down\", 180, 270, angle)\n if (direction === \"bottom_right\") return controlForAxis(\"right\", \"down\", 360, 270, angle)\n return null\n }\n\n const applyJoystickControl = (name, isDown, power = activeJoystickPower) => {\n const control = controlsInstance()\n const joystickControls = control?.joystick\n if (joystickControls?.applyControl) {\n return joystickControls.applyControl(name, isDown, { power })\n }\n const keyboard = control?.keyboard\n if (keyboard?.applyControl) return keyboard.applyControl(name, isDown)\n return control?.applyControl?.(name, isDown, { power })\n }\n\n const stopJoystickRepeat = () => {\n if (!joystickRepeatTimer) return\n clearInterval(joystickRepeatTimer)\n joystickRepeatTimer = null\n }\n\n const startJoystickRepeat = () => {\n if (!activeJoystickControl || joystickRepeatTimer) return\n joystickRepeatTimer = setInterval(() => {\n if (!activeJoystickControl) return\n applyJoystickControl(activeJoystickControl, true)\n }, getJoystickMoveInterval())\n }\n\n const releaseJoystickControl = () => {\n stopJoystickRepeat()\n if (!activeJoystickControl) return\n const previous = activeJoystickControl\n activeJoystickControl = null\n activeJoystickPower = 0\n applyJoystickControl(previous, false, 0)\n }\n\n const activateJoystickControl = (controlName, power = 0) => {\n if (!controlName) {\n releaseJoystickControl()\n return\n }\n activeJoystickPower = power\n if (activeJoystickControl === controlName) {\n startJoystickRepeat()\n return\n }\n releaseJoystickControl()\n activeJoystickControl = controlName\n applyJoystickControl(controlName, true, power)\n startJoystickRepeat()\n }\n\n const publishMobileDebug = (type, payload = {}) => {\n const target = typeof window !== \"undefined\" ? window : globalThis\n if (!target.__RPGJS_MOBILE_DEBUG__) return\n const event = {\n source: \"mobile-gui\",\n type,\n time: Date.now(),\n ...payload\n }\n target.__RPGJS_MOBILE_DEBUG_EVENTS__ = [\n ...(target.__RPGJS_MOBILE_DEBUG_EVENTS__ || []).slice(-199),\n event\n ]\n target.__RPGJS_MOBILE_DEBUG_LAST__ = event\n }\n\n const onJoystickStart = () => {\n publishMobileDebug(\"joystick:start\", {\n config: controlsInstance()?.joystick?.getJoystickConfig?.()\n })\n }\n\n const onJoystickChange = (event) => {\n const threshold = getJoystickThreshold()\n const controlName = event?.power >= threshold ? resolveJoystickControl(event) : null\n publishMobileDebug(\"joystick:change\", {\n direction: event?.direction,\n power: event?.power,\n angle: event?.angle,\n threshold,\n controlName,\n config: controlsInstance()?.joystick?.getJoystickConfig?.()\n })\n activateJoystickControl(controlName, event?.power ?? 0)\n }\n\n const onJoystickEnd = () => {\n publishMobileDebug(\"joystick:end\", {\n controlName: activeJoystickControl\n })\n releaseJoystickControl()\n }\n\n mount((element) => {\n const control = inject('KeyboardControls')\n controlsInstance.set(control)\n applyJoystickOptions(control)\n })\n</script>\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -30,5 +30,6 @@ export { RpgClientEvent } from './Game/Event';
30
30
  export * from './Game/ProjectileManager';
31
31
  export * from './Game/ClientVisuals';
32
32
  export { withMobile } from './components/gui/mobile';
33
+ export type { MobileButtonComponentProps, MobileGuiButtonOptions, MobileGuiComponentsOptions, MobileGuiEnabled, MobileGuiJoystickOptions, MobileGuiJoystickSide, MobileGuiLayoutOptions, MobileGuiMargin, MobileGuiOptions, MobileJoystickComponentProps, } from './components/gui/mobile';
33
34
  export * from './services/AbstractSocket';
34
35
  export * from './i18n';
@@ -0,0 +1,51 @@
1
+ export declare const DEFAULT_CAMERA_FOLLOW_TIME = 1000;
2
+ export declare const DEFAULT_CAMERA_FOLLOW_EASE = "easeInOutSine";
3
+ export declare const CAMERA_FOLLOW_EASES: readonly ["linear", "easeInQuad", "easeOutQuad", "easeInOutQuad", "easeInCubic", "easeOutCubic", "easeInOutCubic", "easeInQuart", "easeOutQuart", "easeInOutQuart", "easeInQuint", "easeOutQuint", "easeInOutQuint", "easeInSine", "easeOutSine", "easeInOutSine", "easeInExpo", "easeOutExpo", "easeInOutExpo", "easeInCirc", "easeOutCirc", "easeInOutCirc", "easeInElastic", "easeOutElastic", "easeInOutElastic", "easeInBack", "easeOutBack", "easeInOutBack", "easeInBounce", "easeOutBounce", "easeInOutBounce"];
4
+ export type CameraFollowEase = typeof CAMERA_FOLLOW_EASES[number];
5
+ export type CameraFollowSmoothMoveOptions = {
6
+ /** Enable or disable the smooth transition when options are sent as an object. */
7
+ enabled?: boolean;
8
+ /** Duration of the transition to the new camera target, in milliseconds. */
9
+ time?: number;
10
+ /** pixi-viewport easing name, for example "easeInOutQuad". */
11
+ ease?: CameraFollowEase;
12
+ /** Continuous follow speed after the transition. 0 keeps the target centered instantly. */
13
+ speed?: number;
14
+ /** Continuous follow acceleration after the transition. */
15
+ acceleration?: number | null;
16
+ /** Center radius where the followed target can move without moving the viewport. */
17
+ radius?: number | null;
18
+ };
19
+ export type CameraFollowSmoothMove = boolean | CameraFollowSmoothMoveOptions;
20
+ export type CameraFollowTarget = {
21
+ x: number;
22
+ y: number;
23
+ destroyed?: boolean;
24
+ } | null | undefined;
25
+ export type CameraFollowPosition = {
26
+ x: number;
27
+ y: number;
28
+ };
29
+ export interface CameraFollowApplyContext {
30
+ viewport: any;
31
+ target: CameraFollowTarget;
32
+ smoothMove: CameraFollowSmoothMove;
33
+ followRevision: number;
34
+ isCurrentRevision: (revision: number) => boolean;
35
+ shouldFollowCamera: () => boolean;
36
+ }
37
+ export declare const smoothMoveEnabled: (smoothMove: CameraFollowSmoothMove) => boolean;
38
+ export declare const cameraFollowAnimationOptions: (smoothMove: CameraFollowSmoothMove) => {
39
+ time: number;
40
+ ease: "easeInOutSine" | "linear" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic" | "easeInQuart" | "easeOutQuart" | "easeInOutQuart" | "easeInQuint" | "easeOutQuint" | "easeInOutQuint" | "easeInSine" | "easeOutSine" | "easeInExpo" | "easeOutExpo" | "easeInOutExpo" | "easeInCirc" | "easeOutCirc" | "easeInOutCirc" | "easeInElastic" | "easeOutElastic" | "easeInOutElastic" | "easeInBack" | "easeOutBack" | "easeInOutBack" | "easeInBounce" | "easeOutBounce" | "easeInOutBounce";
41
+ } | null;
42
+ export declare const cameraFollowOptions: (smoothMove: CameraFollowSmoothMove) => {
43
+ speed?: number;
44
+ acceleration?: number | null;
45
+ radius?: number | null;
46
+ } | undefined;
47
+ export declare const clearCameraFollowPlugins: (viewport: any) => void;
48
+ export declare const ownsCameraFollowRevision: (appliedRevision: number | null, currentRevision: number) => boolean;
49
+ export declare const readCameraFollowPosition: (target: CameraFollowTarget) => CameraFollowPosition | null;
50
+ export declare const followCameraInstantly: (viewport: any, target: CameraFollowTarget, smoothMove: CameraFollowSmoothMove) => boolean;
51
+ export declare const applyCameraFollow: ({ viewport, target, smoothMove, followRevision, isCurrentRevision, shouldFollowCamera, }: CameraFollowApplyContext) => boolean;
@@ -0,0 +1,134 @@
1
+ //#region src/services/cameraFollow.ts
2
+ var DEFAULT_CAMERA_FOLLOW_TIME = 1e3;
3
+ var DEFAULT_CAMERA_FOLLOW_EASE = "easeInOutSine";
4
+ var CAMERA_FOLLOW_EASES = [
5
+ "linear",
6
+ "easeInQuad",
7
+ "easeOutQuad",
8
+ "easeInOutQuad",
9
+ "easeInCubic",
10
+ "easeOutCubic",
11
+ "easeInOutCubic",
12
+ "easeInQuart",
13
+ "easeOutQuart",
14
+ "easeInOutQuart",
15
+ "easeInQuint",
16
+ "easeOutQuint",
17
+ "easeInOutQuint",
18
+ "easeInSine",
19
+ "easeOutSine",
20
+ "easeInOutSine",
21
+ "easeInExpo",
22
+ "easeOutExpo",
23
+ "easeInOutExpo",
24
+ "easeInCirc",
25
+ "easeOutCirc",
26
+ "easeInOutCirc",
27
+ "easeInElastic",
28
+ "easeOutElastic",
29
+ "easeInOutElastic",
30
+ "easeInBack",
31
+ "easeOutBack",
32
+ "easeInOutBack",
33
+ "easeInBounce",
34
+ "easeOutBounce",
35
+ "easeInOutBounce"
36
+ ];
37
+ var finiteNumber = (value, fallback) => {
38
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
39
+ };
40
+ var isCameraFollowEase = (value) => {
41
+ return typeof value === "string" && CAMERA_FOLLOW_EASES.includes(value);
42
+ };
43
+ var smoothMoveEnabled = (smoothMove) => {
44
+ if (smoothMove === false) return false;
45
+ if (typeof smoothMove === "object" && smoothMove !== null && smoothMove.enabled === false) return false;
46
+ return true;
47
+ };
48
+ var cameraFollowAnimationOptions = (smoothMove) => {
49
+ if (!smoothMoveEnabled(smoothMove)) return null;
50
+ const options = typeof smoothMove === "object" && smoothMove !== null ? smoothMove : {};
51
+ return {
52
+ time: Math.max(0, finiteNumber(options.time, DEFAULT_CAMERA_FOLLOW_TIME)),
53
+ ease: isCameraFollowEase(options.ease) ? options.ease : DEFAULT_CAMERA_FOLLOW_EASE
54
+ };
55
+ };
56
+ var cameraFollowOptions = (smoothMove) => {
57
+ if (typeof smoothMove !== "object" || smoothMove === null) return void 0;
58
+ const options = {};
59
+ if (typeof smoothMove.speed === "number" && Number.isFinite(smoothMove.speed)) options.speed = Math.max(0, smoothMove.speed);
60
+ if (typeof smoothMove.acceleration === "number" && Number.isFinite(smoothMove.acceleration)) options.acceleration = Math.max(0, smoothMove.acceleration);
61
+ else if (smoothMove.acceleration === null) options.acceleration = null;
62
+ if (typeof smoothMove.radius === "number" && Number.isFinite(smoothMove.radius)) options.radius = Math.max(0, smoothMove.radius);
63
+ else if (smoothMove.radius === null) options.radius = null;
64
+ return Object.keys(options).length > 0 ? options : void 0;
65
+ };
66
+ var clearCameraFollowPlugins = (viewport) => {
67
+ viewport?.plugins?.remove?.("animate");
68
+ viewport?.plugins?.remove?.("follow");
69
+ };
70
+ var ownsCameraFollowRevision = (appliedRevision, currentRevision) => {
71
+ return appliedRevision !== null && appliedRevision === currentRevision;
72
+ };
73
+ var readCameraFollowPosition = (target) => {
74
+ if (!target) return null;
75
+ try {
76
+ if (target.destroyed) return null;
77
+ const x = target.x;
78
+ const y = target.y;
79
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
80
+ return {
81
+ x,
82
+ y
83
+ };
84
+ } catch {
85
+ return null;
86
+ }
87
+ };
88
+ var createCameraFollowTarget = (target, initialPosition) => {
89
+ let lastPosition = initialPosition;
90
+ const readPosition = () => {
91
+ const nextPosition = readCameraFollowPosition(target);
92
+ if (nextPosition) lastPosition = nextPosition;
93
+ return lastPosition;
94
+ };
95
+ return {
96
+ get x() {
97
+ return readPosition().x;
98
+ },
99
+ get y() {
100
+ return readPosition().y;
101
+ }
102
+ };
103
+ };
104
+ var followCameraInstantly = (viewport, target, smoothMove) => {
105
+ const position = readCameraFollowPosition(target);
106
+ if (!position) return false;
107
+ const followTarget = createCameraFollowTarget(target, position);
108
+ const followOptions = cameraFollowOptions(smoothMove);
109
+ if (followOptions) viewport.follow(followTarget, followOptions);
110
+ else viewport.follow(followTarget);
111
+ return true;
112
+ };
113
+ var applyCameraFollow = ({ viewport, target, smoothMove, followRevision, isCurrentRevision, shouldFollowCamera }) => {
114
+ clearCameraFollowPlugins(viewport);
115
+ const position = readCameraFollowPosition(target);
116
+ if (!position) return false;
117
+ const animationOptions = cameraFollowAnimationOptions(smoothMove);
118
+ if (!animationOptions || animationOptions.time <= 0) return followCameraInstantly(viewport, target, smoothMove);
119
+ viewport.animate({
120
+ position,
121
+ time: animationOptions.time,
122
+ ease: animationOptions.ease,
123
+ callbackOnComplete: () => {
124
+ if (!isCurrentRevision(followRevision) || !shouldFollowCamera()) return;
125
+ if (!readCameraFollowPosition(target)) return;
126
+ followCameraInstantly(viewport, target, smoothMove);
127
+ }
128
+ });
129
+ return true;
130
+ };
131
+ //#endregion
132
+ export { applyCameraFollow, clearCameraFollowPlugins, ownsCameraFollowRevision };
133
+
134
+ //# sourceMappingURL=cameraFollow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cameraFollow.js","names":[],"sources":["../../src/services/cameraFollow.ts"],"sourcesContent":["export const DEFAULT_CAMERA_FOLLOW_TIME = 1000;\nexport const DEFAULT_CAMERA_FOLLOW_EASE = \"easeInOutSine\";\n\nexport const CAMERA_FOLLOW_EASES = [\n \"linear\",\n \"easeInQuad\",\n \"easeOutQuad\",\n \"easeInOutQuad\",\n \"easeInCubic\",\n \"easeOutCubic\",\n \"easeInOutCubic\",\n \"easeInQuart\",\n \"easeOutQuart\",\n \"easeInOutQuart\",\n \"easeInQuint\",\n \"easeOutQuint\",\n \"easeInOutQuint\",\n \"easeInSine\",\n \"easeOutSine\",\n \"easeInOutSine\",\n \"easeInExpo\",\n \"easeOutExpo\",\n \"easeInOutExpo\",\n \"easeInCirc\",\n \"easeOutCirc\",\n \"easeInOutCirc\",\n \"easeInElastic\",\n \"easeOutElastic\",\n \"easeInOutElastic\",\n \"easeInBack\",\n \"easeOutBack\",\n \"easeInOutBack\",\n \"easeInBounce\",\n \"easeOutBounce\",\n \"easeInOutBounce\",\n] as const;\n\nexport type CameraFollowEase = typeof CAMERA_FOLLOW_EASES[number];\n\nexport type CameraFollowSmoothMoveOptions = {\n /** Enable or disable the smooth transition when options are sent as an object. */\n enabled?: boolean;\n /** Duration of the transition to the new camera target, in milliseconds. */\n time?: number;\n /** pixi-viewport easing name, for example \"easeInOutQuad\". */\n ease?: CameraFollowEase;\n /** Continuous follow speed after the transition. 0 keeps the target centered instantly. */\n speed?: number;\n /** Continuous follow acceleration after the transition. */\n acceleration?: number | null;\n /** Center radius where the followed target can move without moving the viewport. */\n radius?: number | null;\n};\n\nexport type CameraFollowSmoothMove = boolean | CameraFollowSmoothMoveOptions;\n\nexport type CameraFollowTarget = {\n x: number;\n y: number;\n destroyed?: boolean;\n} | null | undefined;\n\nexport type CameraFollowPosition = {\n x: number;\n y: number;\n};\n\nexport interface CameraFollowApplyContext {\n viewport: any;\n target: CameraFollowTarget;\n smoothMove: CameraFollowSmoothMove;\n followRevision: number;\n isCurrentRevision: (revision: number) => boolean;\n shouldFollowCamera: () => boolean;\n}\n\nconst finiteNumber = (value: unknown, fallback: number) => {\n return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n};\n\nconst isCameraFollowEase = (value: unknown): value is CameraFollowEase => {\n return typeof value === \"string\" && (CAMERA_FOLLOW_EASES as readonly string[]).includes(value);\n};\n\nexport const smoothMoveEnabled = (smoothMove: CameraFollowSmoothMove) => {\n if (smoothMove === false) return false;\n if (typeof smoothMove === \"object\" && smoothMove !== null && smoothMove.enabled === false) {\n return false;\n }\n return true;\n};\n\nexport const cameraFollowAnimationOptions = (\n smoothMove: CameraFollowSmoothMove\n) => {\n if (!smoothMoveEnabled(smoothMove)) return null;\n const options = typeof smoothMove === \"object\" && smoothMove !== null ? smoothMove : {};\n return {\n time: Math.max(0, finiteNumber(options.time, DEFAULT_CAMERA_FOLLOW_TIME)),\n ease: isCameraFollowEase(options.ease) ? options.ease : DEFAULT_CAMERA_FOLLOW_EASE,\n };\n};\n\nexport const cameraFollowOptions = (smoothMove: CameraFollowSmoothMove) => {\n if (typeof smoothMove !== \"object\" || smoothMove === null) return undefined;\n const options: { speed?: number; acceleration?: number | null; radius?: number | null } = {};\n if (typeof smoothMove.speed === \"number\" && Number.isFinite(smoothMove.speed)) {\n options.speed = Math.max(0, smoothMove.speed);\n }\n if (typeof smoothMove.acceleration === \"number\" && Number.isFinite(smoothMove.acceleration)) {\n options.acceleration = Math.max(0, smoothMove.acceleration);\n } else if (smoothMove.acceleration === null) {\n options.acceleration = null;\n }\n if (typeof smoothMove.radius === \"number\" && Number.isFinite(smoothMove.radius)) {\n options.radius = Math.max(0, smoothMove.radius);\n } else if (smoothMove.radius === null) {\n options.radius = null;\n }\n return Object.keys(options).length > 0 ? options : undefined;\n};\n\nexport const clearCameraFollowPlugins = (viewport: any) => {\n viewport?.plugins?.remove?.(\"animate\");\n viewport?.plugins?.remove?.(\"follow\");\n};\n\nexport const ownsCameraFollowRevision = (\n appliedRevision: number | null,\n currentRevision: number\n) => {\n return appliedRevision !== null && appliedRevision === currentRevision;\n};\n\nexport const readCameraFollowPosition = (\n target: CameraFollowTarget\n): CameraFollowPosition | null => {\n if (!target) return null;\n\n try {\n if (target.destroyed) return null;\n const x = target.x;\n const y = target.y;\n if (!Number.isFinite(x) || !Number.isFinite(y)) return null;\n return { x, y };\n } catch {\n return null;\n }\n};\n\nconst createCameraFollowTarget = (\n target: CameraFollowTarget,\n initialPosition: CameraFollowPosition\n) => {\n let lastPosition = initialPosition;\n\n const readPosition = () => {\n const nextPosition = readCameraFollowPosition(target);\n if (nextPosition) {\n lastPosition = nextPosition;\n }\n return lastPosition;\n };\n\n return {\n get x() {\n return readPosition().x;\n },\n get y() {\n return readPosition().y;\n },\n };\n};\n\nexport const followCameraInstantly = (\n viewport: any,\n target: CameraFollowTarget,\n smoothMove: CameraFollowSmoothMove\n) => {\n const position = readCameraFollowPosition(target);\n if (!position) return false;\n\n const followTarget = createCameraFollowTarget(target, position);\n const followOptions = cameraFollowOptions(smoothMove);\n if (followOptions) {\n viewport.follow(followTarget, followOptions);\n } else {\n viewport.follow(followTarget);\n }\n return true;\n};\n\nexport const applyCameraFollow = ({\n viewport,\n target,\n smoothMove,\n followRevision,\n isCurrentRevision,\n shouldFollowCamera,\n}: CameraFollowApplyContext) => {\n clearCameraFollowPlugins(viewport);\n\n const position = readCameraFollowPosition(target);\n if (!position) return false;\n\n const animationOptions = cameraFollowAnimationOptions(smoothMove);\n if (!animationOptions || animationOptions.time <= 0) {\n return followCameraInstantly(viewport, target, smoothMove);\n }\n\n viewport.animate({\n position,\n time: animationOptions.time,\n ease: animationOptions.ease,\n callbackOnComplete: () => {\n if (!isCurrentRevision(followRevision) || !shouldFollowCamera()) return;\n if (!readCameraFollowPosition(target)) return;\n followCameraInstantly(viewport, target, smoothMove);\n },\n });\n return true;\n};\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};