@rpgjs/client 5.0.0-beta.11 → 5.0.0-beta.13
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.
- package/CHANGELOG.md +19 -0
- package/dist/Game/AnimationManager.d.ts +1 -0
- package/dist/Game/AnimationManager.js +3 -0
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/ClientVisuals.d.ts +61 -0
- package/dist/Game/ClientVisuals.js +96 -0
- package/dist/Game/ClientVisuals.js.map +1 -0
- package/dist/Game/ClientVisuals.spec.d.ts +1 -0
- package/dist/Game/EventComponentResolver.d.ts +16 -0
- package/dist/Game/EventComponentResolver.js +52 -0
- package/dist/Game/EventComponentResolver.js.map +1 -0
- package/dist/Game/EventComponentResolver.spec.d.ts +1 -0
- package/dist/Game/Map.js +9 -0
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.d.ts +2 -0
- package/dist/Game/Object.js +22 -8
- package/dist/Game/Object.js.map +1 -1
- package/dist/Game/Object.spec.d.ts +1 -0
- package/dist/Game/ProjectileManager.d.ts +11 -2
- package/dist/Game/ProjectileManager.js +19 -2
- package/dist/Game/ProjectileManager.js.map +1 -1
- package/dist/Gui/Gui.d.ts +3 -2
- package/dist/Gui/Gui.js +18 -6
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/RpgClient.d.ts +85 -1
- package/dist/RpgClientEngine.d.ts +77 -2
- package/dist/RpgClientEngine.js +290 -31
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/animations/fx.ce.js +58 -0
- package/dist/components/animations/fx.ce.js.map +1 -0
- package/dist/components/animations/index.d.ts +1 -0
- package/dist/components/animations/index.js +3 -1
- package/dist/components/animations/index.js.map +1 -1
- package/dist/components/character.ce.js +192 -19
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +27 -12
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +4 -3
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js +9 -8
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js +7 -5
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +8 -7
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +12 -11
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js +7 -5
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js +4 -2
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js +4 -1
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +10 -9
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +17 -16
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +4 -3
- package/dist/components/gui/title-screen.ce.js.map +1 -1
- package/dist/components/interaction-components.ce.js +20 -0
- package/dist/components/interaction-components.ce.js.map +1 -0
- package/dist/components/scenes/canvas.ce.js +12 -7
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +18 -13
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/i18n.d.ts +55 -0
- package/dist/i18n.js +60 -0
- package/dist/i18n.js.map +1 -0
- package/dist/i18n.spec.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -2
- package/dist/module.js +30 -3
- package/dist/module.js.map +1 -1
- package/dist/services/actionInput.d.ts +3 -1
- package/dist/services/actionInput.js +33 -1
- package/dist/services/actionInput.js.map +1 -1
- package/dist/services/interactions.d.ts +159 -0
- package/dist/services/interactions.js +460 -0
- package/dist/services/interactions.js.map +1 -0
- package/dist/services/interactions.spec.d.ts +1 -0
- package/dist/services/keyboardControls.d.ts +1 -0
- package/dist/services/keyboardControls.js +1 -0
- package/dist/services/keyboardControls.js.map +1 -1
- package/dist/services/standalone.d.ts +3 -1
- package/dist/services/standalone.js +31 -13
- package/dist/services/standalone.js.map +1 -1
- package/dist/utils/mapId.d.ts +1 -0
- package/dist/utils/mapId.js +6 -0
- package/dist/utils/mapId.js.map +1 -0
- package/package.json +4 -4
- package/src/Game/AnimationManager.ts +4 -0
- package/src/Game/ClientVisuals.spec.ts +56 -0
- package/src/Game/ClientVisuals.ts +184 -0
- package/src/Game/EventComponentResolver.spec.ts +84 -0
- package/src/Game/EventComponentResolver.ts +74 -0
- package/src/Game/Map.ts +10 -0
- package/src/Game/Object.spec.ts +59 -0
- package/src/Game/Object.ts +36 -12
- package/src/Game/ProjectileManager.spec.ts +111 -0
- package/src/Game/ProjectileManager.ts +24 -2
- package/src/Gui/Gui.spec.ts +67 -0
- package/src/Gui/Gui.ts +24 -7
- package/src/RpgClient.ts +96 -1
- package/src/RpgClientEngine.ts +378 -45
- package/src/components/animations/fx.ce +101 -0
- package/src/components/animations/index.ts +4 -2
- package/src/components/character.ce +243 -17
- package/src/components/gui/dialogbox/index.ce +35 -14
- package/src/components/gui/gameover.ce +4 -3
- package/src/components/gui/menu/equip-menu.ce +9 -8
- package/src/components/gui/menu/exit-menu.ce +4 -3
- package/src/components/gui/menu/items-menu.ce +8 -7
- package/src/components/gui/menu/main-menu.ce +12 -11
- package/src/components/gui/menu/options-menu.ce +4 -3
- package/src/components/gui/menu/skills-menu.ce +2 -1
- package/src/components/gui/notification/notification.ce +7 -1
- package/src/components/gui/save-load.ce +11 -10
- package/src/components/gui/shop/shop.ce +17 -16
- package/src/components/gui/title-screen.ce +4 -3
- package/src/components/interaction-components.ce +23 -0
- package/src/components/scenes/canvas.ce +12 -7
- package/src/components/scenes/draw-map.ce +16 -5
- package/src/i18n.spec.ts +39 -0
- package/src/i18n.ts +59 -0
- package/src/index.ts +3 -0
- package/src/module.ts +43 -10
- package/src/services/actionInput.spec.ts +54 -0
- package/src/services/actionInput.ts +68 -1
- package/src/services/interactions.spec.ts +175 -0
- package/src/services/interactions.ts +722 -0
- package/src/services/keyboardControls.ts +2 -1
- package/src/services/standalone.ts +39 -10
- package/src/utils/mapId.ts +2 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<Fx
|
|
2
|
+
name={name}
|
|
3
|
+
preset={preset}
|
|
4
|
+
trigger={trigger}
|
|
5
|
+
autostart={autostart}
|
|
6
|
+
loop={loop}
|
|
7
|
+
enabled={enabled}
|
|
8
|
+
x={x}
|
|
9
|
+
y={y}
|
|
10
|
+
rotation={rotation}
|
|
11
|
+
scale={scale}
|
|
12
|
+
alpha={alpha}
|
|
13
|
+
timeScale={timeScale}
|
|
14
|
+
maxParticles={maxParticles}
|
|
15
|
+
preload={preload}
|
|
16
|
+
missingTexture={missingTexture}
|
|
17
|
+
zIndex={zIndex}
|
|
18
|
+
onStart={onStart}
|
|
19
|
+
onComplete={finish}
|
|
20
|
+
onParticleSpawn={onParticleSpawn}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
import { tick } from "canvasengine";
|
|
25
|
+
import { Fx } from "@canvasengine/presets";
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
name,
|
|
29
|
+
preset,
|
|
30
|
+
trigger,
|
|
31
|
+
onFinish,
|
|
32
|
+
onStart,
|
|
33
|
+
onComplete,
|
|
34
|
+
onParticleSpawn,
|
|
35
|
+
displayDuration,
|
|
36
|
+
duration,
|
|
37
|
+
autostart,
|
|
38
|
+
loop,
|
|
39
|
+
enabled,
|
|
40
|
+
x,
|
|
41
|
+
y,
|
|
42
|
+
rotation,
|
|
43
|
+
scale,
|
|
44
|
+
alpha,
|
|
45
|
+
timeScale,
|
|
46
|
+
maxParticles,
|
|
47
|
+
preload,
|
|
48
|
+
missingTexture,
|
|
49
|
+
zIndex,
|
|
50
|
+
} = defineProps({
|
|
51
|
+
autostart: {
|
|
52
|
+
default: true,
|
|
53
|
+
},
|
|
54
|
+
loop: {
|
|
55
|
+
default: false,
|
|
56
|
+
},
|
|
57
|
+
enabled: {
|
|
58
|
+
default: true,
|
|
59
|
+
},
|
|
60
|
+
rotation: {
|
|
61
|
+
default: 0,
|
|
62
|
+
},
|
|
63
|
+
scale: {
|
|
64
|
+
default: 1,
|
|
65
|
+
},
|
|
66
|
+
alpha: {
|
|
67
|
+
default: 1,
|
|
68
|
+
},
|
|
69
|
+
timeScale: {
|
|
70
|
+
default: 1,
|
|
71
|
+
},
|
|
72
|
+
maxParticles: {
|
|
73
|
+
default: 600,
|
|
74
|
+
},
|
|
75
|
+
preload: {
|
|
76
|
+
default: true,
|
|
77
|
+
},
|
|
78
|
+
missingTexture: {
|
|
79
|
+
default: "shape",
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let elapsedTime = 0;
|
|
84
|
+
let finished = false;
|
|
85
|
+
|
|
86
|
+
function finish(instance) {
|
|
87
|
+
if (finished) return;
|
|
88
|
+
finished = true;
|
|
89
|
+
onComplete?.(instance);
|
|
90
|
+
onFinish?.(instance);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
tick(({ deltaTime }) => {
|
|
94
|
+
const maxDuration = displayDuration?.() ?? (loop() ? duration?.() : undefined);
|
|
95
|
+
if (!maxDuration || finished) return;
|
|
96
|
+
elapsedTime += deltaTime;
|
|
97
|
+
if (elapsedTime >= maxDuration) {
|
|
98
|
+
finish();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
</script>
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
<Container
|
|
1
|
+
<Container
|
|
2
|
+
x={smoothX}
|
|
3
|
+
y={smoothY}
|
|
4
|
+
zIndex={z}
|
|
5
|
+
viewportFollow={shouldFollowCamera}
|
|
6
|
+
controls
|
|
7
|
+
onBeforeDestroy
|
|
8
|
+
visible
|
|
9
|
+
cursor={interactionCursor}
|
|
10
|
+
pointerover={interactionPointerOver}
|
|
11
|
+
pointerout={interactionPointerOut}
|
|
12
|
+
pointerdown={interactionPointerDown}
|
|
13
|
+
pointerup={interactionPointerUp}
|
|
14
|
+
pointermove={interactionPointerMove}
|
|
15
|
+
click={interactionClick}
|
|
16
|
+
>
|
|
2
17
|
@for (compConfig of normalizedComponentsBehind) {
|
|
3
18
|
<Container>
|
|
4
19
|
<compConfig.component object={sprite} ...compConfig.props />
|
|
@@ -8,7 +23,7 @@
|
|
|
8
23
|
<PlayerComponents object={sprite} position="left" graphicBounds />
|
|
9
24
|
<Particle emit={emitParticleTrigger} settings={particleSettings} zIndex={1000} name={particleName} />
|
|
10
25
|
<Container>
|
|
11
|
-
@for (graphicObj of
|
|
26
|
+
@for (graphicObj of renderedGraphics) {
|
|
12
27
|
<Container scale={graphicScale(graphicObj)}>
|
|
13
28
|
<Sprite
|
|
14
29
|
sheet={sheet(graphicObj)}
|
|
@@ -20,6 +35,11 @@
|
|
|
20
35
|
/>
|
|
21
36
|
</Container>
|
|
22
37
|
}
|
|
38
|
+
@for (eventComponent of resolvedEventComponents) {
|
|
39
|
+
<Container dependencies={eventComponent.dependencies}>
|
|
40
|
+
<eventComponent.component ...eventComponent.props />
|
|
41
|
+
</Container>
|
|
42
|
+
}
|
|
23
43
|
</Container>
|
|
24
44
|
<PlayerComponents object={sprite} position="center" graphicBounds />
|
|
25
45
|
<PlayerComponents object={sprite} position="right" graphicBounds />
|
|
@@ -29,11 +49,17 @@
|
|
|
29
49
|
<compConfig.component object={sprite} ...compConfig.props />
|
|
30
50
|
</Container>
|
|
31
51
|
}
|
|
52
|
+
<InteractionComponents
|
|
53
|
+
object={sprite}
|
|
54
|
+
bounds={graphicBounds}
|
|
55
|
+
hitboxBounds={hitboxBounds}
|
|
56
|
+
graphicBounds={graphicBounds}
|
|
57
|
+
/>
|
|
32
58
|
@for (attachedGui of attachedGuis) {
|
|
33
59
|
@if (shouldDisplayAttachedGui) {
|
|
34
60
|
<Container>
|
|
35
|
-
<attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={sprite} onFinish={(data) => {
|
|
36
|
-
onAttachedGuiFinish(attachedGui, data)
|
|
61
|
+
<attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={sprite} guiOpenId={attachedGui.openId} onFinish={(data, guiOpenId) => {
|
|
62
|
+
onAttachedGuiFinish(attachedGui, data, guiOpenId)
|
|
37
63
|
}} onInteraction={(name, data) => {
|
|
38
64
|
onAttachedGuiInteraction(attachedGui, name, data)
|
|
39
65
|
}} />
|
|
@@ -52,11 +78,18 @@
|
|
|
52
78
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
53
79
|
import { inject } from "../core/inject";
|
|
54
80
|
import { Direction, Animation } from "@rpgjs/common";
|
|
81
|
+
import { normalizeEventComponent } from "../Game/EventComponentResolver";
|
|
55
82
|
import Hit from "./effects/hit.ce";
|
|
56
83
|
import PlayerComponents from "./player-components.ce";
|
|
84
|
+
import InteractionComponents from "./interaction-components.ce";
|
|
57
85
|
import { RpgGui } from "../Gui/Gui";
|
|
58
86
|
import { getCanMoveValue } from "../utils/readPropValue";
|
|
59
|
-
import {
|
|
87
|
+
import {
|
|
88
|
+
getKeyboardControlBind,
|
|
89
|
+
keyboardEventMatchesBind,
|
|
90
|
+
resolveKeyboardActionInput,
|
|
91
|
+
resolveKeyboardDirectionInput,
|
|
92
|
+
} from "../services/actionInput";
|
|
60
93
|
|
|
61
94
|
const { object, id } = defineProps();
|
|
62
95
|
const sprite = object();
|
|
@@ -213,6 +246,18 @@
|
|
|
213
246
|
const normalizedComponentsInFront = computed(() => {
|
|
214
247
|
return normalizeComponents(componentsInFront());
|
|
215
248
|
});
|
|
249
|
+
|
|
250
|
+
const isEventSprite = () => {
|
|
251
|
+
return typeof sprite?.isEvent === 'function'
|
|
252
|
+
? sprite.isEvent()
|
|
253
|
+
: sprite?._type === 'event';
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const resolvedEventComponents = computed(() => {
|
|
257
|
+
if (!isEventSprite()) return [];
|
|
258
|
+
const eventComponent = normalizeEventComponent(client.resolveEventComponent(sprite), sprite);
|
|
259
|
+
return eventComponent ? [eventComponent] : [];
|
|
260
|
+
});
|
|
216
261
|
|
|
217
262
|
/**
|
|
218
263
|
* Determine if the camera should follow this sprite
|
|
@@ -263,6 +308,12 @@
|
|
|
263
308
|
flashTrigger
|
|
264
309
|
} = sprite;
|
|
265
310
|
|
|
311
|
+
const renderedGraphics = computed(() => {
|
|
312
|
+
const eventComponent = resolvedEventComponents()[0];
|
|
313
|
+
if (eventComponent && !eventComponent.renderGraphic) return [];
|
|
314
|
+
return graphicsSignals();
|
|
315
|
+
});
|
|
316
|
+
|
|
266
317
|
/**
|
|
267
318
|
* Flash configuration signals for dynamic options
|
|
268
319
|
* These signals are updated when the flash trigger is activated with options
|
|
@@ -308,6 +359,108 @@
|
|
|
308
359
|
|
|
309
360
|
const canControls = () => isMe() && getCanMoveValue(sprite)
|
|
310
361
|
const keyboardControls = client.globalConfig.keyboardControls;
|
|
362
|
+
const activeDirectionKeys = new Map();
|
|
363
|
+
|
|
364
|
+
const resolveHeldDirection = () => {
|
|
365
|
+
const directions = Array.from(activeDirectionKeys.values());
|
|
366
|
+
return directions[directions.length - 1];
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const resolveSpriteDirection = () => {
|
|
370
|
+
const heldDirection = resolveHeldDirection();
|
|
371
|
+
if (heldDirection) return heldDirection;
|
|
372
|
+
if (typeof sprite.getDirection === 'function') return sprite.getDirection();
|
|
373
|
+
if (typeof sprite.direction === 'function') return sprite.direction();
|
|
374
|
+
return direction();
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const directionToDashVector = (currentDirection) => {
|
|
378
|
+
switch (currentDirection) {
|
|
379
|
+
case Direction.Left:
|
|
380
|
+
return { x: -1, y: 0 };
|
|
381
|
+
case Direction.Right:
|
|
382
|
+
return { x: 1, y: 0 };
|
|
383
|
+
case Direction.Up:
|
|
384
|
+
return { x: 0, y: -1 };
|
|
385
|
+
case Direction.Down:
|
|
386
|
+
default:
|
|
387
|
+
return { x: 0, y: 1 };
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const withCurrentDirection = (payload) => {
|
|
392
|
+
if (payload.action !== 'action') return payload;
|
|
393
|
+
const data = payload.data && typeof payload.data === 'object'
|
|
394
|
+
? { ...payload.data }
|
|
395
|
+
: {};
|
|
396
|
+
return {
|
|
397
|
+
...payload,
|
|
398
|
+
data: {
|
|
399
|
+
...data,
|
|
400
|
+
direction: data.direction ?? resolveSpriteDirection(),
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const resolveCurrentActionInput = () =>
|
|
406
|
+
withCurrentDirection(
|
|
407
|
+
resolveKeyboardActionInput(keyboardControls.action, client, sprite)
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const playPredictedWalkAnimation = () => {
|
|
411
|
+
if (sprite.animationFixed) return;
|
|
412
|
+
if (sprite.animationIsPlaying && sprite.animationIsPlaying()) return;
|
|
413
|
+
realAnimationName.set('walk');
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const resumeHeldDirectionWalkAnimation = () => {
|
|
417
|
+
if (!isCurrentPlayer()) return false;
|
|
418
|
+
if (activeDirectionKeys.size === 0) return false;
|
|
419
|
+
if (!canControls()) return false;
|
|
420
|
+
if (sprite.animationFixed) return false;
|
|
421
|
+
if (sprite.animationIsPlaying && sprite.animationIsPlaying()) return false;
|
|
422
|
+
realAnimationName.set('walk');
|
|
423
|
+
return true;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const processMovementInput = (input) => {
|
|
427
|
+
if (!canControls()) return;
|
|
428
|
+
client.processInput({ input });
|
|
429
|
+
playPredictedWalkAnimation();
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const processDashInput = () => {
|
|
433
|
+
if (!canControls()) return;
|
|
434
|
+
client.processDash({
|
|
435
|
+
direction: directionToDashVector(resolveSpriteDirection()),
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const actionBind = () => getKeyboardControlBind(keyboardControls.action);
|
|
440
|
+
const keyboardEventId = (event) => `${event.keyCode}:${event.code}:${event.key}`;
|
|
441
|
+
|
|
442
|
+
const handleNativeActionWhileMoving = (event) => {
|
|
443
|
+
const inputDirection = resolveKeyboardDirectionInput(event, keyboardControls);
|
|
444
|
+
if (inputDirection) {
|
|
445
|
+
const keyId = keyboardEventId(event);
|
|
446
|
+
if (event.type === 'keydown') {
|
|
447
|
+
activeDirectionKeys.delete(keyId);
|
|
448
|
+
activeDirectionKeys.set(keyId, inputDirection);
|
|
449
|
+
resumeHeldDirectionWalkAnimation();
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
activeDirectionKeys.delete(keyId);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!isCurrentPlayer()) return;
|
|
457
|
+
if (event.type !== 'keydown' || event.repeat) return;
|
|
458
|
+
if (activeDirectionKeys.size === 0) return;
|
|
459
|
+
if (!keyboardEventMatchesBind(event, actionBind())) return;
|
|
460
|
+
if (!canControls()) return;
|
|
461
|
+
|
|
462
|
+
client.processAction(resolveCurrentActionInput());
|
|
463
|
+
};
|
|
311
464
|
|
|
312
465
|
const visible = computed(() => {
|
|
313
466
|
if (sprite.isEvent()) {
|
|
@@ -321,38 +474,44 @@
|
|
|
321
474
|
repeat: true,
|
|
322
475
|
bind: keyboardControls.down,
|
|
323
476
|
keyDown() {
|
|
324
|
-
|
|
477
|
+
processMovementInput(Direction.Down)
|
|
325
478
|
},
|
|
326
479
|
},
|
|
327
480
|
up: {
|
|
328
481
|
repeat: true,
|
|
329
482
|
bind: keyboardControls.up,
|
|
330
483
|
keyDown() {
|
|
331
|
-
|
|
484
|
+
processMovementInput(Direction.Up)
|
|
332
485
|
},
|
|
333
486
|
},
|
|
334
487
|
left: {
|
|
335
488
|
repeat: true,
|
|
336
489
|
bind: keyboardControls.left,
|
|
337
490
|
keyDown() {
|
|
338
|
-
|
|
491
|
+
processMovementInput(Direction.Left)
|
|
339
492
|
},
|
|
340
493
|
},
|
|
341
494
|
right: {
|
|
342
495
|
repeat: true,
|
|
343
496
|
bind: keyboardControls.right,
|
|
344
497
|
keyDown() {
|
|
345
|
-
|
|
498
|
+
processMovementInput(Direction.Right)
|
|
346
499
|
},
|
|
347
500
|
},
|
|
348
501
|
action: {
|
|
349
502
|
bind: getKeyboardControlBind(keyboardControls.action),
|
|
350
503
|
keyDown() {
|
|
351
504
|
if (canControls()) {
|
|
352
|
-
client.processAction(
|
|
505
|
+
client.processAction(resolveCurrentActionInput())
|
|
353
506
|
}
|
|
354
507
|
},
|
|
355
508
|
},
|
|
509
|
+
dash: {
|
|
510
|
+
bind: keyboardControls.dash,
|
|
511
|
+
keyDown() {
|
|
512
|
+
processDashInput()
|
|
513
|
+
},
|
|
514
|
+
},
|
|
356
515
|
escape: {
|
|
357
516
|
bind: keyboardControls.escape,
|
|
358
517
|
keyDown() {
|
|
@@ -402,7 +561,7 @@
|
|
|
402
561
|
}
|
|
403
562
|
|
|
404
563
|
const graphicScale = (graphicObject) => {
|
|
405
|
-
const scale = graphicObject?.scale;
|
|
564
|
+
const scale = graphicObject?.displayScale ?? graphicObject?.scale;
|
|
406
565
|
if (Array.isArray(scale)) return scale;
|
|
407
566
|
if (typeof scale === 'number') return [scale, scale];
|
|
408
567
|
if (scale && typeof scale === 'object') {
|
|
@@ -648,6 +807,10 @@
|
|
|
648
807
|
const graphicBounds = computed(() => {
|
|
649
808
|
const box = hitbox();
|
|
650
809
|
const fallback = hitboxBounds();
|
|
810
|
+
const customEventComponent = resolvedEventComponents()[0];
|
|
811
|
+
if (customEventComponent && !customEventComponent.renderGraphic) {
|
|
812
|
+
return fallback;
|
|
813
|
+
}
|
|
651
814
|
const dimensions = imageDimensions();
|
|
652
815
|
const graphics = graphicsSignals();
|
|
653
816
|
let bounds = null;
|
|
@@ -710,6 +873,31 @@
|
|
|
710
873
|
};
|
|
711
874
|
});
|
|
712
875
|
|
|
876
|
+
const interactionBounds = () => ({
|
|
877
|
+
bounds: graphicBounds(),
|
|
878
|
+
hitbox: hitboxBounds(),
|
|
879
|
+
graphic: graphicBounds()
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
const interactionCursor = computed(() =>
|
|
883
|
+
client.interactions.cursorFor(sprite, interactionBounds())
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
const handleInteraction = (type) => (event) => {
|
|
887
|
+
client.updatePointerFromInteractionEvent(event);
|
|
888
|
+
client.interactions.handle(sprite, type, {
|
|
889
|
+
event,
|
|
890
|
+
bounds: interactionBounds()
|
|
891
|
+
});
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
const interactionPointerOver = handleInteraction('pointerover');
|
|
895
|
+
const interactionPointerOut = handleInteraction('pointerout');
|
|
896
|
+
const interactionPointerDown = handleInteraction('pointerdown');
|
|
897
|
+
const interactionPointerUp = handleInteraction('pointerup');
|
|
898
|
+
const interactionPointerMove = handleInteraction('pointermove');
|
|
899
|
+
const interactionClick = handleInteraction('click');
|
|
900
|
+
|
|
713
901
|
// Combine animation change detection with movement state from smoothX/smoothY
|
|
714
902
|
const movementAnimations = ['walk', 'stand'];
|
|
715
903
|
const epsilon = 0; // movement threshold to consider the easing still running
|
|
@@ -779,17 +967,28 @@
|
|
|
779
967
|
});
|
|
780
968
|
|
|
781
969
|
const animationMovementSubscription = combineLatest([animationChange$, moving$]).subscribe(([[prev, curr], isMoving]) => {
|
|
782
|
-
|
|
970
|
+
const isMovementAnimation = movementAnimations.includes(curr);
|
|
971
|
+
const isTemporaryAnimationPlaying =
|
|
972
|
+
sprite.animationIsPlaying && sprite.animationIsPlaying();
|
|
973
|
+
|
|
974
|
+
if (sprite.animationFixed && isMovementAnimation) {
|
|
783
975
|
realAnimationName.set(curr);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (curr == 'stand' && !isMoving) {
|
|
980
|
+
if (!resumeHeldDirectionWalkAnimation()) {
|
|
981
|
+
realAnimationName.set(curr);
|
|
982
|
+
}
|
|
784
983
|
}
|
|
785
984
|
else if (curr == 'walk' && isMoving) {
|
|
786
985
|
realAnimationName.set(curr);
|
|
787
986
|
}
|
|
788
|
-
else if (!
|
|
987
|
+
else if (!isMovementAnimation) {
|
|
789
988
|
realAnimationName.set(curr);
|
|
790
989
|
}
|
|
791
|
-
if (!isMoving &&
|
|
792
|
-
if (
|
|
990
|
+
if (!isMoving && isTemporaryAnimationPlaying) {
|
|
991
|
+
if (isMovementAnimation) {
|
|
793
992
|
if (typeof sprite.resetAnimationState === 'function') {
|
|
794
993
|
sprite.resetAnimationState();
|
|
795
994
|
}
|
|
@@ -797,6 +996,16 @@
|
|
|
797
996
|
}
|
|
798
997
|
});
|
|
799
998
|
|
|
999
|
+
const resumeWalkSubscriptions = [
|
|
1000
|
+
sprite._canMove,
|
|
1001
|
+
sprite._animationFixed,
|
|
1002
|
+
sprite.animationIsPlaying,
|
|
1003
|
+
]
|
|
1004
|
+
.filter(signal => signal?.observable)
|
|
1005
|
+
.map(signal => signal.observable.subscribe(() => {
|
|
1006
|
+
resumeHeldDirectionWalkAnimation();
|
|
1007
|
+
}));
|
|
1008
|
+
|
|
800
1009
|
/**
|
|
801
1010
|
* Cleanup subscriptions and call hooks before sprite destruction.
|
|
802
1011
|
*
|
|
@@ -834,8 +1043,13 @@
|
|
|
834
1043
|
const onBeforeDestroy = async () => {
|
|
835
1044
|
await runBeforeRemove();
|
|
836
1045
|
await waitForTemporaryAnimationEnd();
|
|
1046
|
+
if (typeof document !== 'undefined') {
|
|
1047
|
+
document.removeEventListener('keydown', handleNativeActionWhileMoving);
|
|
1048
|
+
document.removeEventListener('keyup', handleNativeActionWhileMoving);
|
|
1049
|
+
}
|
|
837
1050
|
removeTransitionSubscription?.unsubscribe();
|
|
838
1051
|
animationMovementSubscription.unsubscribe();
|
|
1052
|
+
resumeWalkSubscriptions.forEach(subscription => subscription.unsubscribe());
|
|
839
1053
|
xSubscription.unsubscribe();
|
|
840
1054
|
ySubscription.unsubscribe();
|
|
841
1055
|
await lastValueFrom(hooks.callHooks("client-sprite-onDestroy", sprite))
|
|
@@ -843,6 +1057,10 @@
|
|
|
843
1057
|
}
|
|
844
1058
|
|
|
845
1059
|
mount((element) => {
|
|
1060
|
+
if (typeof document !== 'undefined') {
|
|
1061
|
+
document.addEventListener('keydown', handleNativeActionWhileMoving);
|
|
1062
|
+
document.addEventListener('keyup', handleNativeActionWhileMoving);
|
|
1063
|
+
}
|
|
846
1064
|
hooks.callHooks("client-sprite-onAdd", sprite).subscribe()
|
|
847
1065
|
hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, sprite).subscribe()
|
|
848
1066
|
effect(() => {
|
|
@@ -858,8 +1076,16 @@
|
|
|
858
1076
|
* @param gui - The GUI instance
|
|
859
1077
|
* @param data - Data passed from the GUI component
|
|
860
1078
|
*/
|
|
861
|
-
const
|
|
862
|
-
|
|
1079
|
+
const normalizeOpenId = (value) => {
|
|
1080
|
+
const resolved = typeof value === "function" ? value() : value;
|
|
1081
|
+
return typeof resolved === "string" && resolved.length > 0 ? resolved : undefined;
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
const onAttachedGuiFinish = (gui, data, guiOpenId) => {
|
|
1085
|
+
const completedOpenId = normalizeOpenId(guiOpenId);
|
|
1086
|
+
const currentOpenId = normalizeOpenId(gui.openId);
|
|
1087
|
+
if (completedOpenId && currentOpenId && completedOpenId !== currentOpenId) return;
|
|
1088
|
+
guiService.guiClose(gui.name, data, completedOpenId ?? currentOpenId);
|
|
863
1089
|
};
|
|
864
1090
|
|
|
865
1091
|
/**
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
@if (hasChoices()) {
|
|
18
18
|
<Navigation tabindex={selectedItem} controls={controls}>
|
|
19
19
|
<div class="rpg-ui-dialog-choices">
|
|
20
|
-
@for ((choice,index) of
|
|
20
|
+
@for ((choice,index) of dialogChoices()) {
|
|
21
21
|
<div
|
|
22
22
|
class="rpg-ui-dialog-choice"
|
|
23
23
|
class={{active: selectedItem() === index}}
|
|
@@ -62,27 +62,38 @@
|
|
|
62
62
|
engine.stopProcessingInput = true;
|
|
63
63
|
|
|
64
64
|
const selectedItem = signal(0)
|
|
65
|
-
|
|
65
|
+
const ACTION_OPEN_GUARD_MS = 150;
|
|
66
|
+
const openedAt = Date.now();
|
|
66
67
|
|
|
67
68
|
const {
|
|
68
69
|
data,
|
|
69
70
|
onFinish,
|
|
70
|
-
onInteraction
|
|
71
|
+
onInteraction,
|
|
72
|
+
guiOpenId
|
|
71
73
|
} = defineProps();
|
|
72
74
|
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
+
const dialogData = computed(() => data() || {});
|
|
76
|
+
const dialogChoices = computed(() => Array.isArray(dialogData().choices) ? dialogData().choices : []);
|
|
77
|
+
const message = computed(() => dialogData().message);
|
|
78
|
+
const speaker = computed(() => dialogData().speaker);
|
|
79
|
+
const position = computed(() => dialogData().position);
|
|
80
|
+
const typewriterEffect = computed(() => dialogData().typewriterEffect);
|
|
81
|
+
const fullWidth = computed(() => dialogData().fullWidth || false);
|
|
75
82
|
|
|
76
83
|
const resolveProp = (value) => typeof value === "function" ? value() : value;
|
|
84
|
+
const normalizeOpenId = (value) => {
|
|
85
|
+
const resolved = resolveProp(value);
|
|
86
|
+
return typeof resolved === "string" && resolved.length > 0 ? resolved : undefined;
|
|
87
|
+
};
|
|
77
88
|
|
|
78
89
|
const speakerName = computed(() => {
|
|
79
|
-
const value = resolveProp(speaker);
|
|
90
|
+
const value = resolveProp(speaker());
|
|
80
91
|
return value ? String(value) : "";
|
|
81
92
|
});
|
|
82
93
|
|
|
83
|
-
const dialogPosition = computed(() => resolveProp(position) || "bottom");
|
|
94
|
+
const dialogPosition = computed(() => resolveProp(position()) || "bottom");
|
|
84
95
|
const isFullWidth = computed(() => resolveProp(fullWidth) !== false);
|
|
85
|
-
const dialogFace = computed(() => resolveProp(face));
|
|
96
|
+
const dialogFace = computed(() => resolveProp(dialogData().face));
|
|
86
97
|
const hasFace = computed(() => {
|
|
87
98
|
const value = dialogFace();
|
|
88
99
|
return !!(value && value.id);
|
|
@@ -123,9 +134,9 @@
|
|
|
123
134
|
};
|
|
124
135
|
|
|
125
136
|
effect(() => {
|
|
126
|
-
const text = resolveProp(message) || "";
|
|
137
|
+
const text = resolveProp(message()) || "";
|
|
127
138
|
fullMessage.set(text);
|
|
128
|
-
const useTypewriter = resolveProp(typewriterEffect) !== false;
|
|
139
|
+
const useTypewriter = resolveProp(typewriterEffect()) !== false;
|
|
129
140
|
if (!useTypewriter) {
|
|
130
141
|
finishTyping();
|
|
131
142
|
return;
|
|
@@ -134,9 +145,9 @@
|
|
|
134
145
|
});
|
|
135
146
|
|
|
136
147
|
|
|
137
|
-
const hasChoices = computed(() =>
|
|
148
|
+
const hasChoices = computed(() => dialogChoices().length > 0);
|
|
138
149
|
const showIndicator = computed(() => !hasChoices() && !isTyping());
|
|
139
|
-
const nav = createTabindexNavigator(selectedItem, { count: () =>
|
|
150
|
+
const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, 'wrap');
|
|
140
151
|
|
|
141
152
|
function selectChoice(index) {
|
|
142
153
|
return function() {
|
|
@@ -146,13 +157,15 @@
|
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
function _onFinish(value) {
|
|
149
|
-
if (onFinish) onFinish(value);
|
|
160
|
+
if (onFinish) onFinish(value, normalizeOpenId(guiOpenId));
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
const onSelect = (index) => {
|
|
153
164
|
_onFinish(index);
|
|
154
165
|
};
|
|
155
166
|
|
|
167
|
+
const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;
|
|
168
|
+
|
|
156
169
|
const controls = signal({
|
|
157
170
|
up: {
|
|
158
171
|
repeat: true,
|
|
@@ -175,6 +188,9 @@
|
|
|
175
188
|
action: {
|
|
176
189
|
bind: getKeyboardControlBind(keyboardControls.action),
|
|
177
190
|
keyDown() {
|
|
191
|
+
if (!canAcceptAction()) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
178
194
|
if (isTyping()) {
|
|
179
195
|
finishTyping();
|
|
180
196
|
return;
|
|
@@ -192,6 +208,9 @@
|
|
|
192
208
|
action: {
|
|
193
209
|
bind: getKeyboardControlBind(keyboardControls.action),
|
|
194
210
|
keyDown() {
|
|
211
|
+
if (!canAcceptAction()) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
195
214
|
if (isTyping()) {
|
|
196
215
|
finishTyping();
|
|
197
216
|
return;
|
|
@@ -203,6 +222,9 @@
|
|
|
203
222
|
})
|
|
204
223
|
|
|
205
224
|
const faceSheet = (faceValue) => {
|
|
225
|
+
if (!faceValue || !faceValue.id) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
206
228
|
return {
|
|
207
229
|
definition: engine.getSpriteSheet(faceValue.id),
|
|
208
230
|
playing: faceValue.expression || "default",
|
|
@@ -211,7 +233,6 @@
|
|
|
211
233
|
|
|
212
234
|
mount((element) => {
|
|
213
235
|
return () => {
|
|
214
|
-
isDestroyed = true;
|
|
215
236
|
// Wait destroy is finished before start processing input
|
|
216
237
|
delay(() => {
|
|
217
238
|
engine.stopProcessingInput = false;
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
import { getKeyboardControlBind } from "../../services/actionInput";
|
|
35
35
|
|
|
36
36
|
const engine = inject(RpgClientEngine);
|
|
37
|
+
const { t } = engine.i18n();
|
|
37
38
|
const guiService = inject(RpgGui);
|
|
38
39
|
const keyboardControls = engine.globalConfig.keyboardControls;
|
|
39
40
|
|
|
@@ -50,12 +51,12 @@
|
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
const defaultEntries = [
|
|
53
|
-
{ id: "title", label: "
|
|
54
|
-
{ id: "load", label: "
|
|
54
|
+
{ id: "title", label: t("rpg.gameover.title-screen") },
|
|
55
|
+
{ id: "load", label: t("rpg.gameover.load-game") }
|
|
55
56
|
];
|
|
56
57
|
|
|
57
58
|
const resolveProp = (value) => typeof value === "function" ? value() : value;
|
|
58
|
-
const titleText = computed(() => resolveProp(title) || "
|
|
59
|
+
const titleText = computed(() => resolveProp(title) || t("rpg.gameover.title"));
|
|
59
60
|
const subtitleText = computed(() => resolveProp(subtitle) || "");
|
|
60
61
|
const localActionsEnabled = computed(() => resolveProp(localActions) === true);
|
|
61
62
|
|