code:
//=============================================================================
// MPP_MessageEX.js
//=============================================================================
// Copyright (c) 2016 - 2022 Mokusei Penguin
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
//=============================================================================

/*:
 * @target MV MZ
 * @plugindesc Extend the text display function and add display effects.
 * @author Mokusei Penguin
 * @url
 *
 * @help [version 4.4.0]
 * This plugin is for RPG Maker MV and MZ.
 *
 * ▼ [Display text] control characters
 *   \SP[n]        # Text speed(characters per second / 0:Instant display)
 *   \AT[n]        # Change animation type to n(※1)
 *   \SET[n]       # Convert to the set string set(※2)
 *   \CO[S]        # Display character string S as one character
 *   \RB[S,R]      # Display character string S with ruby R(※3)
 *   \MX[n]        # Shift the next letter X coordinate by n pixels
 *   \MY[n]        # Shift the next letter Y coordinate by n pixels
 *   \PX[n]        # Change the X coordinate of the next character to n
 *   \PY[n]        # Change the Y coordinate of the next character to n
 *   \SW[n]        # Turn on switch n
 *   \SN[n]        # Replace with the name of skill ID n
 *   \SIN[n]       # Replace with icon and name of skill ID n
 *   \IN[n]        # Replace with the name of item ID n
 *   \IIN[n]       # Replace with the icon and name of item ID n
 *   \WN[n]        # Replace with the name of weapon ID n
 *   \WIN[n]       # Replace with icon and name of weapon ID n
 *   \AN[n]        # Replace with the name of armor ID n
 *   \AIN[n]       # Replace with icon and name of armor ID n
 *   \WE           # Wait until the event production is over(※4)
 *   \C[r,g,b]     # Specify the font color in RGB
 *   \FS[n]        # Change font size to n / Default:MV=28,MZ=26
 *   \FB           # Invert font bold flag
 *   \FI           # Invert font italic flag
 *   \OP[n]        # Character opacity(0～255) / Default:255
 *   \OC[n]        # Change the color of the character edge to n / 0:Default
 *   \OC[r,g,b]    # Specify the border color of characters in RGB
 *   \OC[r,g,b,a]  # Specify the border color of characters with RGBA(※5)
 *   \OW[n]        # Change the thickness of the character edge / Default:4
 *   \RC[n]        # Changed ruby color to n / 0:Default
 *   \RC[r,g,b]    # Specify ruby color in RGB
 *   \RC[r,g,b,a]  # Specify ruby color with RGBA(※5)
 *   \DF           # Return the text display setting to the default value(※6)
 *   \SV           # Memorize the current text display settings(※6)
 *   \LD           # Calling the settings memorized by \SV(※6)
 *
 *  - The following applies if it is included in the text
 *   \A            # Prohibition of skipping with enter key or shift key
 *   \ES           # Temporarily enable skipping event production(※7)
 *   \FO[n]        # Temporarily change the fade-out type(※8)
 *
 *  - All control characters can be in either case
 *
 * ▼ Control character details
 *  ※1: \AT[n] (Change animation type to n)
 *   - The default animation type is as follows.
 *       0: Default
 *       1: Letters emerge while sliding to the right
 *       2: Characters are displayed while spreading horizontally
 *       3: Characters are displayed while expanding
 *       4: Display characters from the left side(Text speed 6 recommended)
 *
 *  ※2: \SET[n] (Convert to the set string set)
 *   - Converts to the character string specified by the plug-in parameter
 *     [Text Set].
 *   - Control characters can also be set.
 *
 *  ※3: \RB[S,R] (Display character string S with ruby R)
 *   - Character strings with ruby characters are displayed together,
 *     not individually.
 *
 *  ※4: \WE (Wait until the event production is over)
 *   - Wait until the production of the event set in the plug-in parameter
 *     [Wait Effects] is completed.
 *   - Even if you skip sentences, weight will be applied.
 *
 *  ※5: \OC[r,g,b,a] (Specify the border color of characters with RGBA)
 *   - Specify the alpha value (a) from 0.0 to 1.0.
 *
 *  ※6: \DF, \SV, \LD (Initialize / save / recall text display settings)
 *   - The following information is applicable.
 *       - Text speed
 *       - Anime type
 *       - Font color
 *       - Font size
 *       - Font bold flag
 *       - Font italic flag
 *       - Text opacity
 *       - Text outline color
 *       - Text outline width
 *       - Ruby color
 *
 *
 *  ※7: \ES (Temporarily enable skipping event production)
 *   - See the plugin command SetEffectSkip for details.
 *
 *  ※8: \FO[n] (Temporarily change the fade-out type)
 *   - See the plugin command SetMesFadeOut for details.
 *
 * ▼ Plugin command details
 *  - In MV, the variable N is referred to by writing v [N] in the item for
 *    inputting a numerical value.
 *  - In MZ, in the item to enter a numerical value, select the text and
 *    write v [N] to refer to the variable N.
 *
 *  〇 MV / MZ
 *
 *  〇 SetMesRow row  / messageRow
 *       row : Number of lines in the message window
 *   - Change the number of lines displayed in the message window.
 *
 *  〇 SetMesFadeOut type  / messageFadeOut
 *       type : Fade out type
 *   - Change the fade-out type.
 *   - The types to specify are as follows.
 *       0: Default
 *       1: Gradually disappear (fast)
 *       2: Gradually disappear (slow)
 *       3: Scroll up
 *       4: Add text below (Only when Op3 is introduced)
 *
 *  〇 SetEffectSkip bool  / effectSkip
 *       bool : Enabled with true, disabled with false
 *   - When you skip a sentence, you can change whether to skip the production.
 *   - The default setting is invalid.
 *   - The effect to skip can be set in the plug-in parameter [Skip Effects].
 *   - The control character \ES is temporary, but it is always applied after
 *     the command is executed.
 *
 * ================================
 * Mail : wood_penguin＠yahoo.co.jp (＠ is half-width)
 * Blog : http://woodpenguin.blog.fc2.com/
 * License : MIT license
 *
 *  @command messageRow
 *      @desc
 *      @arg row
 *          @desc
 *          @type number
 *              @min 1
 *              @max 99
 *          @default 4
 *
 *  @command messageFadeOut
 *      @desc
 *      @arg type
 *          @desc
 *          @type select
 *              @option 0:Default
 *                  @value 0
 *              @option 1:Gradually disappear (fast)
 *                  @value 1
 *              @option 2:Gradually disappear (slow)
 *                  @value 2
 *              @option 3:Scroll up
 *                  @value 3
 *              @option 4:Op3| Add text below
 *                  @value 4
 *          @default 0
 *
 *  @command effectSkip
 *      @desc
 *      @arg boolean
 *          @desc
 *          @type boolean
 *          @default true
 *
 *
 * @----------------
 *
 *  @param Text Set
 *      @desc An array of sets of strings to call with \SET
 * (From the top, \SET[1], \SET[2] ...)
 *      @type string[]
 *      @default []
 *
 *  @param Wait Effects
 *      @desc \WE Directing to wait for the end when executing
 *      @type struct<WaitEffects>
 *      @default {"Scroll Map":"true","Set Movement Route":"true","Show Animation":"true","Show Balloon Icon":"true","Move Picture":"true","Tint Picture":"true","Tint Screen":"true","Flash Screen":"true","Shake Screen":"false","Set Weather Effect":"false","Fadeout BGM":"false","Fadeout BGS":"false","Play ME":"false"}
 *
 *  @param Skip Effects
 *      @desc A production that skips sentences at the same time when skipping sentences
 *      @type struct<SkipEffects>
 *      @default {"Scroll Map":"true","Set Movement Route":"true","Move Picture":"true","Tint Picture":"true","Tint Screen":"false","Flash Screen":"false","Shake Screen":"false","Set Weather Effect":"false"}
 *
 *  @param Skip Effects Timing
 *      @desc
 *      @type select
 *          @option skip
 *          @option end of text
 *      @default skip
 *      @parent Skip Effects
 *
 *  @param Ruby Font Size
 *      @desc
 *      @type number
 *          @min 4
 *          @max 99
 *      @default 14
 *
 *  @param Ruby Oy
 *      @desc
 *      @type number
 *          @min -9999
 *          @max 9999
 *      @default 0
 *
 *  @param Ruby Outline Width
 *      @desc
 *      @type number
 *          @min 0
 *          @max 99
 *      @default 2
 *
 *  @param Always Leave Ruby Height
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Default
 *      @desc
 *      @type struct<Default>
 *      @default {"Anime Type":"1","Message Row":"4","FadeOut Type":"0","Text Speed":"60","Outline Color":"0,0,0,0.5","Outline Width":"4","Ruby Color":"255,255,255"}
 *
 */

/*~struct~WaitEffects:
 *  @param Scroll Map
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Set Movement Route
 *      @desc Excludes [Repeat operation]
 *      @type boolean
 *      @default true
 *
 *  @param Show Animation
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Show Balloon Icon
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Move Picture
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Picture
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Screen
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Flash Screen
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Shake Screen
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Set Weather Effect
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Fadeout BGM
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Fadeout BGS
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Play ME
 *      @desc
 *      @type boolean
 *      @default false
 *
 */

/*~struct~SkipEffects:
 *  @param Scroll Map
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Set Movement Route
 *      @desc Excludes [Repeat operation]
 *      @type boolean
 *      @default true
 *
 *  @param Move Picture
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Picture
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Screen
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Flash Screen
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Shake Screen
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Set Weather Effect
 *      @desc
 *      @type boolean
 *      @default false
 *
 */

/*~struct~Default:
 *  @param Anime Type
 *      @desc
 *      @type number
 *          @min 0
 *          @max 999
 *      @default 1
 *
 *  @param Message Row
 *      @desc
 *      @type number
 *          @min 1
 *          @max 99
 *      @default 4
 *
 *  @param FadeOut Type
 *      @desc
 *      @type select
 *          @option 0:Default
 *              @value 0
 *          @option 1:Gradually disappear (fast)
 *              @value 1
 *          @option 2:Gradually disappear (slow)
 *              @value 2
 *          @option 3:Scroll up
 *              @value 3
 *          @option 4:Op3| Add text below
 *              @value 4
 *      @default 0
 *
 *  @param Text Speed
 *      @desc
 *      @type number
 *          @min 0
 *          @max 999
 *      @default 60
 *
 *  @param Outline Color
 *      @desc Specified by RGBA / Alpha value is specified from 0 - 1.0
 *      @default 0,0,0,0.5
 *
 *  @param Outline Width
 *      @desc
 *      @type number
 *          @min 0
 *          @max 99
 *      @default 4
 *
 *  @param Ruby Color
 *      @desc Specified by RGB
 *      @default 255,255,255
 *
 */

/*:ja
 * @target MV MZ
 * @plugindesc 文章表示の機能を拡張したり表示の演出を追加します。
 * @author 木星ペンギン
 * @url
 *
 * @help [version 4.4.0]
 * このプラグインはRPGツクールMVおよびMZ用です。
 *
 * ▼ [文章の表示]の制御文字
 *   \SP[n]        # 文章の表示速度(秒間描写文字数n / 0:瞬間表示)
 *   \AT[n]        # アニメーションタイプをn番に変更(※1)
 *   \SET[n]       # 設定した文字列に変換(※2)
 *   \CO[S]        # 文字列Sを１文字として表示
 *   \RB[S,R]      # 文字列SにルビRを付けて表示(※3)
 *   \MX[n]        # 次に表示する文字のX座標をnピクセルずらす
 *   \MY[n]        # 次に表示する文字のY座標をnピクセルずらす
 *   \PX[n]        # 次に表示する文字のX座標をnに変更
 *   \PY[n]        # 次に表示する文字のY座標をnに変更
 *   \SW[n]        # スイッチn番をONにする
 *   \SN[n]        # スキルID n 番の名前に置き換える
 *   \SIN[n]       # スキルID n 番のアイコンと名前に置き換える
 *   \IN[n]        # アイテムID n 番の名前に置き換える
 *   \IIN[n]       # アイテムID n 番のアイコンと名前に置き換える
 *   \WN[n]        # 武器ID n 番の名前に置き換える
 *   \WIN[n]       # 武器ID n 番のアイコンと名前に置き換える
 *   \AN[n]        # 防具ID n 番の名前に置き換える
 *   \AIN[n]       # 防具ID n 番のアイコンと名前に置き換える
 *   \WE           # イベントの演出が終了するまでウェイト(※4)
 *   \C[r,g,b]     # 文字色をRGBで指定
 *   \FS[n]        # 文字サイズをnに変更 / デフォルト値:MV=28,MZ=26
 *   \FB           # 文字の太字フラグを反転
 *   \FI           # 文字の斜体フラグを反転
 *   \OP[n]        # 文字の不透明度(0～255) / デフォルト値:255
 *   \OC[n]        # 文字の縁の色をn番に変更 / 0:デフォルト(黒)
 *   \OC[r,g,b]    # 文字の縁の色をRGBで指定
 *   \OC[r,g,b,a]  # 文字の縁の色をRGBAで指定(※5)
 *   \OW[n]        # 文字の縁の太さを変更 / デフォルト値:4
 *   \RC[n]        # ルビの色をn番に変更 / 0:デフォルト
 *   \RC[r,g,b]    # ルビの色をRGBで指定
 *   \RC[r,g,b,a]  # ルビの色をRGBAで指定(※5)
 *   \DF           # 文章表示の設定をデフォルト値に戻す(※6)
 *   \SV           # 現在の文章表示の設定を記憶(※6)
 *   \LD           # \SVで記憶した設定の呼び出し(※6)
 *
 *  - 以下は文章内に含まれていた場合に適用
 *   \A            # 決定キーやシフトキーによるスキップの禁止
 *   \ES           # イベントの演出のスキップを一時的に有効にする(※7)
 *   \FO[n]        # フェードアウトタイプを一時的に変更する(※8)
 *
 *  - すべての制御文字は大文字小文字どちらでも可能
 *
 * ▼ 制御文字詳細
 *  ※1: \AT[n] (アニメーションタイプをn番に変更)
 *   - アニメーションタイプのデフォルトは以下のようになります。
 *       0: デフォルト
 *       1: 文字が右にスライドしながら浮かび上がる
 *       2: 文字が横に広がりながら表示される
 *       3: 文字が拡大しながら表示される
 *       4: 文字を左側から表示する(表示速度6推奨)
 *
 *  ※2: \SET[n] (設定した文字列に変換)
 *   - プラグインパラメータ[Text Set]で指定した文字列に変換します。
 *   - 制御文字も設定可能です。
 *
 *  ※3: \RB[S,R] (文字列SにルビRを付けて表示)
 *   - ルビを振った文字列は一文字ずつではなくまとめて表示されます。
 *
 *  ※4: \WE (演出が終了するまでウェイト)
 *   - プラグインパラメータ[Wait Effects]で設定したイベントの演出が終了するまで
 *     ウェイトを行います。
 *   - 文章のスキップを行ってもウェイトがかかります。
 *
 *  ※5: \OC[r,g,b,a] (文字の縁の色をRGBAで指定)
 *   - アルファ値(a)は0.0～1.0で指定してください。
 *
 *  ※6: \DF, \SV, \LD (文章表示の設定を初期化/保存/呼び出し)
 *   - 以下の情報が対象となります。
 *       - 文章の表示速度
 *       - アニメーションタイプ
 *       - 文字色
 *       - 文字サイズ
 *       - 文字の太字フラグ
 *       - 文字の斜体フラグ
 *       - 文字の不透明度
 *       - 文字の縁の色
 *       - 文字の縁の太さ
 *       - ルビの色
 *
 *  ※7: \ES (イベントの演出のスキップを一時的に有効にする)
 *   - 詳細はプラグインコマンドの SetEffectSkip を参照。
 *
 *  ※8: \FO[n] (フェードアウトタイプを一時的に変更する)
 *   - 詳細はプラグインコマンドの SetMesFadeOut を参照。
 *
 * ▼ プラグインコマンド詳細
 *  - MVでは数値を入力する項目で v[N] と記述することで変数N番を参照します。
 *  - MZでは数値を入力する項目で、テキストを選択して v[N] と記述することで
 *    変数N番を参照します。
 *
 *  〇 MV / MZ
 *
 *  〇 SetMesRow row  / メッセージ行数設定
 *       row : メッセージウィンドウの行数
 *   - メッセージウィンドウの表示行数をn行に変更します。
 *
 *  〇 SetMesFadeOut type  / メッセージフェードアウト
 *       type : フェードアウトのタイプ
 *   - フェードアウトのタイプを変更します。
 *   - 指定するタイプは以下の通りです。
 *       0: デフォルト
 *       1: 徐々に消える(早め)
 *       2: 徐々に消える(遅め)
 *       3: 上にスクロール
 *       4: 下に文章を追加(Op3 導入時のみ)
 *
 *  〇 SetEffectSkip bool  / 演出スキップ
 *       bool : trueで有効, falseで無効
 *   - 文章のスキップを行った際、演出のスキップをするかどうかを変更できます。
 *   - 初期設定は無効です。
 *   - スキップする演出はプラグインパラメータ[Skip Effects]にて設定できます。
 *   - 制御文字 \ES は一時的なものですが、こちらはコマンド実行後、常に適用されます。
 *
 * ================================
 * Mail : wood_penguin＠yahoo.co.jp (＠は半角)
 * Blog : http://woodpenguin.blog.fc2.com/
 * License : MIT license
 *
 *  @command messageRow
 *      @text メッセージ行数設定
 *      @desc
 *      @arg row
 *          @desc
 *          @type number
 *              @min 1
 *              @max 99
 *          @default 4
 *
 *  @command messageFadeOut
 *      @text メッセージフェードアウト
 *      @desc
 *      @arg type
 *          @desc
 *          @type select
 *              @option 0:デフォルト(瞬時に消える)
 *                  @value 0
 *              @option 1:徐々に消える(早め)
 *                  @value 1
 *              @option 2:徐々に消える(遅め)
 *                  @value 2
 *              @option 3:上にスクロール
 *                  @value 3
 *              @option 4:Op3| 下に文章を追加
 *                  @value 4
 *          @default 0
 *
 *  @command effectSkip
 *      @text 演出スキップ
 *      @desc
 *      @arg boolean
 *          @desc
 *          @type boolean
 *          @default true
 *
 *
 * @----------------
 *
 *  @param Text Set
 *      @text 文字列セット
 *      @desc \SETにて呼び出す文字列のセットの配列
 * (上から \SET[1],\SET[2]... となります)
 *      @type string[]
 *      @default []
 *
 *  @param Wait Effects
 *      @text 終了待ちする演出
 *      @desc \WE実行時に終了待ちをする演出
 *      @type struct<WaitEffects>
 *      @default {"Scroll Map":"true","Set Movement Route":"true","Show Animation":"true","Show Balloon Icon":"true","Move Picture":"true","Tint Picture":"true","Tint Screen":"true","Flash Screen":"true","Shake Screen":"false","Set Weather Effect":"false","Fadeout BGM":"false","Fadeout BGS":"false","Play ME":"false"}
 *
 *  @param Skip Effects
 *      @text スキップする演出
 *      @desc 文章のスキップをした際、同時にスキップを行う演出
 *      @type struct<SkipEffects>
 *      @default {"Scroll Map":"true","Set Movement Route":"true","Move Picture":"true","Tint Picture":"true","Tint Screen":"false","Flash Screen":"false","Shake Screen":"false","Set Weather Effect":"false"}
 *
 *  @param Skip Effects Timing
 *      @text 演出スキップのタイミング
 *      @desc
 *      @type select
 *          @option スキップ時
 *              @value skip
 *          @option 文章の表示終了時
 *              @value end of text
 *      @default skip
 *      @parent Skip Effects
 *
 *  @param Ruby Font Size
 *      @text ルビの文字サイズ
 *      @desc
 *      @type number
 *          @min 4
 *          @max 99
 *      @default 14
 *
 *  @param Ruby Oy
 *      @text ルビY軸補正値
 *      @desc
 *      @type number
 *          @min -9999
 *          @max 9999
 *      @default 0
 *
 *  @param Ruby Outline Width
 *      @text ルビの縁の太さ
 *      @desc
 *      @type number
 *          @min 0
 *          @max 99
 *      @default 2
 *
 *  @param Always Leave Ruby Height
 *      @text 常にルビの高さを空ける
 *      @desc
 *      @type boolean
 *      @default false
 *      @parent Ruby
 *
 *  @param Default
 *      @text デフォルト値
 *      @desc
 *      @type struct<Default>
 *      @default {"Anime Type":"1","Message Row":"4","FadeOut Type":"0","Text Speed":"60","Outline Color":"0,0,0,0.5","Outline Width":"4","Ruby Color":"255,255,255"}
 *
 */

/*~struct~WaitEffects:ja
 *  @param Scroll Map
 *      @text マップのスクロール
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Set Movement Route
 *      @text 移動ルートの設定
 *      @desc [動作を繰り返す]は除く
 *      @type boolean
 *      @default true
 *
 *  @param Show Animation
 *      @text アニメーションの表示
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Show Balloon Icon
 *      @text フキダシアイコンの表示
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Move Picture
 *      @text ピクチャの移動
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Picture
 *      @text ピクチャの色調変更
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Screen
 *      @text 画面の色調変更
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Flash Screen
 *      @text 画面のフラッシュ
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Shake Screen
 *      @text 画面のシェイク
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Set Weather Effect
 *      @text 天候の設定
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Fadeout BGM
 *      @text BGMのフェードアウト
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Fadeout BGS
 *      @text BGSのフェードアウト
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Play ME
 *      @text MEの演奏
 *      @desc
 *      @type boolean
 *      @default false
 *
 */

/*~struct~SkipEffects:ja
 *  @param Scroll Map
 *      @text マップのスクロール
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Set Movement Route
 *      @text 移動ルートの設定
 *      @desc [動作を繰り返す]は除く
 *      @type boolean
 *      @default true
 *
 *  @param Move Picture
 *      @text ピクチャの移動
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Picture
 *      @text ピクチャの色調変更
 *      @desc
 *      @type boolean
 *      @default true
 *
 *  @param Tint Screen
 *      @text 画面の色調変更
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Flash Screen
 *      @text 画面のフラッシュ
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Shake Screen
 *      @text 画面のシェイク
 *      @desc
 *      @type boolean
 *      @default false
 *
 *  @param Set Weather Effect
 *      @text 天候の設定
 *      @desc
 *      @type boolean
 *      @default false
 *
 */

/*~struct~Default:ja
 *  @param Anime Type
 *      @text アニメーションタイプ
 *      @desc
 *      @type number
 *          @min 0
 *          @max 999
 *      @default 1
 *
 *  @param Message Row
 *      @text メッセージ行数
 *      @desc [メッセージウィンドウの表示行数]のデフォルト値
 *      @type number
 *          @min 1
 *          @max 99
 *      @default 4
 *
 *  @param FadeOut Type
 *      @text フェードアウトタイプ
 *      @desc
 *      @type select
 *          @option 0:デフォルト(瞬時に消える)
 *              @value 0
 *          @option 1:徐々に消える(早め)
 *              @value 1
 *          @option 2:徐々に消える(遅め)
 *              @value 2
 *          @option 3:上にスクロール
 *              @value 3
 *          @option 4:Op3| 下に文章を追加
 *              @value 4
 *      @default 0
 *
 *  @param Text Speed
 *      @text 文章の表示速度
 *      @desc
 *      @type number
 *          @min 0
 *          @max 999
 *      @default 60
 *
 *  @param Outline Color
 *      @text 文字の縁の色
 *      @desc RGBAで指定 / アルファ値は 0～1.0 で指定
 *      @default 0,0,0,0.5
 *
 *  @param Outline Width
 *      @text 文字の縁の太さ
 *      @desc
 *      @type number
 *          @min 0
 *          @max 99
 *      @default 4
 *
 *  @param Ruby Color
 *      @text ルビの色
 *      @desc RGBで指定
 *      @default 255,255,255
 *
 */

(() => {
    'use strict';

    const pluginName = 'MPP_MessageEX';

    // Plugin Parameters
    const parameters = PluginManager.parameters(pluginName);
    const paramReplace = (key, value) => {
        try {
            return JSON.parse(value);
        } catch (e) {
            return value;
        }
    };
    const param_TextSet = JSON.parse(parameters['Text Set'] || '[]');
    const param_WaitEffects = JSON.parse(parameters['Wait Effects'] || '{}', paramReplace);
    const param_SkipEffects = JSON.parse(parameters['Skip Effects'] || '{}', paramReplace);
    const param_SkipEffectsTiming = parameters['Skip Effects Timing'] || 'skip';
    const param_RubyFontSize = Number(parameters['Ruby Font Size'] || 14);
    const param_RubyOy = Number(parameters['Ruby Oy'] || 0);
    const param_RubyOutlineWidth = Number(parameters['Ruby Outline Width'] || 2);
    const param_AlwaysLeaveRubyHeight = parameters['Always Leave Ruby Height'] === 'true';

    const param_Default = JSON.parse(parameters['Default'] || '{}', paramReplace);
    const getDefaultParam = (name, def = null) => {
        return name in param_Default ? param_Default[name] : def;
    };

    // Dealing with other plugins
    const __base = (obj, prop) => {
        if (obj.hasOwnProperty(prop)) {
            return obj[prop];
        } else {
            const proto = Object.getPrototypeOf(obj);
            return function () { return proto[prop].apply(this, arguments); };
        }
    };
    const _importedPlugin = (...names) => {
        return names.some(name => PluginManager._scripts.includes(name));
    };

    // MPP_Patch.js
    let param_Patch6 = false;
    if (_importedPlugin('MPP_Patch')) {
        const patchParameters = PluginManager.parameters('MPP_Patch');
        param_Patch6 = patchParameters['Patch6 enabled?'] === 'true';
    }

    // MathExt
    const MathExt = (() => {
        // Number.prototype.clamp と違い、下限優先
        const clamp = (x, min, max) => Math.max(Math.min(x, max), min);
        return { clamp };
    })();

    // RPG Maker Param
    const getTextColor = function(index) {
        return Utils.RPGMAKER_NAME === 'MV'
            ? this.textColor(index)
            : ColorManager.textColor(index);
    };

    //-----------------------------------------------------------------------------
    // Bitmap

    if (!Bitmap.prototype.destroy) {

        Bitmap.prototype.destroy = function() {
            if (this._baseTexture) {
                this._baseTexture.destroy();
                this.__baseTexture = null;
            }
            this._destroyCanvas();
        };

        Bitmap.prototype._destroyCanvas = function() {
            if (this._canvas) {
                this._canvas.width = 0;
                this._canvas.height = 0;
                this.__canvas = null;
            }
        };

    }

    //-------------------------------------------------------------------------
    // WebAudio

    WebAudio.prototype.realVolume = function() {
        return this._gainNode ? this._gainNode.gain.value : 0;
    };

    //-------------------------------------------------------------------------
    // Html5Audio

    if (Utils.RPGMAKER_NAME === 'MV') {

        Html5Audio.realVolume = function() {
            return this._audioElement ? this._audioElement.volume : 0;
        };

    }

    //-------------------------------------------------------------------------
    // AudioManager

    AudioManager.isBgmFadeOuting = function() {
        return (
            this._bgmBuffer &&
            !this._currentBgm &&
            this._bgmBuffer.realVolume() > 0
        );
    };

    AudioManager.isBgsFadeOuting = function() {
        return (
            this._bgsBuffer &&
            !this._currentBgs &&
            this._bgsBuffer.realVolume() > 0
        );
    };

    AudioManager.isMePlaying = function() {
        return this._meBuffer && this._meBuffer.isPlaying();
    };

    //-----------------------------------------------------------------------------
    // Game_Character

    Game_Character.prototype.isMoveRouteForcingNr = function() {
        return this.isMoveRouteForcing() && !this._moveRoute.repeat;
    };

    //-------------------------------------------------------------------------
    // Game_Map

    Game_Map.prototype.isAnyMoveRouteForcingNr = function() {
        return (
            this.events().some(e => e.isMoveRouteForcingNr()) ||
            $gamePlayer.isMoveRouteForcingNr()
        );
    };

    Game_Map.prototype.isAnyAnimationPlaying = function() {
        return (
            this.events().some(e => e.isAnimationPlaying()) ||
            $gamePlayer.isAnimationPlaying()
        );
    };

    Game_Map.prototype.isAnyBalloonPlaying = function() {
        return (
            this.events().some(e => e.isBalloonPlaying()) ||
            $gamePlayer.isBalloonPlaying()
        );
    };

    //-------------------------------------------------------------------------
    // Game_Screen

    Game_Screen.prototype.isAnyPictureMoving = function() {
        return this._pictures.some(p => p && p.isMoving());
    };

    Game_Screen.prototype.isAnyPictureTinting = function() {
        return this._pictures.some(p => p && p.isTinting());
    };

    Game_Screen.prototype.isTinting = function() {
        return this._toneDuration > 0;
    };

    Game_Screen.prototype.isFlashing = function() {
        return this._flashDuration > 0;
    };

    Game_Screen.prototype.isShaking = function() {
        return this._shakeDuration > 0;
    };

    Game_Screen.prototype.isWeatherChanging = function() {
        return this._weatherDuration > 0;
    };

    //-------------------------------------------------------------------------
    // Game_Picture

    Game_Picture.prototype.isMoving = function() {
        return this._duration > 0;
    };

    Game_Picture.prototype.isTinting = function() {
        return this._toneDuration > 0;
    };

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

    const _Game_Message_initialize = Game_Message.prototype.initialize;
    Game_Message.prototype.initialize = function() {
        _Game_Message_initialize.apply(this, arguments);
        this._messageRow = getDefaultParam('Message Row', 4);
        this._fadeOutType = getDefaultParam('FadeOut Type', 0);
        this._effectSkip = false;
    };

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

    Game_Message.prototype.clearSceneEffectSkip = function() {
        this._sceneEffectSkip = false;
    };

    Game_Message.prototype.requestSceneEffectSkip = function() {
        this._sceneEffectSkip = true;
    };

    Game_Message.prototype.messageRow = function() {
        return this._messageRow;
    };

    Game_Message.prototype.fadeOutType = function() {
        return this._fadeOutType;
    };

    Game_Message.prototype.effectSkip = function() {
        return this._effectSkip;
    };

    Game_Message.prototype.sceneEffectSkip = function() {
        return this._sceneEffectSkip;
    };

    Game_Message.prototype.setMessageRow = function(row) {
        this._messageRow = row;
    };

    Game_Message.prototype.setFadeOutType = function(type) {
        this._fadeOutType = type;
    };

    Game_Message.prototype.setEffectSkip = function(skip) {
        this._effectSkip = skip;
    };

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

    //-----------------------------------------------------------------------------
    // Game_Interpreter

    const _mzCommands = {
        SetMesRow: { name:'messageRow', keys:['row'] },
        SetMesFadeOut: { name:'messageFadeOut', keys:['type'] },
        SetEffectSkip: { name:'effectSkip', keys:['boolean'] }
    };
    Object.assign(_mzCommands, {
        'メッセージ行数設定': _mzCommands.SetMesRow,
        'メッセージフェードアウト': _mzCommands.SetMesFadeOut,
        '演出スキップ': _mzCommands.SetEffectSkip
    });

    const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        _Game_Interpreter_pluginCommand.apply(this, arguments);
        const params = _mzCommands[command];
        if (params) {
            const args2 = Object.assign(
                {}, ...params.keys.map((k, i) => ({ [k]: args[i] }))
            );
            PluginManager.callCommandMV(this, pluginName, params.name, args2);
        }
    };

    //-------------------------------------------------------------------------
    // PluginManager

    if (!PluginManager.registerCommand && !PluginManager.registerCommandMV) {
        PluginManager._commandsMV = PluginManager._commandsMV || {};

        PluginManager.registerCommandMV = function(pluginName, commandName, func) {
            const key = pluginName + ':' + commandName;
            this._commandsMV[key] = func;
        };

        PluginManager.callCommandMV = function(self, pluginName, commandName, args) {
            const key = pluginName + ':' + commandName;
            const func = this._commandsMV[key];
            if (typeof func === 'function') {
                func.bind(self)(args);
            }
        };
    }

    {
        const _registerCommand = PluginManager.registerCommand || PluginManager.registerCommandMV;

        _registerCommand.call(PluginManager, pluginName, 'messageRow', args => {
            $gameMessage.setMessageRow(PluginManager.mppValue(args.row));
        });

        _registerCommand.call(PluginManager, pluginName, 'messageFadeOut', args => {
            $gameMessage.setFadeOutType(PluginManager.mppValue(args.type));
        });

        _registerCommand.call(PluginManager, pluginName, 'effectSkip', args => {
            $gameMessage.setEffectSkip(args.boolean === 'true');
        });
    }

    PluginManager.mppValue = function(value) {
        const match = /^V\[(\d+)\]$/i.exec(value);
        return match ? $gameVariables.value(+match[1]) : +value;
    };

    //-------------------------------------------------------------------------
    // Sprite_TextCharacter

    function Sprite_TextCharacter() {
        this.initialize.apply(this, arguments);
    }

    Sprite_TextCharacter.prototype = Object.create(Sprite.prototype);
    Sprite_TextCharacter.prototype.constructor = Sprite_TextCharacter;

    Sprite_TextCharacter.prototype.initialize = function() {
        Sprite.prototype.initialize.call(this);
        this._rect = null;
        this._list = null;
        this._index = -1;
        this._waitCount = 0;
    };

    Sprite_TextCharacter.prototype.setCallback = function(callback) {
        this._drawCallback = callback;
    };

    Sprite_TextCharacter.prototype.setup = function(bitmap, rect, list) {
        this.bitmap = bitmap;
        this._rect = rect;
        this._list = list;
        this._index = 0;
        this._waitCount = 0;
        this.initBasic();
        this.initMove();
        this.initScale();
        this.initOpacity();
        this.initTone();
        this.initRotation();
        this.initFrame();
        this.x = rect.x;
        this.y = rect.y;
        this.scale.x = 1.0;
        this.scale.y = 1.0;
        this.opacity = 255;
        this.anchor.x = 0;
        this.anchor.y = 0;
        this.setColorTone([0, 0, 0, 0]);
        this.rotation = 0;
        this.update();
    };

    Sprite_TextCharacter.prototype.initBasic = function() {
        this._origin = 0;
        this._offsetX = 0;
        this._offsetY = 0;
    };

    Sprite_TextCharacter.prototype.initMove = function() {
        this._targetX = 0;
        this._targetY = 0;
        this._moveDuration = 0;
    };

    Sprite_TextCharacter.prototype.initScale = function() {
        this._targetScaleX = 1;
        this._targetScaleY = 1;
        this._scaleDuration = 0;
    };

    Sprite_TextCharacter.prototype.initOpacity = function() {
        this._targetOpacity = 255;
        this._opacityDuration = 0;
    };

    Sprite_TextCharacter.prototype.initTone = function() {
        this._tone = null;
        this._toneTarget = null;
        this._toneDuration = 0;
    };

    Sprite_TextCharacter.prototype.initRotation = function() {
        this._angle = 0;
        this._targetAngle = 0;
        this._angleDuration = 0;
    };

    Sprite_TextCharacter.prototype.initFrame = function() {
        const bitmap = this._bitmap;
        if (bitmap) {
            this.setFrame(0, 0, bitmap.width, bitmap.height);
        } else {
            this.setFrame(0, 0, 0, 0);
        }
        this._frame2 = null;
        this._frame2Target = null;
        this._frame2Duration = 0;
    };

    Sprite_TextCharacter.prototype.isPlaying = function() {
        return !!this._list;
    };

    Sprite_TextCharacter.prototype.isEffecting = function() {
        return (
            this._moveDuration > 0 ||
            this._scaleDuration > 0 ||
            this._opacityDuration > 0 ||
            this._toneDuration > 0 ||
            this._angleDuration > 0 ||
            this._frame2Duration > 0
        );
    };

    Sprite_TextCharacter.prototype.update = function() {
        Sprite.prototype.update.call(this);
        if (this.isPlaying() && this.updateCommand()) {
            this.updateMove();
            this.updateScale();
            this.updateOpacity();
            this.updateTone();
            this.updateRotation();
            this.updateFrame2();
        }
    };

    Sprite_TextCharacter.prototype.updateCommand = function() {
        for (;;) {
            if (this.updateWaitCount()) {
                return true;
            }
            const code = this._list[this._index];
            if (code) {
                const [command, ...args] = code.split(' ');
                this.callMethod(command, args.map(Number));
            } else {
                this.draw();
                return false;
            }
            this._index++;
        }
    };

    Sprite_TextCharacter.prototype.updateWaitCount = function() {
        if (this._waitCount < 0) {
            if (this.isEffecting()) return true;
            this._waitCount = 0;
        } else if (this._waitCount > 0) {
            this._waitCount--;
            return true;
        }
        return false;
    };

    Sprite_TextCharacter.prototype.callMethod = function(command, args) {
        switch (command) {
            case 'show':
                this.commandShow(args);
                break;
            case 'move':
                this.commandMove(args);
                break;
            case 'scale':
                this.commandScale(args);
                break;
            case 'opacity':
                this.commandOpacity(args);
                break;
            case 'rotate':
                this.commandRotate(args);
                break;
            case 'tone':
                this.commandTone(args);
                break;
            case 'frame':
                this.commandFrame(args);
                break;
            case 'wait':
                this.commandWait(args);
                break;
            case 'finish':
                this.commandFinish(args);
                break;
        }
    };

    Sprite_TextCharacter.prototype.updateMove = function() {
        if (this._moveDuration > 0) {
            const d = this._moveDuration;
            this._offsetX = (this._offsetX * (d - 1) + this._targetX) / d;
            this._offsetY = (this._offsetY * (d - 1) + this._targetY) / d;
            this._moveDuration--;
        }
        this.x = this._rect.x + this._offsetX;
        this.y = this._rect.y + this._offsetY;
        if (this._origin === 1) {
            this.x += this._rect.width / 2;
            this.y += this._rect.height / 2;
        }
        this.refreshAnchor();
    };

    Sprite_TextCharacter.prototype.updateScale = function() {
        if (this._scaleDuration > 0) {
            const d = this._scaleDuration;
            this.scale.x = (this.scale.x * (d - 1) + this._targetScaleX) / d;
            this.scale.y = (this.scale.y * (d - 1) + this._targetScaleY) / d;
            this._scaleDuration--;
        }
    };

    Sprite_TextCharacter.prototype.updateOpacity = function() {
        if (this._opacityDuration > 0) {
            const d = this._opacityDuration;
            this.opacity = (this.opacity * (d - 1) + this._targetOpacity) / d;
            this._opacityDuration--;
        }
    };

    Sprite_TextCharacter.prototype.updateTone = function() {
        if (this._toneDuration > 0) {
            const d = this._toneDuration;
            for (const [i, target] of this._toneTarget.entries()) {
                this._tone[i] = (this._tone[i] * (d - 1) + target) / d;
            }
            this._toneDuration--;
        }
        if (this._tone) this.setColorTone(this._tone);
    };

    Sprite_TextCharacter.prototype.updateRotation = function() {
        if (this._angleDuration > 0) {
            const d = this._angleDuration;
            this._angle = (this._angle * (d - 1) + this._targetAngle) / d;
            this._angleDuration--;
        }
        this.rotation = this._angle * Math.PI / 180;
    };

    Sprite_TextCharacter.prototype.updateFrame2 = function() {
        if (this._frame2Duration > 0) {
            const d = this._frame2Duration;
            for (const [i, target] of this._frame2Target.entries()) {
                this._frame2[i] = (this._frame2[i] * (d - 1) + target) / d;
            }
            this._frame2Duration--;
            this.refreshFrame2();
            this.refreshAnchor();
        }
    };

    Sprite_TextCharacter.prototype.refreshFrame2 = function() {
        const { width, height } = this.bitmap;
        const frame = this._frame2;
        const fx = Math.round(width * frame[0] / 100);
        const fy = Math.round(height * frame[1] / 100);
        const fw = Math.max(Math.round(width * frame[2] / 100) - fx, 0);
        const fh = Math.max(Math.round(height * frame[3] / 100) - fy, 0);
        this.setFrame(fx, fy, fw, fh);
    };

    Sprite_TextCharacter.prototype.refreshAnchor = function() {
        const ox = (this._origin === 0 ? 0 : this._rect.width / 2) + 4;
        const oy = this._origin === 0 ? 0 : this._rect.height / 2;
        this.anchor.x = this.width > 0 ? ox / this.width : 0;
        this.anchor.y = this.height > 0 ? oy / this.height : 0;
    };

    Sprite_TextCharacter.prototype.draw = function() {
        if (this._drawCallback) {
            this._drawCallback(this.bitmap, this._rect);
        }
        this.delete();
    };

    Sprite_TextCharacter.prototype.delete = function() {
        this.parent.removeChild(this);
        this.bitmap.destroy();
        this.bitmap = null;
        this._list = null;
    };

    Sprite_TextCharacter.prototype.commandShow = function(args) {
        this._origin = MathExt.clamp(args[0] || 0, 0, 1);
        this._offsetX = args[1] || 0;
        this._offsetY = args[2] || 0;
        this.scale.x = args.length > 3 ? args[3] / 100 : 1;
        this.scale.y = args.length > 4 ? args[4] / 100 : 1;
        this.opacity = args.length > 5 ? args[5] : 255;
        this.initMove();
        this.initScale();
        this.initOpacity();
        this.initTone();
        this.initRotation();
        this.initFrame();
    };

    Sprite_TextCharacter.prototype.commandMove = function(args) {
        this._targetX = args[0] || 0;
        this._targetY = args[1] || 0;
        this._moveDuration = Math.max(args[2] || 0, 0);
        if (this._moveDuration === 0) {
            this._offsetX = this._targetX;
            this._offsetY = this._targetY;
        }
    };

    Sprite_TextCharacter.prototype.commandScale = function(args) {
        this._targetScaleX = args.length > 0 ? args[0] / 100 : 1;
        this._targetScaleY = args.length > 0 ? args[1] / 100 : 1;
        this._scaleDuration = Math.max(args[2] || 0, 0);
        if (this._scaleDuration === 0) {
            this.scale.x  = this._targetScaleX;
            this.scale.y  = this._targetScaleY;
        }
    };

    Sprite_TextCharacter.prototype.commandOpacity = function(args) {
        this._targetOpacity = args.length > 0 ? MathExt.clamp(args[0], 0, 255) : 255;
        this._opacityDuration = Math.max(args[1] || 0, 0);
        if (this._opacityDuration === 0) {
            this.opacity  = this._targetOpacity;
        }
    };

    Sprite_TextCharacter.prototype.commandRotate = function(args) {
        this._targetAngle = args[0] || 0;
        this._angleDuration = Math.max(args[1] || 0, 0);
        if (this._angleDuration === 0) {
            this._angle = this._targetAngle;
        }
    };

    Sprite_TextCharacter.prototype.commandTone = function(args) {
        if (!this._tone) this._tone = [0, 0, 0, 0];
        this._toneTarget = [];
        for (let i = 0; i < 4; i++) {
            this._toneTarget[i] = MathExt.clamp(args[i] || 0, -255, 255);
        }
        this._toneDuration = Math.max(args[4] || 0, 0);
        if (this._toneDuration === 0) {
            this._tone = this._toneTarget.clone();
            this.setColorTone(this._tone);
        }
    };

    Sprite_TextCharacter.prototype.commandFrame = function(args) {
        if (!this._frame2) this._frame2 = [0, 0, 100, 100];
        this._frame2Target = [];
        this._frame2Target[0] = args.length > 0 ? MathExt.clamp(args[0], 0, 100) : 0;
        this._frame2Target[1] = args.length > 1 ? MathExt.clamp(args[1], 0, 100) : 0;
        this._frame2Target[2] = args.length > 2 ? MathExt.clamp(args[2], 0, 100) : 100;
        this._frame2Target[3] = args.length > 3 ? MathExt.clamp(args[3], 0, 100) : 100;
        this._frame2Duration = Math.max(args[4] || 0, 0);
        if (this._frame2Duration === 0) {
            this._frame2 = this._frame2Target.clone();
            this.refreshFrame2();
        }
    };

    Sprite_TextCharacter.prototype.commandWait = function(args) {
        this._waitCount = args.length > 0 ? Math.max(args[0], 0) : -1;
    };

    Sprite_TextCharacter.prototype.commandFinish = function(args) {
        const count = Math.max(args[0] || 0, 0);
        this.commandMove([0, 0, count]);
        this.commandScale([100, 100, count]);
        this.commandOpacity([255, count]);
        this.commandRotate([0, count]);
        this.commandTone([0, 0, 0, 0, count]);
        this.commandFrame([0, 0, 100, 100, count]);
        this.commandWait([count]);
    };

    //-----------------------------------------------------------------------------
    // Window_Base

    const _Window_Base_convertEscapeCharacters = Window_Base.prototype.convertEscapeCharacters;
    Window_Base.prototype.convertEscapeCharacters = function(text) {
        return _Window_Base_convertEscapeCharacters.apply(this, arguments)
            .replace(/\x1bSET\[(\d+)\]/gi, (_, p1) => {
                const index = +p1;
                const setText = index > 0 ? param_TextSet[index - 1] : null;
                return setText ? this.convertEscapeCharacters(setText) : '';
            })
            .replace(/\x1b([SIWA])(I?)N\[(\d+)\]/gi, (_, p1, p2, p3) => {
                return this.mppConvertItemName(p1, p2 !== '', +p3);
            });
    };

    Window_Base.prototype.mppConvertItemName = function(type, icon, itemId) {
        let text = '';
        let item;
        switch (type.toUpperCase()) {
            case 'S':
                item = $dataSkills[itemId];
                break;
            case 'I':
                item = $dataItems[itemId];
                break;
            case 'W':
                item = $dataWeapons[itemId];
                break;
            case 'A':
                item = $dataArmors[itemId];
                break;
        }
        if (item) {
            if (icon) text += `\x1bI[${item.iconIndex}]`;
            text += item.name;
        }
        return text;
    };

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

    Window_Message.ANIMATIONS = [
        null,
        [ // type 1
            'show 0 -6 0 100 100 0',
            'finish 15'
        ],
        [ // type 2
            'show 0 0 0 0 100',
            'scale 75 100 6',
            'wait',
            'finish 6'
        ],
        [ // type 3
            'show 1 0 0 0 0',
            'scale 60 60 7',
            'wait',
            'scale 100 100 7',
            'wait',
            'scale 120 120 8',
            'wait',
            'finish 8'
        ],
        [ // type 4
            'show',
            'frame 0 0 0 100 0',
            'finish 10'
        ],
    ];

    const _Window_Message_initialize = Window_Message.prototype.initialize;
    Window_Message.prototype.initialize = function() {
        this._messageRow = $gameMessage.messageRow();
        this._textInfo = [];
        this._rubyBitmap = new Bitmap();
        _Window_Message_initialize.apply(this, arguments);
        this.createCharacterContainer();
        this._characterSprites = [];
        this._waitEffect = false;
    };

    const _Window_Message_destroy = __base(Window_Message.prototype, 'destroy');
    Window_Message.prototype.destroy = function(options) {
        this._rubyBitmap.destroy();
        _Window_Message_destroy.apply(this, arguments);
    };

    Window_Message.prototype.createCharacterContainer = function() {
        if (Utils.RPGMAKER_NAME === 'MV') {
            this._characterContainer = new Sprite();
            this.addChild(this._characterContainer);
            this.updateCharacterContainer();
        }
    };

    const _Window_Message_fittingHeight = __base(Window_Message.prototype, 'fittingHeight');
    Window_Message.prototype.fittingHeight = function(numLines) {
        let height = _Window_Message_fittingHeight.apply(this, arguments);
        if (param_AlwaysLeaveRubyHeight) {
            height += this.baseRubyHeight() * numLines;
        }
        return height;
    };

    const _Window_Message_resetFontSettings = __base(Window_Message.prototype, 'resetFontSettings');
    Window_Message.prototype.resetFontSettings = function() {
        _Window_Message_resetFontSettings.apply(this, arguments);
        const rubyRgb = getDefaultParam('Ruby Color');
        const rubyColor = rubyRgb ? `rgb(${rubyRgb})` : getTextColor.call(this, 0);
        const outlineColor = `rgba(${getDefaultParam('Outline Color', '0, 0, 0, 0.5')})`;
        this.contents.fontBold = false;
        this.contents.fontItalic = false;
        this.contents.paintOpacity = 255;
        this._paintOpacity = 255;
        this.contents.outlineColor = outlineColor;
        this.contents.outlineWidth = getDefaultParam('Outline Width', 4);
        this._rubyBitmap.textColor = rubyColor;
        this._rubyBitmap.fontSize = param_RubyFontSize;
        this._rubyBitmap.outlineWidth = param_RubyOutlineWidth;
        this._rubyBitmap.outlineColor = outlineColor;
    };

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

    // overwrite mv
    Window_Message.prototype.windowHeight = function() {
        return this.fittingHeight(this.numVisibleRows());
    };

    const _Window_Message_clearFlags = Window_Message.prototype.clearFlags;
    Window_Message.prototype.clearFlags = function() {
        _Window_Message_clearFlags.apply(this, arguments);
        this.clearMessageExDefault();
        this._lastBottomY = 0;
        this._messageCount = 0;
    };

    Window_Message.prototype.clearMessageExFlags = function() {
        this._auto = false;
        this._effectSkip = $gameMessage.effectSkip();
        this._fadeOut = false;
        this._fadeOutType = $gameMessage.fadeOutType();
    };

    Window_Message.prototype.clearMessageExDefault = function() {
        this._speed = getDefaultParam('Text Speed', 60);
        this._animeType = getDefaultParam('Anime Type', 0);
    };

    Window_Message.prototype.numVisibleRows = function() {
        return this._messageRow;
    };

    Window_Message.prototype.getAnimationList = function() {
        return Window_Message.ANIMATIONS[this._animeType];
    };

    Window_Message.prototype.isCharacterAnimation = function() {
        return this.getAnimationList() && !this._showFast && !this._lineShowFast;
    };

    Window_Message.prototype.getTextCharacterSprite = function() {
        let sprite = this._characterSprites.find(sprite => !sprite.isPlaying());
        if (!sprite) {
            sprite = new Sprite_TextCharacter();
            sprite.setCallback(this.drawTextCharacter.bind(this));
            this._characterSprites.push(sprite);
        }
        if (this._characterContainer) {
            this._characterContainer.addChild(sprite);
        } else {
            this.addInnerChild(sprite);
        }
        return sprite;
    };

    Window_Message.prototype.drawTextCharacter = function(bitmap, rect) {
        this.contents.blt(bitmap, 0, 0, bitmap.width, bitmap.height, rect.x - 4, rect.y);
    };

    const _Window_Message_flushTextState = __base(Window_Base.prototype, 'flushTextState');
    Window_Message.prototype.flushTextState = function(textState) {
        if (textState.drawing && this.isCharacterAnimation()) {
            this.flushTextStateForAnimation(textState);
        } else {
            this.contents.paintOpacity = this._paintOpacity;
            _Window_Message_flushTextState.apply(this, arguments);
            this.contents.paintOpacity = 255;
        }
    };

    Window_Message.prototype.flushTextStateForAnimation = function(textState) {
        const list = this.getAnimationList();
        const text = textState.buffer;
        const rtl = textState.rtl;
        const { y, height } = textState;
        const width = this.textWidth(text);
        const x = rtl ? textState.x - width : textState.x;
        const bitmap = this.createCharacterBitmap(width + 8, height);
        bitmap.drawText(text, 4, 0, width + 4, height);

        const sprite = this.getTextCharacterSprite();
        const rect = new Rectangle(x, y, width, height);
        sprite.setup(bitmap, rect, list);
        textState.x += rtl ? -width : width;
        textState.buffer = this.createTextBuffer(rtl);

        const outputWidth = Math.abs(textState.x - textState.startX);
        if (textState.outputWidth < outputWidth) {
            textState.outputWidth = outputWidth;
        }
        textState.outputHeight = y - textState.startY + height;
    };

    const _Window_Message_convertEscapeCharacters = __base(Window_Message.prototype, 'convertEscapeCharacters');
    Window_Message.prototype.convertEscapeCharacters = function(text) {
        return _Window_Message_convertEscapeCharacters.apply(this, arguments)
            .replace(/\x1bA(?![NIT])/gi, () => {
                this._auto = true;
                return '';
            })
            .replace(/\x1bES/gi, () => {
                this._effectSkip = true;
                return '';
            });
    };

    const _Window_Message_update = Window_Message.prototype.update;
    Window_Message.prototype.update = function() {
        _Window_Message_update.apply(this, arguments);
        this.updateCharacterContainer();
    };

    Window_Message.prototype.updateCharacterContainer = function() {
        if (this._characterContainer) {
            const pad = this._padding;
            this._characterContainer.move(pad, pad);
        }
    };

    const _Window_Message_startMessage = Window_Message.prototype.startMessage;
    Window_Message.prototype.startMessage = function() {
        this.clearMessageExFlags();
        _Window_Message_startMessage.apply(this, arguments);
        if (this.contents.height !== this.contentsHeight()) {
            this.createContents();
        }
    };

    const _Window_Message_updatePlacement = Window_Message.prototype.updatePlacement;
    Window_Message.prototype.updatePlacement = function() {
        this._messageRow = $gameMessage.messageRow();
        this.height = this.windowHeight();
        _Window_Message_updatePlacement.apply(this, arguments);
    };

        const _Window_Message_terminateMessage = Window_Message.prototype.terminateMessage;
    Window_Message.prototype.terminateMessage = function() {
        if (!this.isOpen() || this._fadeOutType === 0) {
            this.terminateMessageEx();
        } else {
            this._fadeOut = true;
        }
    };

    Window_Message.prototype.terminateMessageEx = function() {
        _Window_Message_terminateMessage.call(this);
        if (this._effectSkip && param_SkipEffectsTiming === 'end of text') {
            $gameMessage.requestSceneEffectSkip();
        }
    };

    const _Window_Message_updateWait = Window_Message.prototype.updateWait;
    Window_Message.prototype.updateWait = function() {
        return (
            _Window_Message_updateWait.apply(this, arguments) ||
            this.updateFadeOut() ||
            this.updateWaitEffects()
        );
    };

    const _Window_Message_updateMessage = Window_Message.prototype.updateMessage;
    Window_Message.prototype.updateMessage = function() {
        if (this._textState) {
            this.updateShowFast();
            this._messageCount += this._speed;
            while (this.isContinueMessage()) {
                if (_Window_Message_updateMessage.apply(this, arguments)) {
                    this._messageCount = Math.max(this._messageCount - 60, 0);
                } else {
                    break;
                }
            }
            return true;
        }
        return this._characterSprites.some(sprite => sprite.isPlaying());
    };

    Window_Message.prototype.isContinueMessage = function() {
        return (this._messageCount >= 60 || this._speed === 0) && !this.isWaiting();
    };

    Window_Message.prototype.updateFadeOut = function() {
        if (this._fadeOut) {
            if (this.updateFadeOutMove()) {
                this._fadeOut = false;
                this.terminateMessageEx();
            }
            return true;
        }
        return false;
    };

    Window_Message.prototype.updateFadeOutMove = function() {
        switch (this._fadeOutType) {
            case 1:
                this.contentsOpacity -= 8;
                return (this.contentsOpacity === 0);
            case 2:
                this.contentsOpacity -= 3;
                return (this.contentsOpacity === 0);
            case 3:
                this.origin.y += 6;
                return (this.origin.y >= this._lastBottomY);
        }
        return true;
    };

    Window_Message.prototype.updateWaitEffects = function() {
        if (this._waitEffect) {
            if (param_Patch6) this.updateShowFast();
            this._waitEffect = this.isEffectingEx(param_WaitEffects);
            return true;
        }
        return false;
    };

    const _Window_Message_areSettingsChanged = Window_Message.prototype.areSettingsChanged;
    Window_Message.prototype.areSettingsChanged = function() {
        return (
            _Window_Message_areSettingsChanged.apply(this, arguments) ||
            this._messageRow !== $gameMessage.messageRow()
        );
    };

    const _Window_Message_updateShowFast = Window_Message.prototype.updateShowFast;
    Window_Message.prototype.updateShowFast = function() {
        const lastShowFast = this._showFast;
        if (!this._auto) {
            _Window_Message_updateShowFast.apply(this, arguments);
        }
        if (!lastShowFast && this._showFast) {
            if (this._effectSkip && param_SkipEffectsTiming === 'skip') {
                $gameMessage.requestSceneEffectSkip();
            }
            for (const item of this._characterSprites) {
                if (item.isPlaying()) item.draw();
            }
        }
    };

    const _Window_Message_newPage = Window_Message.prototype.newPage;
    Window_Message.prototype.newPage = function(textState) {
        for (const item of this._characterSprites) {
            if (item.isPlaying()) item.delete();
        }
        _Window_Message_newPage.apply(this, arguments);
        this.contentsOpacity = 255;
        this.origin.y = 0;
        textState.rubyHeight = this.realRubyHeight(textState);
        textState.y += textState.rubyHeight;
        this._lastBottomY = 0;
    };

    Window_Message.prototype.realRubyHeight = function(textState) {
        const line = textState.text.slice(textState.index).split('\n')[0];
        if (param_AlwaysLeaveRubyHeight || /\x1bRB\[.+?\]/i.test(line)) {
            return this.baseRubyHeight();
        }
        return 0;
    };

    Window_Message.prototype.baseRubyHeight = function() {
        return Math.max(param_RubyFontSize - param_RubyOy, 0);
    };

    const _Window_Message_processCharacter = __base(Window_Message.prototype, 'processCharacter');
    Window_Message.prototype.processCharacter = function(textState) {
        _Window_Message_processCharacter.apply(this, arguments);
        this._lastBottomY = textState.y + textState.height;
    };

    const _Window_Message_processNewLine = Window_Message.prototype.processNewLine;
    Window_Message.prototype.processNewLine = function(textState) {
        if (this.isEndOfText(textState) && textState.x === textState.left) {
            return;
        }
        _Window_Message_processNewLine.apply(this, arguments);
        textState.rubyHeight = this.realRubyHeight(textState);
        textState.y += textState.rubyHeight;
        if (this.needsNewPage(textState)) {
            this.startPause();
        }
    };

    const _Window_Message_processEscapeCharacter = Window_Message.prototype.processEscapeCharacter;
    Window_Message.prototype.processEscapeCharacter = function(code, textState) {
        switch (code) {
            case 'SP':
                this._speed = this.obtainEscapeParam(textState);
                break;
            case 'AT':
                this._animeType = this.obtainEscapeParam(textState);
                break;
            case 'CO':
                this.processGroupCharacter(textState, this.obtainEscapeTexts(textState));
                break;
            case 'RB':
                this.processRubyCharacter(textState, this.obtainEscapeTexts(textState));
                break;
            case 'MX':
                textState.x += this.obtainEscapeParam2(textState);
                break;
            case 'MY':
                textState.y += this.obtainEscapeParam2(textState);
                break;
            case 'PX':
                textState.x = this.obtainEscapeParam(textState);
                break;
            case 'PY':
                textState.y = this.obtainEscapeParam(textState);
                break;
            case 'SW':
                $gameSwitches.setValue(this.obtainEscapeParam(textState), true);
                break;
            case 'WE':
                this.waitForEffect();
                break;
            case 'C':
                this.contents.textColor = this.obtainEscapeColor(textState);
                break;
            case 'FS':
                this.contents.fontSize = this.obtainEscapeParam(textState);
                break;
            case 'FB':
                this.contents.fontBold = !this.contents.fontBold;
                break;
            case 'FI':
                this.contents.fontItalic = !this.contents.fontItalic;
                break;
            case 'OP':
                this._paintOpacity = this.obtainEscapeParam(textState);
                break;
            case 'OC': {
                const color = this.obtainEscapeColor(textState, 'rgba(0,0,0,0.5)');
                this.contents.outlineColor = color;
                this._rubyBitmap.outlineColor = color;
                break;
            }
            case 'OW':
                this.contents.outlineWidth = this.obtainEscapeParam(textState);
                break;
            case 'RC':
                const rubyRgb = getDefaultParam('Ruby Color');
                const rubyColor = rubyRgb ? `rgb(${rubyRgb})` : getTextColor.call(this, 0);
                this._rubyBitmap.textColor = this.obtainEscapeColor(textState, rubyColor);
                break;
            case 'DF':
                this.defaultTextInfo();
                break;
            case 'SV':
                this.saveTextInfo();
                break;
            case 'LD':
                this.loadTextInfo();
                break;
            case 'FO':
                this._fadeOutType = this.obtainEscapeParam(textState);
                break;
            default:
                _Window_Message_processEscapeCharacter.apply(this, arguments);
                break;
        }
    };

    Window_Message.prototype.waitForEffect = function() {
        if (this.isEffectingEx(param_WaitEffects)) {
            this._waitEffect = true;
            this._waitCount = 1;
        }
    };

    Window_Message.prototype.obtainEscapeParam2 = function(textState) {
        const arr = /^\[-?\d+\]/.exec(textState.text.slice(textState.index));
        if (arr) {
            textState.index += arr[0].length;
            return parseInt(arr[0].slice(1));
        }
        return '';
    };

    Window_Message.prototype.obtainEscapeTexts = function(textState) {
        const arr = /^\[(.+?)\]/.exec(textState.text.slice(textState.index));
        if (arr) {
            textState.index += arr[0].length;
            return arr[1].split(',');
        }
        return [];
    };

    Window_Message.prototype.obtainEscapeColor = function(textState, defaultColor) {
        const arr = /^\[([\d\s,\.]+)\]/.exec(textState.text.slice(textState.index));
        if (arr) {
            textState.index += arr[0].length;
            switch (arr[1].split(',').length) {
                case 1:
                    const index = +arr[1];
                    return index === 0
                        ? defaultColor || getTextColor.call(this, 0)
                        : getTextColor.call(this, index);
                case 3:
                    return `rgb(${arr[1]})`;
                case 4:
                    return `rgba(${arr[1]})`;
            }
        }
        return '';
    };

    Window_Message.prototype.defaultTextInfo = function() {
        this.clearMessageExDefault();
        this.resetFontSettings();
    };

    Window_Message.prototype.saveTextInfo = function() {
        this._textInfo[0] = this._speed;
        this._textInfo[1] = this._animeType;
        this._textInfo[2] = this.contents.textColor;
        this._textInfo[3] = this.contents.fontSize;
        this._textInfo[4] = this.contents.fontBold;
        this._textInfo[5] = this.contents.fontItalic;
        this._textInfo[6] = this._paintOpacity;
        this._textInfo[7] = this.contents.outlineColor;
        this._textInfo[8] = this.contents.outlineWidth;
        this._textInfo[9] = this._rubyBitmap.textColor;
        this._textInfo[10] = this._rubyBitmap.outlineColor;
    };

    Window_Message.prototype.loadTextInfo = function() {
        if (this._textInfo.length > 0) {
            this._speed = this._textInfo[0];
            this._animeType = this._textInfo[1];
            this.contents.textColor = this._textInfo[2];
            this.contents.fontSize = this._textInfo[3];
            this.contents.fontBold = this._textInfo[4];
            this.contents.fontItalic = this._textInfo[5];
            this._paintOpacity = this._textInfo[6];
            this.contents.outlineColor = this._textInfo[7];
            this.contents.outlineWidth = this._textInfo[8];
            this._rubyBitmap.textColor = this._textInfo[9];
            this._rubyBitmap.outlineColor = this._textInfo[10];
        }
    };

    Window_Message.prototype.isEffectingEx = function(info) {
        return  (
            (info['Scroll Map']         && $gameMap.isScrolling()) ||
            (info['Set Movement Route'] && $gameMap.isAnyMoveRouteForcingNr()) ||
            (info['Show Animation']     && $gameMap.isAnyAnimationPlaying()) ||
            (info['Show Balloon Icon']  && $gameMap.isAnyBalloonPlaying()) ||
            (info['Move Picture']       && $gameScreen.isAnyPictureMoving()) ||
            (info['Tint Picture']       && $gameScreen.isAnyPictureTinting()) ||
            (info['Tint Screen']        && $gameScreen.isTinting()) ||
            (info['Flash Screen']       && $gameScreen.isFlashing()) ||
            (info['Shake Screen']       && $gameScreen.isShaking()) ||
            (info['Set Weather Effect'] && $gameScreen.isWeatherChanging()) ||
            (info['Fadeout BGM']        && AudioManager.isBgmFadeOuting()) ||
            (info['Fadeout BGS']        && AudioManager.isBgsFadeOuting()) ||
            (info['Play ME']            && AudioManager.isMePlaying())
        );
    };

    const _Window_Message_processNormalCharacter = __base(Window_Message.prototype, 'processNormalCharacter');
    Window_Message.prototype.processNormalCharacter = function(textState) {
        if (this.isCharacterAnimation()) {
            this.processNormalCharacterForAnimation(textState);
        } else {
            this.contents.paintOpacity = this._paintOpacity;
            _Window_Message_processNormalCharacter.apply(this, arguments);
            this.contents.paintOpacity = 255;
        }
    };

    Window_Message.prototype.processNormalCharacterForAnimation = function(textState) {
        const list = this.getAnimationList();
        const c = textState.text[textState.index++];
        const { x, y, height } = textState;
        const width = this.textWidth(c);
        const bitmap = this.createCharacterBitmap(width + 8, height);
        bitmap.drawText(c, 4, 0, width + 4, height);

        const sprite = this.getTextCharacterSprite();
        const rect = new Rectangle(x, y, width, height);
        sprite.setup(bitmap, rect, list);
        textState.x += width;
    };

    // overwrite
    Window_Message.prototype.processDrawIcon = function(iconIndex, textState) {
        const pw = Window_Base._iconWidth || ImageManager.iconWidth;
        const ph = Window_Base._iconHeight || ImageManager.iconHeight;
        if (Utils.RPGMAKER_NAME === 'MV' || textState.drawing) {
            if (this.isCharacterAnimation()) {
                this.processDrawIconForAnimation(iconIndex, textState);
            } else {
                const offsetY = Math.floor(textState.height - ph) / 2;
                this.contents.paintOpacity = this._paintOpacity;
                this.drawIcon(iconIndex, textState.x + 2, textState.y + offsetY);
                this.contents.paintOpacity = 255;
            }
        }
        textState.x += pw + 4;
    };

    Window_Message.prototype.processDrawIconForAnimation = function(iconIndex, textState) {
        const pw = Window_Base._iconWidth || ImageManager.iconWidth;
        const ph = Window_Base._iconHeight || ImageManager.iconHeight;
        const { x, y, height } = textState;
        const offsetY = Math.floor(height - ph) / 2;
        const list = this.getAnimationList();
        const bitmap = this.createCharacterBitmap(pw + 12, height);
        const iconSet = ImageManager.loadSystem('IconSet');
        const sx = iconIndex % 16 * pw;
        const sy = Math.floor(iconIndex / 16) * ph;
        bitmap.blt(iconSet, sx, sy, pw, ph, 6, offsetY);

        const sprite = this.getTextCharacterSprite();
        const rect = new Rectangle(x, y, pw + 4, height);
        sprite.setup(bitmap, rect, list);
    };

    Window_Message.prototype.processGroupCharacter = function(textState, texts) {
        const c = texts[0];
        const width = this.textWidth(c);
        if (Utils.RPGMAKER_NAME === 'MV' || textState.drawing) {
            if (this.isCharacterAnimation()) {
                this.processGroupCharacterForAnimation(textState, texts);
            } else {
                this.contents.paintOpacity = this._paintOpacity;
                this.contents.drawText(c, textState.x, textState.y, w * 2, w);
                this.contents.paintOpacity = 255;
            }
        }
        textState.x += width;
    };

    Window_Message.prototype.processGroupCharacterForAnimation = function(textState, texts) {
        const c = texts[0];
        const width = this.textWidth(c);
        const { x, y, height } = textState;
        const list = this.getAnimationList();
        const bitmap = this.createCharacterBitmap(width + 8, height);
        bitmap.drawText(c, 4, 0, width + 4, height);

        const sprite = this.getTextCharacterSprite();
        const rect = new Rectangle(x, y, width, height);
        sprite.setup(bitmap, rect, list);
    };

    Window_Message.prototype.processRubyCharacter = function(textState, texts) {
        const [ c, r ] = texts;
        const { x, y, height } = textState;
        const cw = this.textWidth(c);
        const rubyBitmap = this._rubyBitmap;
        rubyBitmap.fontBold = this.contents.fontBold;
        rubyBitmap.fontItalic = this.contents.fontItalic;
        const rw = rubyBitmap.measureTextWidth(r);
        const rh = textState.rubyHeight;
        const rs = rubyBitmap.fontSize;
        const width = Math.max(cw, rw);
        if (Utils.RPGMAKER_NAME === 'MV' || textState.drawing) {
            rubyBitmap.clear();
            rubyBitmap.resize(rw + 16, rs + 8);
            rubyBitmap.drawText(r, 4, 0, rw + 4, rs + 8);
            if (this.isCharacterAnimation()) {
                const list = this.getAnimationList();
                const bitmap = this.createCharacterBitmap(width + 8, height + rh);
                const cx = (width - cw) / 2;
                const rx = (width - rw) / 2;
                const ry = -4;
                bitmap.drawText(c, cx + 4, rh, width + 4, height);
                bitmap.blt(rubyBitmap, 0, 0, rw + 8, rs + 8, rx, ry);

                const sprite = this.getTextCharacterSprite();
                const rect = new Rectangle(x, y - rh, width, height);
                sprite.setup(bitmap, rect, list);
            } else {
                const cx = x + (width - cw) / 2;
                const rx = x + (width - rw) / 2;
                const ry = y - rh - 4;
                this.contents.paintOpacity = this._paintOpacity;
                this.contents.drawText(c, cx, y, cw + 4, height);
                this.contents.blt(rubyBitmap, 0, 0, rw + 8, rs + 8, rx - 4, ry);
                this.contents.paintOpacity = 255;
            }
        }
        textState.x += width;
    };

    Window_Message.prototype.createCharacterBitmap = function(width, height) {
        const bitmap = new Bitmap(width, height);
        bitmap.fontFace = this.contents.fontFace;
        bitmap.fontSize = this.contents.fontSize;
        bitmap.fontBold = this.contents.fontBold;
        bitmap.fontItalic = this.contents.fontItalic;
        bitmap.textColor = this.contents.textColor;
        bitmap.paintOpacity = this._paintOpacity;
        bitmap.outlineColor = this.contents.outlineColor;
        bitmap.outlineWidth = this.contents.outlineWidth;
        return bitmap;
    };

    const _Window_Message_calcTextHeight = Window_Message.prototype.calcTextHeight;
    Window_Message.prototype.calcTextHeight = function(textState, all) {
        if (Utils.RPGMAKER_NAME === 'MZ') {
            return _Window_Message_calcTextHeight.apply(this, arguments);
        }
        const lines = textState.text.slice(textState.index).split('\n');
        const regExp = /\x1bFS\[(\d+)\]/gi;
        let maxFontSize = this.contents.fontSize;
        for (;;) {
            const arr = regExp.exec(lines[0]);
            if (arr) {
                maxFontSize = Math.max(maxFontSize, +arr[1]);
            } else {
                break;
            }
        }
        const height = _Window_Message_calcTextHeight.apply(this, arguments);
        return Math.max(height, maxFontSize + 8);
    };

    if (!Window_Message.prototype.isWaiting) {
        Window_Message.prototype.isWaiting = function() {
            return this.pause || this._waitCount > 0;
        };
    }

    const _Window_Message_isWaiting = Window_Message.prototype.isWaiting;
    Window_Message.prototype.isWaiting = function() {
        return _Window_Message_isWaiting.apply(this, arguments) || this._waitEffect;
    };

    //-------------------------------------------------------------------------
    // Scene_Map

    const _Scene_Map_updateMainMultiply = Scene_Map.prototype.updateMainMultiply;
    Scene_Map.prototype.updateMainMultiply = function() {
        _Scene_Map_updateMainMultiply.apply(this, arguments);
        if ($gameMessage.sceneEffectSkip()) {
            for (let i = 1; i < 99; i++) {
                if (this._messageWindow.isEffectingEx(param_SkipEffects)) {
                    this.updateMain();
                } else {
                    $gameMessage.clearSceneEffectSkip();
                    break;
                }
            }
        }
    };

})();

repo_name: agatutya/country

license: unknown


code:
//=============================================================================
// MessageWindowHidden.js
// ----------------------------------------------------------------------------
// (C)2015 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 2.8.0 2022/06/06 トリガースイッチをウィンドウの表示状態と同期する機能を追加
// 2.7.0 2021/01/25 ウィンドウ消去時、決定動作によってウィンドウを再表示できる設定を追加(by unaunagiさま)
// 2.6.2 2020/09/02 MZ向けにコード修正
// 2.6.1 2020/05/21 トリガースイッチと連動無効スイッチの状態によってエラーが起きうる記述を修正
// 2.6.0 2020/05/20 ピクチャの連動を無効にできるスイッチを追加
// 2.5.1 2020/05/19 2.3.0の仕様変更で連動表示ピクチャの透明度が復元できない問題を修正
// 2.5.0 2020/05/17 指定したスイッチに連動させてウィンドウの表示/非表示を切り替える機能を追加
// 2.4.0 2020/05/08 選択肢表示中でもウィンドウを隠せるよう設定を追加
// 2.3.0 2019/10/22 ウィンドウを表示状態にもどしたとき、連動ピクチャは、もともと表示していた不透明度を復元するよう仕様変更
// 2.2.0 2019/06/09 メッセージウィンドウと逆に連動して指定したピクチャの表示/非表示が自動で切り替わる機能を追加
// 2.1.0 2018/10/10 戦闘中にプラグインを無効化できる機能を追加。
// 2.0.0 2018/03/31 消去するトリガーを複数指定できる機能を追加。パラメータの指定方法を見直し。
// 1.4.0 2018/03/10 指定したスイッチがONの間はウィンドウ消去を無効化できる機能を追加
// 1.3.2 2017/08/02 ponidog_BackLog_utf8.jsとの競合を解消
// 1.3.1 2017/07/03 古いYEP_MessageCore.jsのネーム表示ウィンドウが再表示できない不具合の修正(by DarkPlasmaさま)
// 1.3.0 2017/03/16 連動して非表示にできるピクチャを複数指定できる機能を追加
// 1.2.1 2017/02/07 端末依存の記述を削除
// 1.2.0 2016/01/02 メッセージウィンドウと連動して指定したピクチャの表示/非表示が自動で切り替わる機能を追加
// 1.1.0 2016/08/25 選択肢の表示中はウィンドウを非表示にできないよう仕様変更
// 1.0.4 2016/07/22 YEP_MessageCore.jsのネーム表示ウィンドウと連携する機能を追加
// 1.0.3 2016/01/24 メッセージウィンドウが表示されていないときも非表示にできてしまう現象の修正
// 1.0.2 2016/01/02 競合対策
// 1.0.1 2015/12/31 コメント追加＋英語対応（仕様に変化なし）
// 1.0.0 2015/12/30 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc メッセージウィンドウ一時消去プラグイン
 * @target MZ
 * @url https://github.com/triacontane/RPGMakerMV/tree/mz_master/MessageWindowHidden.js
 * @author トリアコンタン
 *
 * @param triggerButton
 * @text ボタン名称
 * @desc ウィンドウを消去するボタンです。(複数登録可能) プラグイン等で入力可能なボタンを追加した場合は直接入力
 * @default ["右クリック"]
 * @type combo[]
 * @option 右クリック
 * @option shift
 * @option control
 * @option tab
 * @option pageup
 * @option pagedown
 * @option debug
 *
 * @param triggerSwitch
 * @text トリガースイッチ
 * @desc 指定したスイッチに連動させてウィンドウを消去します。並列処理などを使ってON/OFFを適切に管理してください。
 * @default 0
 * @type switch
 *
 * @param syncSwitch
 * @text トリガースイッチ同期
 * @desc 有効にすると、手動で消去した場合も含めてウィンドウの表示状態とトリガースイッチが同期します。
 * @default false
 * @type boolean
 *
 * @param linkPictureNumbers
 * @text 連動ピクチャ番号
 * @desc ウィンドウ消去時に連動して不透明度を[0]にするピクチャの番号です。
 * @default []
 * @type number[]
 *
 * @param linkShowPictureNumbers
 * @text 連動表示ピクチャ番号
 * @desc ウィンドウ消去時に連動して不透明度を[255]にするピクチャの番号です。
 * @default []
 * @type number[]
 *
 * @param disableLinkSwitchId
 * @text 連動ピクチャ無効スイッチ
 * @desc 指定した番号のスイッチがONのとき、ピクチャの連動が無効になります。
 * @default 0
 * @type switch
 *
 * @param disableSwitchId
 * @text 無効スイッチ
 * @desc 指定した番号のスイッチがONのとき、プラグイン全体の機能が無効になります。
 * @default 0
 * @type switch
 *
 * @param disableInBattle
 * @text 戦闘中無効化
 * @desc trueのとき、戦闘中にプラグインの機能を無効にします。
 * @default false
 * @type boolean
 *
 * @param disableInChoice
 * @text 選択肢表示中は無効
 * @desc 選択肢の表示中はウィンドウを非表示にできなくなります。
 * @default true
 * @type boolean
 *
 * @param restoreByDecision
 * @text 決定動作で復帰
 * @desc メッセージ消去時、決定動作により非表示になっていたメッセージウィンドウを再表示できます。
 * @default false
 * @type boolean
 *
 * @help メッセージウィンドウを表示中に指定したボタンを押下することで
 * メッセージウィンドウを消去します。もう一度押すと戻ります。
 *
 * ウィンドウ消去時に連動して不透明度を[0]にするピクチャを指定することができます。
 * 背景に特定のピクチャを使用している場合などに指定してください。
 * 再表示すると不透明度は、ウィンドウ非表示まえの不透明度で復元されます。
 *
 * ver2.0.0よりパラメータの指定方法が一部変更になりました。
 * 以前のバージョンを使っていた方は再設定をお願いします。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */
(function() {
    'use strict';

    /**
     * Create plugin parameter. param[paramName] ex. param.commandPrefix
     * @param pluginName plugin name(EncounterSwitchConditions)
     * @returns {Object} Created parameter
     */
    const createPluginParameter = function(pluginName) {
        const paramReplacer = function(key, value) {
            if (value === 'null') {
                return value;
            }
            if (value[0] === '"' && value[value.length - 1] === '"') {
                return value;
            }
            try {
                return JSON.parse(value);
            } catch (e) {
                return value;
            }
        };
        const parameter     = JSON.parse(JSON.stringify(PluginManager.parameters(pluginName), paramReplacer));
        PluginManager.setParameters(pluginName, parameter);
        return parameter;
    };
    const param = createPluginParameter('MessageWindowHidden');

    //=============================================================================
    // Game_Picture
    //  メッセージウィンドウの表示可否と連動します。
    //=============================================================================
    Game_Picture.prototype.linkWithMessageWindow = function(opacity) {
        this._opacity       = opacity;
        this._targetOpacity = opacity;
    };

    //=============================================================================
    // Window_Message
    //  指定されたボタン押下時にウィンドウとサブウィンドウを非表示にします。
    //=============================================================================
    const _Window_Message_updateWait      = Window_Message.prototype.updateWait;
    Window_Message.prototype.updateWait = function() {
        if (!this.isClosed() && this.isTriggeredHidden() && this.isEnableInChoice()) {
            if (!this.isHidden()) {
                this.hideAllWindow();
            } else {
                this.showAllWindow();
            }
        } else if (this.isHidden() && this.isTriggered() && param.restoreByDecision) {
            this.showAllWindow();
            Input.update();
        }
        const wait = _Window_Message_updateWait.apply(this, arguments);
        if (this.isHidden() && this.visible) {
            this.hideAllWindow();
        }
        return wait;
    };

    Window_Message.prototype.isEnableInChoice = function() {
        return !(param.disableInChoice && $gameMessage.isChoice());
    };

    Window_Message.prototype.hideAllWindow = function() {
        this.hide();
        this.subWindows().forEach(function(subWindow) {
            this.hideSubWindow(subWindow);
        }.bind(this));
        this._originalPictureOpacities = {};
        this.linkPictures(0, param.linkPictureNumbers);
        this.linkPictures(255, param.linkShowPictureNumbers);
        this._hideByMessageWindowHidden = true;
    };

    Window_Message.prototype.subWindows = function() {
        return [this._goldWindow, this._nameBoxWindow,
            this._choiceListWindow, this._numberInputWindow,
            this._eventItemWindow
        ];
    };

    Window_Message.prototype.showAllWindow = function() {
        this.show();
        this.subWindows().forEach(function(subWindow) {
            this.showSubWindow(subWindow);
        }.bind(this));
        this.linkPictures(null, param.linkShowPictureNumbers);
        this.linkPictures(null, param.linkPictureNumbers);
        this._hideByMessageWindowHidden = false;
    };

    Window_Message.prototype.isHidden = function() {
        return this._hideByMessageWindowHidden;
    };

    Window_Message.prototype.linkPictures = function(opacity, pictureNumbers) {
        if (!pictureNumbers || $gameSwitches.value(param.disableLinkSwitchId)) {
            return;
        }
        pictureNumbers.forEach(function(pictureId) {
            this.linkPicture(opacity, pictureId);
        }, this);
    };

    Window_Message.prototype.linkPicture = function(opacity, pictureId) {
        const picture = $gameScreen.picture(pictureId);
        if (!picture) {
            return;
        }
        if (opacity === null) {
            if (!this._originalPictureOpacities.hasOwnProperty(pictureId)) {
                return;
            }
            opacity = this._originalPictureOpacities[pictureId];
        } else {
            this._originalPictureOpacities[pictureId] = picture.opacity();
        }
        picture.linkWithMessageWindow(opacity);
    };

    Window_Message.prototype.hideSubWindow = function(subWindow) {
        subWindow.prevVisible = subWindow.visible;
        subWindow.hide();
    };

    Window_Message.prototype.showSubWindow = function(subWindow) {
        if (subWindow.prevVisible) subWindow.show();
        subWindow.prevVisible = undefined;
    };

    Window_Message.prototype.hasNameWindow = function() {
        return this._nameWindow && typeof Window_NameBox !== 'undefined';
    };

    // 古いYEP_MessageCore.jsでは、ネーム表示ウィンドウはsubWindowsに含まれる
    Window_Message.prototype.nameWindowIsSubWindow = function() {
        return this.subWindows().filter(function(subWindow) {
            return subWindow === this._nameWindow;
        }, this).length > 0;
    };

    Window_Message.prototype.disableWindowHidden = function () {
        return (param.disableSwitchId > 0 && $gameSwitches.value(param.disableSwitchId)) ||
            (param.disableInBattle && $gameParty.inBattle());
    };

    Window_Message.prototype.isTriggeredHidden = function() {
        if (this.disableWindowHidden()) {
            return false;
        }
        if (param.triggerSwitch > 0 && this.isTriggeredHiddenSwitch()) {
            return true;
        }
        const result = param.triggerButton.some(function(button) {
            switch (button) {
                case '':
                case '右クリック':
                case 'light_click':
                    return TouchInput.isCancelled();
                case 'ok':
                    return false;
                default:
                    return Input.isTriggered(button);
            }
        });
        if (param.syncSwitch && param.triggerSwitch > 0 && result) {
            $gameSwitches.setValue(param.triggerSwitch, !this.isHidden());
        }
        return result;
    };

    Window_Message.prototype.isTriggeredHiddenSwitch = function() {
        const triggerSwitch = $gameSwitches.value(param.triggerSwitch);
        if (triggerSwitch && !this.isHidden()) {
            return true;
        }
        if (!triggerSwitch && this.isHidden()) {
            return true;
        }
        return false;
    };

    const _Window_Message_updateInput      = Window_Message.prototype.updateInput;
    Window_Message.prototype.updateInput = function() {
        if (this.isHidden()) return true;
        return _Window_Message_updateInput.apply(this, arguments);
    };

    //=============================================================================
    // Window_ChoiceList、Window_NumberInput、Window_EventItem
    //  非表示の間は更新を停止します。
    //=============================================================================
    const _Window_ChoiceList_update      = Window_ChoiceList.prototype.update;
    Window_ChoiceList.prototype.update = function() {
        if (!this.visible) return;
        _Window_ChoiceList_update.apply(this, arguments);
    };

    const _Window_NumberInput_update      = Window_NumberInput.prototype.update;
    Window_NumberInput.prototype.update = function() {
        if (!this.visible) return;
        _Window_NumberInput_update.apply(this, arguments);
    };

    const _Window_EventItem_update      = Window_EventItem.prototype.update;
    Window_EventItem.prototype.update = function() {
        if (!this.visible) return;
        _Window_EventItem_update.apply(this, arguments);
    };
})();