@rpgjs/client 5.0.0-beta.23 → 5.0.0-beta.25
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 +29 -0
- package/dist/Game/Object.d.ts +5 -0
- package/dist/Game/Object.js +48 -2
- package/dist/Game/Object.js.map +1 -1
- package/dist/Gui/Gui.js +5 -0
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/components/character.ce.js +9 -2
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +54 -4
- 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/index.d.ts +3 -1
- package/dist/components/gui/index.js +2 -0
- package/dist/components/gui/input-field.ce.js +123 -0
- package/dist/components/gui/input-field.ce.js.map +1 -0
- package/dist/components/gui/input.ce.js +40 -0
- package/dist/components/gui/input.ce.js.map +1 -0
- 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/i18n.d.ts +11 -0
- package/dist/i18n.js +11 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.js +13 -11
- package/package.json +4 -4
- package/src/Game/Object.spec.ts +26 -2
- package/src/Game/Object.ts +71 -2
- package/src/Gui/Gui.spec.ts +5 -0
- package/src/Gui/Gui.ts +5 -1
- package/src/components/character.ce +12 -6
- package/src/components/gui/dialogbox/index.ce +82 -41
- package/src/components/gui/hud/hud.ce +2 -2
- package/src/components/gui/index.ts +5 -1
- package/src/components/gui/input-field.ce +106 -0
- package/src/components/gui/input.ce +26 -0
- 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/i18n.ts +11 -0
- package/vite.config.ts +5 -2
|
@@ -1,49 +1,61 @@
|
|
|
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
|
+
}
|
|
34
|
+
@if (hasInput()) {
|
|
35
|
+
<InputField
|
|
36
|
+
data={dialogInput}
|
|
37
|
+
layout="dialog"
|
|
38
|
+
showMessage={false}
|
|
39
|
+
submit={submitInput}
|
|
40
|
+
cancel={cancelInput}
|
|
41
|
+
/>
|
|
42
|
+
}
|
|
16
43
|
</div>
|
|
17
|
-
@if (
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
click={selectChoice(index)}
|
|
27
|
-
>{{ choice.text }}</div>
|
|
28
|
-
}
|
|
29
|
-
</div>
|
|
30
|
-
</Navigation>
|
|
44
|
+
@if (hasFace()) {
|
|
45
|
+
<div class="rpg-ui-dialog-face">
|
|
46
|
+
<DOMSprite
|
|
47
|
+
sheet={faceSheet(dialogFace())}
|
|
48
|
+
width="100%"
|
|
49
|
+
height="100%"
|
|
50
|
+
objectFit="contain"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
31
53
|
}
|
|
32
54
|
</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>
|
|
55
|
+
@if (showIndicator) {
|
|
56
|
+
<div class="rpg-ui-dialog-indicator"></div>
|
|
42
57
|
}
|
|
43
58
|
</div>
|
|
44
|
-
@if (showIndicator) {
|
|
45
|
-
<div class="rpg-ui-dialog-indicator"></div>
|
|
46
|
-
}
|
|
47
59
|
</div>
|
|
48
60
|
</div>
|
|
49
61
|
</DOMContainer>
|
|
@@ -54,6 +66,7 @@
|
|
|
54
66
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
55
67
|
import { delay } from "@rpgjs/common";
|
|
56
68
|
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
69
|
+
import InputField from "../input-field.ce";
|
|
57
70
|
|
|
58
71
|
const engine = inject(RpgClientEngine);
|
|
59
72
|
const currentPlayer = engine.scene.currentPlayer
|
|
@@ -79,6 +92,8 @@
|
|
|
79
92
|
const position = computed(() => dialogData().position);
|
|
80
93
|
const typewriterEffect = computed(() => dialogData().typewriterEffect);
|
|
81
94
|
const fullWidth = computed(() => dialogData().fullWidth || false);
|
|
95
|
+
const dialogInput = computed(() => dialogData().input || null);
|
|
96
|
+
const hasInput = computed(() => !!dialogInput());
|
|
82
97
|
|
|
83
98
|
const resolveProp = (value) => typeof value === "function" ? value() : value;
|
|
84
99
|
const normalizeOpenId = (value) => {
|
|
@@ -146,7 +161,7 @@
|
|
|
146
161
|
|
|
147
162
|
|
|
148
163
|
const hasChoices = computed(() => dialogChoices().length > 0);
|
|
149
|
-
const showIndicator = computed(() => !hasChoices() && !isTyping());
|
|
164
|
+
const showIndicator = computed(() => !hasChoices() && !hasInput() && !isTyping());
|
|
150
165
|
const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, 'wrap');
|
|
151
166
|
|
|
152
167
|
function selectChoice(index) {
|
|
@@ -160,10 +175,35 @@
|
|
|
160
175
|
if (onFinish) onFinish(value, normalizeOpenId(guiOpenId));
|
|
161
176
|
}
|
|
162
177
|
|
|
178
|
+
const stopEvent = (event) => {
|
|
179
|
+
event?.stopPropagation?.();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const closeDialog = () => {
|
|
183
|
+
if (hasInput()) {
|
|
184
|
+
onInteraction?.("cancel");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
_onFinish();
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const closeDialogFromPointer = (event) => {
|
|
191
|
+
stopEvent(event);
|
|
192
|
+
closeDialog();
|
|
193
|
+
};
|
|
194
|
+
|
|
163
195
|
const onSelect = (index) => {
|
|
164
196
|
_onFinish(index);
|
|
165
197
|
};
|
|
166
198
|
|
|
199
|
+
const submitInput = (payload) => {
|
|
200
|
+
onInteraction?.("submit", payload);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const cancelInput = () => {
|
|
204
|
+
onInteraction?.("cancel");
|
|
205
|
+
};
|
|
206
|
+
|
|
167
207
|
const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;
|
|
168
208
|
|
|
169
209
|
const controls = signal({
|
|
@@ -215,6 +255,7 @@
|
|
|
215
255
|
finishTyping();
|
|
216
256
|
return;
|
|
217
257
|
}
|
|
258
|
+
if (hasInput()) return;
|
|
218
259
|
if (hasChoices()) return;
|
|
219
260
|
_onFinish();
|
|
220
261
|
}
|
|
@@ -12,6 +12,8 @@ import ExitMenuComponent from "./menu/exit-menu.ce";
|
|
|
12
12
|
import NotificationComponent from "./notification/notification.ce";
|
|
13
13
|
import TitleScreenComponent from "./title-screen.ce";
|
|
14
14
|
import GameoverComponent from "./gameover.ce";
|
|
15
|
+
import InputComponent from "./input.ce";
|
|
16
|
+
import InputFieldComponent from "./input-field.ce";
|
|
15
17
|
|
|
16
18
|
export {
|
|
17
19
|
DialogboxComponent,
|
|
@@ -27,5 +29,7 @@ export {
|
|
|
27
29
|
ExitMenuComponent,
|
|
28
30
|
NotificationComponent,
|
|
29
31
|
TitleScreenComponent,
|
|
30
|
-
GameoverComponent
|
|
32
|
+
GameoverComponent,
|
|
33
|
+
InputComponent,
|
|
34
|
+
InputFieldComponent
|
|
31
35
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<form
|
|
2
|
+
class="rpg-ui-input-form"
|
|
3
|
+
data-layout={layout()}
|
|
4
|
+
submit={submitForm}
|
|
5
|
+
keydown={handleKeydown}
|
|
6
|
+
>
|
|
7
|
+
@if (showMessage()) {
|
|
8
|
+
<label class="rpg-ui-input-message" for={fieldId()}>{formData().message}</label>
|
|
9
|
+
}
|
|
10
|
+
@if (formData().control === "textarea") {
|
|
11
|
+
<textarea
|
|
12
|
+
id={fieldId()}
|
|
13
|
+
class="rpg-ui-input rpg-ui-input-dialog-field"
|
|
14
|
+
name="value"
|
|
15
|
+
value={value}
|
|
16
|
+
placeholder={formData().placeholder || ""}
|
|
17
|
+
rows={formData().rows || 4}
|
|
18
|
+
minlength={formData().minLength}
|
|
19
|
+
maxlength={formData().maxLength}
|
|
20
|
+
aria-required={formData().required ? "true" : "false"}
|
|
21
|
+
></textarea>
|
|
22
|
+
}
|
|
23
|
+
@else {
|
|
24
|
+
<input
|
|
25
|
+
id={fieldId()}
|
|
26
|
+
class="rpg-ui-input rpg-ui-input-dialog-field"
|
|
27
|
+
name="value"
|
|
28
|
+
type={formData().type || "text"}
|
|
29
|
+
value={value}
|
|
30
|
+
placeholder={formData().placeholder || ""}
|
|
31
|
+
min={formData().min}
|
|
32
|
+
max={formData().max}
|
|
33
|
+
step={formData().step}
|
|
34
|
+
minlength={formData().minLength}
|
|
35
|
+
maxlength={formData().maxLength}
|
|
36
|
+
aria-required={formData().required ? "true" : "false"}
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
@if (errorText()) {
|
|
40
|
+
<div class="rpg-ui-input-error" role="alert">{errorText()}</div>
|
|
41
|
+
}
|
|
42
|
+
<div class="rpg-ui-input-actions">
|
|
43
|
+
@if (showCancelButton()) {
|
|
44
|
+
<button class="rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-cancel" type="button" click={cancelForm}>
|
|
45
|
+
{formData().cancelText || t("rpg.input.cancel")}
|
|
46
|
+
</button>
|
|
47
|
+
}
|
|
48
|
+
<button
|
|
49
|
+
class="rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-confirm"
|
|
50
|
+
data-variant="primary"
|
|
51
|
+
type="submit"
|
|
52
|
+
>
|
|
53
|
+
{formData().confirmText || t("rpg.input.confirm")}
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
</form>
|
|
57
|
+
|
|
58
|
+
<script>
|
|
59
|
+
import { computed, effect, signal } from "canvasengine";
|
|
60
|
+
import { inject } from "../../core/inject";
|
|
61
|
+
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
62
|
+
|
|
63
|
+
const engine = inject(RpgClientEngine);
|
|
64
|
+
const { t } = engine.i18n();
|
|
65
|
+
const { data, layout, showMessage } = defineProps({
|
|
66
|
+
layout: "standalone",
|
|
67
|
+
showMessage: true,
|
|
68
|
+
});
|
|
69
|
+
const { submit, cancel } = defineEmits();
|
|
70
|
+
const formData = computed(() => data() || {});
|
|
71
|
+
const value = signal(String(formData().defaultValue ?? ""));
|
|
72
|
+
const fieldId = computed(() => `rpg-input-value-${layout()}`);
|
|
73
|
+
const errorText = computed(() => formData().errorKey
|
|
74
|
+
? t(formData().errorKey, formData().errorParams || {})
|
|
75
|
+
: ""
|
|
76
|
+
);
|
|
77
|
+
const showCancelButton = computed(() => formData().cancelButton !== false);
|
|
78
|
+
let focused = false;
|
|
79
|
+
|
|
80
|
+
function submitForm(event, collectedData) {
|
|
81
|
+
const submittedValue = collectedData && Object.prototype.hasOwnProperty.call(collectedData, "value")
|
|
82
|
+
? collectedData.value
|
|
83
|
+
: event?.currentTarget?.elements?.value?.value ?? value();
|
|
84
|
+
value.set(String(submittedValue ?? ""));
|
|
85
|
+
submit({ value: submittedValue ?? "" });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function cancelForm() {
|
|
89
|
+
cancel();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function handleKeydown(event) {
|
|
93
|
+
if (event?.key !== "Escape") return;
|
|
94
|
+
event.preventDefault?.();
|
|
95
|
+
event.stopPropagation?.();
|
|
96
|
+
cancelForm();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
effect(() => {
|
|
100
|
+
if (focused) return;
|
|
101
|
+
focused = true;
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
document.getElementById(fieldId())?.focus?.();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<DOMContainer width="100%" height="100%">
|
|
2
|
+
<div class="rpg-ui-input-layer">
|
|
3
|
+
<div class="rpg-ui-input-dialog rpg-ui-window rpg-anim-fade-in">
|
|
4
|
+
@if (title()) {
|
|
5
|
+
<div class="rpg-ui-window-title">{title()}</div>
|
|
6
|
+
}
|
|
7
|
+
<InputField data={data} layout="standalone" submit={submit} cancel={cancel} />
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</DOMContainer>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import { computed } from "canvasengine";
|
|
14
|
+
import InputField from "./input-field.ce";
|
|
15
|
+
|
|
16
|
+
const { data, onInteraction } = defineProps();
|
|
17
|
+
const title = computed(() => data().title || "");
|
|
18
|
+
|
|
19
|
+
function submit(payload) {
|
|
20
|
+
onInteraction?.("submit", payload);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cancel() {
|
|
24
|
+
onInteraction?.("cancel");
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
@@ -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()
|
package/src/i18n.ts
CHANGED
|
@@ -40,6 +40,17 @@ export const RpgClientBuiltinI18n = {
|
|
|
40
40
|
"rpg.gameover.title": "Game Over",
|
|
41
41
|
"rpg.gameover.title-screen": "Title Screen",
|
|
42
42
|
"rpg.gameover.load-game": "Load Game",
|
|
43
|
+
"rpg.input.confirm": "Confirm",
|
|
44
|
+
"rpg.input.cancel": "Cancel",
|
|
45
|
+
"rpg.input.error.invalid": "Invalid value.",
|
|
46
|
+
"rpg.input.error.required": "A value is required.",
|
|
47
|
+
"rpg.input.error.number": "Enter a valid number.",
|
|
48
|
+
"rpg.input.error.min": "The value must be at least {min}.",
|
|
49
|
+
"rpg.input.error.max": "The value must be at most {max}.",
|
|
50
|
+
"rpg.input.error.step": "The value must use increments of {step}.",
|
|
51
|
+
"rpg.input.error.min-length": "Enter at least {minLength} characters.",
|
|
52
|
+
"rpg.input.error.max-length": "Enter no more than {maxLength} characters.",
|
|
53
|
+
"rpg.input.error.email": "Enter a valid email address.",
|
|
43
54
|
"rpg.shop.default-message": "Welcome to my shop!",
|
|
44
55
|
"rpg.shop.choose-action": "Choose an action",
|
|
45
56
|
"rpg.shop.buy": "Buy",
|
package/vite.config.ts
CHANGED
|
@@ -11,7 +11,10 @@ export default defineConfig({
|
|
|
11
11
|
canvasengine(),
|
|
12
12
|
dts({
|
|
13
13
|
include: ['src/**/*.ts'],
|
|
14
|
-
|
|
14
|
+
outDirs: 'dist',
|
|
15
|
+
afterDiagnostic(diagnostics) {
|
|
16
|
+
if (diagnostics.length > 0) throw new Error(`Declaration generation failed with ${diagnostics.length} TypeScript diagnostic(s)`)
|
|
17
|
+
}
|
|
15
18
|
})
|
|
16
19
|
],
|
|
17
20
|
build: {
|
|
@@ -33,4 +36,4 @@ export default defineConfig({
|
|
|
33
36
|
}
|
|
34
37
|
},
|
|
35
38
|
},
|
|
36
|
-
})
|
|
39
|
+
})
|