Newer
Older
rpg / packages / engine / src / ui / DialogueBox.ts
import { Container, Graphics, Text } from 'pixi.js';

/**
 * Универсальное окно диалога: имя говорящего, текст, подсказка «далее».
 * Жанронезависимо: игра сама решает, чей это диалог и что идёт дальше.
 */
export interface DialogueLine {
    speaker: string;
    text: string;
}

export interface DialogueBoxOptions {
    /** Виртуальные пиксели. */
    width: number;
    height: number;
    /** Отступы панели от краёв экрана. */
    margin: number;
}

export class DialogueBox {
    readonly view: Container;
    visible = false;

    private panel: Graphics;
    private nameText: Text;
    private bodyText: Text;
    private hintText: Text;

    constructor(options: DialogueBoxOptions) {
        this.view = new Container();
        this.view.visible = false;
        this.view.eventMode = 'static';

        this.panel = new Graphics();
        this.nameText = new Text({
            text: '',
            style: { fontFamily: 'monospace', fontSize: 8, fill: 0xffffcc }
        });
        this.bodyText = new Text({
            text: '',
            style: {
                fontFamily: 'monospace',
                fontSize: 8,
                fill: 0xffffff,
                wordWrap: true,
                wordWrapWidth: options.width - options.margin * 4,
                lineHeight: 10
            }
        });
        this.hintText = new Text({
            text: 'далее ▸',
            style: { fontFamily: 'monospace', fontSize: 8, fill: 0xaaaaaa }
        });

        this.view.addChild(this.panel, this.nameText, this.bodyText, this.hintText);
        this.layout(options);
    }

    private layout(options: DialogueBoxOptions): void {
        const w = options.width;
        const h = options.height;
        const m = options.margin;
        this.panel.rect(m, h - m - 48, w - m * 2, 48).fill({ color: 0x101018, alpha: 0.92 });
        this.panel.rect(m, h - m - 48, w - m * 2, 48).stroke({ color: 0x8899aa, width: 1 });
        this.nameText.position.set(m + 4, h - m - 44);
        this.bodyText.position.set(m + 4, h - m - 34);
        this.hintText.position.set(w - m - 40, h - m - 8);
    }

    show(line: DialogueLine): void {
        this.nameText.text = line.speaker;
        this.bodyText.text = line.text;
        this.view.visible = true;
        this.visible = true;
    }

    hide(): void {
        this.view.visible = false;
        this.visible = false;
    }
}