111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
/**
|
|
* Pausenmenü (Esc) mit Lautstärkereglern. Als HTML-Overlay umgesetzt, damit die
|
|
* Schieberegler per Maus, Tastatur und Screenreader bedienbar sind.
|
|
* @module scenes/PauseScene
|
|
*/
|
|
|
|
import { ORBITAL } from '../config.js';
|
|
import { formatSequence } from '../logic/format.js';
|
|
import { PAUSE } from '../content/texts.js';
|
|
import { oncePerEvent } from '../systems/keyboard.js';
|
|
import { el, Overlay } from '../systems/Overlay.js';
|
|
import { VOLUME_CHANNELS } from '../systems/Settings.js';
|
|
|
|
export class PauseScene extends Phaser.Scene {
|
|
constructor() {
|
|
super('Pause');
|
|
}
|
|
|
|
create() {
|
|
const { width, height } = this.scale;
|
|
const services = this.registry.get('services');
|
|
this.input.setDefaultCursor('default');
|
|
// Solange die Pause offen ist, soll der Browser Tasten normal verarbeiten
|
|
// (Pfeiltasten für Schieberegler, Tab für den Fokus).
|
|
this.input.keyboard.disableGlobalCapture();
|
|
services.audio.duck(true);
|
|
this.add.rectangle(0, 0, width, height, 0x000000, 0.45).setOrigin(0);
|
|
|
|
const content = [el('p', 'disclaimer__lead', PAUSE.subtitle)];
|
|
for (const channel of VOLUME_CHANNELS) {
|
|
content.push(this.volumeField(channel, services.settings));
|
|
}
|
|
const codes = el('p', 'field__hint');
|
|
codes.textContent = ORBITAL.commands
|
|
.map((c) => `${c.shortName}: ${formatSequence(c.sequence)}`)
|
|
.join(' · ');
|
|
content.push(codes);
|
|
|
|
let quitArmed = false;
|
|
this.overlay = new Overlay({
|
|
id: 'pause-overlay',
|
|
title: PAUSE.title,
|
|
content,
|
|
buttons: [
|
|
{ label: PAUSE.resume, onClick: () => this.resume(), testId: 'pause-resume' },
|
|
{
|
|
label: PAUSE.quit,
|
|
secondary: true,
|
|
testId: 'pause-quit',
|
|
onClick: () => {
|
|
// Zweistufig statt Browser-Dialog: erster Klick fragt nach, zweiter bricht ab.
|
|
if (!quitArmed) {
|
|
quitArmed = true;
|
|
quitButton.textContent = PAUSE.confirmQuit;
|
|
return;
|
|
}
|
|
this.quit();
|
|
},
|
|
},
|
|
],
|
|
onEscape: () => this.resume(),
|
|
});
|
|
const quitButton = this.overlay.actions.lastElementChild;
|
|
|
|
this.input.keyboard.on(
|
|
'keydown-ESC',
|
|
oncePerEvent((event) => {
|
|
if (!event.repeat) this.resume();
|
|
}),
|
|
);
|
|
this.events.once('shutdown', () => {
|
|
services.audio.duck(false);
|
|
if (this.overlay.element.isConnected) this.overlay.close();
|
|
this.input.keyboard?.enableGlobalCapture();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Schieberegler für einen Lautstärkekanal.
|
|
* @param {'master'|'music'|'sfx'} channel
|
|
* @param {import('../systems/Settings.js').Settings} settings
|
|
* @returns {HTMLElement}
|
|
*/
|
|
volumeField(channel, settings) {
|
|
const field = el('label', 'field');
|
|
const label = el('span', 'field__label', PAUSE.volumes[channel]);
|
|
const input = /** @type {HTMLInputElement} */ (el('input', 'field__input'));
|
|
input.type = 'range';
|
|
input.min = '0';
|
|
input.max = '100';
|
|
input.step = '5';
|
|
input.value = String(Math.round(settings.getVolume(channel) * 100));
|
|
input.dataset.testid = `volume-${channel}`;
|
|
input.addEventListener('input', () => settings.setVolume(channel, Number(input.value) / 100));
|
|
field.append(label, input);
|
|
return field;
|
|
}
|
|
|
|
resume() {
|
|
this.scene.resume('Game');
|
|
this.scene.get('Game').input.setDefaultCursor('none');
|
|
this.scene.stop();
|
|
}
|
|
|
|
quit() {
|
|
this.scene.stop('Hud');
|
|
this.scene.stop('Game');
|
|
this.scene.start('Menu');
|
|
}
|
|
}
|