Files
2026-09-24 17:57:54 +02:00

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');
}
}