slop game oder so
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Einfache Informationsseiten im Canvas: Ehrentafel, Steuerung, Credits.
|
||||
* Alle kehren mit Esc, Enter oder Klick auf „Zurück“ ins Hauptmenü zurück.
|
||||
* @module scenes/InfoScenes
|
||||
*/
|
||||
|
||||
import { ORBITAL } from '../config.js';
|
||||
import { CONTROLS, CREDITS, HIGHSCORES } from '../content/texts.js';
|
||||
import { formatScore, formatSequence } from '../logic/format.js';
|
||||
import { oncePerEvent } from '../systems/keyboard.js';
|
||||
import { textStyle, UI, uiStyle } from '../ui/theme.js';
|
||||
|
||||
/**
|
||||
* Gemeinsamer Rahmen: Hintergrund, Titel und Zurück-Schaltfläche.
|
||||
* @param {Phaser.Scene} scene
|
||||
* @param {string} title
|
||||
* @param {string} backLabel
|
||||
*/
|
||||
function frame(scene, title, backLabel) {
|
||||
const { width, height } = scene.scale;
|
||||
scene.input.setDefaultCursor('default');
|
||||
const bg = scene.add.graphics();
|
||||
bg.fillGradientStyle(0x0d1321, 0x0d1321, 0x1b2438, 0x1b2438, 1);
|
||||
bg.fillRect(0, 0, width, height);
|
||||
bg.lineStyle(3, 0xf2c14e, 1);
|
||||
bg.strokeRect(40, 30, width - 80, height - 60);
|
||||
|
||||
scene.add.text(width / 2, 70, title, textStyle({ fontSize: '38px', color: UI.colors.gold })).setOrigin(0.5);
|
||||
const back = scene.add
|
||||
.text(width / 2, height - 70, backLabel, textStyle({ fontSize: '22px' }))
|
||||
.setOrigin(0.5)
|
||||
.setInteractive({ useHandCursor: true });
|
||||
back.on('pointerover', () => back.setColor(UI.colors.gold));
|
||||
back.on('pointerout', () => back.setColor(UI.colors.cream));
|
||||
const leave = () => scene.scene.start('Menu');
|
||||
back.on('pointerup', leave);
|
||||
const leaveByKey = oncePerEvent((event) => {
|
||||
if (!event.repeat) leave();
|
||||
});
|
||||
scene.input.keyboard.on('keydown-ESC', leaveByKey);
|
||||
scene.input.keyboard.on('keydown-ENTER', leaveByKey);
|
||||
}
|
||||
|
||||
export class HighscoresScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super('Highscores');
|
||||
}
|
||||
|
||||
create() {
|
||||
frame(this, HIGHSCORES.title, HIGHSCORES.back);
|
||||
const { width } = this.scale;
|
||||
this.statusText = this.add.text(width / 2, 300, HIGHSCORES.loading, uiStyle({ fontSize: '22px', color: UI.colors.muted })).setOrigin(0.5);
|
||||
this.registry
|
||||
.get('services')
|
||||
.api.fetchHighscores()
|
||||
.then((result) => {
|
||||
if (!this.sys.isActive()) return;
|
||||
if (!result.ok) this.statusText.setText(HIGHSCORES.error);
|
||||
else if (result.highscores.length === 0) this.statusText.setText(HIGHSCORES.empty);
|
||||
else {
|
||||
this.statusText.destroy();
|
||||
this.renderTable(result.highscores);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {import('../systems/ApiClient.js').HighscoreEntry[]} entries */
|
||||
renderTable(entries) {
|
||||
const columns = { rank: 280, name: 360, score: 900, wave: 1000 };
|
||||
const top = 130;
|
||||
const rowHeight = 40;
|
||||
const header = uiStyle({ fontSize: '17px', color: UI.colors.muted });
|
||||
this.add.text(columns.rank, top, HIGHSCORES.columns.rank, header);
|
||||
this.add.text(columns.name, top, HIGHSCORES.columns.name, header);
|
||||
this.add.text(columns.score, top, HIGHSCORES.columns.score, header).setOrigin(1, 0);
|
||||
this.add.text(columns.wave, top, HIGHSCORES.columns.wave, header).setOrigin(1, 0);
|
||||
entries.forEach((entry, i) => {
|
||||
const y = top + 40 + i * rowHeight;
|
||||
const color = i === 0 ? UI.colors.gold : UI.colors.cream;
|
||||
const style = uiStyle({ fontSize: '24px', color });
|
||||
this.add.text(columns.rank, y, `${entry.rank}.`, style);
|
||||
// Canvas-Text interpretiert kein HTML – Namen werden hier sicher als Text gezeichnet.
|
||||
this.add.text(columns.name, y, entry.name, style);
|
||||
this.add.text(columns.score, y, formatScore(entry.score), style).setOrigin(1, 0);
|
||||
this.add.text(columns.wave, y, String(entry.wave), style).setOrigin(1, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ControlsScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super('Controls');
|
||||
}
|
||||
|
||||
create() {
|
||||
frame(this, CONTROLS.title, CONTROLS.back);
|
||||
const keyStyle = uiStyle({ fontSize: '20px', color: UI.colors.gold });
|
||||
const valueStyle = uiStyle({ fontSize: '20px' });
|
||||
CONTROLS.rows.forEach(([key, action], i) => {
|
||||
const y = 130 + i * 38;
|
||||
this.add.text(560, y, key, keyStyle).setOrigin(1, 0);
|
||||
this.add.text(590, y, action, valueStyle);
|
||||
});
|
||||
const top = 130 + CONTROLS.rows.length * 38 + 30;
|
||||
this.add.text(this.scale.width / 2, top, CONTROLS.orbitalTitle, textStyle({ fontSize: '22px', color: UI.colors.gold })).setOrigin(0.5);
|
||||
ORBITAL.commands.forEach((command, i) => {
|
||||
const y = top + 40 + i * 36;
|
||||
this.add.text(560, y, command.name, valueStyle).setOrigin(1, 0);
|
||||
this.add.text(590, y, formatSequence(command.sequence, ' '), { ...keyStyle, fontFamily: UI.symbolFont });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class CreditsScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super('Credits');
|
||||
}
|
||||
|
||||
create() {
|
||||
frame(this, CREDITS.title, CREDITS.back);
|
||||
const { width } = this.scale;
|
||||
let y = 118;
|
||||
for (const section of CREDITS.sections) {
|
||||
const heading = this.add.text(width / 2, y, section.heading, textStyle({ fontSize: '21px', color: UI.colors.gold })).setOrigin(0.5, 0);
|
||||
y += heading.height + 4;
|
||||
for (const line of section.lines) {
|
||||
const text = this.add
|
||||
.text(width / 2, y, line, { ...uiStyle({ fontSize: '17px', align: 'center' }), wordWrap: { width: width - 200 } })
|
||||
.setOrigin(0.5, 0);
|
||||
// Tatsächliche Höhe verwenden: umgebrochene Zeilen sind höher als eine Zeile.
|
||||
y += text.height + 2;
|
||||
}
|
||||
y += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user