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