fix: apply audio volume during fades and redesign player settings
This commit is contained in:
1 parent
fa82c99a52
commit
06ff19ac4a
5 files changed
+276
-35
No files matched your search
@@ -129,7 +129,13 @@ export class Engine {
|
||||
this.imagesByTag.get(tag)!.push({ attrs, key });
|
||||
}
|
||||
|
||||
this.settings = loadJSON("pgvn:settings", DEFAULT_SETTINGS);
|
||||
this.settings = { ...loadJSON("pgvn:settings", DEFAULT_SETTINGS) };
|
||||
for (const key of Object.keys(DEFAULT_SETTINGS) as (keyof Settings)[]) {
|
||||
const value = this.settings[key];
|
||||
const [min, max] = key === "cps" ? [0, 150] : key === "autoDelay" ? [0.3, 4] : [0, 1];
|
||||
this.settings[key] = typeof value === "number" && Number.isFinite(value)
|
||||
? Math.max(min, Math.min(max, value)) : DEFAULT_SETTINGS[key];
|
||||
}
|
||||
this.persistent = this.makePersistent();
|
||||
const scope: Scope = {
|
||||
get: (n) => (n in this.store ? this.store[n] : this.builtins[n]),
|
||||
@@ -1276,7 +1282,6 @@ export class Engine {
|
||||
}
|
||||
|
||||
private onKey = (ev: KeyboardEvent) => {
|
||||
if ((ev.target as HTMLElement)?.tagName === "INPUT") return;
|
||||
const screenOpen = !this.screen.classList.contains("pg-hidden");
|
||||
if (ev.key === "Escape") {
|
||||
if (this.screen.classList.contains("pg-main")) return;
|
||||
@@ -1284,6 +1289,7 @@ export class Engine {
|
||||
else if (this.running) this.openGameMenu();
|
||||
return;
|
||||
}
|
||||
if ((ev.target as HTMLElement)?.tagName === "INPUT") return;
|
||||
if (screenOpen) return;
|
||||
if (ev.key === " " || ev.key === "Enter") {
|
||||
ev.preventDefault();
|
||||
@@ -1306,7 +1312,9 @@ export class Engine {
|
||||
|
||||
private screenOnClose: (() => void) | null = null;
|
||||
|
||||
private openScreen(title: string, build: (body: HTMLElement) => void, onClose?: () => void) {
|
||||
private openScreen(title: string, build: (body: HTMLElement) => void, onClose?: () => void,
|
||||
unscaled = false) {
|
||||
this.placeScreen(unscaled);
|
||||
this.screen.textContent = "";
|
||||
this.screen.classList.remove("pg-main");
|
||||
this.container.classList.remove("pg-in-main");
|
||||
@@ -1315,6 +1323,7 @@ export class Engine {
|
||||
const head = el("div", "pg-screen-head");
|
||||
head.append(el("h2", "", title));
|
||||
const close = el("button", "pg-close", "✕");
|
||||
close.setAttribute("aria-label", "Закрыть");
|
||||
close.onclick = () => this.dismissScreen();
|
||||
head.append(close);
|
||||
const body = el("div", "pg-screen-body");
|
||||
@@ -1335,9 +1344,16 @@ export class Engine {
|
||||
this.screen.classList.remove("pg-main");
|
||||
this.container.classList.remove("pg-in-main");
|
||||
this.screen.style.backgroundImage = "";
|
||||
this.placeScreen(false);
|
||||
}
|
||||
|
||||
private placeScreen(unscaled: boolean) {
|
||||
this.screen.classList.toggle("pg-settings-screen", unscaled);
|
||||
(unscaled ? this.container : this.renderer.stage).append(this.screen);
|
||||
}
|
||||
|
||||
private showMainMenu() {
|
||||
this.placeScreen(false);
|
||||
this.running = false;
|
||||
this.gen++;
|
||||
this.wakeAll();
|
||||
@@ -1467,39 +1483,63 @@ export class Engine {
|
||||
private openSettings(fromMain = false) {
|
||||
this.openScreen("Настройки", (body) => {
|
||||
const s = this.settings;
|
||||
const row = (label: string, min: number, max: number, step: number, value: number,
|
||||
const layout = el("div", "pg-settings");
|
||||
body.append(layout);
|
||||
const section = (title: string, description: string) => {
|
||||
const card = el("section", "pg-settings-card");
|
||||
card.append(el("h3", "", title), el("p", "pg-settings-hint", description));
|
||||
layout.append(card);
|
||||
return card;
|
||||
};
|
||||
const row = (parent: HTMLElement, label: string, min: number, max: number, step: number, value: number,
|
||||
fmt: (v: number) => string, set: (v: number) => void) => {
|
||||
const r = el("label", "pg-setting");
|
||||
const out = el("span", "pg-setting-val", fmt(value));
|
||||
const heading = el("span", "pg-setting-head");
|
||||
const out = el("output", "pg-setting-val", fmt(value));
|
||||
const input = el("input");
|
||||
Object.assign(input, { type: "range", min, max, step, value: String(value) });
|
||||
input.setAttribute("aria-label", label);
|
||||
const update = () => {
|
||||
out.value = fmt(Number(input.value));
|
||||
input.setAttribute("aria-valuetext", out.value);
|
||||
input.style.setProperty("--pg-progress", `${(Number(input.value) - min) / (max - min) * 100}%`);
|
||||
};
|
||||
update();
|
||||
input.oninput = () => {
|
||||
set(Number(input.value));
|
||||
out.textContent = fmt(Number(input.value));
|
||||
update();
|
||||
saveJSON("pgvn:settings", this.settings);
|
||||
this.audio.setVolumes(this.settings);
|
||||
};
|
||||
r.append(el("span", "", label), input, out);
|
||||
body.append(r);
|
||||
heading.append(el("span", "", label), out);
|
||||
r.append(heading, input);
|
||||
parent.append(r);
|
||||
};
|
||||
row("Скорость текста", 0, 150, 5, s.cps, (v) => (v === 0 ? "мгновенно" : `${v} симв/с`), (v) => (s.cps = v));
|
||||
row("Задержка авто", 0.3, 4, 0.1, s.autoDelay, (v) => `${v.toFixed(1)} с`, (v) => (s.autoDelay = v));
|
||||
row("Музыка", 0, 1, 0.05, s.music, (v) => `${Math.round(v * 100)}%`, (v) => (s.music = v));
|
||||
row("Звуки", 0, 1, 0.05, s.sound, (v) => `${Math.round(v * 100)}%`, (v) => (s.sound = v));
|
||||
row("Голос", 0, 1, 0.05, s.voice, (v) => `${Math.round(v * 100)}%`, (v) => (s.voice = v));
|
||||
const audio = section("Звук", "Громкость меняется сразу. 0% — выключить канал.");
|
||||
const percent = (v: number) => `${Math.round(v * 100)}%`;
|
||||
row(audio, "Музыка", 0, 1, 0.01, s.music, percent, (v) => (s.music = v));
|
||||
row(audio, "Звуки", 0, 1, 0.01, s.sound, percent, (v) => (s.sound = v));
|
||||
row(audio, "Голос", 0, 1, 0.01, s.voice, percent, (v) => (s.voice = v));
|
||||
const reading = section("Чтение", "Настройте темп появления текста и автопрочтения.");
|
||||
row(reading, "Скорость текста", 0, 150, 5, s.cps, (v) => (v === 0 ? "Мгновенно" : `${v} симв/с`), (v) => (s.cps = v));
|
||||
row(reading, "Задержка авто", 0.3, 4, 0.1, s.autoDelay, (v) => `${v.toFixed(1)} с`, (v) => (s.autoDelay = v));
|
||||
const display = section("Игра", "Изменения сохраняются автоматически в этом браузере.");
|
||||
const langs = this.languages();
|
||||
if (langs.length > 1) {
|
||||
const r = el("label", "pg-setting");
|
||||
r.append(el("span", "", "Язык"), this.languageSelect(langs, () => {}), el("span", ""));
|
||||
body.append(r);
|
||||
r.append(el("span", "", "Язык"), this.languageSelect(langs, () => {}));
|
||||
display.append(r);
|
||||
}
|
||||
const fs = el("button", "pg-wide", "Полный экран");
|
||||
const fs = el("button", "pg-wide", document.fullscreenElement ? "Выйти из полного экрана" : "Полный экран");
|
||||
fs.onclick = () => {
|
||||
if (document.fullscreenElement) void document.exitFullscreen();
|
||||
else void this.root.requestFullscreen();
|
||||
const action = document.fullscreenElement ? document.exitFullscreen() : this.root.requestFullscreen();
|
||||
void action.then(() => {
|
||||
fs.textContent = document.fullscreenElement ? "Выйти из полного экрана" : "Полный экран";
|
||||
}).catch(() => this.notify("Не удалось изменить полноэкранный режим"));
|
||||
};
|
||||
body.append(fs);
|
||||
}, fromMain ? () => this.showMainMenu() : undefined);
|
||||
fs.hidden = !document.fullscreenEnabled;
|
||||
display.append(fs);
|
||||
}, fromMain ? () => this.showMainMenu() : undefined, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user