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

111 lines
3.4 KiB
JavaScript

/**
* Einstiegspunkt des Frontends: liest die Server-URLs aus dem HTML, baut die
* gemeinsamen Dienste (Einstellungen, Audio, API) und startet Phaser.
* @module main
*/
import { GAME } from './config.js';
import { ERRORS } from './content/texts.js';
import { BootScene } from './scenes/BootScene.js';
import { BriefingScene } from './scenes/BriefingScene.js';
import { DisclaimerScene } from './scenes/DisclaimerScene.js';
import { GameOverScene } from './scenes/GameOverScene.js';
import { GameScene } from './scenes/GameScene.js';
import { HudScene } from './scenes/HudScene.js';
import { ControlsScene, CreditsScene, HighscoresScene } from './scenes/InfoScenes.js';
import { MenuScene } from './scenes/MenuScene.js';
import { PauseScene } from './scenes/PauseScene.js';
import { ApiClient } from './systems/ApiClient.js';
import { AudioManager } from './systems/AudioManager.js';
import { SafeStorage, Settings } from './systems/Settings.js';
/** @param {string} message */
function showBootError(message) {
const target = document.getElementById('boot-message');
if (target) {
target.textContent = message;
target.classList.add('boot-message--error');
}
}
/**
* Liest die vom Server gerenderten URLs (funktioniert auch unter einer Unter-URL).
* @param {HTMLElement} shell
*/
function readContext(shell) {
return {
staticBase: shell.dataset.staticBase ?? '/static/',
api: {
highscores: shell.dataset.apiHighscores ?? '/api/highscores',
runs: shell.dataset.apiRuns ?? '/api/runs',
},
disclaimerUrl: shell.dataset.disclaimerUrl ?? '/disclaimer',
};
}
function start() {
const shell = document.getElementById('game-shell');
if (!shell) return;
if (typeof window.Phaser === 'undefined') {
showBootError(ERRORS.phaserMissing);
return;
}
const context = readContext(shell);
const settings = new Settings(SafeStorage.forBrowser());
const services = {
settings,
audio: new AudioManager(settings, { staticBase: context.staticBase }),
api: new ApiClient(context.api),
};
// Klangdateien schon jetzt als Bytes laden; dekodiert wird nach der ersten Interaktion.
services.audio.preload();
const game = new Phaser.Game({
type: Phaser.AUTO,
parent: 'game',
width: GAME.width,
height: GAME.height,
backgroundColor: GAME.backgroundColor,
banner: false,
// Rechtsklick soll kein Browser-Kontextmenü über dem Spiel öffnen.
disableContextMenu: true,
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
physics: { default: 'arcade', arcade: { debug: false } },
// Ton übernimmt der eigene AudioManager (Kanäle, Synthesizer, Musik). Ohne diese
// Einstellung würde Phaser zusätzlich einen eigenen AudioContext anlegen.
audio: { noAudio: true },
// Reihenfolge = Zeichenreihenfolge: HUD und Pause liegen über dem Spiel.
scene: [
BootScene,
MenuScene,
DisclaimerScene,
BriefingScene,
GameScene,
HudScene,
PauseScene,
GameOverScene,
HighscoresScene,
ControlsScene,
CreditsScene,
],
callbacks: {
preBoot: (g) => {
g.registry.set('context', context);
g.registry.set('services', services);
},
},
});
// Für End-to-End-Tests und Fehlersuche in der Browser-Konsole.
window.gloryDefense = game;
}
try {
start();
} catch (error) {
console.error(error);
showBootError(ERRORS.generic);
}