Newer
Older
rpg / packages / engine / src / render / fx / FxFade.ts
import { Container } from 'pixi.js';
import type { Updatable } from '../../core/Updatables';
import { sampleFade, type FxFadeSpec } from './fxSim';

/** Опции растворения: математика (FxFadeSpec) + жизненный цикл вьюхи. */
export interface FxFadeOptions extends FxFadeSpec {
    /** true — не уничтожать вьюху в конце (только альфа). */
    keep?: boolean;
    /** Колбэк завершения (в т.ч. после destroy вьюхи). */
    onDone?: () => void;
}

/**
 * Растворение/проявление произвольной вьюхи + авто-удаление. Не display-объект:
    * тикается в реестре, применяет альфу к чужой вьюхе. В конце removeChild +
 * destroy({children: true}) — с защитой от двойного destroy (вьюху могли
 * уничтожить извне, пока эффект играл).
 */
export class FxFade implements Updatable {
    private t = 0;
    private finished = false;
    private readonly from: number;

    constructor(
        private readonly view: Container,
        private readonly opts: FxFadeOptions
    ) {
        this.from = view.alpha;
    }

    /** Прекратить эффект и вернуть базовую альфу (вьюху не трогаем дальше). */
    cancel(): void {
        this.finished = true;
        if (!this.view.destroyed) this.view.alpha = this.from;
    }

    /** Вьюха разрушена извне или эффект закончился — запись вычистится сама. */
    get destroyed(): boolean {
        return this.finished || this.view.destroyed;
    }

    update(dt: number): void {
        if (this.finished) return;
        if (this.view.destroyed) return; // реестр вычистит по destroyed
        this.t += dt;
        this.view.alpha = sampleFade(this.t, this.from, this.opts);
        const total = (this.opts.delay ?? 0) + this.opts.duration;
        if (this.t >= total - 1e-6) { // эпсилон: накопление 1/60 не дотягивает до границы
            this.finished = true;
            if (!this.opts.keep) {
                this.view.parent?.removeChild(this.view);
                this.view.destroy({ children: true });
            }
            this.opts.onDone?.();
        }
    }
}