systems/vibration.system.js

import {EVENTCODES, EventGamepad, EventGamepadDigital, Stage, System, UTILS, Vibration} from '../index.js';

/**
 * Creates vibration systems.
 *
 * @example
 *
 * const system = new SystemVibration();
 */
class SystemVibration extends System {

    /**
     * Stores the delay before a vibration ends.
     * @type {number}
     * @public
     * @readonly
     * @static
     */
    static DELAYVIBRATIONEND = 1000;

    /**
     * Stores the mapping between the playing vibrations and their elapsed time.
     * @type {Map<Vibration, number>}
     * @private
     */
    $mappingVibrationsPlaying;

    /**
     * Creates a new vibration system.
     */
    constructor() {

        super();
    }

    /**
     * Called when the system is being initiated.
     * @public
     */
    onInitiate() {

        this.$mappingVibrationsPlaying = new Map();
    }

    /**
     * Called when the system is being terminated.
     * @returns {(void | Promise<void>)}
     * @public
     */
    onTerminate() {

        window.dispatchEvent(new EventGamepadDigital('gamepadvibrate', EVENTCODES.GAMEPADXBOX.VIBRATEEND));
    }

    /**
     * Called when the system is being updated by one tick update.
     * @param {Object} $parameters The given parameters.
     * @param {Stage} $parameters.$stage The stage on which to execute the system.
     * @param {number} $parameters.$timetick The tick duration (in ms).
     * @public
     */
    onTick({$stage, $timetick}) {

        /**
         * @type {Array<Vibration>}
         */
        const previous = Array.from(this.$mappingVibrationsPlaying.keys());

        $stage.actors.forEach(($actor) => {

            /**
             * @type {Array<Vibration>}
             */
            const finished = [];

            $actor.vibrations.forEach(($vibration) => {

                if (this.$mappingVibrationsPlaying.has($vibration) === false) {

                    this.$mappingVibrationsPlaying.set($vibration, 0);

                    return;
                }

                const elapsedTime = this.$mappingVibrationsPlaying.get($vibration) + $timetick;

                if (elapsedTime >= $vibration.duration) {

                    finished.push($vibration);

                    return;
                }

                this.$mappingVibrationsPlaying.set($vibration, elapsedTime);

                UTILS.extract($vibration, previous);
            });

            finished.forEach(($vibration) => {

                $actor.removeVibration($vibration);
            });
        });

        previous.forEach(($vibration) => {

            this.$mappingVibrationsPlaying.delete($vibration);
        });

        /**
         * @type {Array<Vibration>}
         */
        const current = Array.from(this.$mappingVibrationsPlaying.keys());

        let intensityFrequencyHigh = 0;
        let intensityFrequencyLow = 0;

        current.forEach(($vibration) => {

            intensityFrequencyHigh = Math.max(intensityFrequencyHigh, $vibration.intensityFrequencyHigh);
            intensityFrequencyLow = Math.max(intensityFrequencyLow, $vibration.intensityFrequencyLow);
        });

        window.dispatchEvent(new EventGamepad('gamepadvibrate', EVENTCODES.GAMEPADXBOX.VIBRATESTART, new Vibration({

            $duration: SystemVibration.DELAYVIBRATIONEND,
            $intensityFrequencyHigh: intensityFrequencyHigh,
            $intensityFrequencyLow: intensityFrequencyLow
        })));
    }
}

export {

    SystemVibration
};

export default SystemVibration;