//=============================================================================
// RPGツクールMZ - LL_GalgeChoiceWindow.js v2.0.1
//-----------------------------------------------------------------------------
// ルルの教会 (Lulu's Church)
// https://nine-yusha.com/
//
// URL below for license details.
// https://nine-yusha.com/plugin/
//=============================================================================

/*:
 * @target MZ
 * @plugindesc ノベルゲーム風選択肢ウィンドウプラグイン
 * @author ルルの教会
 * @url https://nine-yusha.com/plugin-galgechoicewindow/
 *
 * @help LL_GalgeChoiceWindow.js
 *
 * ノベルゲーム風の選択肢ウィンドウを表示します。
 * 独自のウィンドウ画像、ボタン画像を設定することもできます。
 * (画像はimg/systemフォルダに配置してください)
 *
 * プラグインコマンド:
 *   選択肢の表示: 選択肢を表示し、結果を変数で受け取ります。
 *
 * 利用規約:
 *   ・著作権表記は必要ございません。
 *   ・利用するにあたり報告の必要は特にございません。
 *   ・商用・非商用問いません。
 *   ・R18作品にも使用制限はありません。
 *   ・ゲームに合わせて自由に改変していただいて問題ございません。
 *   ・プラグイン素材としての再配布（改変後含む）は禁止させていただきます。
 *
 * 作者: ルルの教会
 * 作成日: 2022/12/9
 *
 * @command showChoice
 * @text 選択肢の表示
 * @desc 選択肢を表示し、結果を変数で受け取ります。
 *
 * @arg messageText
 * @text 質問メッセージ
 * @desc 質問メッセージです。 (2行以内推奨)
 * \V[n]、\N[n]、\P[n]、\Gの制御文字が使用できます。
 * @type multiline_string
 *
 * @arg choices
 * @text 選択肢リスト
 * @desc 選択肢として表示する文字列のリストです。
 * @default []
 * @type struct<choices>[]
 *
 * @arg maxCols
 * @text 選択肢列数
 * @desc 選択肢を何列で表示するかの設定です。
 * @default 1
 * @min 1
 * @max 20
 * @type number
 *
 * @arg variableNumber
 * @text 結果受け取り変数
 * @desc 選択結果を受け取る変数IDを指定します。
 * @type variable
 *
 * @arg cancelType
 * @text キャンセル許可
 * @desc キャンセルされた場合は-1が変数に代入されます。
 * @default true
 * @type boolean
 *
 * @arg iniPosition
 * @text 初期カーソル位置
 * @desc カーソルの初期位置です。(0にすると選択なし)
 * 数値を入力するか変数で指定することもできます。
 * @default 1
 * @type combo
 * @option $gameVariables.value(1)   // 変数ID:1の値
 * @option 0
 * @option 1
 * @option 2
 * @option 3
 * @option 4
 * @option 5
 * @option 6
 *
 * @arg windowWidth
 * @text ウィンドウ幅
 * @desc 選択肢ウィンドウの幅です。
 * 背景画像を使用する場合は横幅を調整してください。
 * @default 480
 * @min 0
 * @max 9999
 * @type number
 *
 * @arg windowBgImage
 * @text ウィンドウ画像
 * @desc ウィンドウ画像を使用する場合は画像を選択してください。
 * 標準のウィンドウ枠は非表示になります。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonBgImage
 * @text ボタン画像
 * @desc ボタン画像を使用する場合は画像を選択してください。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonFocusImage
 * @text ボタン画像(選択時)
 * @desc フォーカス時のボタン画像を選択してください。
 * ベースとなるボタン画像と大きさを必ず統一してください。
 * @dir img/system
 * @type file
 * @require 1
 *
 * @arg buttonImageAdjustX
 * @text ボタン画像X座標
 * @desc ボタン画像の表示位置(X)の調整値です。(初期値: 0)
 * ＋で右へ、－で左へ調整します。
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @arg buttonImageAdjustY
 * @text ボタン画像Y座標
 * @desc ボタン画像の表示位置(Y)の調整値です。(初期値: 0)
 * ＋で下へ、－で上へ調整します。
 * @default 0
 * @min -9999
 * @max 9999
 * @type number
 *
 * @arg itemHeightAdjust
 * @text 選択肢項目高さ
 * @desc 選択肢項目の高さの調整値です。(初期値: 8)
 * 画像が重なってしまう場合はこの数値を大きくしてください。
 * @default 8
 * @min -9999
 * @max 9999
 * @type number
 */

/*~struct~choices:
 *
 * @param label
 * @text 選択肢文字列
 * @desc 選択肢として表示する文字列です。
 * \V[n]、\N[n]、\P[n]、\Gの制御文字が使用できます。
 * @type string
 *
 * @param switchId
 * @text スイッチID
 * @desc スイッチON時のみ選択肢を表示したい場合に設定します。
 * 通常は「なし」のままで問題ありません。
 * @type switch
 *
 * @param resultNumber
 * @text 結果返却値
 * @desc この選択肢が選ばれた時に変数に入力する値を指定できます。
 * 「0」にすると自動で選択肢番号が返却されます。
 * @default 0
 * @min 0
 * @max 99999999
 * @type number
 */

(() => {
    "use strict";
    const pluginName = "LL_GalgeChoiceWindow";

    const parameters = PluginManager.parameters(pluginName);

    // Padding
    const gcwWindowPadding = 32;
    // キャンセル許可時のタッチUI戻るボタンの表示
    const gcwCancelButtonEnabled = false;
    // フェードイン効果フレーム
    const gcwFadeInFrame = 32;

    // 設定保存用変数定義
    let messageTextLists = [];
    let choiceLists = [];
    let maxCols = 1;
    let variableNumber = 0;
    let cancelType = true;
    let iniPosition = 1;
    let gcwWindowWidth = 480;
    let backgroundImage = "";
    let gcwButtonImage = "";
    let gcwButtonFocusImage = "";
    let gcwButtonAdjustX = 0;
    let gcwButtonAdjustY = 0;
    let gcwItemHeightAdjust = 8;

    PluginManager.registerCommand(pluginName, "showChoice", function(args) {
        // 設定を取得
        const messageText = String(args.messageText).split("\n");
        const choices = JSON.parse(args.choices || "null");
        maxCols = Number(args.maxCols || 1);
        variableNumber = Number(args.variableNumber);
        cancelType = String(args.cancelType) === "true" ? true : false;
        iniPosition = eval(args.iniPosition);
        gcwWindowWidth = Number(args.windowWidth || 480);
        backgroundImage = String(args.windowBgImage || "");
        gcwButtonImage = String(args.buttonBgImage || "");
        gcwButtonFocusImage = String(args.buttonFocusImage || "");
        gcwButtonAdjustX = Number(args.buttonImageAdjustX || 0);
        gcwButtonAdjustY = Number(args.buttonImageAdjustY || 0);
        gcwItemHeightAdjust = Number(args.itemHeightAdjust || 8);

        messageTextLists = [];
        if (messageText != "") {
            messageText.forEach((elm) => {
                messageTextLists.push(String(elm));
            });
        }

        // 選択肢を定義
        choiceLists = [];
        if (choices) {
            choices.forEach((elm) => {
                if (Number(JSON.parse(elm).switchId) === 0 || $gameSwitches.value(Number(JSON.parse(elm).switchId))) {
                    choiceLists.push(JSON.parse(elm));
                }
            });
        }

        // 選択肢が空の場合、終了
        if (choiceLists.length == 0) {
            console.log(pluginName + ": 有効な選択肢が存在しなかったため、コマンドがスキップされました");
            return;
        }

        if (!$gameMessage.isBusy()) {
            $gameMessage.setGalgeChoices(choiceLists);
            this.setWaitMode('message');
        }
    });


    //-----------------------------------------------------------------------------
    // Game_Message
    //

    const _Game_Message_clear = Game_Message.prototype.clear;
    Game_Message.prototype.clear = function() {
        _Game_Message_clear.apply(this, arguments);

        this._galgeChoices = [];
    };

    Game_Message.prototype.galgeChoices = function() {
        return this._galgeChoices;
    };

    Game_Message.prototype.isGalgeChoice = function() {
        return this._galgeChoices.length > 0;
    };

    Game_Message.prototype.setGalgeChoices = function(choices) {
        this._galgeChoices = choices;
    };

    const _Game_Message_isBusy = Game_Message.prototype.isBusy;
    Game_Message.prototype.isBusy = function() {
        return (
            _Game_Message_isBusy.apply(this, arguments) ||
            this.isGalgeChoice()
        );
    };


    //-----------------------------------------------------------------------------
    // Scene_Message
    //

    const _Scene_Message_createAllWindows = Scene_Message.prototype.createAllWindows;
    Scene_Message.prototype.createAllWindows = function() {
        this.createGalgeChoiceListWindow();
        _Scene_Message_createAllWindows.apply(this, arguments);
    };

    Scene_Message.prototype.createGalgeChoiceListWindow = function() {
        this._galgeChoiceListWindow = new Window_GalgeChoiceList();
        this.addWindow(this._galgeChoiceListWindow);
    };

    const _Scene_Message_associateWindows = Scene_Message.prototype.associateWindows;
    Scene_Message.prototype.associateWindows = function() {
        _Scene_Message_associateWindows.apply(this, arguments);

        const messageWindow = this._messageWindow;
        messageWindow.setGalgeChoiceListWindow(this._galgeChoiceListWindow);
        if (this._galgeChoiceListWindow) this._galgeChoiceListWindow.setMessageWindow(messageWindow);
    };


    //-----------------------------------------------------------------------------
    // Window_Message
    //

    const _Window_Message_initMembers = Window_Message.prototype.initMembers;
    Window_Message.prototype.initMembers = function() {
        _Window_Message_initMembers.apply(this, arguments);

        this._galgeChoiceListWindow = null;
    };

    const _Window_Message_startInput = Window_Message.prototype.startInput;
    Window_Message.prototype.startInput = function() {
        if ($gameMessage.isGalgeChoice()) {
            this._galgeChoiceListWindow.start();
            return true;
        }

        return _Window_Message_startInput.apply(this, arguments);
    };

    Window_Message.prototype.setGalgeChoiceListWindow = function(galgeChoiceListWindow) {
        this._galgeChoiceListWindow = galgeChoiceListWindow;
    };

    const _Window_Message_isAnySubWindowActive = Window_Message.prototype.isAnySubWindowActive;
    Window_Message.prototype.isAnySubWindowActive = function() {
        if (this._galgeChoiceListWindow) {
            return (
                _Window_Message_isAnySubWindowActive.apply(this, arguments) ||
                this._galgeChoiceListWindow.active
            );
        }

        return _Window_Message_isAnySubWindowActive.apply(this, arguments);
    };

    //-----------------------------------------------------------------------------
    // Window_GalgeChoiceList
    //
    // ノベルゲーム風選択肢ウィンドウを定義します。

    function Window_GalgeChoiceList() {
        this.initialize(...arguments);
    }

    Window_GalgeChoiceList.prototype = Object.create(Window_Command.prototype);
    Window_GalgeChoiceList.prototype.constructor = Window_GalgeChoiceList;

    Window_GalgeChoiceList.prototype.initialize = function() {
        Window_Command.prototype.initialize.call(this, new Rectangle());
        this.createCancelButton();
        this.openness = 0;
        this.deactivate();
        this._background = 0;
        this._canRepeat = false;
        // フォーカスアニメーション判定
        this._originalCursorSpriteFade = true;
    };

    Window_GalgeChoiceList.prototype.setMessageWindow = function(messageWindow) {
        this._messageWindow = messageWindow;
    };

    Window_GalgeChoiceList.prototype.createCancelButton = function() {
        if (ConfigManager.touchUI) {
            this._cancelButton = new Sprite_Button("cancel");
            this._cancelButton.visible = false;
            this.addChild(this._cancelButton);
        }
    };

    Window_GalgeChoiceList.prototype.wrapMessageText = function() {
        const maxWidth = gcwWindowWidth - gcwWindowPadding * 2;
        const tmp = new Bitmap(1, 1);
        tmp.fontFace = $gameSystem.mainFontFace();
        tmp.fontSize = $gameSystem.mainFontSize();
        const wrapped = [];
        messageTextLists.forEach(function(line) {
            if (tmp.measureTextWidth(line) <= maxWidth) {
                wrapped.push(line);
            } else {
                const words = line.split(" ");
                let currentLine = "";
                words.forEach(function(word) {
                    const testLine = currentLine ? currentLine + " " + word : word;
                    if (tmp.measureTextWidth(testLine) > maxWidth && currentLine) {
                        wrapped.push(currentLine);
                        currentLine = word;
                    } else {
                        currentLine = testLine;
                    }
                });
                if (currentLine) wrapped.push(currentLine);
            }
        });
        messageTextLists = wrapped;
    };

    Window_GalgeChoiceList.prototype.start = function() {
        this.wrapMessageText();
        this.updatePlacement();
        this.createCancelButton();
        this.placeCancelButton();
        this.createContents();
        this.refresh();
        this.setQuestionText();
        this.setSymbol();
        this.createOriginalBackground();
        this.open();
        this.activate();
    };

    const _Window_GalgeChoiceList_refresh = Window_GalgeChoiceList.prototype.refresh;
    Window_GalgeChoiceList.prototype.refresh = function() {
        this.removeChild(this._bgSprite);
        this._bgSprite = null;
        this.removeChild(this._bgDummy);
        this._bgDummy = null;
        this.removeChild(this._originalCursorSprite);
        this._originalCursorSprite = null;
        this.removeChild(this._originalButtonSprite);
        this._originalButtonSprite = null;

        // ウィンドウ枠、カーソルを初期化
        this.setCursorRect(0, 0, 0, 0);
        this.opacity = 255;

        _Window_GalgeChoiceList_refresh.apply(this, arguments);
    };

    Window_GalgeChoiceList.prototype.updatePlacement = function() {
        const wh = this.calcWindowHeight();
        const ww = gcwWindowWidth;
        const wx = (Graphics.boxWidth - ww) / 2;
        const wy = (Graphics.boxHeight - wh) / 2;

        this.width = ww;
        this.height = wh;
        this.x = wx;
        this.y = wy;
    };

    Window_GalgeChoiceList.prototype.calcWindowHeight = function() {
        // ウィンドウの高さを計算
        const itemCols = Math.ceil(choiceLists.length / maxCols);
        let height = this.lineHeight() * messageTextLists.length;
        if (messageTextLists.length > 0) height += gcwWindowPadding;
        height += (this.itemHeight()) * itemCols;
        height += gcwWindowPadding * 2;  // padding
        return height;
    };

    Window_GalgeChoiceList.prototype.createCancelButton = function() {
        if (ConfigManager.touchUI) {
            this._cancelButton = new Sprite_Button("cancel");
            this._cancelButton.visible = false;
            this.addChild(this._cancelButton);
        }
    };

    Window_GalgeChoiceList.prototype.createOriginalBackground = function() {
        if (backgroundImage != "") {
            this._bgSprite = new Sprite();
            this._bgSprite.bitmap = ImageManager.loadSystem(backgroundImage);
            this._bgSprite.bitmap.addLoadListener(function() {
                this._bgSprite.x = this.width / 2 - this._bgSprite.width / 2;
                this._bgSprite.y = this.height / 2 - this._bgSprite.height / 2;
            }.bind(this));
            this._bgSprite.opacity = 0;
            this.addChildToBack(this._bgSprite);
            // ウィンドウ枠を消去
            this.opacity = 0;
        }
    };

    Window_GalgeChoiceList.prototype.drawAllItems = function() {
        this.createOriginalCursor();
        this.createOriginalButton();
        Window_Selectable.prototype.drawAllItems.call(this);
    };

    Window_GalgeChoiceList.prototype.createOriginalCursor = function() {
        if (gcwButtonFocusImage != "") {
            this._originalCursorSprite = null;
            this._originalCursorSprite = new Sprite();
            this._originalCursorSprite.bitmap = ImageManager.loadSystem(gcwButtonFocusImage);
            this._originalCursorSprite.opacity = 128;
            this._originalCursorSprite.visible = true;
            this.addChildToBack(this._originalCursorSprite);
        }
    };

    Window_GalgeChoiceList.prototype.createOriginalButton = function() {
        if (gcwButtonImage != "") {
            this._originalButtonSprite = null;
            this._originalButtonSprite = new Sprite();
            this._originalButtonSprite.visible = false;
            this.addChildToBack(this._originalButtonSprite);
        }
    };

    Window_GalgeChoiceList.prototype.drawBackgroundRect = function(rect) {
        const c1 = ColorManager.itemBackColor1();
        const c2 = ColorManager.itemBackColor2();
        const x = rect.x;
        const y = rect.y;
        const w = rect.width;
        const h = rect.height;
        if (gcwButtonImage != "") {
            const originalButtonSprite = new Sprite();
            originalButtonSprite.bitmap = ImageManager.loadSystem(gcwButtonImage);
            originalButtonSprite.x = x + gcwButtonAdjustX + gcwWindowPadding;
            originalButtonSprite.y = y + gcwButtonAdjustY + gcwWindowPadding;
            this._originalButtonSprite.addChild(originalButtonSprite);
        } else {
            this.contentsBack.gradientFillRect(x, y, w, h, c1, c2, true);
            this.contentsBack.strokeRect(x, y, w, h, c1);
        }
    };

    Window_GalgeChoiceList.prototype.refreshCursor = function() {
        if (this._cursorAll) {
            this.refreshCursorForAll();
        } else if (this.index() >= 0) {
            const rect = this.itemRect(this.index());
            if (gcwButtonFocusImage != "") {
                if (this._originalCursorSprite) {
                    this._originalCursorSprite.x = rect.x + gcwButtonAdjustX + gcwWindowPadding;
                    this._originalCursorSprite.y = rect.y + gcwButtonAdjustY + gcwWindowPadding;
                }
            } else {
                this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
            }
        } else {
            this.setCursorRect(0, 0, 0, 0);
        }
    };

    Window_GalgeChoiceList.prototype.refreshCursorForAll = function() {
        const maxItems = this.maxItems();
        if (maxItems > 0) {
            const rect = this.itemRect(0);
            rect.enlarge(this.itemRect(maxItems - 1));
            if (gcwButtonFocusImage != "") {
                if (this._originalCursorSprite) {
                    this._originalCursorSprite.x = rect.x + gcwButtonAdjustX;
                    this._originalCursorSprite.y = rect.y + gcwButtonAdjustY;
                }
            } else {
                this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
            }
        } else {
            this.setCursorRect(0, 0, 0, 0);
        }
    };

    Window_GalgeChoiceList.prototype.update = function() {
        Window_Command.prototype.update.call(this);
        this.updateCancelButton();
        this.updateOriginalButton();
        this.updateOriginalCursorButton();
        if (this._bgSprite) this._bgSprite.opacity = this.openness;
    };

    Window_GalgeChoiceList.prototype.updateOriginalButton = function() {
        if (this._originalButtonSprite) {
            if (this.isOpen()) {
                this._originalButtonSprite.visible = true;
            } else {
                this._originalButtonSprite.visible = false;
            }
        }
    };

    Window_GalgeChoiceList.prototype.updateOriginalCursorButton = function() {
        if (this._originalCursorSprite) {
            if (this.isOpen() && this._index > -1) {
                this._originalCursorSprite.visible = true;
                if (this._originalCursorSprite.opacity >= 255) this._originalCursorSpriteFade = false;
                if (this._originalCursorSprite.opacity <= 128) this._originalCursorSpriteFade = true;
                this._originalCursorSprite.opacity += this._originalCursorSpriteFade ? 6 : -6;
            } else {
                this._originalCursorSprite.visible = false;
            }
        }
    };

    Window_GalgeChoiceList.prototype.updateOpen = function() {
        if (this._opening) {
            this.openness += gcwFadeInFrame;
            if (this.isOpen()) {
                this._opening = false;
            }
        }
    };

    Window_GalgeChoiceList.prototype.updateCancelButton = function() {
        if (this._cancelButton) {
            this._cancelButton.visible = this.needsCancelButton() && this.isOpen() && gcwCancelButtonEnabled;
        }
    };

    Window_GalgeChoiceList.prototype.needsCancelButton = function() {
        return cancelType;
    };

    Window_GalgeChoiceList.prototype.placeCancelButton = function() {
        if (this._cancelButton) {
            const spacing = 8;
            const button = this._cancelButton;
            const right = this.x + this.width;
            if (right < Graphics.boxWidth - button.width + spacing) {
                button.x = this.width + spacing;
            } else {
                button.x = -button.width - spacing;
            }
            button.y = this.height / 2 - button.height / 2;
        }
    };

    Window_GalgeChoiceList.prototype.maxCols = function() {
        return maxCols;
    };

    Window_GalgeChoiceList.prototype.breakLigatures = function(text) {
        // Insert zero-width non-joiner (U+200C) to prevent missing ligature glyphs (fi, fl, ff, ffi, ffl, etc.)
        return text.replace(/f([filt])/g, "f\u200C$1");
    };

    Window_GalgeChoiceList.prototype.setQuestionText = function() {
        messageTextLists.forEach((text, index) => {
            this.drawText(this.breakLigatures(this.convertEscapeCharacters(text)), 0, this.lineHeight() * index, this.innerWidth, "center");
        }, this);
    };

    Window_GalgeChoiceList.prototype.makeCommandList = function() {
        choiceLists.forEach(function(elm, index) {
            this.addCommand(String(elm.label), Number(elm.resultNumber || 0) !== 0 ? Number(elm.resultNumber) : index + 1);
        }, this);
    };

    Window_GalgeChoiceList.prototype.updatePadding = function() {
        this.padding = gcwWindowPadding;
    };

    Window_GalgeChoiceList.prototype.scrollBaseY = function(index) {
        let margin = this.lineHeight() * messageTextLists.length;
        if (messageTextLists.length > 0) margin += gcwWindowPadding;
        return this._scrollBaseY - margin;
    };

    Window_GalgeChoiceList.prototype.setSymbol = function() {
        if (iniPosition > 0) {
            this.selectSymbol(iniPosition);
        } else {
            this.deselect();
        }
    };

    Window_GalgeChoiceList.prototype.itemHeight = function() {
        return Window_Scrollable.prototype.itemHeight.call(this) + gcwItemHeightAdjust;
    };

    Window_GalgeChoiceList.prototype.drawItem = function(index) {
        const rect = this.itemLineRect(index);
        const align = this.itemTextAlign();
        this.resetTextColor();
        this.changePaintOpacity(this.isCommandEnabled(index));
        this.drawText(this.breakLigatures(this.convertEscapeCharacters(this.commandName(index))), rect.x, rect.y, rect.width, align);
    };

    Window_GalgeChoiceList.prototype.isCancelEnabled = function() {
        return cancelType;
    };

    Window_GalgeChoiceList.prototype.isOkTriggered = function() {
        return Input.isTriggered('ok');
    };

    Window_GalgeChoiceList.prototype.callOkHandler = function() {
        $gameVariables.setValue(variableNumber, this.currentSymbol());
        this._messageWindow.terminateMessage();
        this.close();
    };

    Window_GalgeChoiceList.prototype.callCancelHandler = function() {
        $gameVariables.setValue(variableNumber, -1);
        this._messageWindow.terminateMessage();
        this.close();
    };
})();
