Files
RogueHunter/src/game/scenes/gameover/GameOverScene.ts
Nicolas Civade c55e0751e6 Scaffold RogueHunter: roguelike deck-builder on Phaser 4 + React
Vite + TypeScript + React 19 shell hosting a Phaser 4 game canvas,
structured as a Slay the Spire-style single-player deck-builder.

Architecture - strict core/render split:
- core/ holds all game rules as pure TypeScript with zero Phaser
  imports, so the domain is deterministic and engine-agnostic:
  - cards/      data-driven card model, library, and starter deck
  - combat/     turn-based CombatEngine (energy, block, draw/discard/
                exhaust piles, enemy intents) + enemy definitions
  - run/        RunManager (deck/hp/gold) and seeded MapGenerator
  - shared/rng  seedable mulberry32 for reproducible runs
- game/ is the Phaser layer: it renders core state and forwards input
  - scenes/     Menu -> Map -> Combat -> Reward -> GameOver
  - components/ CardSprite, CombatantSprite, Button
  - events/     typed EventBus bridging combat/run state to a React HUD
  - config/     centralized scene keys, colors, fonts, layout constants

Playable loop: traverse a deterministic 10-node map, fight turn-based
card combat against enemies with telegraphed intents, pick a card
reward after each win, culminating in a boss.

Verified: npm run lint and npm run build pass.
2026-08-12 22:10:32 +02:00

74 lines
2.0 KiB
TypeScript

import * as Phaser from 'phaser';
import { SceneKeys, Colors, Fonts } from '../../config';
import { Button } from '../../components/button/Button';
interface GameOverData {
floor?: number;
victory?: boolean;
}
export class GameOverScene extends Phaser.Scene {
private floor = 1;
private victory = false;
constructor() {
super({ key: SceneKeys.GAME_OVER });
}
init(data: GameOverData): void {
this.floor = data.floor ?? 1;
this.victory = data.victory ?? false;
}
create(): void {
this.layout();
const onResize = () => this.layout();
this.scale.on('resize', onResize);
this.events.once('shutdown', () => this.scale.off('resize', onResize));
}
private layout(): void {
this.children.removeAll(true);
const cx = this.scale.width / 2;
const cy = this.scale.height / 2;
this.add.text(cx, cy - 140, this.victory ? 'RUN COMPLETE' : 'DEFEATED', {
...Fonts.title,
color: this.victory ? '#66cc88' : '#ff4444',
stroke: Colors.stroke,
strokeThickness: 6,
}).setOrigin(0.5);
this.add.text(cx, cy - 60,
this.victory
? `You conquered all ${this.floor} floors.`
: `You fell on floor ${this.floor}.`,
{ ...Fonts.button, color: Colors.text, stroke: Colors.stroke, strokeThickness: 3 },
).setOrigin(0.5);
new Button({
scene: this,
x: cx,
y: cy + 40,
label: 'NEW RUN',
onClick: () => {
this.registry.remove('run');
this.scene.start(SceneKeys.MAP);
},
});
new Button({
scene: this,
x: cx,
y: cy + 120,
label: 'MAIN MENU',
onClick: () => {
this.registry.remove('run');
this.scene.start(SceneKeys.MENU);
},
});
}
}