code:
//=============================================================================
// Yanfly Engine Plugins - Message Core
// YEP_MessageCore.js
//=============================================================================

var Imported = Imported || {};
Imported.YEP_MessageCore = true;

var Yanfly = Yanfly || {};
Yanfly.Message = Yanfly.Message || {};
Yanfly.Message.version = 1.19;

//=============================================================================
/*:ja
 * @plugindesc v1.19 文章の表示方法や機能を変更できます。
 * @author Yanfly Engine Plugins
 *
 * @param ---一般---
 * @text ---一般---
 * @default
 *
 * @param Default Rows
 * @parent ---一般---
 * @type number
 * @min 0
 * @text 文章ウィンドウの行数
 * @desc デフォルト:4
 * @default 4
 *
 * @param Default Width
 * @parent ---一般---
 * @text 文章ウィンドウの幅
 * @desc ピクセル数。デフォルト:Graphics.boxWidth
 * @default Graphics.boxWidth
 *
 * @param Face Indent
 * @parent ---一般---
 * @text 顔画像使用時のインデント値
 * @desc デフォルト:Window_Base._faceWidth + 24
 * @default Window_Base._faceWidth + 24
 *
 * @param Fast Forward Key
 * @parent ---一般---
 * @text 早送りキー
 * @default pagedown
 *
 * @param Enable Fast Forward
 * @parent ---一般---
 * @type boolean
 * @on 有効
 * @off 無効
 * @text 早送りキー有効化
 * @desc 無効:false / 有効:true
 * @default true
 *
 * @param Word Wrapping
 * @parent ---一般---
 * @type boolean
 * @on 有効
 * @off 無効
 * @text ワードラップ有効化(2バイト文字は非機能)
 * @desc 無効:false / 有効:true
 * @default false
 *
 * @param Description Wrap
 * @parent ---一般---
 * @type boolean
 * @on 有効
 * @off 無効
 * @text 詳細欄のワードラップ有効化(2バイト文字は非機能)
 * @desc 無効:false / 有効:true
 * @default false
 *
 * @param Word Wrap Space
 * @parent ---一般---
 * @type boolean
 * @on 有効
 * @off 無効
 * @text 手動改行を有効化
 * @desc 無効:false / 有効:true
 * @default false
 *
 * @param Tight Wrap
 * @parent ---一般---
 * @type boolean
 * @on 有効
 * @off 無効
 * @text 顔画像使用時にタイトラップ
 * @desc 無効:false / 有効:true
 * @default false
 *
 * @param ---フォント---
 * @text ---フォント---
 * @default
 *
 * @param Font Name
 * @parent ---フォント---
 * @text 文章ウィンドウのフォント
 * @desc デフォルト:GameFont
 * @default GameFont
 *
 * @param Font Name CH
 * @parent ---フォント---
 * @text 中国語のデフォルトフォント
 * @desc デフォルト:SimHei, Heiti TC, sans-serif
 * @default SimHei, Heiti TC, sans-serif
 *
 * @param Font Name KR
 * @parent ---フォント---
 * @text 韓国語のデフォルトフォント
 * @desc デフォルト:Dotum, AppleGothic, sans-serif
 * @default Dotum, AppleGothic, sans-serif
 *
 * @param Font Size
 * @parent ---フォント---
 * @type number
 * @min 1
 * @text 文章ウィンドウのフォントサイズ
 * @desc デフォルト:28
 * @default 28
 *
 * @param Font Size Change
 * @parent ---フォント---
 * @type number
 * @min 1
 * @text サイズ変更
 * @desc \{ と \} が使われた際、このフォントサイズを適用。デフォルト:12
 * @default 12
 *
 * @param Font Changed Max
 * @parent ---フォント---
 * @type number
 * @min 1
 * @text 最大フォントサイズ
 * @desc \{ による最大サイズ。デフォルト:96
 * @default 96
 *
 * @param Font Changed Min
 * @parent ---フォント---
 * @type number
 * @min 1
 * @text 最小フォントサイズ
 * @desc \{ による最小サイズ。デフォルト:12
 * @default 12
 *
 * @param Font Outline
 * @parent ---フォント---
 * @type number
 * @min 0
 * @text アウトライン幅
 * @desc デフォルト:4
 * @default 4
 *
 * @param Maintain Font
 * @parent ---フォント---
 * @type boolean
 * @on 維持
 * @off 非維持
 * @text フォント維持
 * @desc フォント名/サイズを変更時、次の文章で維持
 * 非維持:false / 維持:true
 * @default false
 *
 * @param ---名前ボックス---
 * @text ---名前ボックス---
 * @default
 *
 * @param Name Box Buffer X
 * @parent ---名前ボックス---
 * @type number
 * @min -9007
 * @max 9007
 * @text X軸位置
 * @default -28
 *
 * @param Name Box Buffer Y
 * @parent ---名前ボックス---
 * @type number
 * @min -9007
 * @max 9007
 * @text Y軸位置
 * @default 0
 *
 * @param Name Box Padding
 * @parent ---名前ボックス---
 * @text ボックス幅
 * @default this.standardPadding() * 4
 *
 * @param Name Box Color
 * @parent ---名前ボックス---
 * @type number
 * @min 0
 * @max 31
 * @text テキスト色
 * @default 0
 *
 * @param Name Box Clear
 * @parent ---名前ボックス---
 * @type boolean
 * @on 透明化
 * @off 通常
 * @text 透明化
 * @desc 通常:false / 透明化:true
 * @default false
 *
 * @param Name Box Added Text
 * @parent ---名前ボックス---
 * @text 追加テキスト
 * @desc 名前ボックスに自動的に追加されるテキスト。自動的にカラー設定をしたい場合などに使われます。
 * @default \c[6]
 *
 * @param Name Box Auto Close
 * @parent ---名前ボックス---
 * @type boolean
 * @on 閉じる
 * @off 閉じない
 * @text 自動クローズ
 * @desc 名前ボックスに別の名前が表示される時、文章ウィンドウを閉じる。閉じる:true / 閉じない:false
 * @default false
 *
 * @help
 * 翻訳:ムノクラ
 * https://fungamemake.com/
 * https://twitter.com/munokura/
 *
 * ===========================================================================
 * 導入
 * ===========================================================================
 *
 * RPGツクールMVでは文章システムが強化されましたが、
 * このプラグインを使えば、更にアイテム・武器・防具・衣装の名前や、
 * アイコンの制御文字の変換を容易に行なうことができます。
 *
 * またこのスクリプトでは、ゲーム中に文章ウィンドウのサイズを最適化したり
 * セパレートフォントや、テキストの早送り機能を提供します。
 *
 * ===========================================================================
 * ワードラップ
 * ===========================================================================
 *
 * ※ワードラップ機能は2バイト文字では動作しません。
 *
 * ワードラップ機能は、文章システム上で使うことができます。
 * プラグインコマンドを用いて、有効/無効の切り替えを行ってください。
 * ワードラップを使うことで、文章ウィンドウ外へはみ出た文章を、
 * 自動的に次の行に折り返します。この際、エディターの改行入力は無効化され、
 * プラグインによる改行が優先されます。
 *
 *
もしくは<line break>という制御文字を使って、改行を行います。
 * 新しい行を始めたい部分の前、もしくは後にこのコードを入力してください。
 *
 * ワードラップ機能は、文章ウィンドウ向けの機能ですが、
 * アイテム詳細など、それ以外の部分にもこの機能を用いたい場合、
 * そのテキストの先頭に<WordWrap>と挿入すれば、利用することができます。
 *
 * ===========================================================================
 * 制御文字
 * ===========================================================================
 *
 * 文章に特定の制御文字を使用し、
 * 次のコードに置き換えることができます。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * Text Code   Function
 *   \V[n]       n 番目の変数の値と置き換えられます。
 *   \N[n]       n 番目のアクター名と置き換えられます。
 *   \P[n]       n 番目のパーティメンバー名と置き換えられます。
 *   \G          所持金と置き換えられます。
 *   \C[n]       続くテキストを、n 番目のカラーで表示します。
 *   \I[n]       n 番目のアイコンを表示します。
 *   \{          テキストサイズを一段階大きくします。
 *   \}          テキストサイズを一段階小さくします。
 *   \\          \のキャラクターと置き換えられます。
 *   \$          所持金ウィンドウを開きます。
 *   \.          1/4 秒の間をあけます。
 *   \|          1 秒の間をあけます。
 *   \!          ボタンインプットを待ちます。
 *   \>          同じラインの残りのテキストを一気に表示します。
 *   \<          上記のエフェクトをキャンセルします。
 *   \^          テキスト表示後にインプットを待たなくなります。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Wait:       Effect:
 *    \w[x]     -  x フレーム分の間をあけます (60フレーム = 1秒)
 *                 文章ウィンドウ限定の機能です。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  NameWindow: Effect:
 *    \n<x>     - 左揃えで x の文字列で名前ボックスを作成します。 *注
 *    \nc<x>    - 中央揃えで x の文字列で名前ボックスを作成します。 *注
 *    \nr<x>    - 右揃えで x の文字列で名前ボックスを作成します。 *注
 *
 *              *注:文章ウィンドウのみに有効
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Line Break  Effect:
 *
      - ワードラップモードで改行を行います。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Position:   Effect:
 *    \px[x]    - テキストのx方向の位置を指定
 *    \py[x]    - テキストのy方向の位置を指定
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Outline:    Effect:
 *   \oc[x]    - アウトラインの色を x にします。
 *   \ow[x]    - アウトラインの幅を x にします。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Font:       Effect:
 *    \fr       - 全てのフォント変更をリセットします。
 *    \fs[x]    - フォントサイズを x に変更します。
 *    \fn<x>    - フォント名を x に変更します。
 *    \fb       - ボールド設定を切り替えます。
 *    \fi       - イタリック設定を切り替えます。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Actor:      Effect:
 *    \af[x]    - アクター x の顔を表示します。(注)
 *    \ac[x]    - アクターの職業名を表示します。
 *    \an[x]    - アクターのニックネームを表示します。
 *
 *              *注:文章ウィンドウのみで有効
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Party:      Effect:
 *    \pf[x]    - パーティメンバー x の顔を表示します。*注
 *    \pc[x]    - パーティメンバー x の職業名を表示します。
 *    \pn[x]    - パーティメンバー x のニックネームを表示します。
 *
 *              *注:文章ウィンドウのみで有効
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Names:      Effect:
 *    \nc[x]    - 職業 x の名前を表示します。
 *    \ni[x]    - アイテム x の名前を表示します。
 *    \nw[x]    - 武器 x の名前を表示します。
 *    \na[x]    - 防具 x の名前を表示します。
 *    \ns[x]    - スキル x の名前を表示します。
 *    \nt[x]    - ステート x の名前を表示します。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Icon Names: Effect:
 *    \nc[x]    - 職業 x の名前を表示します。
 *    \ni[x]    - アイテム x の名前を表示します。
 *    \nw[x]    - 武器 x の名前を表示します。
 *    \na[x]    - 防具 x の名前を表示します。
 *    \ns[x]    - スキル x の名前を表示します。
 *    \nt[x]    - ステート x の名前を表示します。
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * これらはスクリプトと共に加えられた制御文字です。これらのコードは、
 * 文章ウィンドウにのみ働くものであることに気を付けてください。
 * またそれ以外にも、ヘルプの詳細、アクタープロフィールなどにも有効です。
 *
 * ===========================================================================
 * プラグインコマンド
 * ===========================================================================
 *
 * イベントエディタを通して下記のプラグインコマンドを用いることで、文章
 * システムの様々な要素を変更することができます。
 *
 * プラグインコマンド
 *   MessageRows 6
 *   - 表示される文章の列を6にします。
 *     もし［文章の表示］を継続的に使っている場合、
 *     行の上限に到達するまで、
 *     あとに続く文章を表示し続けます。
 *     それ以降の文章については、
 *     重複回避のためにカットされます。
 *
 *   MessageWidth 400
 *   - 文章ウィンドウの幅を400ピクセルに変更します。
 *     右側に寄りすぎている文字は、
 *     適宜カットされ調整が行われます。
 *
 *   EnableWordWrap
 *   - ワードラップを有効にします。
 *     文がウィンドウサイズを超えてしまった時に、自動的に次の行に進みます。
 *     改行する際は '\br'を挿入してください。
 *
 *   DisableWordWrap
 *   - ワードラップを無効にします。
 *     エディタ内で新しい文が始まった地点で、自動的に改行されます。
 *
 *   EnableFastForward
 *   - 文章内の早送りキーを有効にします。
 *
 *   DisableFastForward
 *   - 文章内の早送りキーを無効にします。
 *
 * ===========================================================================
 * Changelog
 * ===========================================================================
 *
 * Version 1.19:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.18:
 * - Added new plugin parameters: 'Font Name CH' and 'Font Name KR'.
 *
 * Version 1.17:
 * - Compatibility update with Message Macros for 'Name Box Auto Close'
 *   option.
 *
 * Version 1.16:
 * - Added 'Tight Wrap' plugin parameter as a word wrap option to make the
 * word wrap tighter when using faces.
 *
 * Version 1.15:
 * - Added a failsafe where if the name box window would be off the screen, it
 * will automatically reposition itself to under the main message window.
 *
 * Version 1.14:
 * - Added 'Name Box Close' plugin parameter. If this is enabled, the message
 * window will check for the Name Window speaker each time a follow up message
 * occurs. If the name in the currently Name Window matches the name in the
 * following Name Window, the message window will remain open. If it doesn't,
 * the Name Window will close and reopen to indicate a new speaker.
 *
 * Version 1.13:
 * - Added 'Maintain Font' plugin parameter under the Font category. This will
 * allow you to use text codes \fn<x> and \fs[x] to permanently change the
 * font of your messages until you use it again. \fr will reset them to the
 * plugin's default parameter settings.
 *
 * Version 1.12:
 * - 'Word Wrap Space' parameter no longer leaves a space at the beginning of
 * each message.
 *
 * Version 1.11:
 * - Added 'Font Outline' parameter for the plugin parameters. This adjusts
 * the font outline width used by default for only message fonts.
 *
 * Version 1.10:
 * - Updated the Message Row system for Extended Message Pack 1's Autosizing
 * feature to work with extended heights.
 *
 * Version 1.09:
 * - Replaced 'Fast Forward' parameter with the 'Fast Forward Key' parameter
 * and 'Enable Fast Forward' parameter. Two new Plugin Commands are added.
 * They are 'EnableFastForward' and 'DisableFastForward' for control over when
 * fast forwarding is allowed as to not cause timed cutscenes to desynch.
 *
 * Version 1.08:
 * - Fixed a bug regarding Input Number positioning when the Message Window's
 * position was middle.
 *
 * Version 1.07:
 * - Added 'Word Wrap Space' for word wrap users. This parameter will leave a
 * space behind for those who want a space left behind.
 *
 * Version 1.06:
 * - Fixed a bug that would cause masking problems with mobile devices.
 *
 * Version 1.05:
 * - Fixed a bug that would cause the namebox window to appear distorted.
 *
 * Version 1.04:
 * - Fixed a bug that captured too many text codes with the namebox window.
 * - Timed Name Window's closing speed with main window's closing speed.
 *
 * Verison 1.03:
 * - Fixed a bug with textcodes that messed up wordwrapping.
 * - Fixed a bug with font reset, italic, and bold textcodes.
 *
 * Version 1.02:
 * - Namebox Window's overlap feature that's in every MV window is now
 * disabled to allow for overlapping with main message window.
 * - Updated window positioning for Branch Choices, Number Input, and Item
 * Selection windows.
 *
 * Version 1.01:
 * - Added 'Description Wrap' into the parameters to allow for all item
 * descriptions to be automatically processed with word wrapping.
 *
 * Version 1.00:
 * - Finished plugin!
 */
/*:
 * @plugindesc v1.19 Adds more features to the Message Window to customized
 * the way your messages appear and functions.
 * @author Yanfly Engine Plugins
 *
 * @param ---General---
 * @default
 *
 * @param Default Rows
 * @parent ---General---
 * @type number
 * @min 0
 * @desc This is default amount of rows the message box will have.
 * Default: 4
 * @default 4
 *
 * @param Default Width
 * @parent ---General---
 * @desc This is default width for the message box in pixels.
 * Default: Graphics.boxWidth
 * @default Graphics.boxWidth
 *
 * @param Face Indent
 * @parent ---General---
 * @desc If using a face graphic, this is how much text indents by.
 * Default: Window_Base._faceWidth + 24
 * @default Window_Base._faceWidth + 24
 *
 * @param Fast Forward Key
 * @parent ---General---
 * @type combo
 * @option tab
 * @option shift
 * @option control
 * @option pageup
 * @option pagedown
 * @desc This is the key used for fast forwarding.
 * @default pagedown
 *
 * @param Enable Fast Forward
 * @parent ---General---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Enable fast forward button for your messages by default?
 * NO - false     YES - true
 * @default true
 *
 * @param Word Wrapping
 * @parent ---General---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Use this to enable or disable word wrapping by default.
 * OFF - false     ON - true
 * @default false
 *
 * @param Description Wrap
 * @parent ---General---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Enable or disable word wrapping for descriptions.
 * OFF - false     ON - true
 * @default false
 *
 * @param Word Wrap Space
 * @parent ---General---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Insert a space with manual line breaks?
 * NO - false     YES - true
 * @default false
 *
 * @param Tight Wrap
 * @parent ---General---
 * @type boolean
 * @on YES
 * @off NO
 * @desc If true and using a face for the message, the message will
 * wrap tighter. NO - false     YES - true
 * @default false
 *
 * @param ---Font---
 * @default
 *
 * @param Font Name
 * @parent ---Font---
 * @desc This is the default font used for the Message Window.
 * Default: GameFont
 * @default GameFont
 *
 * @param Font Name CH
 * @parent ---Font---
 * @desc This is the default font used for the Message Window for Chinese.
 * Default: SimHei, Heiti TC, sans-serif
 * @default SimHei, Heiti TC, sans-serif
 *
 * @param Font Name KR
 * @parent ---Font---
 * @desc This is the default font used for the Message Window for Korean.
 * Default: Dotum, AppleGothic, sans-serif
 * @default Dotum, AppleGothic, sans-serif
 *
 * @param Font Size
 * @parent ---Font---
 * @type number
 * @min 1
 * @desc This is the default font size used for the Message Window.
 * Default: 28
 * @default 28
 *
 * @param Font Size Change
 * @parent ---Font---
 * @type number
 * @min 1
 * @desc Whenever \{ and \} are used, they adjust by this value.
 * Default: 12
 * @default 12
 *
 * @param Font Changed Max
 * @parent ---Font---
 * @type number
 * @min 1
 * @desc This is the maximum size achieved by \{.
 * Default: 96
 * @default 96
 *
 * @param Font Changed Min
 * @parent ---Font---
 * @type number
 * @min 1
 * @desc This is the minimum size achieved by \{.
 * Default: 12
 * @default 12
 *
 * @param Font Outline
 * @parent ---Font---
 * @type number
 * @min 0
 * @desc This is the default font outline width for messages.
 * Default: 4
 * @default 4
 *
 * @param Maintain Font
 * @parent ---Font---
 * @type boolean
 * @on YES
 * @off NO
 * @desc When changing the font name or size, maintain for following
 * messages. NO - false     YES - true
 * @default false
 *
 * @param ---Name Box---
 * @default
 *
 * @param Name Box Buffer X
 * @parent ---Name Box---
 * @type number
 * @desc This is the buffer for the x location of the Name Box.
 * @default -28
 *
 * @param Name Box Buffer Y
 * @parent ---Name Box---
 * @type number
 * @desc This is the buffer for the y location of the Name Box.
 * @default 0
 *
 * @param Name Box Padding
 * @parent ---Name Box---
 * @desc This is the value for the padding of the Name Box.
 * @default this.standardPadding() * 4
 *
 * @param Name Box Color
 * @parent ---Name Box---
 * @type number
 * @min 0
 * @max 31
 * @desc This is the text color used for the Name Box.
 * @default 0
 *
 * @param Name Box Clear
 * @parent ---Name Box---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Do you wish for the Name Box window to be clear?
 * NO - false     YES - true
 * @default false
 *
 * @param Name Box Added Text
 * @parent ---Name Box---
 * @desc This text is always added whenever the name box is used.
 * This can be used to automatically set up colors.
 * @default \c[6]
 *
 * @param Name Box Auto Close
 * @parent ---Name Box---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Close the message window each time the namebox displays a
 * different name? YES - true     NO - false
 * @default false
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * While RPG Maker MV Ace certainly improved the message system a whole lot, it
 * wouldn't hurt to add in a few more features, such as name windows,
 * converting textcodes to write out the icons and/or names of items, weapons,
 * armours, and* more in quicker fashion. This script also gives the developer
 * the ability to adjust the size of the message window during the game, give
 * it a separate font, and to give the player a text fast-forward feature.
 *
 * ============================================================================
 * Word Wrapping
 * ============================================================================
 *
 * Word wrapping is now possible through the message system. You can enable and
 * disable Word wrap using Plugin Commands. While using word wrap, if the word
 * is to extend past the message window's area, it will automatically go to the
 * following line. That said, word wrap will disable the editor's line breaks
 * and will require you to use the ones provided by the plugin:
 *
 *
 or <line break> is text code to apply a line break. Use this before or
 * after a part in which you wish to start a new line.
 *
 * Keep in mind word wrapping is mostly for message windows. However, in other
 * places that you'd like to see word wrapping, such as item descriptions,
 * insert <WordWrap> at the beginning of the text to enable it.
 *
 * ============================================================================
 * Text Codes
 * ============================================================================
 *
 * By using certain text codes in your messages, you can have the game replace
 * them with the following:
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * Text Code   Function
 *   \V[n]       Replaced by the value of the nth variable.
 *   \N[n]       Replaced by the name of the nth actor.
 *   \P[n]       Replaced by the name of the nth party member.
 *   \G          Replaced by the currency unit.
 *   \C[n]       Draw the subsequent text in the nth color.
 *   \I[n]       Draw the nth icon.
 *   \{          Increases the text size by one step.
 *   \}          Decreases the text size by one step.
 *   \\          Replaced with the backslash character.
 *   \$          Opens the gold window.
 *   \.          Waits 1/4th seconds.
 *   \|          Waits 1 second.
 *   \!          Waits for button input.
 *   \>          Display remaining text on same line all at once.
 *   \<          Cancel the effect that displays text all at once.
 *   \^          Do not wait for input after displaying text.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Wait:       Effect:
 *    \w[x]     - Waits x frames (60 frames = 1 second). Message window only.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  NameWindow: Effect:
 *    \n<x>     - Creates a name box with x string. Left side. *Note
 *    \nc<x>    - Creates a name box with x string. Centered. *Note
 *    \nr<x>    - Creates a name box with x string. Right side. *Note
 *
 *              *Note: Works for message window only.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Line Break  Effect:
 *
      - If using word wrap mode, this will cause a line break.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Position:   Effect:
 *    \px[x]    - Sets x position of text to x.
 *    \py[x]    - Sets y position of text to y.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Outline:    Effect:
 *   \oc[x]    - Sets outline colour to x.
 *   \ow[x]    - Sets outline width to x.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Font:       Effect:
 *    \fr       - Resets all font changes.
 *    \fs[x]    - Changes font size to x.
 *    \fn<x>    - Changes font name to x.
 *    \fb       - Toggles font boldness.
 *    \fi       - Toggles font italic.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Actor:      Effect:
 *    \af[x]    - Shows face of actor x. *Note
 *    \ac[x]    - Writes out actor's class name.
 *    \an[x]    - Writes out actor's nickname.
 *
 *              *Note: Works for message window only.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Party:      Effect:
 *    \pf[x]    - Shows face of party member x. *Note
 *    \pc[x]    - Writes out party member x's class name.
 *    \pn[x]    - Writes out party member x's nickname.
 *
 *              *Note: Works for message window only.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Names:      Effect:
 *    \nc[x]    - Writes out class x's name.
 *    \ni[x]    - Writes out item x's name.
 *    \nw[x]    - Writes out weapon x's name.
 *    \na[x]    - Writes out armour x's name.
 *    \ns[x]    - Writes out skill x's name.
 *    \nt[x]    - Writes out state x's name.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *  Icon Names: Effect:
 *    \ii[x]    - Writes out item x's name including icon.
 *    \iw[x]    - Writes out weapon x's name including icon.
 *    \ia[x]    - Writes out armour x's name including icon.
 *    \is[x]    - Writes out skill x's name including icon.
 *    \it[x]    - Writes out state x's name including icon.
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * And those are the text codes added with this script. Keep in mind that some
 * of these text codes only work for the Message Window. Otherwise, they'll
 * work for help descriptions, actor biographies, and others.
 *
 * ============================================================================
 * Plugin Commands
 * ============================================================================
 *
 * The following are some plugin commands you can use through the Event Editor
 * to change various aspects about the Message system.
 *
 * Plugin Comand
 *   MessageRows 6
 *   - Changes the Message Rows displayed to 6. If you are using continuous
 *   Show Text events, this will continue displaying the following lines's
 *   texts until it hits the row limit. Anything after that is cut off until
 *   the next message starts to avoid accidental overlap.
 *
 *   MessageWidth 400
 *   - Changes the Message Window Width to 400 pixels. This will cut off any
 *   words that are shown too far to the right so adjust accordingly!
 *
 *   EnableWordWrap
 *   - Enables wordwrapping. If a word extends past the window size, it will
 *   automatically move onto the next line. Keep in mind, you will need to use
 *   \br to perform line breaks.
 *
 *   DisableWordWrap
 *   - This disables wordwrapping. Line breaks will be automatic at points
 *   where a new line is started in the editor.
 *
 *   EnableFastForward
 *   - Enables Fast Forward key from working with messages.
 *
 *   DisableFastForward
 *   - Disables Fast Forward key from working with messages.
 *
 * ============================================================================
 * Changelog
 * ============================================================================
 *
 * Version 1.19:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.18:
 * - Added new plugin parameters: 'Font Name CH' and 'Font Name KR'.
 *
 * Version 1.17:
 * - Compatibility update with Message Macros for 'Name Box Auto Close' option.
 *
 * Version 1.16:
 * - Added 'Tight Wrap' plugin parameter as a word wrap option to make the
 * word wrap tighter when using faces.
 *
 * Version 1.15:
 * - Added a failsafe where if the name box window would be off the screen, it
 * will automatically reposition itself to under the main message window.
 *
 * Version 1.14:
 * - Added 'Name Box Close' plugin parameter. If this is enabled, the message
 * window will check for the Name Window speaker each time a follow up message
 * occurs. If the name in the currently Name Window matches the name in the
 * following Name Window, the message window will remain open. If it doesn't,
 * the Name Window will close and reopen to indicate a new speaker.
 *
 * Version 1.13:
 * - Added 'Maintain Font' plugin parameter under the Font category. This will
 * allow you to use text codes \fn<x> and \fs[x] to permanently change the font
 * of your messages until you use it again. \fr will reset them to the plugin's
 * default parameter settings.
 *
 * Version 1.12:
 * - 'Word Wrap Space' parameter no longer leaves a space at the beginning of
 * each message.
 *
 * Version 1.11:
 * - Added 'Font Outline' parameter for the plugin parameters. This adjusts the
 * font outline width used by default for only message fonts.
 *
 * Version 1.10:
 * - Updated the Message Row system for Extended Message Pack 1's Autosizing
 * feature to work with extended heights.
 *
 * Version 1.09:
 * - Replaced 'Fast Forward' parameter with the 'Fast Forward Key' parameter
 * and 'Enable Fast Forward' parameter. Two new Plugin Commands are added. They
 * are 'EnableFastForward' and 'DisableFastForward' for control over when fast
 * forwarding is allowed as to not cause timed cutscenes to desynch.
 *
 * Version 1.08:
 * - Fixed a bug regarding Input Number positioning when the Message Window's
 * position was middle.
 *
 * Version 1.07:
 * - Added 'Word Wrap Space' for word wrap users. This parameter will leave a
 * space behind for those who want a space left behind.
 *
 * Version 1.06:
 * - Fixed a bug that would cause masking problems with mobile devices.
 *
 * Version 1.05:
 * - Fixed a bug that would cause the namebox window to appear distorted.
 *
 * Version 1.04:
 * - Fixed a bug that captured too many text codes with the namebox window.
 * - Timed Name Window's closing speed with main window's closing speed.
 *
 * Verison 1.03:
 * - Fixed a bug with textcodes that messed up wordwrapping.
 * - Fixed a bug with font reset, italic, and bold textcodes.
 *
 * Version 1.02:
 * - Namebox Window's overlap feature that's in every MV window is now disabled
 * to allow for overlapping with main message window.
 * - Updated window positioning for Branch Choices, Number Input, and Item
 * Selection windows.
 *
 * Version 1.01:
 * - Added 'Description Wrap' into the parameters to allow for all item
 * descriptions to be automatically processed with word wrapping.
 *
 * Version 1.00:
 * - Finished plugin!
 */
//=============================================================================

//=============================================================================
// Parameter Variables
//=============================================================================

Yanfly.Parameters = PluginManager.parameters("YEP_MessageCore");
Yanfly.Param = Yanfly.Param || {};

Yanfly.Param.MSGDefaultRows = String(Yanfly.Parameters["Default Rows"]);
Yanfly.Param.MSGDefaultWidth = String(Yanfly.Parameters["Default Width"]);
Yanfly.Param.MSGFaceIndent = String(Yanfly.Parameters["Face Indent"]);
Yanfly.Param.MSGFastForwardKey = String(Yanfly.Parameters["Fast Forward Key"]);
Yanfly.Param.MSGFFOn = eval(String(Yanfly.Parameters["Enable Fast Forward"]));
Yanfly.Param.MSGWordWrap = String(Yanfly.Parameters["Word Wrapping"]);
Yanfly.Param.MSGWordWrap = eval(Yanfly.Param.MSGWordWrap);
Yanfly.Param.MSGDescWrap = String(Yanfly.Parameters["Description Wrap"]);
Yanfly.Param.MSGWrapSpace = eval(String(Yanfly.Parameters["Word Wrap Space"]));
Yanfly.Param.MSGTightWrap = eval(String(Yanfly.Parameters["Tight Wrap"]));

Yanfly.Param.MSGFontName = String(Yanfly.Parameters["Font Name"]);
Yanfly.Param.MSGCNFontName = String(Yanfly.Parameters["Font Name CH"]);
Yanfly.Param.MSGKRFontName = String(Yanfly.Parameters["Font Name KR"]);
Yanfly.Param.MSGFontSize = Number(Yanfly.Parameters["Font Size"]);
Yanfly.Param.MSGFontSizeChange = String(Yanfly.Parameters["Font Size Change"]);
Yanfly.Param.MSGFontChangeMax = String(Yanfly.Parameters["Font Changed Max"]);
Yanfly.Param.MSGFontChangeMin = String(Yanfly.Parameters["Font Changed Min"]);
Yanfly.Param.MSGFontOutline = Number(Yanfly.Parameters["Font Outline"]);
Yanfly.Param.MSGFontMaintain = eval(String(Yanfly.Parameters["Maintain Font"]));

Yanfly.Param.MSGNameBoxBufferX = String(Yanfly.Parameters["Name Box Buffer X"]);
Yanfly.Param.MSGNameBoxBufferY = String(Yanfly.Parameters["Name Box Buffer Y"]);
Yanfly.Param.MSGNameBoxPadding = String(Yanfly.Parameters["Name Box Padding"]);
Yanfly.Param.MSGNameBoxColor = Number(Yanfly.Parameters["Name Box Color"]);
Yanfly.Param.MSGNameBoxClear = String(Yanfly.Parameters["Name Box Clear"]);
Yanfly.Param.MSGNameBoxText = String(Yanfly.Parameters["Name Box Added Text"]);
Yanfly.Param.MSGNameBoxClose = String(Yanfly.Parameters["Name Box Auto Close"]);
Yanfly.Param.MSGNameBoxClose = eval(Yanfly.Param.MSGNameBoxClose);

//=============================================================================
// Bitmap
//=============================================================================

Yanfly.Message.Bitmap_initialize = Bitmap.prototype.initialize;
Bitmap.prototype.initialize = function (width, height) {
  Yanfly.Message.Bitmap_initialize.call(this, width, height);
  this.fontBold = false;
};

Yanfly.Message.Bitmap_makeFontNameText = Bitmap.prototype._makeFontNameText;
Bitmap.prototype._makeFontNameText = function () {
  if (this.fontBold) return "Bold " + this.fontSize + "px " + this.fontFace;
  return Yanfly.Message.Bitmap_makeFontNameText.call(this);
};

//=============================================================================
// Game_System
//=============================================================================

Yanfly.Message.Game_System_initialize = Game_System.prototype.initialize;
Game_System.prototype.initialize = function () {
  Yanfly.Message.Game_System_initialize.call(this);
  this.initMessageSystem();
  this.initMessageFontSettings();
};

Game_System.prototype.initMessageSystem = function () {
  this._wordWrap = Yanfly.Param.MSGWordWrap;
  this._fastForward = Yanfly.Param.MSGFFOn;
};

Game_System.prototype.initMessageFontSettings = function () {
  if ($dataSystem.locale.match(/^zh/)) {
    this._msgFontName = Yanfly.Param.MSGCNFontName;
  } else if ($dataSystem.locale.match(/^ko/)) {
    this._msgFontName = Yanfly.Param.MSGKRFontName;
  } else {
    this._msgFontName = Yanfly.Param.MSGFontName;
  }
  this._msgFontSize = Yanfly.Param.MSGFontSize;
  this._msgFontOutline = Yanfly.Param.MSGFontOutline;
};

Game_System.prototype.messageRows = function () {
  var rows = eval(this._messageRows) || eval(Yanfly.Param.MSGDefaultRows);
  return Math.max(1, Number(rows));
};

Game_System.prototype.messageWidth = function () {
  return eval(this._messageWidth) || eval(Yanfly.Param.MSGDefaultWidth);
};

Game_System.prototype.wordWrap = function () {
  if (this._wordWrap === undefined) this.initMessageSystem();
  return this._wordWrap;
};

Game_System.prototype.setWordWrap = function (state) {
  if (this._wordWrap === undefined) this.initMessageSystem();
  this._wordWrap = state;
};

Game_System.prototype.isFastFowardEnabled = function () {
  if (this._fastForward === undefined) this.initMessageSystem();
  return this._fastForward;
};

Game_System.prototype.setFastFoward = function (state) {
  if (this._fastForward === undefined) this.initMessageSystem();
  this._fastForward = state;
};

Game_System.prototype.getMessageFontName = function () {
  if (this._msgFontName === undefined) this.initMessageFontSettings();
  return this._msgFontName;
};

Game_System.prototype.setMessageFontName = function (value) {
  if (this._msgFontName === undefined) this.initMessageFontSettings();
  this._msgFontName = value;
};

Game_System.prototype.getMessageFontSize = function () {
  if (this._msgFontSize === undefined) this.initMessageFontSettings();
  return this._msgFontSize;
};

Game_System.prototype.setMessageFontSize = function (value) {
  if (this._msgFontSize === undefined) this.initMessageFontSettings();
  this._msgFontSize = value;
};

Game_System.prototype.getMessageFontOutline = function () {
  if (this._msgFontOutline === undefined) this.initMessageFontSettings();
  return this._msgFontOutline;
};

Game_System.prototype.setMessageFontOutline = function (value) {
  if (this._msgFontOutline === undefined) this.initMessageFontSettings();
  this._msgFontOutline = value;
};

//=============================================================================
// Game_Message
//=============================================================================

Game_Message.prototype.addText = function (text) {
  if ($gameSystem.wordWrap()) text = "<WordWrap>" + text;
  this.add(text);
};

//=============================================================================
// Game_Interpreter
//=============================================================================

Yanfly.Message.Game_Interpreter_pluginCommand =
  Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function (command, args) {
  Yanfly.Message.Game_Interpreter_pluginCommand.call(this, command, args);
  if (command === "MessageRows") $gameSystem._messageRows = args[0];
  if (command === "MessageWidth") $gameSystem._messageWidth = args[0];
  if (command === "EnableWordWrap") $gameSystem.setWordWrap(true);
  if (command === "DisableWordWrap") $gameSystem.setWordWrap(false);
  if (command === "EnableFastForward") $gameSystem.setFastFoward(true);
  if (command === "DisableFastForward") $gameSystem.setFastFoward(false);
};

Game_Interpreter.prototype.command101 = function () {
  if (!$gameMessage.isBusy()) {
    $gameMessage.setFaceImage(this._params[0], this._params[1]);
    $gameMessage.setBackground(this._params[2]);
    $gameMessage.setPositionType(this._params[3]);
    while (this.isContinueMessageString()) {
      this._index++;
      if (this._list[this._index].code === 401) {
        $gameMessage.addText(this.currentCommand().parameters[0]);
      }
      if ($gameMessage._texts.length >= $gameSystem.messageRows()) break;
    }
    switch (this.nextEventCode()) {
      case 102:
        this._index++;
        this.setupChoices(this.currentCommand().parameters);
        break;
      case 103:
        this._index++;
        this.setupNumInput(this.currentCommand().parameters);
        break;
      case 104:
        this._index++;
        this.setupItemChoice(this.currentCommand().parameters);
        break;
    }
    this._index++;
    this.setWaitMode("message");
  }
  return false;
};

Game_Interpreter.prototype.isContinueMessageString = function () {
  if (this.nextEventCode() === 101 && $gameSystem.messageRows() > 4) {
    return true;
  } else {
    return this.nextEventCode() === 401;
  }
};

//=============================================================================
// Window_Base
//=============================================================================

Yanfly.Message.Window_Base_resetFontSettings =
  Window_Base.prototype.resetFontSettings;
Window_Base.prototype.resetFontSettings = function () {
  Yanfly.Message.Window_Base_resetFontSettings.call(this);
  this.contents.fontBold = false;
  this.contents.fontItalic = false;
  this.contents.outlineColor = "rgba(0, 0, 0, 0.5)";
  this.contents.outlineWidth = $gameSystem.getMessageFontOutline();
};

Window_Base.prototype.textWidthEx = function (text) {
  return this.drawTextEx(text, 0, this.contents.height + this.lineHeight());
};

Yanfly.Message.Window_Base_convertEscapeCharacters =
  Window_Base.prototype.convertEscapeCharacters;
Window_Base.prototype.convertEscapeCharacters = function (text) {
  text = this.setWordWrap(text);
  text = Yanfly.Message.Window_Base_convertEscapeCharacters.call(this, text);
  text = this.convertExtraEscapeCharacters(text);
  return text;
};

Window_Base.prototype.setWordWrap = function (text) {
  this._wordWrap = false;
  if (text.match(/<(?:WordWrap)>/i)) {
    this._wordWrap = true;
    text = text.replace(/<(?:WordWrap)>/gi, "");
  }
  if (this._wordWrap) {
    var replace = Yanfly.Param.MSGWrapSpace ? " " : "";
    text = text.replace(/[\n\r]+/g, replace);
  }
  if (this._wordWrap) {
    text = text.replace(/<(?:BR|line break)>/gi, "\n");
  } else {
    text = text.replace(/<(?:BR|line break)>/gi, "");
  }
  return text;
};

Window_Base.prototype.convertExtraEscapeCharacters = function (text) {
  // Font Codes
  text = text.replace(/\x1bFR/gi, "\x1bMSGCORE[0]");
  text = text.replace(/\x1bFB/gi, "\x1bMSGCORE[1]");
  text = text.replace(/\x1bFI/gi, "\x1bMSGCORE[2]");
  // \AC[n]
  text = text.replace(
    /\x1bAC\[(\d+)\]/gi,
    function () {
      return this.actorClassName(parseInt(arguments[1]));
    }.bind(this)
  );
  // \AN[n]
  text = text.replace(
    /\x1bAN\[(\d+)\]/gi,
    function () {
      return this.actorNickname(parseInt(arguments[1]));
    }.bind(this)
  );
  // \PC[n]
  text = text.replace(
    /\x1bPC\[(\d+)\]/gi,
    function () {
      return this.partyClassName(parseInt(arguments[1]));
    }.bind(this)
  );
  // \PN[n]
  text = text.replace(
    /\x1bPN\[(\d+)\]/gi,
    function () {
      return this.partyNickname(parseInt(arguments[1]));
    }.bind(this)
  );
  // \NC[n]
  text = text.replace(
    /\x1bNC\[(\d+)\]/gi,
    function () {
      return $dataClasses[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NI[n]
  text = text.replace(
    /\x1bNI\[(\d+)\]/gi,
    function () {
      return $dataItems[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NW[n]
  text = text.replace(
    /\x1bNW\[(\d+)\]/gi,
    function () {
      return $dataWeapons[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NA[n]
  text = text.replace(
    /\x1bNA\[(\d+)\]/gi,
    function () {
      return $dataArmors[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NE[n]
  text = text.replace(
    /\x1bNE\[(\d+)\]/gi,
    function () {
      return $dataEnemies[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NS[n]
  text = text.replace(
    /\x1bNS\[(\d+)\]/gi,
    function () {
      return $dataSkills[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \NT[n]
  text = text.replace(
    /\x1bNT\[(\d+)\]/gi,
    function () {
      return $dataStates[parseInt(arguments[1])].name;
    }.bind(this)
  );
  // \II[n]
  text = text.replace(
    /\x1bII\[(\d+)\]/gi,
    function () {
      return this.escapeIconItem(arguments[1], $dataItems);
    }.bind(this)
  );
  // \IW[n]
  text = text.replace(
    /\x1bIW\[(\d+)\]/gi,
    function () {
      return this.escapeIconItem(arguments[1], $dataWeapons);
    }.bind(this)
  );
  // \IA[n]
  text = text.replace(
    /\x1bIA\[(\d+)\]/gi,
    function () {
      return this.escapeIconItem(arguments[1], $dataArmors);
    }.bind(this)
  );
  // \IS[n]
  text = text.replace(
    /\x1bIS\[(\d+)\]/gi,
    function () {
      return this.escapeIconItem(arguments[1], $dataSkills);
    }.bind(this)
  );
  // \IT[n]
  text = text.replace(
    /\x1bIT\[(\d+)\]/gi,
    function () {
      return this.escapeIconItem(arguments[1], $dataStates);
    }.bind(this)
  );
  // Finish
  return text;
};

Window_Base.prototype.actorClassName = function (n) {
  var actor = n >= 1 ? $gameActors.actor(n) : null;
  return actor ? actor.currentClass().name : "";
};

Window_Base.prototype.actorNickname = function (n) {
  var actor = n >= 1 ? $gameActors.actor(n) : null;
  return actor ? actor.nickname() : "";
};

Window_Base.prototype.partyClassName = function (n) {
  var actor = n >= 1 ? $gameParty.members()[n - 1] : null;
  return actor ? actor.currentClass().name : "";
};

Window_Base.prototype.partyNickname = function (n) {
  var actor = n >= 1 ? $gameParty.members()[n - 1] : null;
  return actor ? actor.nickname() : "";
};

Window_Base.prototype.escapeIconItem = function (n, database) {
  return "\x1bI[" + database[n].iconIndex + "]" + database[n].name;
};

Window_Base.prototype.obtainEscapeString = function (textState) {
  var arr = /^\<(.*?)\>/.exec(textState.text.slice(textState.index));
  if (arr) {
    textState.index += arr[0].length;
    return String(arr[0].slice(1, arr[0].length - 1));
  } else {
    return "";
  }
};

Yanfly.Message.Window_Base_processEscapeCharacter =
  Window_Base.prototype.processEscapeCharacter;
Window_Base.prototype.processEscapeCharacter = function (code, textState) {
  switch (code) {
    case "MSGCORE":
      var id = this.obtainEscapeParam(textState);
      if (id === 0) {
        $gameSystem.initMessageFontSettings();
        this.resetFontSettings();
      }
      if (id === 1) this.contents.fontBold = !this.contents.fontBold;
      if (id === 2) this.contents.fontItalic = !this.contents.fontItalic;
      break;
    case "FS":
      var size = this.obtainEscapeParam(textState);
      this.contents.fontSize = size;
      if (Yanfly.Param.MSGFontMaintain) $gameSystem.setMessageFontSize(size);
      break;
    case "FN":
      var name = this.obtainEscapeString(textState);
      this.contents.fontFace = name;
      if (Yanfly.Param.MSGFontMaintain) $gameSystem.setMessageFontName(name);
      break;
    case "OC":
      var id = this.obtainEscapeParam(textState);
      this.contents.outlineColor = this.textColor(id);
      break;
    case "OW":
      this.contents.outlineWidth = this.obtainEscapeParam(textState);
      break;
    case "PX":
      textState.x = this.obtainEscapeParam(textState);
      break;
    case "PY":
      textState.y = this.obtainEscapeParam(textState);
      break;
    default:
      Yanfly.Message.Window_Base_processEscapeCharacter.call(
        this,
        code,
        textState
      );
      break;
  }
};

Window_Base.prototype.makeFontBigger = function () {
  var size = this.contents.fontSize + eval(Yanfly.Param.MSGFontSizeChange);
  this.contents.fontSize = Math.min(size, Yanfly.Param.MSGFontChangeMax);
};

Window_Base.prototype.makeFontSmaller = function () {
  var size = this.contents.fontSize - eval(Yanfly.Param.MSGFontSizeChange);
  this.contents.fontSize = Math.max(size, Yanfly.Param.MSGFontChangeMin);
};

Yanfly.Message.Window_Base_processNormalCharacter =
  Window_Base.prototype.processNormalCharacter;
Window_Base.prototype.processNormalCharacter = function (textState) {
  if (this.checkWordWrap(textState)) return this.processNewLine(textState);
  Yanfly.Message.Window_Base_processNormalCharacter.call(this, textState);
};

Window_Base.prototype.checkWordWrap = function (textState) {
  if (!textState) return false;
  if (!this._wordWrap) return false;
  if (textState.text[textState.index] === " ") {
    var nextSpace = textState.text.indexOf(" ", textState.index + 1);
    var nextBreak = textState.text.indexOf("\n", textState.index + 1);
    if (nextSpace < 0) nextSpace = textState.text.length + 1;
    if (nextBreak > 0) nextSpace = Math.min(nextSpace, nextBreak);
    var word = textState.text.substring(textState.index, nextSpace);
    var size = this.textWidthExCheck(word);
  }
  return size + textState.x > this.wordwrapWidth();
};

Window_Base.prototype.wordwrapWidth = function () {
  return this.contents.width;
};

Window_Base.prototype.saveCurrentWindowSettings = function () {
  this._saveFontFace = this.contents.fontFace;
  this._saveFontSize = this.contents.fontSize;
  this._savetextColor = this.contents.textColor;
  this._saveFontBold = this.contents.fontBold;
  this._saveFontItalic = this.contents.fontItalic;
  this._saveOutlineColor = this.contents.outlineColor;
  this._saveOutlineWidth = this.contents.outlineWidth;
};

Window_Base.prototype.restoreCurrentWindowSettings = function () {
  this.contents.fontFace = this._saveFontFace;
  this.contents.fontSize = this._saveFontSize;
  this.contents.textColor = this._savetextColor;
  this.contents.fontBold = this._saveFontBold;
  this.contents.fontItalic = this._saveFontItalic;
  this.contents.outlineColor = this._saveOutlineColor;
  this.contents.outlineWidth = this._saveOutlineWidth;
};

Window_Base.prototype.clearCurrentWindowSettings = function () {
  this._saveFontFace = undefined;
  this._saveFontSize = undefined;
  this._savetextColor = undefined;
  this._saveFontBold = undefined;
  this._saveFontItalic = undefined;
  this._saveOutlineColor = undefined;
  this._saveOutlineWidth = undefined;
};

Window_Base.prototype.textWidthExCheck = function (text) {
  var setting = this._wordWrap;
  this._wordWrap = false;
  this.saveCurrentWindowSettings();
  this._checkWordWrapMode = true;
  var value = this.drawTextEx(text, 0, this.contents.height);
  this._checkWordWrapMode = false;
  this.restoreCurrentWindowSettings();
  this.clearCurrentWindowSettings();
  this._wordWrap = setting;
  return value;
};

//=============================================================================
// Window_Help
//=============================================================================

Yanfly.Message.Window_Help_setItem = Window_Help.prototype.setItem;
Window_Help.prototype.setItem = function (item) {
  if (eval(Yanfly.Param.MSGDescWrap)) {
    this.setText(item ? "<WordWrap>" + item.description : "");
  } else {
    Yanfly.Message.Window_Help_setItem.call(this, item);
  }
};

//=============================================================================
// Window_ChoiceList
//=============================================================================

Window_ChoiceList.prototype.standardFontFace = function () {
  return $gameSystem.getMessageFontName();
};

Window_ChoiceList.prototype.standardFontSize = function () {
  return $gameSystem.getMessageFontSize();
};

Yanfly.Message.Window_ChoiceList_updatePlacement =
  Window_ChoiceList.prototype.updatePlacement;
Window_ChoiceList.prototype.updatePlacement = function () {
  Yanfly.Message.Window_ChoiceList_updatePlacement.call(this);
  var messagePosType = $gameMessage.positionType();
  if (messagePosType === 0) {
    this.y = this._messageWindow.height;
  } else if (messagePosType === 2) {
    this.y = Graphics.boxHeight - this._messageWindow.height - this.height;
  }
};

//=============================================================================
// Window_NumberInput
//=============================================================================

Yanfly.Message.Window_NumberInput_updatePlacement =
  Window_NumberInput.prototype.updatePlacement;
Window_NumberInput.prototype.updatePlacement = function () {
  Yanfly.Message.Window_NumberInput_updatePlacement.call(this);
  var messageY = this._messageWindow.y;
  var messagePosType = $gameMessage.positionType();
  if (messagePosType === 0) {
    this.y = this._messageWindow.height;
  } else if (messagePosType === 1) {
    if (messageY >= Graphics.boxHeight / 2) {
      this.y = messageY - this.height;
    } else {
      this.y = messageY + this._messageWindow.height;
    }
  } else if (messagePosType === 2) {
    this.y = Graphics.boxHeight - this._messageWindow.height - this.height;
  }
};

//=============================================================================
// Window_EventItem
//=============================================================================

Yanfly.Message.Window_EventItem_updatePlacement =
  Window_EventItem.prototype.updatePlacement;
Window_EventItem.prototype.updatePlacement = function () {
  Yanfly.Message.Window_EventItem_updatePlacement.call(this);
  var messagePosType = $gameMessage.positionType();
  if (messagePosType === 0) {
    this.y = Graphics.boxHeight - this.height;
  } else if (messagePosType === 2) {
    this.y = 0;
  }
};

//=============================================================================
// Window_ScrollText
//=============================================================================

Window_ScrollText.prototype.standardFontFace = function () {
  return $gameSystem.getMessageFontName();
};

Window_ScrollText.prototype.standardFontSize = function () {
  return $gameSystem.getMessageFontSize();
};

//=============================================================================
// Window_NameBox
//=============================================================================

Yanfly.DisableWebGLMask = false;

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

Window_NameBox.prototype = Object.create(Window_Base.prototype);
Window_NameBox.prototype.constructor = Window_NameBox;

Window_NameBox.prototype.initialize = function (parentWindow) {
  this._parentWindow = parentWindow;
  Window_Base.prototype.initialize.call(this, 0, 0, 240, this.windowHeight());
  this._text = "";
  this._lastNameText = "";
  this._openness = 0;
  this._closeCounter = 0;
  this.deactivate();
  if (eval(Yanfly.Param.MSGNameBoxClear)) {
    this.backOpacity = 0;
    this.opacity = 0;
  }
  this.hide();
};

Window_NameBox.prototype.windowWidth = function () {
  this.resetFontSettings();
  var dw = this.textWidthEx(this._text);
  dw += this.padding * 2;
  var width = dw + eval(Yanfly.Param.MSGNameBoxPadding);
  return Math.ceil(width);
};

Window_NameBox.prototype.textWidthEx = function (text) {
  return this.drawTextEx(text, 0, this.contents.height);
};

Window_NameBox.prototype.calcNormalCharacter = function (textState) {
  return this.textWidth(textState.text[textState.index++]);
};

Window_NameBox.prototype.windowHeight = function () {
  return this.fittingHeight(1);
};

Window_NameBox.prototype.standardFontFace = function () {
  return $gameSystem.getMessageFontName();
};

Window_NameBox.prototype.standardFontSize = function () {
  return $gameSystem.getMessageFontSize();
};

Window_NameBox.prototype.update = function () {
  Window_Base.prototype.update.call(this);
  if (this.active) return;
  if (this.isClosed()) return;
  if (this.isClosing()) return;
  if (this._closeCounter-- > 0) return;
  if (this._parentWindow.isClosing()) {
    this._openness = this._parentWindow.openness;
  }
  this.close();
};

Window_NameBox.prototype.refresh = function (text, position) {
  this.show();
  this._lastNameText = text;
  this._text = Yanfly.Param.MSGNameBoxText + text;
  this._position = position;
  this.width = this.windowWidth();
  this.createContents();
  this.contents.clear();
  this.resetFontSettings();
  this.changeTextColor(this.textColor(Yanfly.Param.MSGNameBoxColor));
  var padding = eval(Yanfly.Param.MSGNameBoxPadding) / 2;
  this.drawTextEx(this._text, padding, 0, this.contents.width);
  this._parentWindow.adjustWindowSettings();
  this._parentWindow.updatePlacement();
  this.adjustPositionX();
  this.adjustPositionY();
  this.open();
  this.activate();
  this._closeCounter = 4;
  return "";
};

Window_NameBox.prototype.adjustPositionX = function () {
  if (this._position === 1) {
    this.x = this._parentWindow.x;
    this.x += eval(Yanfly.Param.MSGNameBoxBufferX);
  } else if (this._position === 2) {
    this.x = this._parentWindow.x;
    this.x += (this._parentWindow.width * 3) / 10;
    this.x -= this.width / 2;
  } else if (this._position === 3) {
    this.x = this._parentWindow.x;
    this.x += this._parentWindow.width / 2;
    this.x -= this.width / 2;
  } else if (this._position === 4) {
    this.x = this._parentWindow.x;
    this.x += (this._parentWindow.width * 7) / 10;
    this.x -= this.width / 2;
  } else {
    this.x = this._parentWindow.x + this._parentWindow.width;
    this.x -= this.width;
    this.x -= eval(Yanfly.Param.MSGNameBoxBufferX);
  }
  this.x = this.x.clamp(0, Graphics.boxWidth - this.width);
};

Window_NameBox.prototype.adjustPositionY = function () {
  if ($gameMessage.positionType() === 0) {
    this.y = this._parentWindow.y + this._parentWindow.height;
    this.y -= eval(Yanfly.Param.MSGNameBoxBufferY);
  } else {
    this.y = this._parentWindow.y;
    this.y -= this.height;
    this.y += eval(Yanfly.Param.MSGNameBoxBufferY);
  }
  if (this.y < 0) {
    this.y = this._parentWindow.y + this._parentWindow.height;
    this.y -= eval(Yanfly.Param.MSGNameBoxBufferY);
  }
};

//=============================================================================
// Window_Message
//=============================================================================

Yanfly.Message.Window_Message_createSubWindows =
  Window_Message.prototype.createSubWindows;
Window_Message.prototype.createSubWindows = function () {
  Yanfly.Message.Window_Message_createSubWindows.call(this);
  this._nameWindow = new Window_NameBox(this);
  Yanfly.nameWindow = this._nameWindow;
  var scene = SceneManager._scene;
  scene.addChild(this._nameWindow);
};

Window_Message.prototype.numVisibleRows = function () {
  return $gameSystem.messageRows();
};

Window_Message.prototype.windowWidth = function () {
  return $gameSystem.messageWidth();
};

Window_Message.prototype.wordwrapWidth = function () {
  if (Yanfly.Param.MSGTightWrap && $gameMessage.faceName() !== "") {
    return this.contents.width - this.newLineX();
  }
  return Window_Base.prototype.wordwrapWidth.call(this);
};

Window_Message.prototype.adjustWindowSettings = function () {
  this.width = this.windowWidth();
  this.height = Math.min(this.windowHeight(), Graphics.boxHeight);
  if (Math.abs(Graphics.boxHeight - this.height) < this.lineHeight()) {
    this.height = Graphics.boxHeight;
  }
  this.createContents();
  this.x = (Graphics.boxWidth - this.width) / 2;
};

Yanfly.Message.Window_Message_startMessage =
  Window_Message.prototype.startMessage;
Window_Message.prototype.startMessage = function () {
  this._nameWindow.deactivate();
  Yanfly.Message.Window_Message_startMessage.call(this);
};

Yanfly.Message.Window_Message_terminateMessage =
  Window_Message.prototype.terminateMessage;
Window_Message.prototype.terminateMessage = function () {
  this._nameWindow.deactivate();
  Yanfly.Message.Window_Message_terminateMessage.call(this);
};

Yanfly.Message.Window_Message_newPage = Window_Message.prototype.newPage;
Window_Message.prototype.newPage = function (textState) {
  this.adjustWindowSettings();
  Yanfly.Message.Window_Message_newPage.call(this, textState);
};

Window_Message.prototype.standardFontFace = function () {
  return $gameSystem.getMessageFontName();
};

Window_Message.prototype.standardFontSize = function () {
  return $gameSystem.getMessageFontSize();
};

Window_Message.prototype.newLineX = function () {
  if ($gameMessage.faceName() === "") {
    return 0;
  } else {
    return eval(Yanfly.Param.MSGFaceIndent);
  }
};

Window_Message.prototype.isFastForward = function () {
  if (!$gameSystem.isFastFowardEnabled()) return false;
  return Input.isPressed(Yanfly.Param.MSGFastForwardKey);
};

Yanfly.Message.Window_Message_updateInput =
  Window_Message.prototype.updateInput;
Window_Message.prototype.updateInput = function () {
  if (this.pause && this.isFastForward()) {
    if (!this._textState) {
      this.pause = false;
      this.terminateMessage();
    }
  }
  return Yanfly.Message.Window_Message_updateInput.call(this);
};

Yanfly.Message.Window_Message_updateShowFast =
  Window_Message.prototype.updateShowFast;
Window_Message.prototype.updateShowFast = function () {
  if (this.isFastForward()) this._showFast = true;
  Yanfly.Message.Window_Message_updateShowFast.call(this);
};

Yanfly.Message.Window_Message_updateWait = Window_Message.prototype.updateWait;
Window_Message.prototype.updateWait = function () {
  if (this.isFastForward()) return false;
  return Yanfly.Message.Window_Message_updateWait.call(this);
};

Yanfly.Message.Window_Message_startWait = Window_Message.prototype.startWait;
Window_Message.prototype.startWait = function (count) {
  if (this._checkWordWrapMode) return;
  Yanfly.Message.Window_Message_startWait.call(this, count);
  if (this.isFastForward()) this._waitCount = 0;
};

Yanfly.Message.Window_Message_startPause = Window_Message.prototype.startPause;
Window_Message.prototype.startPause = function () {
  if (this._checkWordWrapMode) return;
  Yanfly.Message.Window_Message_startPause.call(this);
};

Window_Message.prototype.convertEscapeCharacters = function (text) {
  text = Window_Base.prototype.convertEscapeCharacters.call(this, text);
  text = this.convertNameBox(text);
  text = this.convertMessageCharacters(text);
  return text;
};

Window_Message.prototype.convertNameBox = function (text) {
  text = text.replace(
    /\x1bN\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 1);
    },
    this
  );
  text = text.replace(
    /\x1bN1\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 1);
    },
    this
  );
  text = text.replace(
    /\x1bN2\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 2);
    },
    this
  );
  text = text.replace(
    /\x1bN3\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 3);
    },
    this
  );
  text = text.replace(
    /\x1bNC\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 3);
    },
    this
  );
  text = text.replace(
    /\x1bN4\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 4);
    },
    this
  );
  text = text.replace(
    /\x1bN5\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 5);
    },
    this
  );
  text = text.replace(
    /\x1bNR\<(.*?)\>/gi,
    function () {
      return Yanfly.nameWindow.refresh(arguments[1], 5);
    },
    this
  );
  return text;
};

Window_Message.prototype.convertMessageCharacters = function (text) {
  text = text.replace(
    /\x1bAF\[(\d+)\]/gi,
    function () {
      var i = parseInt(arguments[1]);
      return this.convertActorFace($gameActors.actor(i));
    }.bind(this)
  );
  text = text.replace(
    /\x1bPF\[(\d+)\]/gi,
    function () {
      var i = parseInt(arguments[1]);
      return this.convertActorFace($gameParty.members()[i - 1]);
    }.bind(this)
  );
  return text;
};

Window_Message.prototype.convertActorFace = function (actor) {
  $gameMessage.setFaceImage(actor.faceName(), actor.faceIndex());
  return "";
};

Yanfly.Message.Window_Message_processEscapeCharacter =
  Window_Message.prototype.processEscapeCharacter;
Window_Message.prototype.processEscapeCharacter = function (code, textState) {
  switch (code) {
    case "!":
      if (!this.isFastForward()) this.startPause();
      break;
    case "W":
      this.startWait(this.obtainEscapeParam(textState));
    default:
      Yanfly.Message.Window_Message_processEscapeCharacter.call(
        this,
        code,
        textState
      );
      break;
  }
};

if (Yanfly.Param.MSGNameBoxClose) {
  Yanfly.Message.Window_Message_doesContinue =
    Window_Message.prototype.doesContinue;
  Window_Message.prototype.doesContinue = function () {
    var value = Yanfly.Message.Window_Message_doesContinue.call(this);
    if (!value) return false;
    if (this.hasDifferentNameBoxText()) {
      return false;
    }
    return true;
  };

  Window_Message.prototype.hasDifferentNameBoxText = function () {
    var texts = $gameMessage._texts;
    var length = texts.length;
    var open = this._nameWindow.isOpen();
    for (var i = 0; i < length; ++i) {
      var text = texts[i];
      if (text.length <= 0) continue;
      if (Yanfly.MsgMacro) {
        text = this.convertMacroText(text);
        text = text.replace(/\x1b/gi, "\\");
      }
      if (text.match(/\\(?:N|N1|N2|N3|N4|N5|NC|NR)<(.*)>/i)) {
        var name = String(RegExp.$1);
      } else if (text.match(/\\(?:ND|ND1|ND2|ND3|ND4|ND5|NDC|NDR)<(.*)>/i)) {
        var name = String(RegExp.$1);
      } else if (text.match(/\\(?:NT|NT1|NT2|NT3|NT4|NT5|NTC|NTR)<(.*)>/i)) {
        var name = String(RegExp.$1);
      }
      if (name) {
        name = name.replace(
          /\\V\[(\d+)\]/gi,
          function () {
            return $gameVariables.value(parseInt(arguments[1]));
          }.bind(this)
        );
        name = name.replace(
          /\\V\[(\d+)\]/gi,
          function () {
            return $gameVariables.value(parseInt(arguments[1]));
          }.bind(this)
        );
        name = name.replace(
          /\\N\[(\d+)\]/gi,
          function () {
            return this.actorName(parseInt(arguments[1]));
          }.bind(this)
        );
        name = name.replace(
          /\\P\[(\d+)\]/gi,
          function () {
            return this.partyMemberName(parseInt(arguments[1]));
          }.bind(this)
        );
        name = name.replace(/\\/gi, "\x1b");
      }
      if (name && !open) return true;
      if (name && name !== this._nameWindow._lastNameText) {
        return true;
      }
    }
    if (open && !name) return true;
    return false;
  };
} // Yanfly.Param.MSGNameBoxClose

//=============================================================================
// End of File
//=============================================================================


code:
//============================================================================
// YKNR_SaveThumbnail.js - ver.1.2.0
// ---------------------------------------------------------------------------
// Copyright (c) 2019 Yakinori
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
//============================================================================
/*:
 * ===========================================================================
 * @plugindesc セーブファイルにサムネイル用の画像を保存し、
 * セーブ/ロード画面でファイル毎にサムネイルを表示する
 * @author 焼きノリ
 * ===========================================================================
 *
 * @param AutoSnapForThumbnail
 * @text サムネイルの自動生成
 * @desc マップ画面からメニュー画面に切り替えたタイミングで
 * サムネイル用の画像を撮影するかを設定します。
 * @type boolean
 * @default true
 *
 * @param FastLoadingData
 * @text 読み込み速度高速化
 * @desc セーブ/ロード画面に入ったときやファイルリストを
 * スクロールしたときの読み込み速度を改善します。
 * @type boolean
 * @default true
 *
 * @param SnapSettings
 * @text サムネイルの保存設定
 * @desc 階層分けのために用意したパラメータなので、
 * ここには何も設定しないでください。
 *
 * @param ThumbQuality
 * @text 画質の設定
 * @desc 保存するサムネイルの画質を設定します。
 * 値が小さいほどデータサイズを小さくできます。
 * @type number
 * @min 0
 * @max 100
 * @default 90
 * @parent SnapSettings
 *
 * @param ThumbSaveWidth
 * @text サイズの設定：幅
 * @desc 保存するサムネイルの幅を設定します。
 * 値が小さいほどデータサイズを小さくできます。
 * @type number
 * @min 0
 * @default 122
 * @parent SnapSettings
 *
 * @param ThumbSaveHeight
 * @text サイズの設定：高さ
 * @desc 保存するサムネイルの高さを設定します。
 * 値が小さいほどデータサイズを小さくできます。
 * @type number
 * @min 0
 * @default 94
 * @parent SnapSettings
 *
 * @param ShowInSavefileList
 * @text ファイルリスト内に表示
 * @desc リストの項目にサムネイルを表示するか設定します。
 * @type boolean
 * @on 表示する
 * @off 表示しない
 * @default true
 *
 * @param ThumbItemPosX
 * @text サムネイルのX座標
 * @desc 描画するX座標をJavascriptで設定します。
 * rect = 項目の範囲, width = サムネイル幅
 * @type string
 * @default rect.x + rect.width - width;
 * @parent ShowInSavefileList
 *
 * @param ThumbItemPosY
 * @text サムネイルのY座標
 * @desc 描画するY座標をJavascriptで設定します。
 * rect = 項目の範囲, height = サムネイル高さ
 * @type string
 * @default rect.y + 5
 * @parent ShowInSavefileList
 *
 * @param ThumbItemScale
 * @text サムネイルのスケール値
 * @desc 描画するスケールを設定します。
 * 「サムネイルの保存設定」で設定した幅/高さを 1 とします。
 * @type number
 * @min 0
 * @decimals 2
 * @default 1.00
 * @parent ShowInSavefileList
 *
 * @param OtherWindowClass
 * @text 別ウィンドウの表示設定
 * @desc 別ウィンドウにサムネイルを表示したいときに設定します。
 * 対象となるウィンドウクラス名を記入します。
 * @type string
 * @default
 *
 * @param ThumbOWPosX
 * @text サムネイルのX座標
 * @desc 別ウィンドウに描画するX座標をJavascriptで設定します。
 * rect = ウィンドウ内の範囲, width = サムネイル幅
 * @type string
 * @default rect.x + rect.width - width;
 * @parent OtherWindowClass
 *
 * @param ThumbOWPosY
 * @text サムネイルのY座標
 * @desc 別ウィンドウに描画するY座標をJavascriptで設定します。
 * rect = ウィンドウ内の範囲, height = サムネイル高さ
 * @type string
 * @default rect.y + 5
 * @parent OtherWindowClass
 *
 * @param ThumbOWScale
 * @text サムネイルのスケール値
 * @desc 別ウィンドウに描画するスケールを設定します。
 * 「サムネイルの保存設定」で設定した幅/高さを 1 とします。
 * @type number
 * @min 0
 * @decimals 2
 * @default 1.00
 * @parent OtherWindowClass
 *
 * @help
 * ===========================================================================
 *【！注意！】
 * ※ツクールMV本体のバージョンが 1.6.1 未満の場合、動作できません。
 * ===========================================================================
 *【機能紹介】
 * セーブデータにマップの現在地の画像をサムネイルとして保存し、
 * セーブ画面/ロード画面のリストにそのサムネイルを表示する機能を追加します。
 *
 * サムネイル撮影のタイミングは、メニュー画面を開くときに自動に撮影されます。
 *
 * community-1.3 の「オートセーブ機能」でセーブされたデータには、
 * サムネイルは表示しない仕様となっています。
 *
 * ---------------------------------------------------------------------------
 *【パラメータ説明】
 * ・「サムネイルの自動生成」
 *   false にした場合、サムネイルの撮影は手動で行うことになります。
 *   イベントコマンドの「スクリプト」から以下を呼び出してください。
 * -------------------------------------
 * SceneManager.snapForThumbnail();
 * -------------------------------------
 *
 * ・「画質の設定」「サイズの設定：幅」「サイズの設定：高さ」
 *   保存するデータサイズに関わる設定です。
 *   サイズを大きく画質を良くすれば、サムネイルの品質は良くなりますが、
 *   データが肥大化するので注意が必要です。
 *
 * ・「ファイルリスト内に表示」
 *   デフォルトのリストのウィンドウ内にサムネイルを表示します。
 *   ここに表示する必要性が無ければ false にしてください。
 *
 * ・「別ウィンドウの表示設定」
 *   ファイルリスト内ではなく、他プラグインによって追加された
 *   任意のウィンドウに表示することも可能です。
 *   表示したいウィンドウのクラス名を設定することで表示できます。
 *   例えば、Window_Help と設定すると
 *   ヘルプウィンドウ内に選択中のセーブデータのサムネイルが表示されます。
 *   (SceneManager._scene._listWindow を参照しているため、
 *    これが存在しない場合は正常に動作しません。)
 *
 * ---------------------------------------------------------------------------
 *【更新履歴】
 * [2019/01/04] [1.0.0] 公開
 * [2019/12/02] [1.1.0] 「ファイルリスト内に表示」、「別ウィンドウの表示設定」
 *   が機能していない問題の修正
 * [2020/05/28] [1.1.1] デプロイメント時に「画像の暗号化」にチェックを入れて
 *   出力されたゲームでサムネイル表示時にエラーが出ていた問題の修正
 * [2021/02/16] [1.1.2] Window_SavefileListでのサムネイルのコンテナ数の調整
 * [2021/02/18] [1.2.0] サムネイル表示処理の修正。「読み込み速度高速化」を追加
 *
 * ===========================================================================
 * [Blog]   : http://mata-tuku.ldblog.jp/
 * [Twitter]: https://twitter.com/Noritake0424
 * [Github] : https://github.com/Yakinori0424/RPGMakerMVPlugins
 * ---------------------------------------------------------------------------
 * 本プラグインは MITライセンス のもとで配布されています。
 * 利用はどうぞご自由に。
 * http://opensource.org/licenses/mit-license.php
 */

(function () {
  "use strict";

  //------------------------------------------------------------------------

  /**
   * 対象のオブジェクト上の関数を別の関数に差し替えます.
   *
   * @method monkeyPatch
   * @param {Object} target 対象のオブジェクト
   * @param {String} methodName オーバーライドする関数名
   * @param {Function} newMethod 新しい関数を返す関数
   */
  function monkeyPatch(target, methodName, newMethod) {
    target[methodName] = newMethod(target[methodName]);
  }

  //------------------------------------------------------------------------

  /**
   * Jsonをパースし, プロパティの値を変換して返す
   *
   * @method jsonParamsParse
   * @param {String} json JSON文字列
   * @return {Object} パース後のオブジェクト
   */
  function jsonParamsParse(json) {
    return JSON.parse(json, parseRevive);
  }

  function parseRevive(key, value) {
    if (key === "") {
      return value;
    }
    try {
      return JSON.parse(value, parseRevive);
    } catch (e) {
      return value;
    }
  }

  /**
   * Jsonをパースして変換後, 配列ならば連想配列に変換して返す
   *
   * @method jsonParamsParse
   * @param {String} json JSON文字列
   * @param {String} keyName 連想配列のキーとする要素のあるプロパティ名
   * @param {String} valueName 連想配列の値とする要素のあるプロパティ名
   * @return {Object} パース後の連想配列
   */
  function parseArrayToHash(json, keyName, valueName) {
    let hash = {};
    const array = jsonParamsParse(json);
    if (Array.isArray(array)) {
      for (let i = 0, l = array.length; i < l; i++) {
        const key = array[i][keyName];
        if (key && key !== "") {
          hash[key] = array[i][valueName] || null;
        }
      }
    }
    return hash;
  }

  //------------------------------------------------------------------------
  // パラメータを受け取る.
  const pluginName = "YKNR_SaveThumbnail";
  const parameters = PluginManager.parameters(pluginName);
  const isAutoSnap = parameters["AutoSnapForThumbnail"] === "true";
  const isFastLoadingData = parameters["FastLoadingData"] === "true";
  const thumbQuality = parseInt(parameters["ThumbQuality"]);
  const thumbSaveWidth = parseInt(parameters["ThumbSaveWidth"]);
  const thumbSaveHeight = parseInt(parameters["ThumbSaveHeight"]);
  const isShowInList = parameters["ShowInSavefileList"] === "true";
  const thumbItemPosX = parameters["ThumbItemPosX"] || "0";
  const thumbItemPosY = parameters["ThumbItemPosY"] || "0";
  const thumbItemScale = parseFloat(parameters["ThumbItemScale"]);
  const otherWindowClass = parameters["OtherWindowClass"];
  const thumbOtherPosX = parameters["ThumbOWPosX"] || "0";
  const thumbOtherPosY = parameters["ThumbOWPosY"] || "0";
  const thumbOtherScale = parseFloat(parameters["ThumbOWScale"]);

  //------------------------------------------------------------------------

  /**
   * セーブデータのサムネイルで使用するユニークなキーを返します
   *
   * @param {number} savefileId セーブファイルのID
   * @return {string}
   */
  function generateThumbUniqueKey(savefileId) {
    if (DataManager.isThisGameFile(savefileId)) {
      const info = DataManager.loadSavefileInfo(savefileId);
      return savefileId + ":" + info.timestamp;
    }
    return undefined;
  }

  //------------------------------------------------------------------------

  /**
   * Takes a snapshot of the game screen and returns a new bitmap object.\
   * 出力するビットマップの幅と高さを指定できるよう, Bitmap.snap を元に拡張しています.\
   * 幅/高さが未指定の場合は, 従来のように Graphics.width, Graphics.height となります.\
   * また, 出力前の元々の幅と高さをさらに指定することで,\
   * その値を元にビットマップの幅と高さに合わせて拡大率を調整します.\
   * こちらの指定がない場合は, ビットマップの幅と高さと同値となり, 拡大率を変えません.
   *
   * @static
   * @param {Stage} stage The stage object
   * @param {number} dw 出力するビットマップの幅
   * @param {number} dh 出力するビットマップの高さ
   * @param {number} cw 元の幅
   * @param {number} ch 元の高さ
   * @return {Bitmap} リサイズされたビットマップ
   */
  Bitmap.snap2 = function (
    stage,
    dw = Graphics.width,
    dh = Graphics.height,
    cw = dw,
    ch = dh
  ) {
    const bitmap = new Bitmap(dw, dh);
    if (stage) {
      const context = bitmap._context;
      const renderTexture = PIXI.RenderTexture.create(dw, dh);
      const last_sx = stage.scale.x;
      const last_sy = stage.scale.y;
      stage.scale.x = dw / cw;
      stage.scale.y = dh / ch;
      Graphics._renderer.render(stage, renderTexture);
      stage.worldTransform.identity();
      stage.scale.x = last_sx;
      stage.scale.y = last_sy;
      let canvas = null;
      if (Graphics.isWebGL()) {
        canvas = Graphics._renderer.extract.canvas(renderTexture);
      } else {
        canvas = renderTexture.baseTexture._canvasRenderTarget.canvas;
      }
      context.drawImage(canvas, 0, 0);
      renderTexture.destroy({ destroyBase: true });
      bitmap._setDirty();
    }
    return bitmap;
  };

  //------------------------------------------------------------------------

  monkeyPatch(Decrypter, "checkImgIgnore", function ($) {
    return function (url) {
      // Base64形式なら暗号化の対象外にする
      if (url.includes("data:image/jpeg;base64")) {
        return true;
      }
      return $.call(this, url);
    };
  });

  //------------------------------------------------------------------------

  /**
   * セーブファイルがこのゲームの何番目のものか取得します.
   *
   * @param {Object} info セーブデータ
   * @return {number}
   */
  DataManager.getSavefileId = function (info) {
    const globalInfo = this.loadGlobalInfo();
    if (globalInfo) {
      for (let id = 1, l = globalInfo.length; id < l; id++) {
        if (this.isThisGameFile(id)) {
          if (this.isEqualSavefile(info, globalInfo[id])) {
            return id;
          }
        }
      }
    }
    return -1;
  };

  /**
   * セーブファイルが同じものか判定します.
   *
   * @param {Object} a セーブデータA
   * @param {Object} b セーブデータB
   * @return {boolean}
   */
  DataManager.isEqualSavefile = function (a, b) {
    // NOTE : thumbnailは長くなりがちなので比較しなくてよいかなという判断
    return (
      a.globalId === b.globalId &&
      a.title === b.title &&
      a.timestamp === b.timestamp &&
      a.playtime === b.playtime &&
      // && a.thumbnail === b.thumbnail
      a.characters.equals(b.characters) &&
      a.faces.equals(b.faces)
    );
  };

  //------------------------------------------------------------------------

  /**
   * Base64形式用のキャッシュキーを返します.\
   * データURIは長くなるので, ユニークなキーを指定して代用します.
   *
   * @param {number|string} cacheKey 任意のキー
   * @param {number} hue 色相
   * @return {string}
   */
  ImageManager._generateBase64CacheKey = function (cacheKey, hue) {
    return "Base64:" + cacheKey + ":" + hue;
  };

  /**
   * ビットマップをjpgのBase64形式文字列に変換します
   *
   * @param {Bitmap} bitmap ビットマップ
   * @return {string} Base64形式の文字列を返します
   */
  ImageManager.toBase64 = function (bitmap) {
    const minetype = "image/jpeg";
    const quality = thumbQuality / 100;
    return bitmap._canvas.toDataURL(minetype, quality);
  };

  /**
   * Base64形式の文字列からビットマップをロードします.\
   * キャッシュに対応しています.
   *
   * @param {string} src Base64形式の文字列
   * @param {number|string} cacheKey キャッシュに使用する任意のキー
   * @param {number} hue 色相
   * @return {Bitmap}
   */
  ImageManager.loadBase64Bitmap = function (src, cacheKey, hue = 0) {
    const b64cacheKey = this._generateBase64CacheKey(cacheKey, hue);
    let bitmap = this._imageCache.get(b64cacheKey);
    if (!bitmap) {
      bitmap = Bitmap.load(src);
      if (this._callCreationHook) {
        // community-1.3 の プログレスバー 対応
        this._callCreationHook(bitmap);
      }
      bitmap.addLoadListener(function () {
        bitmap.rotateHue(hue);
      });
      this._imageCache.add(b64cacheKey, bitmap);
    } else if (!bitmap.isReady()) {
      bitmap.decode();
    }
    return bitmap;
  };

  /**
   * Base64形式の文字列からビットマップをリクエストします.\
   * キャッシュに対応しています.
   *
   * @param {string} src Base64形式の文字列
   * @param {number|string} cacheKey キャッシュに使用する任意のキー
   * @param {number} hue 色相
   * @return {Bitmap}
   */
  ImageManager.requestBase64Bitmap = function (src, cacheKey, hue = 0) {
    const b64cacheKey = this._generateBase64CacheKey(cacheKey, hue);
    let bitmap = this._imageCache.get(b64cacheKey);
    if (!bitmap) {
      bitmap = Bitmap.request(src);
      if (this._callCreationHook) {
        // community-1.3 の プログレスバー 対応
        this._callCreationHook(bitmap);
      }
      bitmap.addLoadListener(function () {
        bitmap.rotateHue(hue);
      });
      this._imageCache.add(b64cacheKey, bitmap);
      this._requestQueue.enqueue(b64cacheKey, bitmap);
    } else {
      this._requestQueue.raisePriority(b64cacheKey);
    }
    return bitmap;
  };

  //------------------------------------------------------------------------

  /**
   * セーブファイルからサムネイルをロードします.
   *
   * @param {number} savefileId セーブファイルのID
   * @param {object} info セーブデータ
   * @param {number} hue 色相
   * @return {Bitmap}
   */
  ImageManager.loadThumbnail = function (savefileId, info, hue) {
    const cacheKey = generateThumbUniqueKey(savefileId);
    if (info && info.thumbnail && cacheKey) {
      return ImageManager.loadBase64Bitmap(info.thumbnail, cacheKey, hue);
    }
    return this.loadEmptyBitmap();
  };

  /**
   * セーブファイルからサムネイルをリクエストします.
   *
   * @param {number} savefileId セーブファイルのID
   * @param {object} info セーブデータ
   * @param {number} hue 色相
   * @return {Bitmap}
   */
  ImageManager.requestThumbnail = function (savefileId, info, hue) {
    const cacheKey = generateThumbUniqueKey(savefileId);
    if (info && info.thumbnail && cacheKey) {
      return this.requestBase64Bitmap(info.thumbnail, cacheKey, hue);
    }
    return this.loadEmptyBitmap();
  };

  /**
   * セーブ/ロード画面用のサムネイル読み込み中のビットマップを返します.
   *
   * @param {number} width
   * @param {number} height
   * @return {Bitmap}
   */
  ImageManager.loadBusyThumbBitmap = function (width, height) {
    const cacheKey = "busyThumb:" + width + "_" + height;
    let empty = this._imageCache.get(cacheKey);
    if (!empty) {
      empty = new Bitmap(width, height);
      empty.fillAll("#000000");
      this._imageCache.add(cacheKey, empty);
    }
    return empty;
  };

  /**
   * サムネイルの自動撮影を行うか判定.
   *
   * @return {boolean}
   */
  SceneManager.isAutoSnapForThumbnail = function () {
    if (!isAutoSnap || !$gameSystem.isSaveEnabled()) {
      return false;
    }
    // 現在のシーンがマップ画面以外であれば false.
    if (!this._scene || this._scene.constructor !== Scene_Map) {
      return false;
    }
    // 次のシーンが指定のいずれかであれば true.
    //const scenes = [Scene_Menu, Scene_Load, Scene_Save];
    const scenes = [Scene_Menu];
    return scenes.some((scene) => this.isNextScene(scene));
  };

  monkeyPatch(SceneManager, "snapForBackground", function ($) {
    return function () {
      $.call(this);
      if (this.isAutoSnapForThumbnail()) {
        this.snapForThumbnail();
      }
    };
  });

  /**
   * マップ画面を指定のサイズのビットマップを保存します.
   */
  SceneManager.snapForThumbnail = function () {
    if (this._scene) {
      const cw = Graphics.width;
      const ch = Graphics.height;
      this._thumbnailBitmap = Bitmap.snap2(
        this._scene,
        thumbSaveWidth,
        thumbSaveHeight,
        cw,
        ch
      );
    }
  };

  /**
   * サムネイルのビットマップを削除します.
   */
  SceneManager.clearThumbnail = function () {
    this._thumbnailBitmap = null;
  };

  /**
   * サムネイル用に保存したビットマップのBase64形式の文字列を返します.
   *
   * @return {string}
   */
  SceneManager.thumbnailBase64 = function () {
    if (this._thumbnailBitmap) {
      return ImageManager.toBase64(this._thumbnailBitmap);
    }
    return "";
  };

  monkeyPatch(DataManager, "makeSavefileInfo", function ($) {
    return function () {
      let info = $.call(this);
      info.thumbnail = SceneManager.thumbnailBase64();
      if (!info.thumbnail) {
        delete info.thumbnail;
      }
      return info;
    };
  });

  monkeyPatch(DataManager, "loadSavefileImages", function ($) {
    return function (info) {
      $.call(this, info);
      if (info.thumbnail) {
        ImageManager.requestThumbnail(info);
      }
    };
  });

  //------------------------------------------------------------------------

  /**
   * Base64形式の文字列データから画像を読み込んで描画する.
   *
   * @param {string} src Base64形式の文字列
   * @param {number|string} cacheKey キャッシュに使用する任意のキー
   * @param {number} x 描画 X 座標
   * @param {number} y 描画 Y 座標
   * @param {number} width 描画する幅
   * @param {number} height 描画する高さ
   * @param {Function} onDrawAfter 描画後の処理
   */
  Window_Base.prototype.drawBase64Data = function (
    src,
    cacheKey,
    x,
    y,
    width = 0,
    height = 0,
    onDrawAfter = null
  ) {
    const bitmap = ImageManager.loadBase64Bitmap(src, cacheKey);
    const lastOpacity = this.contents.paintOpacity;
    if (!bitmap.isReady() && width > 0 && height > 0) {
      this.contents.fillRect(x, y, width, height, "#000000");
    }
    bitmap.addLoadListener(() => {
      this._onLoadBase64Data(
        bitmap,
        x,
        y,
        lastOpacity,
        width,
        height,
        onDrawAfter
      );
    });
  };

  /**
   * drawBase64Data のイベントリスナー関数.
   *
   * @param {Bitmap} bitmap Base64形式から生成されたビットマップ
   * @param {number} x 描画 X 座標
   * @param {number} y 描画 Y 座標
   * @param {number} lastOpacity 描画前の透明度
   * @param {number} width 描画する幅
   * @param {number} height 描画する高さ
   * @param {Function} onDrawAfter 描画後の処理
   */
  Window_Base.prototype._onLoadBase64Data = function (
    bitmap,
    x,
    y,
    lastOpacity,
    width,
    height,
    onDrawAfter = null
  ) {
    const bw = bitmap.width;
    const bh = bitmap.height;
    width = width || bw;
    height = height || bh;
    //const lastOpacity = this.contents.paintOpacity;
    this.contents.paintOpacity = lastOpacity;
    this.contents.blt(bitmap, 0, 0, bw, bh, x, y, width, height);
    if (onDrawAfter) {
      onDrawAfter();
    }
    //this.contents.paintOpacity = lastOpacity;
  };

  /**
   * Base64形式の文字列データから画像を読み込んで描画する.\
   * 読み込みの遅延により描画が遅れると, 高速スクロールさせることで\
   * 描画位置や透明度がずれることがあるのでその対策込みで再定義.
   *
   * @param {string} src Base64形式の文字列
   * @param {number|string} cacheKey キャッシュに使用する任意のキー
   * @param {number} x 描画 X 座標
   * @param {number} y 描画 Y 座標
   * @param {number} width 描画する幅
   * @param {number} height 描画する高さ
   * @param {Function} onDrawAfter 描画後の処理
   */
  Window_Selectable.prototype.drawBase64Data = function (
    src,
    cacheKey,
    x,
    y,
    width = 0,
    height = 0,
    onDrawAfter = null
  ) {
    const bitmap = ImageManager.loadBase64Bitmap(src, cacheKey);
    const lastOpacity = this.contents.paintOpacity;
    const lastTopRow = this.topRow();
    const lastLeftCol = Math.floor(this._scrollX / this.itemWidth());
    if (!bitmap.isReady() && width > 0 && height > 0) {
      this.contents.fillRect(x, y, width, height, "#000000");
    }
    bitmap.addLoadListener(() => {
      if (
        this.topRow() !== lastTopRow ||
        Math.floor(this._scrollX / this.itemWidth()) !== lastLeftCol
      ) {
        return;
      }
      this._onLoadBase64Data(
        bitmap,
        x,
        y,
        lastOpacity,
        width,
        height,
        onDrawAfter
      );
    });
  };

  //------------------------------------------------------------------------

  // Scene_File 上での DataManager.loadGlobalInfo の実行回数を減らすための対応
  if (isFastLoadingData) {
    /** @type {Object[]} */
    let _globalInfo;

    monkeyPatch(DataManager, "loadGlobalInfo", function ($) {
      return function () {
        return _globalInfo ? _globalInfo : $.call(this);
      };
    });

    monkeyPatch(Scene_File.prototype, "initialize", function ($) {
      return function () {
        _globalInfo = DataManager.loadGlobalInfo();
        $.call(this);
      };
    });

    monkeyPatch(Scene_File.prototype, "terminate", function ($) {
      return function () {
        $.call(this);
        _globalInfo = null;
      };
    });
  }

  //------------------------------------------------------------------------

  // リストウィンドウ内に, サムネイル表示を行う処理を追加します
  if (isShowInList) {
    Window_SavefileList.prototype.thumbnailX = eval(
      "(function(rect, width) { return %1; });".format(thumbItemPosX)
    );
    Window_SavefileList.prototype.thumbnailY = eval(
      "(function(rect, height) { return %1; });".format(thumbItemPosY)
    );

    monkeyPatch(Window_SavefileList.prototype, "initialize", function ($) {
      return function (x, y, width, height) {
        $.call(this, x, y, width, height);
        this.createThumbnail();
      };
    });

    Window_SavefileList.prototype.createThumbnail = function () {
      const contentsIndex = this.children.indexOf(this._windowContentsSprite);
      this._thumbContainer = new PIXI.Container();
      this.addChildAt(this._thumbContainer, contentsIndex);
      let thumb;
      let maxVisibleRows = this.maxItems();
      //let maxVisibleRows = this.maxVisibleItems();
      for (let i = 0, l = maxVisibleRows; i < l; i++) {
        thumb = new Sprite();
        thumb.scale.x = thumbItemScale;
        thumb.scale.y = thumbItemScale;
        thumb.bitmap = null;
        thumb.visible = false;
        this._thumbContainer.addChild(thumb);
      }
      this.refreshThumbnailParts();
    };

    monkeyPatch(
      Window_SavefileList.prototype,
      "_refreshContents",
      function ($) {
        return function () {
          $.call(this);
          this.refreshThumbnailParts();
        };
      }
    );

    Window_SavefileList.prototype.refreshThumbnailParts = function () {
      if (this._thumbContainer) {
        this._thumbContainer.x = this.padding;
        this._thumbContainer.y = this.padding;
      }
    };

    Window_SavefileList.prototype.bottomIndex = function () {
      return this.topIndex() + this.maxPageItems() - 1;
    };

    monkeyPatch(Window_SavefileList.prototype, "refresh", function ($) {
      return function () {
        this.clearThumbnail();
        $.call(this);
      };
    });

    Window_SavefileList.prototype.clearThumbnail = function () {
      const thumbs = this._thumbContainer.children;
      let thumb;
      for (let i = 0, l = thumbs.length; i < l; i++) {
        thumb = thumbs[i];
        thumb.bitmap = null;
        thumb.visible = false;
      }
    };

    monkeyPatch(Window_SavefileList.prototype, "drawContents", function ($) {
      return function (info, rect, valid) {
        $.call(this, info, rect, valid);
        let thumbRect = new Rectangle();
        thumbRect.width = Math.floor(thumbSaveWidth * thumbItemScale);
        thumbRect.height = Math.floor(thumbSaveHeight * thumbItemScale);
        thumbRect.x = this.thumbnailX(rect, thumbRect.width);
        thumbRect.y = this.thumbnailY(rect, thumbRect.height);
        this.drawThumbnail(info, thumbRect, valid);
      };
    });

    /**
     * サムネイルを描画する
     *
     * @param {Object} info セーブファイルのインフォデータ
     * @param {Rectangle} thumbRect
     * @param {boolean} valid
     */
    Window_SavefileList.prototype.drawThumbnail = function (
      info,
      thumbRect,
      valid
    ) {
      const savefileId = DataManager.getSavefileId(info);
      const listIndex = savefileId - 1;
      if (savefileId > 0 && info.thumbnail) {
        let sprite = this._thumbContainer.children[listIndex];
        sprite.visible = true;
        sprite.x = thumbRect.x;
        sprite.y = thumbRect.y;
        const thunmbBitmap = ImageManager.loadThumbnail(savefileId, info);
        if (!thunmbBitmap.isReady()) {
          // 読み込み終わるまで別のビットマップを表示
          const empty = ImageManager.loadBusyThumbBitmap(
            thumbRect.width,
            thumbRect.height
          );
          sprite.bitmap = empty;
        }
        thunmbBitmap.addLoadListener(() => {
          // 読み込み終わったときにリスト表示範囲内であれば描画
          if (this.topIndex() <= listIndex && this.bottomIndex() >= listIndex) {
            sprite.visible = true;
            sprite.bitmap = thunmbBitmap;
            sprite.bitmap.paintOpacity = valid
              ? 255
              : this.translucentOpacity();
          } else {
            sprite.visible = false;
          }
        });
      }
    };
  }

  // 任意のウィンドウに, サムネイル表示を行う処理を追加します
  if (otherWindowClass) {
    /** @type {Window_Base} */
    const AnyWindowClass = eval(otherWindowClass);

    if (
      !AnyWindowClass ||
      !AnyWindowClass.prototype ||
      !(AnyWindowClass.prototype instanceof Window_Base)
    ) {
      throw new Error(
        AnyWindowClass + ": This is not a class extended 'Window_Base' class"
      );
    }

    AnyWindowClass.prototype._thumbnailX = eval(
      "(function(rect, width) { return %1; });".format(thumbOtherPosX)
    );
    AnyWindowClass.prototype._thumbnailY = eval(
      "(function(rect, height) { return %1; });".format(thumbOtherPosY)
    );

    monkeyPatch(AnyWindowClass.prototype, "initialize", function ($) {
      return function () {
        $.call(this, ...arguments);
        if (SceneManager._scene instanceof Scene_File) {
          this._createThumbnail();
        }
      };
    });

    AnyWindowClass.prototype._createThumbnail = function () {
      const contentsIndex = this.children.indexOf(this._windowContentsSprite);
      this._thumbContainer = new PIXI.Container();
      this.addChildAt(this._thumbContainer, contentsIndex);
      this._thumbSprite = new Sprite();
      this._thumbSprite.scale.x = thumbItemScale;
      this._thumbSprite.scale.y = thumbItemScale;
      this._thumbSprite.bitmap = null;
      this._thumbSprite.visible = false;
      this._thumbContainer.addChild(this._thumbSprite);
      // contents からはみ出ないためのマスクをつける
      this._maskGraphic = new PIXI.Graphics();
      this._thumbContainer.addChild(this._maskGraphic);
      this._thumbContainer.mask = this._maskGraphic;
      this._refreshThumbnailParts();
    };

    monkeyPatch(AnyWindowClass.prototype, "_refreshContents", function ($) {
      return function () {
        $.call(this);
        this._refreshThumbnailParts();
      };
    });

    AnyWindowClass.prototype._refreshThumbnailParts = function () {
      if (this._thumbContainer) {
        this._thumbContainer.x = this.padding;
        this._thumbContainer.y = this.padding;
      }
      if (this._maskGraphic) {
        this._maskGraphic.clear();
        this._maskGraphic.beginFill("#000000");
        this._maskGraphic.drawRect(
          0,
          0,
          this.contentsWidth(),
          this.contentsHeight()
        );
        this._maskGraphic.endFill();
      }
    };

    monkeyPatch(AnyWindowClass.prototype, "update", function ($) {
      return function () {
        if ($) {
          $.call(this);
        }
        if (SceneManager._scene instanceof Scene_File) {
          this._updateThumbnail();
        }
      };
    });

    AnyWindowClass.prototype._updateThumbnail = function () {
      /** @type {Window_SavefileList} */
      const list = SceneManager._scene._listWindow;
      if (list) {
        const savefileId = list.index() + 1;
        if (savefileId !== this._savefileId) {
          this._savefileId = savefileId;
          const rect = new Rectangle(
            0,
            0,
            this.contentsWidth(),
            this.contentsHeight()
          );
          let thumbRect = new Rectangle();
          thumbRect.width = Math.floor(thumbSaveWidth * thumbOtherScale);
          thumbRect.height = Math.floor(thumbSaveHeight * thumbOtherScale);
          thumbRect.x = this._thumbnailX(rect, thumbRect.width);
          thumbRect.y = this._thumbnailY(rect, thumbRect.height);
          this._drawThumbnail(thumbRect);
        }
      }
    };

    AnyWindowClass.prototype._drawThumbnail = function (thumbRect) {
      const savefileId = this._savefileId;
      const info = DataManager.loadSavefileInfo(savefileId);
      if (this._savefileId > 0 && info && info.thumbnail) {
        const valid = DataManager.isThisGameFile(savefileId);
        this._thumbSprite.visible = true;
        this._thumbSprite.x = thumbRect.x;
        this._thumbSprite.y = thumbRect.y;
        const thunmbBitmap = ImageManager.loadThumbnail(savefileId, info);
        if (!thunmbBitmap.isReady()) {
          // 読み込み終わるまで別のビットマップを表示
          const empty = ImageManager.loadBusyThumbBitmap(
            thumbRect.width,
            thumbRect.height
          );
          this._thumbSprite.bitmap = empty;
        }
        thunmbBitmap.addLoadListener(() => {
          // 読み込み終わって位置が変わっていないなら描画
          if (savefileId === this._savefileId) {
            this._thumbSprite.bitmap = thunmbBitmap;
            this._thumbSprite.bitmap.paintOpacity = valid
              ? 255
              : this.translucentOpacity();
          }
        });
      } else {
        this._thumbSprite.visible = false;
        this._thumbSprite.bitmap = null;
      }
    };
  }

  //------------------------------------------------------------------------

  // community-1.3 の オートセーブ 対応
  if (DataManager.autoSaveGame) {
    monkeyPatch(DataManager, "autoSaveGame", function ($) {
      return function () {
        if (
          this._autoSaveFileId !== 0 &&
          !this.isEventTest() &&
          $gameSystem.isSaveEnabled()
        ) {
          SceneManager.clearThumbnail();
        }
        $.call(this);
      };
    });
  }
})();

repo_name: Idkwtcm/violated

license: unknown


code:
//=============================================================================
// ダッシュ禁止プラグイン
// dashBan.js
// Copyright (c) 2018 村人Ａ
//=============================================================================

/*:ja
 * @plugindesc ダッシュを禁止するプラグインです
 * @author 村人A
 *
 * @help
 *　=======================================
 *
 *　バージョン管理
 *　2019/3/7 バージョン１.01 リリース
 *　         セーブ・ロード後もダッシュ禁止・許可が反映されているようにしました。
 *　2018/1/2 バージョン１.00 リリース
 *
 *　=======================================
 *
 * プレイヤーのダッシュをコマンドで禁止するプラグインです
 * ダッシュが許可されているマップに移動してもダッシュ禁止は
 * 継続されます。
 *
 * プラグインコマンド:
 *   ダッシュ禁止    # ダッシュを禁止
 *   ダッシュ許可    # ダッシュを許可
 */

(function () {
  villaA_dashBan = false;

  var _Game_Interpreter_pluginCommand =
    Game_Interpreter.prototype.pluginCommand;
  Game_Interpreter.prototype.pluginCommand = function (command, args) {
    _Game_Interpreter_pluginCommand.call(this, command, args);
    if (command === "ダッシュ禁止") {
      $gamePlayer.villaA_dashBan = true;
    }

    if (command === "ダッシュ許可") {
      $gamePlayer.villaA_dashBan = false;
    }
  };

  Game_Player.prototype.isDashing = function () {
    if ($gamePlayer.villaA_dashBan) {
      return false;
    } else {
      return this._dashing;
    }
  };
})();