Source: DialogueManager.js

/**
 * @callback DialogueManager.dialogueCallback
 * @param {function} resolve - Call resolve once you are done with the dialogue
 * @param {Subquest} subquest=false - Subquest passed if the dialogue id is equal to a dialogueKey of a subquest
 */

/**
 * The config object used in DialogueManager#userInput
 * @typedef {object} DialogueManager.userInputConfig
 * @property {array<string>} array - Array of strings to display as input. If selected, the function will return the (index + 1) of the selected string.
 * @property {string} [dialogue=""] - Text to display on the dialogue box
 * @property {string} [character="nullPixel"] - Character id to display in the background. If there is a hyphen(-) in this parameter, it will ignore the id and use the given string as a texture key
 * @property {string} [mapKey] - mapKeys are used for quests. If you want the userInput to show quest buttons then pass the mapKey.
 * @property {boolean} [exit = false] - If the user should be able to leave. Returns false if enabled.
 */

/**
 * The config object used in DialogueManager#talk
 * Texture layers are created in this order:
 * `characterID-special-emotion-clothes`
 * ```
 * talk("Queen", "Hi", {
 *     naked: true
 * });
 * ```
 * Will return a neutral naked image of Queen
 * ```
 * talk("Scarlett", "Hi...", {
 *     naked: true,
 *     layers: ["Scarlett-Layer-Used"]
 * });
 * ```
 * Will return a neutral naked image of Scarlett with the 'Scarlett-Layer-Used' texture on top
 * @typedef DialogueManager.talkConfig
 * @property {boolean} naked=false - Set to true if the girl should be naked. Calls .setNaked(true) on the girl image container
 * @property {string} emotion="Neutral" - Changes the face texture for main characters, changes the texture key for NPCs
 * @property {array} layers="" - Any amount of texture layers to add to the image
 */

/**
 * The DialogueManager is shared by all mods and the game. Any names added to the list will be available for everyone to use.
 * @class DialogueManager
 */
class DialogueManager {
    constructor() {
        this._names = {
            '': '',
            'Queen': 'Queen',
            'Suki': 'Suki',
            'Esxea': 'Esxea',
            'Scarlett': 'Scarlett',
            'Ardura': 'Ardura',
            'AviaGuard': 'Avia Guard',
            'Brior': 'Brior',
            'Daniel': 'Daniel',
            'Darrak': 'Darrak',
            'Dathea': 'Dathea',
            'Geoff': 'Geoff',
            'King': 'King of Avia',
            'KingEvil': 'King of Avia',
            'Leon': 'Leon',
            'Mayor': 'Mayor',
            'Naknu': 'Naknu',
            'NotNaknu': 'Not Naknu',
            'Nirvokk': 'Nirvokk',
            'Peasant': 'Peasant',
            'Principal': 'Principal',
            'ScaryMan': 'Scary Man',
            'Thisa': 'Thisa',
            'CLady': 'Cafeteria Lady',
            'AviaCaptain': 'Avia Captain'
        };

        this._dialogues = {};
        /**
         * The Dialogue scene is a Phaser scene. The scene shuts down once the playDialogue promise is resolved.
         * This variable is filled inside the playDialogue function.
         * You're going to have to learn Phaser if you want to use this scene to the fullest.
         * @example
         * this.scene.add.image(0, 0, "fullScreenButton").setOrigin(0);
         * @name DialogueManager#scene
         * @see {@link https://photonstorm.github.io/phaser3-docs/Phaser.Scene.html}
         */
        this.scene = null;
    }

    /**
     * A popUpBoard is the message board that pops up and you either press "okay" or "yes/no"
     * @method popUpBoard
     * @memberOf DialogueManager
     * @instance
     * @param {string} text - Text to display in the box
     * @param {boolean} type=false - Set to true for a "yes/no" pop up board. It will return a boolean if set to true.
     * @returns Promise<boolean>
     */
    popUpBoard(text, type) {
        return new Promise((resolve) => {
            text = text || "";
            type = type || false;
            game.scene.start('popUpBoard', {pauseAllScenes: true, text: text, type: type});
            game.scene.getScene('popUpBoard').events.once('shutdown', (scene, data) => {
                resolve(data.answer);
            });
        })
    }

    /**
     * Creates a regular dialogue pop-up. The promise is resolved once the user presses space or clicks the dialogue box
     * @method talk
     * @memberOf DialogueManager
     * @instance
     * @param {String} characterID="" - The ID of the character
     * @param {String} dialogueText="" - The text to put in the dialogue box
     * @param {DialogueManager.talkConfig} [options] - Any config options you want to add
     * @returns Promise
     */
    talk(characterID, dialogueText, options) {
        return new Promise((resolve) => {
            if (game.scene.getScene('dialogueBackground').sys.isActive() === false) {
                game.scene.start('dialogueBackground');
            }

            game.scene.start('setDialogue', {
                character: characterID,
                text: dialogueText,
                options: options,
                pauseAllScenes: true
            });
            game.scene.getScene('setDialogue').events.once('shutdown', resolve);
        })
    }

    /**
     * Get the full name of the character using the characterID
     * @method getName
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     * @returns {string}
     */
    getName(id) {
        return this._names[id];
    }

    /**
     * Adds a new character to the list
     * @method addName
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     * @param name
     */
    addName(id, name) {
        this._names[id] = name;
    }

    /**
     * There's like no reason to use this ever, but if you really want to remove a name from the list go ahead.
     * @method removeName
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     */
    removeName(id) {
        delete this._names[id];
    }

    /**
     * @method userInput
     * @memberOf DialogueManager
     * @instance
     * @param {DialogueManager.userInputConfig} config
     * @returns Promise<number|object|false> - Will return a number if something from the array is selected. Will return a {@link QuestManager.Subquest} if a quest is selected. Will return false if exited.
     */
    userInput(config) {
        return new Promise((resolve) => {
            game.scene.start('UserInput', {
                config: config,
                pauseAllScenes: true
            });
            game.scene.getScene('UserInput').events.once('shutdown', (scene, data) => {
                resolve(data.answer);
            });
        })
    }

    /**
     * Returns the function associated with the dialogue id
     * @method getDialogue
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     * @return {function}
     */
    getDialogue(id) {
        return this._dialogues[id];
    }

    /**
     * Adds a dialogue function to the dialogue list using the id. You can retrieve the dialogue after adding it by using {@link DialogueManager#getDialogue}
     * @method addDialogue
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     * @param {DialogueManager.dialogueCallback} callback
     */
    addDialogue(id, callback) {
        this._dialogues[id] = callback;
    }

    /**
     * Removes the dialogue from the dialogue list
     * @method removeDialogue
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     */
    removeDialogue(id) {
        delete this._dialogues[id];
    }

    /**
     * Gets the dialogue function and executes it. You can call this function during a dialogue and it won't interfere with the scene.
     * This function will return anything you pass into the resolve function inside the dialogue function.
     * The scope of the executed function will always be DialogueManager.
     * @method playDialogue
     * @memberOf DialogueManager
     * @instance
     * @param {string} id
     * @return {Promise<any>}
     */
    playDialogue(id) {
        this.scene = game.scene.getScene('Dialogue');

        if (this.getDialogue(id) === undefined) {
            console.error("No dialogue id " + id + " found.");
        }
        if (game.scene.getScene('Dialogue').sys.isActive()) {
            return new Promise((resolve) => {
                game.scene.getScene('Dialogue').play(id).then(resolve);
            });
        } else {
            return new Promise((resolve) => {
                game.scene.start('Dialogue', {
                    pauseAllScenes: true
                });
                game.scene.getScene('Dialogue').play(id).then((data) => {
                    game.scene.stop('Dialogue');
                    resolve(data);
                });
            });
        }
    }

    /**
     * Searches through all of the dialogues to find a string. Use this to quickly find the dialogue id of any text in the game.
     * @method search
     * @memberOf DialogueManager
     * @instance
     * @param {string} string
     * @return {Array<string>}
     */
    search(string) {
        let found = [];

        for (let dialogue in this._dialogues) {
            if (this._dialogues[dialogue].toString().search(string) !== -1) {
                found.push(dialogue);
            }
        }

        return found;
    }
}