Newer
Older
rpg / apps / game / src / scenes / MenuScene.ts
import { Container, Text } from 'pixi.js';
import type { Scene } from '@rpg/engine';
import type { Game } from '../Game';
import { LocationScene } from './LocationScene';

/**
 * Главное меню: название, «новая игра», «продолжить» (если есть сейв).
 */
export class MenuScene implements Scene {
    private view = new Container();

    constructor(private game: Game) {}

    enter(): void {
        const title = new Text({
            text: 'ПЕПЕЛЬНЫЕ ЛУГА',
            style: { fontFamily: 'monospace', fontSize: 24, fill: 0xd8c79a }
        });
        title.anchor.set(0.5);
        title.position.set(240, 70);
        this.view.addChild(title);

        const subtitle = new Text({
            text: 'пепел всё ещё дышит',
            style: { fontFamily: 'monospace', fontSize: 8, fill: 0x8a8a9a }
        });
        subtitle.anchor.set(0.5);
        subtitle.position.set(240, 96);
        this.view.addChild(subtitle);

        this.addMenuItem('Новая игра', 140, () => {
            void this.game.scenes.replace(new LocationScene(this.game, null));
        });

        if (this.game.saves.listSlots().length > 0) {
            this.addMenuItem('Продолжить', 160, () => {
                const save = this.game.saves.load<SaveData>('autosave');
                void this.game.scenes.replace(new LocationScene(this.game, save));
            });
        }

        const hint = new Text({
            text: 'клик по тайлу — идти · клик по NPC — говорить\nEsc в игре — меню с сохранением',
            style: {
                fontFamily: 'monospace',
                fontSize: 8,
                fill: 0x666677,
                align: 'center',
                lineHeight: 10
            }
        });
        hint.anchor.set(0.5);
        hint.position.set(240, 230);
        this.view.addChild(hint);

        this.game.renderer.uiRoot.addChild(this.view);
    }

    private addMenuItem(label: string, y: number, onClick: () => void): void {
        const item = new Text({
            text: label,
            style: { fontFamily: 'monospace', fontSize: 12, fill: 0x9fb7a4 }
        });
        item.anchor.set(0.5);
        item.position.set(240, y);
        item.eventMode = 'static';
        item.cursor = 'pointer';
        item.on('pointerdown', onClick);
        item.on('pointerover', () => {
            item.style.fill = 0xe8f0e9;
        });
        item.on('pointerout', () => {
            item.style.fill = 0x9fb7a4;
        });
        this.view.addChild(item);
    }

    update(_dt: number): void {}

    render(): void {}

    exit(): void {
        this.view.destroy({ children: true });
    }
}

/** Формат автосейва. */
export interface SaveData {
    pos: { x: number; y: number };
    flags: Record<string, boolean>;
}