Files
slop-game-opus5.5/app/static/js/scenes/MenuScene.js
T
2026-09-24 17:57:54 +02:00

119 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Hauptmenü mit Propaganda-Laufband. Beim allerersten Start wird zuerst der
* Disclaimer angezeigt; das Menü ist erst danach bedienbar.
* @module scenes/MenuScene
*/
import { GAME } from '../config.js';
import { MENU, PROPAGANDA, TAGLINE, TITLE } from '../content/texts.js';
import { TEXTURE_KEYS } from '../systems/Placeholders.js';
import { MenuList } from '../ui/MenuList.js';
import { textStyle, UI, uiStyle } from '../ui/theme.js';
/** Geschwindigkeit des Laufbands in px/s. */
const TICKER_SPEED = 90;
/** Anzahl Scheinwerferkegel im Hintergrund. */
const SEARCHLIGHTS = 3;
export class MenuScene extends Phaser.Scene {
constructor() {
super('Menu');
}
create() {
const { width, height } = this.scale;
const services = this.registry.get('services');
this.input.setDefaultCursor('default');
this.drawBackground(width, height);
this.add.image(width / 2, 150, TEXTURE_KEYS.emblem).setDisplaySize(150, 150);
this.add
.text(width / 2, 262, TITLE, textStyle({ fontSize: '68px', color: UI.colors.gold, strokeThickness: 10, stroke: '#7a0a12' }))
.setOrigin(0.5);
this.add.text(width / 2, 318, TAGLINE, textStyle({ fontSize: '24px' })).setOrigin(0.5);
this.menu = new MenuList(this, width / 2, 372, [
{ label: MENU.start, onSelect: () => this.startMission() },
{ label: MENU.highscores, onSelect: () => this.scene.start('Highscores') },
{ label: MENU.controls, onSelect: () => this.scene.start('Controls') },
{ label: MENU.disclaimer, onSelect: () => this.openDisclaimer(false) },
{ label: MENU.credits, onSelect: () => this.scene.start('Credits') },
], { spacing: 54 });
this.add.text(width / 2, height - 62, MENU.hint, uiStyle({ fontSize: '16px', color: UI.colors.muted })).setOrigin(0.5);
this.createTicker(width, height);
// Reine Touch-Geräte (Handy, Tablet) haben weder WASD noch Maus – lieber gleich sagen.
if (globalThis.matchMedia?.('(pointer: coarse)').matches && !globalThis.matchMedia?.('(any-pointer: fine)').matches) {
this.add.text(width / 2, height - 92, MENU.touchNotice, uiStyle({ fontSize: '18px', color: UI.colors.danger })).setOrigin(0.5);
}
// Ton darf erst nach einer Nutzeraktion starten (Browser-Richtlinie); die Musik
// wird vorgemerkt und beginnt mit dem ersten Klick oder Tastendruck.
services.audio.startMusic();
this.input.once('pointerdown', () => services.audio.unlock());
this.input.keyboard.once('keydown', () => services.audio.unlock());
if (!services.settings.isDisclaimerAccepted()) {
this.openDisclaimer(true);
}
}
/**
* @param {boolean} mandatory Beim ersten Start muss bestätigt werden.
*/
openDisclaimer(mandatory) {
this.menu.setEnabled(false);
this.scene.launch('Disclaimer', { mandatory });
this.scene.get('Disclaimer').events.once('disclaimer-closed', () => {
this.menu.setEnabled(true);
});
}
startMission() {
this.registry.get('services').audio.unlock();
this.scene.start('Briefing');
}
drawBackground(width, height) {
const bg = this.add.graphics();
bg.fillGradientStyle(0x0d1321, 0x0d1321, 0x2a0a10, 0x2a0a10, 1);
bg.fillRect(0, 0, width, height);
// Diagonale Streifen wie auf einem Propagandaplakat.
bg.fillStyle(0xc1121f, 0.08);
for (let x = -height; x < width; x += 90) {
bg.fillTriangle(x, height, x + 45, height, x + height + 45, 0);
}
for (let i = 0; i < SEARCHLIGHTS; i += 1) {
const beam = this.add.triangle(width * (0.2 + 0.3 * i), height + 20, 0, 0, -120, -height * 1.1, 120, -height * 1.1, 0xf2c14e, 0.06);
beam.setOrigin(0, 0);
this.tweens.add({
targets: beam,
angle: { from: -25, to: 25 },
duration: 4200 + i * 900,
yoyo: true,
repeat: -1,
ease: 'Sine.easeInOut',
});
}
}
createTicker(width, height) {
const bar = this.add.rectangle(0, height - 36, width, 36, 0xc1121f, 0.9).setOrigin(0, 0);
bar.setDepth(GAME.depth.hud);
const line = PROPAGANDA.join(' ★ ');
this.ticker = this.add
.text(width, height - 18, line, uiStyle({ fontSize: '19px', strokeThickness: 0 }))
.setOrigin(0, 0.5)
.setDepth(GAME.depth.hud + 1);
}
update(_time, delta) {
if (!this.ticker) return;
this.ticker.x -= (TICKER_SPEED * delta) / 1000;
if (this.ticker.x < -this.ticker.width) this.ticker.x = this.scale.width;
}
}