@rpgjs/client 5.0.0-beta.22 → 5.0.0-beta.24
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 +22 -0
- package/dist/Game/Object.d.ts +6 -0
- package/dist/Game/Object.js +58 -2
- package/dist/Game/Object.js.map +1 -1
- package/dist/RpgClientEngine.d.ts +2 -0
- package/dist/RpgClientEngine.js +15 -6
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/character-hitbox.d.ts +13 -0
- package/dist/components/character-hitbox.js +38 -0
- package/dist/components/character-hitbox.js.map +1 -0
- package/dist/components/character-hitbox.spec.d.ts +1 -0
- package/dist/components/character.ce.js +38 -29
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +31 -2
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js +2 -2
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +27 -4
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +210 -155
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +33 -4
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +47 -4
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/services/standalone.d.ts +6 -1
- package/dist/services/standalone.js +35 -17
- package/dist/services/standalone.js.map +1 -1
- package/dist/utils/syncHitbox.d.ts +1 -0
- package/dist/utils/syncHitbox.js +69 -0
- package/dist/utils/syncHitbox.js.map +1 -0
- package/dist/utils/syncHitbox.spec.d.ts +1 -0
- package/package.json +4 -4
- package/src/Game/Object.spec.ts +70 -6
- package/src/Game/Object.ts +90 -2
- package/src/RpgClientEngine.ts +19 -6
- package/src/components/character-hitbox.spec.ts +33 -0
- package/src/components/character-hitbox.ts +72 -0
- package/src/components/character.ce +52 -45
- package/src/components/gui/dialogbox/index.ce +56 -40
- package/src/components/gui/hud/hud.ce +2 -2
- package/src/components/gui/menu/items-menu.ce +12 -2
- package/src/components/gui/menu/main-menu.ce +37 -6
- package/src/components/gui/save-load.ce +48 -32
- package/src/components/gui/shop/shop.ce +28 -3
- package/src/services/standalone.spec.ts +47 -0
- package/src/services/standalone.ts +53 -20
- package/src/utils/syncHitbox.spec.ts +79 -0
- package/src/utils/syncHitbox.ts +104 -0
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
<Particle emit={emitParticleTrigger} settings={particleSettings} zIndex={1000} name={particleName} />
|
|
24
24
|
<Container>
|
|
25
25
|
@for (graphicObj of renderedGraphics) {
|
|
26
|
-
<Container scale={
|
|
26
|
+
<Container scale={graphicContainerScale(graphicObj)}>
|
|
27
27
|
<Sprite
|
|
28
28
|
sheet={sheet(graphicObj)}
|
|
29
29
|
direction
|
|
30
30
|
tint
|
|
31
|
-
hitbox
|
|
31
|
+
hitbox={graphicHitbox(graphicObj)}
|
|
32
32
|
shadowCaster={shadowCaster(graphicObj)}
|
|
33
33
|
flash={flashConfig}
|
|
34
34
|
/>
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
import { GameEngineToken, ModulesToken, shouldRenderLightingShadows } from "@rpgjs/common";
|
|
77
77
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
78
78
|
import { applyCameraFollow, clearCameraFollowPlugins, ownsCameraFollowRevision } from "../services/cameraFollow";
|
|
79
|
+
import { resolveScaledHitboxAnchor, scaleHitboxForGraphicDisplay, toPositiveNumber } from "./character-hitbox";
|
|
79
80
|
import { inject } from "../core/inject";
|
|
80
81
|
import { Direction, Animation } from "@rpgjs/common";
|
|
81
82
|
import { normalizeEventComponent } from "../Game/EventComponentResolver";
|
|
@@ -520,6 +521,16 @@
|
|
|
520
521
|
});
|
|
521
522
|
};
|
|
522
523
|
|
|
524
|
+
let lastEscapeActionAt = 0;
|
|
525
|
+
const processEscapeInput = () => {
|
|
526
|
+
const now = Date.now();
|
|
527
|
+
if (now - lastEscapeActionAt < 50) return;
|
|
528
|
+
lastEscapeActionAt = now;
|
|
529
|
+
if (canControls()) {
|
|
530
|
+
client.processAction({ action: 'escape' })
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
523
534
|
const actionBind = () => getKeyboardControlBind(keyboardControls.action);
|
|
524
535
|
const keyboardEventId = (event) => `${event.keyCode}:${event.code}:${event.key}`;
|
|
525
536
|
|
|
@@ -615,17 +626,13 @@
|
|
|
615
626
|
back: {
|
|
616
627
|
bind: keyboardControls.escape,
|
|
617
628
|
keyDown() {
|
|
618
|
-
|
|
619
|
-
client.processAction({ action: 'escape' })
|
|
620
|
-
}
|
|
629
|
+
processEscapeInput()
|
|
621
630
|
},
|
|
622
631
|
},
|
|
623
632
|
escape: {
|
|
624
633
|
bind: keyboardControls.escape,
|
|
625
634
|
keyDown() {
|
|
626
|
-
|
|
627
|
-
client.processAction({ action: 'escape' })
|
|
628
|
-
}
|
|
635
|
+
processEscapeInput()
|
|
629
636
|
},
|
|
630
637
|
},
|
|
631
638
|
joystick: {
|
|
@@ -685,7 +692,7 @@
|
|
|
685
692
|
}
|
|
686
693
|
|
|
687
694
|
const graphicScale = (graphicObject) => {
|
|
688
|
-
const scale = graphicObject?.
|
|
695
|
+
const scale = graphicObject?.scale;
|
|
689
696
|
if (Array.isArray(scale)) return scale;
|
|
690
697
|
if (typeof scale === 'number') return [scale, scale];
|
|
691
698
|
if (scale && typeof scale === 'object') {
|
|
@@ -696,12 +703,34 @@
|
|
|
696
703
|
return undefined;
|
|
697
704
|
}
|
|
698
705
|
|
|
706
|
+
const graphicContainerScale = (graphicObject) => {
|
|
707
|
+
const scale = graphicObject?.displayScale;
|
|
708
|
+
if (Array.isArray(scale)) return scale;
|
|
709
|
+
if (typeof scale === 'number') return [scale, scale];
|
|
710
|
+
if (scale && typeof scale === 'object') {
|
|
711
|
+
const x = typeof scale.x === 'number' ? scale.x : 1;
|
|
712
|
+
const y = typeof scale.y === 'number' ? scale.y : x;
|
|
713
|
+
return [x, y];
|
|
714
|
+
}
|
|
715
|
+
return [1, 1];
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const multiplyScale = (left, right) => {
|
|
719
|
+
const a = normalizePair(left);
|
|
720
|
+
const b = normalizePair(right);
|
|
721
|
+
return [a[0] * b[0], a[1] * b[1]];
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const graphicHitbox = (graphicObject) => {
|
|
725
|
+
const box = hitbox();
|
|
726
|
+
const scale = graphicEffectiveScale(graphicObject);
|
|
727
|
+
return scaleHitboxForGraphicDisplay(box, scale);
|
|
728
|
+
}
|
|
729
|
+
|
|
699
730
|
const shadowCaster = (graphicObject) => {
|
|
700
731
|
const box = hitbox();
|
|
701
732
|
const bounds = graphicBounds();
|
|
702
|
-
const
|
|
703
|
-
const scaleY = Array.isArray(scale) && typeof scale[1] === 'number' ? Math.abs(scale[1]) : 1;
|
|
704
|
-
const height = Math.max(bounds?.height ?? box?.h ?? 32, box?.h ?? 32) * scaleY;
|
|
733
|
+
const height = Math.max(bounds?.height ?? box?.h ?? 32, box?.h ?? 32);
|
|
705
734
|
|
|
706
735
|
return {
|
|
707
736
|
enabled: shadowsEnabled,
|
|
@@ -722,11 +751,6 @@
|
|
|
722
751
|
const imageDimensions = signal({});
|
|
723
752
|
const loadingImageDimensions = new Set();
|
|
724
753
|
|
|
725
|
-
const toPositiveNumber = (value) => {
|
|
726
|
-
const number = typeof value === 'number' ? value : parseFloat(value);
|
|
727
|
-
return Number.isFinite(number) && number > 0 ? number : undefined;
|
|
728
|
-
};
|
|
729
|
-
|
|
730
754
|
const toFiniteNumber = (value, fallback = 0) => {
|
|
731
755
|
const number = typeof value === 'number' ? value : parseFloat(value);
|
|
732
756
|
return Number.isFinite(number) ? number : fallback;
|
|
@@ -826,6 +850,13 @@
|
|
|
826
850
|
return frame?.[prop] ?? textureOptions?.[prop] ?? graphicObject?.[prop];
|
|
827
851
|
};
|
|
828
852
|
|
|
853
|
+
const graphicEffectiveScale = (graphicObject) => {
|
|
854
|
+
const textureOptions = resolveTextureOptions(graphicObject);
|
|
855
|
+
const frame = resolveFirstAnimationFrame(textureOptions);
|
|
856
|
+
const spriteScale = normalizePair(optionValue('scale', frame, textureOptions, graphicObject) ?? graphicScale(graphicObject));
|
|
857
|
+
return multiplyScale(spriteScale, graphicContainerScale(graphicObject));
|
|
858
|
+
};
|
|
859
|
+
|
|
829
860
|
const resolveFrameSize = (textureOptions, dimensions) => {
|
|
830
861
|
const framesWidth = toPositiveNumber(textureOptions?.framesWidth) ?? 1;
|
|
831
862
|
const framesHeight = toPositiveNumber(textureOptions?.framesHeight) ?? 1;
|
|
@@ -846,31 +877,6 @@
|
|
|
846
877
|
};
|
|
847
878
|
};
|
|
848
879
|
|
|
849
|
-
const resolveHitboxAnchor = (spriteWidth, spriteHeight, realSize, box) => {
|
|
850
|
-
if (!spriteWidth || !spriteHeight || !box) {
|
|
851
|
-
return [0, 0];
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
const heightOfSprite = typeof realSize === 'number' ? realSize : realSize?.height;
|
|
855
|
-
const resolvedHeight = toPositiveNumber(heightOfSprite) ?? spriteHeight;
|
|
856
|
-
const gap = Math.max(0, (spriteHeight - resolvedHeight) / 2);
|
|
857
|
-
const hitboxTopLeftX = clampRatio((spriteWidth - box.w) / 2 / spriteWidth);
|
|
858
|
-
const hitboxTopLeftY = clampRatio((spriteHeight - box.h - gap) / spriteHeight);
|
|
859
|
-
const hitboxCenterX = clampRatio(hitboxTopLeftX + box.w / 2 / spriteWidth);
|
|
860
|
-
const hitboxCenterY = clampRatio(hitboxTopLeftY + box.h / 2 / spriteHeight);
|
|
861
|
-
const footY = clampRatio((spriteHeight - gap) / spriteHeight);
|
|
862
|
-
|
|
863
|
-
switch (box.anchorMode ?? 'top-left') {
|
|
864
|
-
case 'center':
|
|
865
|
-
return [hitboxCenterX, hitboxCenterY];
|
|
866
|
-
case 'foot':
|
|
867
|
-
return [hitboxCenterX, footY];
|
|
868
|
-
case 'top-left':
|
|
869
|
-
default:
|
|
870
|
-
return [hitboxTopLeftX, hitboxTopLeftY];
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
|
|
874
880
|
const loadImageDimensions = (image) => {
|
|
875
881
|
const source = resolveImageSource(image);
|
|
876
882
|
if (!source || imageDimensions()[source] || loadingImageDimensions.has(source)) {
|
|
@@ -950,14 +956,15 @@
|
|
|
950
956
|
return;
|
|
951
957
|
}
|
|
952
958
|
|
|
959
|
+
const scale = graphicEffectiveScale(graphicObject);
|
|
953
960
|
const explicitAnchor = normalizeAnchor(optionValue('anchor', frame, textureOptions, graphicObject));
|
|
954
|
-
const anchor = explicitAnchor ??
|
|
961
|
+
const anchor = explicitAnchor ?? resolveScaledHitboxAnchor(
|
|
955
962
|
spriteWidth,
|
|
956
963
|
spriteHeight,
|
|
957
964
|
optionValue('spriteRealSize', frame, textureOptions, graphicObject),
|
|
958
|
-
box
|
|
965
|
+
box,
|
|
966
|
+
scale
|
|
959
967
|
);
|
|
960
|
-
const scale = normalizePair(optionValue('scale', frame, textureOptions, graphicObject) ?? graphicScale(graphicObject));
|
|
961
968
|
const x = toFiniteNumber(optionValue('x', frame, textureOptions, graphicObject), 0);
|
|
962
969
|
const y = toFiniteNumber(optionValue('y', frame, textureOptions, graphicObject), 0);
|
|
963
970
|
const leftEdge = -anchor[0] * spriteWidth * scale[0];
|
|
@@ -1,49 +1,52 @@
|
|
|
1
1
|
<DOMContainer width="100%" height="100%" controls={dialogControls}>
|
|
2
|
-
<div
|
|
3
|
-
class="rpg-ui-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
<div class="rpg-ui-dialog-layer" click={closeDialogFromPointer}>
|
|
3
|
+
<button class="rpg-ui-close-button" type="button" title="Close" aria-label="Close dialog" click={closeDialogFromPointer}>x</button>
|
|
4
|
+
<div
|
|
5
|
+
class="rpg-ui-dialog-container"
|
|
6
|
+
data-position={dialogPosition()}
|
|
7
|
+
data-full-width={isFullWidth() ? "true" : "false"}
|
|
8
|
+
data-has-face={hasFace() ? "true" : "false"}
|
|
9
|
+
>
|
|
10
|
+
<div class="rpg-ui-dialog rpg-anim-fade-in" click={stopEvent}>
|
|
11
|
+
<div class="rpg-ui-dialog-body">
|
|
12
|
+
<div>
|
|
13
|
+
@if (speakerName()) {
|
|
14
|
+
<div class="rpg-ui-dialog-speaker">{speakerName()}</div>
|
|
15
|
+
}
|
|
16
|
+
<div class="rpg-ui-dialog-content">
|
|
17
|
+
{displayMessage()}
|
|
18
|
+
</div>
|
|
19
|
+
@if (hasChoices()) {
|
|
20
|
+
<Navigation tabindex={selectedItem} controls={controls}>
|
|
21
|
+
<div class="rpg-ui-dialog-choices">
|
|
22
|
+
@for ((choice,index) of dialogChoices()) {
|
|
23
|
+
<div
|
|
24
|
+
class="rpg-ui-dialog-choice"
|
|
25
|
+
class={{active: selectedItem() === index}}
|
|
26
|
+
tabindex={index}
|
|
27
|
+
data-choice-index={index}
|
|
28
|
+
click={selectChoice(index)}
|
|
29
|
+
>{{ choice.text }}</div>
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
</Navigation>
|
|
33
|
+
}
|
|
16
34
|
</div>
|
|
17
|
-
@if (
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
click={selectChoice(index)}
|
|
27
|
-
>{{ choice.text }}</div>
|
|
28
|
-
}
|
|
29
|
-
</div>
|
|
30
|
-
</Navigation>
|
|
35
|
+
@if (hasFace()) {
|
|
36
|
+
<div class="rpg-ui-dialog-face">
|
|
37
|
+
<DOMSprite
|
|
38
|
+
sheet={faceSheet(dialogFace())}
|
|
39
|
+
width="100%"
|
|
40
|
+
height="100%"
|
|
41
|
+
objectFit="contain"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
31
44
|
}
|
|
32
45
|
</div>
|
|
33
|
-
@if (
|
|
34
|
-
<div class="rpg-ui-dialog-
|
|
35
|
-
<DOMSprite
|
|
36
|
-
sheet={faceSheet(dialogFace())}
|
|
37
|
-
width="100%"
|
|
38
|
-
height="100%"
|
|
39
|
-
objectFit="contain"
|
|
40
|
-
/>
|
|
41
|
-
</div>
|
|
46
|
+
@if (showIndicator) {
|
|
47
|
+
<div class="rpg-ui-dialog-indicator"></div>
|
|
42
48
|
}
|
|
43
49
|
</div>
|
|
44
|
-
@if (showIndicator) {
|
|
45
|
-
<div class="rpg-ui-dialog-indicator"></div>
|
|
46
|
-
}
|
|
47
50
|
</div>
|
|
48
51
|
</div>
|
|
49
52
|
</DOMContainer>
|
|
@@ -160,6 +163,19 @@
|
|
|
160
163
|
if (onFinish) onFinish(value, normalizeOpenId(guiOpenId));
|
|
161
164
|
}
|
|
162
165
|
|
|
166
|
+
const stopEvent = (event) => {
|
|
167
|
+
event?.stopPropagation?.();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const closeDialog = () => {
|
|
171
|
+
_onFinish();
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const closeDialogFromPointer = (event) => {
|
|
175
|
+
stopEvent(event);
|
|
176
|
+
closeDialog();
|
|
177
|
+
};
|
|
178
|
+
|
|
163
179
|
const onSelect = (index) => {
|
|
164
180
|
_onFinish(index);
|
|
165
181
|
};
|
|
@@ -63,8 +63,9 @@
|
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
65
65
|
@if (confirmOpen) {
|
|
66
|
-
<div class="rpg-ui-menu-confirm">
|
|
67
|
-
<
|
|
66
|
+
<div class="rpg-ui-menu-confirm" click={cancelConfirmFromPointer}>
|
|
67
|
+
<button class="rpg-ui-close-button" type="button" title="Close" aria-label="Close confirmation" click={cancelConfirmFromPointer}>x</button>
|
|
68
|
+
<div class="rpg-ui-menu-confirm-card" click={stopEvent}>
|
|
68
69
|
<div class="rpg-ui-menu-confirm-title">{t("rpg.menu.use")} {confirmItem()?.name}?</div>
|
|
69
70
|
<Navigation tabindex={selectedConfirm} controls={confirmControls}>
|
|
70
71
|
<div class="rpg-ui-menu-confirm-actions">
|
|
@@ -197,6 +198,15 @@
|
|
|
197
198
|
});
|
|
198
199
|
};
|
|
199
200
|
|
|
201
|
+
const stopEvent = (event) => {
|
|
202
|
+
event?.stopPropagation?.();
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const cancelConfirmFromPointer = (event) => {
|
|
206
|
+
stopEvent(event);
|
|
207
|
+
cancelConfirm();
|
|
208
|
+
};
|
|
209
|
+
|
|
200
210
|
function confirmSelect(index) {
|
|
201
211
|
return function() {
|
|
202
212
|
selectedConfirm.set(index);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<DOMContainer width="100%" height="100%" controls={menuControls}>
|
|
2
|
-
<div class="rpg-ui-main-menu rpg-anim-fade-in">
|
|
3
|
-
<div class="rpg-ui-main-menu-
|
|
2
|
+
<div class="rpg-ui-main-menu rpg-anim-fade-in" click={closeMenuFromPointer}>
|
|
3
|
+
<div class="rpg-ui-main-menu-backdrop"></div>
|
|
4
|
+
<button class="rpg-ui-close-button" type="button" title="Close" aria-label="Close menu" click={closeMenuFromPointer}>x</button>
|
|
5
|
+
<div class="rpg-ui-main-menu-layout" click={stopEvent}>
|
|
4
6
|
<div class="rpg-ui-main-menu-left rpg-ui-menu rpg-ui-panel">
|
|
5
7
|
<div class="rpg-ui-menu-header">{t("rpg.menu.title")}</div>
|
|
6
8
|
<div class="rpg-ui-main-menu-list">
|
|
@@ -77,8 +79,8 @@
|
|
|
77
79
|
</div>
|
|
78
80
|
@if (saveOverlay) {
|
|
79
81
|
<div class="rpg-ui-main-menu-overlay">
|
|
80
|
-
<div class="rpg-ui-main-menu-overlay-backdrop"></div>
|
|
81
|
-
<div class="rpg-ui-main-menu-overlay-content">
|
|
82
|
+
<div class="rpg-ui-main-menu-overlay-backdrop" click={closeSaveOverlayFromPointer}></div>
|
|
83
|
+
<div class="rpg-ui-main-menu-overlay-content" click={stopEvent}>
|
|
82
84
|
<SaveLoadComponent
|
|
83
85
|
data={saveLoadData}
|
|
84
86
|
onFinish={closeSaveOverlay}
|
|
@@ -202,6 +204,28 @@
|
|
|
202
204
|
});
|
|
203
205
|
};
|
|
204
206
|
|
|
207
|
+
const stopEvent = (event) => {
|
|
208
|
+
event?.stopPropagation?.();
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const closeSaveOverlayFromPointer = (event) => {
|
|
212
|
+
stopEvent(event);
|
|
213
|
+
closeSaveOverlay();
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const closeMenu = () => {
|
|
217
|
+
if (saveOverlay()) {
|
|
218
|
+
closeSaveOverlay();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
onFinish();
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const closeMenuFromPointer = (event) => {
|
|
225
|
+
stopEvent(event);
|
|
226
|
+
closeMenu();
|
|
227
|
+
};
|
|
228
|
+
|
|
205
229
|
const confirmExit = () => {
|
|
206
230
|
if (onInteraction) onInteraction("exit");
|
|
207
231
|
};
|
|
@@ -285,11 +309,18 @@
|
|
|
285
309
|
});
|
|
286
310
|
|
|
287
311
|
mount((element) => {
|
|
312
|
+
const inputLockToken = {};
|
|
313
|
+
const previousStopProcessingInput = engine.stopProcessingInput;
|
|
314
|
+
const previousGamePause = engine.gamePause();
|
|
315
|
+
engine.__rpgMainMenuInputLock = inputLockToken;
|
|
316
|
+
engine.stopProcessingInput = true;
|
|
288
317
|
engine.gamePause.set(true);
|
|
289
318
|
return () => {
|
|
290
319
|
delay(() => {
|
|
291
|
-
engine.
|
|
292
|
-
engine.
|
|
320
|
+
if (engine.__rpgMainMenuInputLock !== inputLockToken) return;
|
|
321
|
+
engine.__rpgMainMenuInputLock = undefined;
|
|
322
|
+
engine.stopProcessingInput = previousStopProcessingInput;
|
|
323
|
+
engine.gamePause.set(previousGamePause);
|
|
293
324
|
});
|
|
294
325
|
}
|
|
295
326
|
});
|
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
<DOMContainer width="100%" height="100%">
|
|
2
|
-
<div class="rpg-ui-save-load
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
<div class="rpg-ui-save-load-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<div class="rpg-ui-save-load-list">
|
|
9
|
-
@for ((item,displayIndex) of displaySlots) {
|
|
10
|
-
<div
|
|
11
|
-
class="rpg-ui-save-load-slot"
|
|
12
|
-
class={{active: selectedSlot() === displayIndex}}
|
|
13
|
-
tabindex={displayIndex}
|
|
14
|
-
data-slot-index={displayIndex}
|
|
15
|
-
click={selectSlot(displayIndex)}
|
|
16
|
-
>
|
|
17
|
-
<div class="rpg-ui-save-load-slot-index">{item.label}</div>
|
|
18
|
-
@if (item.slot) {
|
|
19
|
-
<div class="rpg-ui-save-load-slot-meta">
|
|
20
|
-
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.level")}: {item.slot.level ?? "-"}</div>
|
|
21
|
-
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.exp")}: {item.slot.exp ?? "-"}</div>
|
|
22
|
-
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.map")}: {item.slot.map ?? "-"}</div>
|
|
23
|
-
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.date")}: {item.slot.date ?? "-"}</div>
|
|
24
|
-
</div>
|
|
25
|
-
}
|
|
26
|
-
@else {
|
|
27
|
-
<div class="rpg-ui-save-load-slot-empty">{t("rpg.save.empty-slot")}</div>
|
|
28
|
-
}
|
|
29
|
-
</div>
|
|
30
|
-
}
|
|
2
|
+
<div class="rpg-ui-save-load-layer" click={closeSaveLoadFromPointer}>
|
|
3
|
+
<button class="rpg-ui-close-button" type="button" title="Close" aria-label="Close save menu" click={closeSaveLoadFromPointer}>x</button>
|
|
4
|
+
<div class="rpg-ui-save-load rpg-anim-fade-in" click={stopEvent}>
|
|
5
|
+
<div class="rpg-ui-save-load-header">
|
|
6
|
+
<div class="rpg-ui-save-load-title">{title()}</div>
|
|
7
|
+
<div class="rpg-ui-save-load-subtitle">{subtitle()}</div>
|
|
31
8
|
</div>
|
|
32
|
-
|
|
9
|
+
<Navigation tabindex={selectedSlot} controls={controls}>
|
|
10
|
+
<div class="rpg-ui-save-load-list">
|
|
11
|
+
@for ((item,displayIndex) of displaySlots) {
|
|
12
|
+
<div
|
|
13
|
+
class="rpg-ui-save-load-slot"
|
|
14
|
+
class={{active: selectedSlot() === displayIndex}}
|
|
15
|
+
tabindex={displayIndex}
|
|
16
|
+
data-slot-index={displayIndex}
|
|
17
|
+
click={selectSlot(displayIndex)}
|
|
18
|
+
>
|
|
19
|
+
<div class="rpg-ui-save-load-slot-index">{item.label}</div>
|
|
20
|
+
@if (item.slot) {
|
|
21
|
+
<div class="rpg-ui-save-load-slot-meta">
|
|
22
|
+
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.level")}: {item.slot.level ?? "-"}</div>
|
|
23
|
+
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.exp")}: {item.slot.exp ?? "-"}</div>
|
|
24
|
+
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.map")}: {item.slot.map ?? "-"}</div>
|
|
25
|
+
<div class="rpg-ui-save-load-slot-line">{t("rpg.save.date")}: {item.slot.date ?? "-"}</div>
|
|
26
|
+
</div>
|
|
27
|
+
}
|
|
28
|
+
@else {
|
|
29
|
+
<div class="rpg-ui-save-load-slot-empty">{t("rpg.save.empty-slot")}</div>
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
}
|
|
33
|
+
</div>
|
|
34
|
+
</Navigation>
|
|
35
|
+
</div>
|
|
33
36
|
</div>
|
|
34
37
|
</DOMContainer>
|
|
35
38
|
|
|
@@ -169,6 +172,20 @@
|
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
|
|
175
|
+
const stopEvent = (event) => {
|
|
176
|
+
event?.stopPropagation?.();
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const closeSaveLoad = () => {
|
|
180
|
+
if (onFinish) onFinish();
|
|
181
|
+
gui.hide(PrebuiltGui.Save);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const closeSaveLoadFromPointer = (event) => {
|
|
185
|
+
stopEvent(event);
|
|
186
|
+
closeSaveLoad();
|
|
187
|
+
};
|
|
188
|
+
|
|
172
189
|
const controls = signal({
|
|
173
190
|
up: {
|
|
174
191
|
repeat: true,
|
|
@@ -195,8 +212,7 @@
|
|
|
195
212
|
escape: {
|
|
196
213
|
bind: keyboardControls.escape,
|
|
197
214
|
keyDown() {
|
|
198
|
-
|
|
199
|
-
gui.hide(PrebuiltGui.Save);
|
|
215
|
+
closeSaveLoad();
|
|
200
216
|
}
|
|
201
217
|
},
|
|
202
218
|
gamepad: {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<DOMContainer width="100%" height="100%">
|
|
2
|
-
<div class="rpg-shop-
|
|
2
|
+
<div class="rpg-shop-layer" click={closeShopFromPointer}>
|
|
3
|
+
<button class="rpg-ui-close-button" type="button" title="Close" aria-label="Close shop" click={closeShopFromPointer}>x</button>
|
|
4
|
+
<div class="rpg-shop-container rpg-anim-fade-in" click={stopEvent}>
|
|
3
5
|
<div class="rpg-shop-header">
|
|
4
6
|
<div class="rpg-shop-merchant">
|
|
5
7
|
<div>
|
|
@@ -143,8 +145,8 @@
|
|
|
143
145
|
</div>
|
|
144
146
|
}
|
|
145
147
|
@if (quantityDialogOpen) {
|
|
146
|
-
<div class="rpg-shop-modal">
|
|
147
|
-
<div class="rpg-shop-modal-card">
|
|
148
|
+
<div class="rpg-shop-modal" click={closeQuantityDialogFromPointer}>
|
|
149
|
+
<div class="rpg-shop-modal-card" click={stopEvent}>
|
|
148
150
|
<div class="rpg-shop-modal-title">{{ actionLabel }}</div>
|
|
149
151
|
<div class="rpg-shop-modal-item">{{ currentItem()?.name || "" }}</div>
|
|
150
152
|
@if (currentItem()?.quantity !== undefined) {
|
|
@@ -174,6 +176,7 @@
|
|
|
174
176
|
</div>
|
|
175
177
|
</div>
|
|
176
178
|
</div>
|
|
179
|
+
</div>
|
|
177
180
|
</DOMContainer>
|
|
178
181
|
|
|
179
182
|
<script>
|
|
@@ -351,6 +354,28 @@
|
|
|
351
354
|
}
|
|
352
355
|
}
|
|
353
356
|
|
|
357
|
+
const stopEvent = (event) => {
|
|
358
|
+
event?.stopPropagation?.()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const closeQuantityDialogFromPointer = (event) => {
|
|
362
|
+
stopEvent(event)
|
|
363
|
+
quantityDialogOpen.set(false)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const closeShop = () => {
|
|
367
|
+
if (quantityDialogOpen()) {
|
|
368
|
+
quantityDialogOpen.set(false)
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
onFinish()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const closeShopFromPointer = (event) => {
|
|
375
|
+
stopEvent(event)
|
|
376
|
+
closeShop()
|
|
377
|
+
}
|
|
378
|
+
|
|
354
379
|
function confirmTrade() {
|
|
355
380
|
return function() {
|
|
356
381
|
const item = currentItem()
|
|
@@ -51,4 +51,51 @@ describe("standalone websocket bridge", () => {
|
|
|
51
51
|
expect(standaloneProvider.useFactory(context).mode).toBe("standalone");
|
|
52
52
|
expect(mmorpgProvider.useFactory(context).mode).toBe("mmorpg");
|
|
53
53
|
});
|
|
54
|
+
|
|
55
|
+
test("reconnects standalone rooms with updated room and query", async () => {
|
|
56
|
+
const connects: Array<{ roomId: string; url: string; sessionId: string }> = [];
|
|
57
|
+
class Server {
|
|
58
|
+
subRoom = {
|
|
59
|
+
onStart: vi.fn(),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
constructor(public room: any) {}
|
|
63
|
+
|
|
64
|
+
async onStart() {}
|
|
65
|
+
|
|
66
|
+
async onConnect(conn: any, ctx: any) {
|
|
67
|
+
connects.push({
|
|
68
|
+
roomId: this.room.id,
|
|
69
|
+
url: ctx.request.url,
|
|
70
|
+
sessionId: conn.sessionId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const context = new Context();
|
|
76
|
+
const standaloneProvider = provideRpg(Server).find(
|
|
77
|
+
(provider: any) => provider.provide === WebSocketToken,
|
|
78
|
+
) as any;
|
|
79
|
+
const socket = standaloneProvider.useFactory(context);
|
|
80
|
+
|
|
81
|
+
await socket.connection();
|
|
82
|
+
socket.updateProperties({
|
|
83
|
+
room: "map-center-map",
|
|
84
|
+
query: { transferToken: "token-1" },
|
|
85
|
+
});
|
|
86
|
+
await socket.reconnect();
|
|
87
|
+
|
|
88
|
+
expect(connects).toEqual([
|
|
89
|
+
expect.objectContaining({
|
|
90
|
+
roomId: "lobby-1",
|
|
91
|
+
sessionId: "player-client-id",
|
|
92
|
+
}),
|
|
93
|
+
expect.objectContaining({
|
|
94
|
+
roomId: "map-center-map",
|
|
95
|
+
sessionId: "player-client-id",
|
|
96
|
+
}),
|
|
97
|
+
]);
|
|
98
|
+
expect(connects[1].url).toContain("transferToken=token-1");
|
|
99
|
+
expect(socket.getServer().room.id).toBe("map-center-map");
|
|
100
|
+
});
|
|
54
101
|
});
|