code:
// DarkPlasma_ShopStock
// Copyright (c) 2019 DarkPlasma
// This software is released under the MIT license.
// http://opensource.org/licenses/mit-license.php

/**
 * 2020/05/19 2.2.2 DarkPlasma_ShopBuyByCategory.js と併用するとエラーになる不具合を修正
 *            2.2.1 売り切れ商品が購入できる不具合を修正
 *                  ニューゲーム開始時に前回の在庫数を引き継ぐ不具合を修正
 * 2020/05/05 2.2.0 売り切れ商品を末尾に表示するか選択する設定を追加
 *                  商品の順番が環境によって元と変化してしまう不具合を修正
 * 2020/04/23 2.1.0 同一リスト内で同一アイテムIDに対して異なる在庫数を設定可能
 *                  アイテム売却時に在庫に追加する設定項目を追加
 * 2020/04/21 2.0.3 デフォルト在庫リストIDが有効でなかった不具合を修正
 *            2.0.2 add english help
 * 2020/04/13 2.0.1 セーブデータをロードした時にエラーになる不具合を修正
 * 2020/04/11 2.0.0 大規模リファクタ/機能追加。1.0.0からのセーブデータ互換性なし
 *                  戦闘回数や経過時間による在庫補充機能追加
 * 2019/09/23 1.0.0 公開
 */
/*:
 * @plugindesc Shop with stock plugin
 * @author DarkPlasma
 * @license MIT
 *
 * @param stockIdVariable
 * @desc use stock with ID setting by this variable.
 * @text Variable for stock ID
 * @type variable
 * @default 0
 *
 * @param stockNumberLabel
 * @desc label for stock count
 * @text stock count label
 * @type string
 * @default stock
 *
 * @param soldOutLabel
 * @desc label for sold out
 * @text sold out label
 * @type string
 * @default sold out
 *
 * @param shopStock
 * @desc shop stock setting
 * @text shop stock setting
 * @type struct<ShopStockEn>[]
 * @default []
 *
 * @param Supply Stock When Sell Item
 * @desc supply stock when sell the item
 * @text supply stock when sell
 * @type boolean
 * @default false
 *
 * @param Sold Out Item At Bottom
 * @desc Display sold item at bottom of shop
 * @text sold out item at bottom
 * @type boolean
 * @default true
 *
 * @help
 * You can set shop item stock.
 * Default stock ID is 1.
 * If you don't set stock count of item,
 * the item stock is infinite.
 *
 * With this plugin, RMMV savedata includes stock data.
 *
 * You can set different stock count between same item id in same stock setting.
 * e.g.) One potion is 30 stocked, another is 10.
 * If you set stock count in this order and set multi same items to shop goods,
 * shop display them in the same order.
 * (Stock count of potion displayed upper is 30, lower is 10.)
 * Please note that if you set "supply stock when sell" is true,
 * and sell item that is multi displayed in shop,
 * the only top item of the same id is supplied.
 *
 * If you set different price between same item id in the same shop,
 * please consider that you use DarkPlasma_AnotherPriceInSameShop.js.
 * Then, please add this plugin lower position than it.
 */
/*~struct~ShopStockEn:
 *
 * @param id
 * @desc stock ID
 * @text stock ID
 * @default 1
 * @type number
 *
 * @param stockItemList
 * @desc list of stock item
 * @text stock item list
 * @default []
 * @type struct<StockItemEn>[]
 *
 * @param stockWeaponList
 * @desc list of stock weapon
 * @text stock weapon list
 * @default []
 * @type struct<StockWeaponEn>[]
 *
 * @param stockArmorList
 * @desc list of stock armor
 * @text stock armor list
 * @default []
 * @type struct<StockArmorEn>[]
 *
 * @param defaultSupplyType
 * @desc default supply method
 * @text default supply type
 * @type select
 * @option no supplying
 * @value 0
 * @option battle count
 * @value 1
 * @option play time
 * @value 2
 * @default 0
 *
 * @param defaultSupplyCount
 * @desc default supply count by one time
 * @text default supply count
 * @type number
 * @default 1
 *
 * @param defaultSupplyFrequency
 * @desc default supply frequency(battle count or play time(seconds))
 * @text default supply frequency
 * @type number
 * @default 5
 */
/*~struct~StockItemEn:
 *
 * @param itemId
 * @desc item
 * @text item
 * @default 0
 * @type item
 *
 * @param stockNum
 * @desc default stock count
 * @text default stock count
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc use own supply setting
 * @text use own supply setting
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc own supply type (enable when use own supply setting is true.)
 * @text own supply type
 * @type select
 * @option no supplying
 * @value 0
 * @option battle count
 * @value 1
 * @option play time
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc own supply count by one time (enable when use own supply setting is true.)
 * @text own supply count
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc own supply frequency(battle count or play time(seconds)) (enable when use own supply setting is true.)
 * @text own supply frequency
 * @type number
 * @default 5
 */
/*~struct~StockWeaponEn:
 *
 * @param weaponId
 * @desc weapon
 * @text weapon
 * @default 0
 * @type weapon
 *
 * @param stockNum
 * @desc default stock count
 * @text default stock count
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc use own supply setting
 * @text use own supply setting
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc own supply type (enable when use own supply setting is true.)
 * @text own supply type
 * @type select
 * @option no supplying
 * @value 0
 * @option battle count
 * @value 1
 * @option play time
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc own supply count by one time (enable when use own supply setting is true.)
 * @text own supply count
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc own supply frequency(battle count or play time(seconds)) (enable when use own supply setting is true.)
 * @text own supply frequency
 * @type number
 * @default 5
 */
/*~struct~StockArmorEn:
 *
 * @param armorId
 * @desc armor
 * @text armor
 * @default 0
 * @type armor
 *
 * @param stockNum
 * @desc default stock count
 * @text default stock count
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc use own supply setting
 * @text use own supply setting
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc own supply type (enable when use own supply setting is true.)
 * @text own supply type
 * @type select
 * @option no supplying
 * @value 0
 * @option battle count
 * @value 1
 * @option play time
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc own supply count by one time (enable when use own supply setting is true.)
 * @text own supply count
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc own supply frequency(battle count or play time(seconds)) (enable when use own supply setting is true.)
 * @text own supply frequency
 * @type number
 * @default 5
 */
/*:ja
 * @plugindesc 在庫ありショッププラグイン
 * @author DarkPlasma
 * @license MIT
 *
 * @param stockIdVariable
 * @desc ここで指定された変数と一致する在庫リストIDを使用します
 * @text 在庫リストID指定用変数
 * @type variable
 * @default 0
 *
 * @param stockNumberLabel
 * @desc 在庫数の表記を設定します
 * @text 在庫数表記
 * @type string
 * @default 在庫数
 *
 * @param soldOutLabel
 * @desc 売り切れの表記を設定します
 * @text 売り切れ表記
 * @type string
 * @default 売り切れ
 *
 * @param shopStock
 * @desc ショップの初期在庫を設定します
 * @text ショップ在庫設定
 * @type struct<ShopStock>[]
 * @default []
 *
 * @param Supply Stock When Sell Item
 * @desc 売却時にそのアイテムの在庫を追加する
 * @text 売却時在庫補充
 * @type boolean
 * @default false
 *
 * @param Sold Out Item At Bottom
 * @desc 売り切れ商品をショップの一番下に表示する
 * @text 売り切れを後ろに
 * @type boolean
 * @default true
 *
 * @help
 * このプラグインはショップに初期在庫を設定できます。
 * 在庫リストID:1のリストをデフォルトの在庫リストとして扱います。
 * 在庫リストで指定されなかったアイテムの在庫は無限です。
 *
 * 在庫はセーブデータに記録されます。
 *
 * 同じ在庫リストの中で、同一アイテムに対して異なる在庫数を設定できます。
 * 例えば、片方のポーションは在庫30あるが、もう片方は10しかない、等。
 * 在庫リストの中で設定した順番が上記の通りである場合、
 * ショップ内の順番も同様です。
 * （上に設定したポーションの在庫が30、下に設定したポーションの在庫が10になります）
 * 売却時の補充は同一アイテムのうち、一番上のアイテムにしか効かないのでご注意ください。
 *
 * 同一ショップ内の同一アイテムに別価格を設定したい場合、
 * DarkPlasma_AnotherPriceInSameShop.js との併用をご検討ください。
 * その場合、 DarkPlasma_AnotherPriceInSameShop.js よりも
 * このプラグインを下に読み込んでください。
 */
/*~struct~ShopStock:
 *
 * @param id
 * @desc 在庫リストID
 * @text 在庫リストID
 * @default 1
 * @type number
 *
 * @param stockItemList
 * @desc 在庫アイテムリスト
 * @text 在庫アイテムリスト
 * @default []
 * @type struct<StockItem>[]
 *
 * @param stockWeaponList
 * @desc 在庫武器リスト
 * @text 在庫武器リスト
 * @default []
 * @type struct<StockWeapon>[]
 *
 * @param stockArmorList
 * @desc 在庫防具リスト
 * @text 在庫防具リスト
 * @default []
 * @type struct<StockArmor>[]
 *
 * @param defaultSupplyType
 * @desc 在庫の補充形式
 * @text 在庫補充形式
 * @type select
 * @option 補充なし
 * @value 0
 * @option 戦闘回数
 * @value 1
 * @option 時間経過
 * @value 2
 * @default 0
 *
 * @param defaultSupplyCount
 * @desc 1度の補充ごとに補充される在庫数
 * @text 補充数
 * @type number
 * @default 1
 *
 * @param defaultSupplyFrequency
 * @desc 補充間隔（戦闘回数 or 経過時間（秒））
 * @text 補充間隔
 * @type number
 * @default 5
 */
/*~struct~StockItem:
 *
 * @param itemId
 * @desc アイテム
 * @text アイテム
 * @default 0
 * @type item
 *
 * @param stockNum
 * @desc 初期在庫数
 * @text 初期在庫数
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc アイテムごとの補充設定を利用するか
 * @text 独自補充設定
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc 在庫の補充形式（独自補充設定がONの場合のみ有効）
 * @text 在庫補充形式
 * @type select
 * @option 補充なし
 * @value 0
 * @option 戦闘回数
 * @value 1
 * @option 時間経過
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc 1度の補充ごとに補充される在庫数（独自補充設定がONの場合のみ有効）
 * @text 補充数
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc 補充間隔（戦闘回数 or 経過時間（秒））（独自補充設定がONの場合のみ有効）
 * @text 補充間隔
 * @type number
 * @default 5
 */
/*~struct~StockWeapon:
 *
 * @param weaponId
 * @desc 武器
 * @text 武器
 * @default 0
 * @type weapon
 *
 * @param stockNum
 * @desc 初期在庫数
 * @text 初期在庫数
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc アイテムごとの補充設定を利用するか
 * @text 独自補充設定
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc 在庫の補充形式（独自補充設定がONの場合のみ有効）
 * @text 在庫補充形式
 * @type select
 * @option 補充なし
 * @value 0
 * @option 戦闘回数
 * @value 1
 * @option 時間経過
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc 1度の補充ごとに補充される在庫数（独自補充設定がONの場合のみ有効）
 * @text 補充数
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc 補充間隔（戦闘回数 or 経過時間（秒））（独自補充設定がONの場合のみ有効）
 * @text 補充間隔
 * @type number
 * @default 5
 */
/*~struct~StockArmor:
 *
 * @param armorId
 * @desc 防具
 * @text 防具
 * @default 0
 * @type armor
 *
 * @param stockNum
 * @desc 初期在庫数
 * @text 初期在庫数
 * @default 1
 * @type number
 *
 * @param useOwnSupplySetting
 * @desc アイテムごとの補充設定を利用するか
 * @text 独自補充設定
 * @type boolean
 * @default false
 *
 * @param supplyType
 * @desc 在庫の補充形式（独自補充設定がONの場合のみ有効）
 * @text 在庫補充形式
 * @type select
 * @option 補充なし
 * @value 0
 * @option 戦闘回数
 * @value 1
 * @option 時間経過
 * @value 2
 * @default 0
 *
 * @param supplyCount
 * @desc 1度の補充ごとに補充される在庫数（独自補充設定がONの場合のみ有効）
 * @text 補充数
 * @type number
 * @default 1
 *
 * @param supplyFrequency
 * @desc 補充間隔（戦闘回数 or 経過時間（秒））（独自補充設定がONの場合のみ有効）
 * @text 補充間隔
 * @type number
 * @default 5
 */

(function () {
  'use strict';
  const pluginName = document.currentScript.src.replace(/^.*\/(.*).js$/, function () {
    return arguments[1];
  });
  const pluginParameters = PluginManager.parameters(pluginName);

  const SUPPLY_TYPE = {
    NONE: 0,
    BATTLE_COUNT: 1,
    PLAY_TIME: 2
  };

  const ITEM_KIND = {
    ITEM: 1,
    WEAPON: 2,
    ARMOR: 3,
  };

  const DEFAULT_STOCK_ID = 1;

  const settings = {
    stockIdVariable: Number(pluginParameters['stockIdVariable'] || 0),
    stockNumberLabel: String(pluginParameters['stockNumberLabel'] || '在庫数'),
    soldOutLabel: String(pluginParameters['soldOutLabel'] || '売り切れ'),
    supplyWhenSell: String(pluginParameters['Supply Stock When Sell Item'] || 'false') === 'true',
    soldOutAtBottom: String(pluginParameters['Sold OUt Item At Bottom'] || 'true') === 'true',
    shopStock: JsonEx.parse(pluginParameters['shopStock'] || '[]').map(shopStock => {
      const parsed = JsonEx.parse(shopStock);
      return {
        id: Number(parsed['id'] || 0),
        stockItemList: JsonEx.parse(parsed['stockItemList'] || '[]').map(stockItemList => {
          const parsed = JsonEx.parse(stockItemList);
          return {
            id: Number(parsed['itemId'] || 0),
            stockNum: Number(parsed['stockNum'] || 1),
            useOwnSupplySetting: String(parsed['useOwnSupplySetting'] || 'false') === 'true',
            supplyType: Number(parsed['supplyType'] || 0),
            supplyCount: Number(parsed['supplyCount'] || 1),
            supplyFrequency: Number(parsed['supplyFrequency'] || 5)
          };
        }),
        stockWeaponList: JsonEx.parse(parsed['stockWeaponList'] || '[]').map(stockItemList => {
          const parsed = JsonEx.parse(stockItemList);
          return {
            id: Number(parsed['weaponId'] || 0),
            stockNum: Number(parsed['stockNum'] || 1),
            useOwnSupplySetting: String(parsed['useOwnSupplySetting'] || 'false') === 'true',
            supplyType: Number(parsed['supplyType'] || 0),
            supplyCount: Number(parsed['supplyCount'] || 1),
            supplyFrequency: Number(parsed['supplyFrequency'] || 5)
          };
        }),
        stockArmorList: JsonEx.parse(parsed['stockArmorList'] || '[]').map(stockItemList => {
          const parsed = JsonEx.parse(stockItemList);
          return {
            id: Number(parsed['armorId'] || 0),
            stockNum: Number(parsed['stockNum'] || 1),
            useOwnSupplySetting: String(parsed['useOwnSupplySetting'] || 'false') === 'true',
            supplyType: Number(parsed['supplyType'] || 0),
            supplyCount: Number(parsed['supplyCount'] || 1),
            supplyFrequency: Number(parsed['supplyFrequency'] || 5)
          };
        }),
        defaultSupplyType: Number(parsed['defaultSupplyType'] || 0),
        defaultSupplyCount: Number(parsed['defaultSupplyCount'] || 1),
        defaultSupplyFrequency: Number(parsed['defaultSupplyFrequency'] || 5),
      };
    })
  };

  class ShopStockManager {
    constructor() {
      this.initialize();
    }

    initialize() {
      /**
       * @type {ShopStock[]}
       */
      this._shopStock = settings.shopStock.map(stockSetting => ShopStock.fromSetting(stockSetting));
    }

    /**
     * 現在の在庫リストIDに記録されている在庫数を返す
     * 在庫リストIDが指定されていない、または在庫リストの中にアイテムが存在しない場合nullを返す
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @return {number|null} 現在の在庫数
     */
    stockCount(item, index) {
      const currentStock = this.currentShopStock();
      return currentStock ? currentStock.stockCount(item, index) : null;
    }

    /**
     * 指定したIDの在庫リストを返す
     * 存在しない場合はundefinedを返す
     * @param {number} stockId 在庫リストID
     * @return {ShopStock|undefined}
     */
    shopStock(stockId) {
      return this._shopStock.find(stock => stock.id === stockId);
    }

    /**
     * 現在の在庫リストを返す
     * 現在の在庫リストIDが指し示す在庫リストが存在しない場合、undefinedを返す
     * @return {ShopStock|undefined}
     */
    currentShopStock() {
      const currentStockId = $gameVariables.value(settings.stockIdVariable) || DEFAULT_STOCK_ID;
      return this._shopStock.find(stock => stock.id === currentStockId);
    }

    /**
     * 現在の在庫リストの在庫を補充する
     */
    supplyCurrentShopStock() {
      const shopStock = this.currentShopStock();
      if (shopStock) {
        shopStock.supply();
      }
    }

    /**
     * 現在の在庫リストにおいて、指定したアイテムの在庫数を増やす
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @param {number} count 在庫を増やす数
     */
    increaseCurrentStockCount(item, index, count) {
      const shopStock = this.currentShopStock();
      if (shopStock) {
        shopStock.increaseStockCount(item, index, count);
      }
    }

    /**
     * 現在の在庫リストにおいて、指定したアイテムの在庫数を減らす
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @param {number} count 在庫を減らす数
     */
    decreaseCurrentStockCount(item, index, count) {
      const shopStock = this.currentShopStock();
      if (shopStock) {
        shopStock.decreaseStockCount(item, index, count);
      }
    }

    /**
     * セーブデータから在庫数を更新する
     * @param {object[]} stockSaveData セーブデータ形式の在庫データ
     */
    updateCountBySaveData(stockSaveData) {
      stockSaveData.forEach(saveData => {
        const shopStock = this._shopStock.find(stock => saveData.id === stock.id);
        if (shopStock) {
          shopStock.updateCountBySaveData(saveData.stockItems);
        }
      });
    }

    /**
     * セーブデータ用の形式に変換して取得する
     * 以下のような形式
     * [
     *   {
     *     id: 在庫リストID,
     *     stockItems: [
     *       {
     *         id: アイテムID,
     *         kind: アイテム種別ID,
     *         count: 在庫数,
     *         supplyType: 補充形式,
     *         counterForSupply: 補充用カウンター
     *       },
     *       ....
     *     ]
     *   },
     *   ....
     * ]
     * @return {object[]} セーブデータ用の形式
     */
    toSaveData() {
      return this._shopStock.map(stock => stock.toSaveData());
    }
  }

  /**
   * 在庫リスト
   */
  class ShopStock {
    /**
     * @param {number} id 在庫リストID
     * @param {StockItem} stockItems 在庫アイテムリスト
     */
    constructor(id, stockItems) {
      this._id = id;
      this._stockItems = stockItems;
    }

    /**
     * @param {object} setting 設定
     * @return {ShopStock}
     */
    static fromSetting(setting) {
      return new ShopStock(
        setting.id,
        setting.stockItemList.map(stockItem => {
          return StockItem.fromSetting(
            stockItem,
            ITEM_KIND.ITEM,
            stockItem.useOwnSupplySetting ? stockItem.supplyType : setting.defaultSupplyType,
            stockItem.useOwnSupplySetting ? stockItem.supplyCount : setting.defaultSupplyCount,
            stockItem.useOwnSupplySetting ? stockItem.supplyFrequency : setting.defaultSupplyFrequency
          );
        }).concat(setting.stockWeaponList.map(stockWeapon => {
          return StockItem.fromSetting(
            stockWeapon,
            ITEM_KIND.WEAPON,
            stockWeapon.useOwnSupplySetting ? stockWeapon.supplyType : setting.defaultSupplyType,
            stockWeapon.useOwnSupplySetting ? stockWeapon.supplyCount : setting.defaultSupplyCount,
            stockWeapon.useOwnSupplySetting ? stockWeapon.supplyFrequency : setting.defaultSupplyFrequency
          );
        })).concat(setting.stockArmorList.map(stockArmor => {
          return StockItem.fromSetting(
            stockArmor,
            ITEM_KIND.ARMOR,
            stockArmor.useOwnSupplySetting ? stockArmor.supplyType : setting.defaultSupplyType,
            stockArmor.useOwnSupplySetting ? stockArmor.supplyCount : setting.defaultSupplyCount,
            stockArmor.useOwnSupplySetting ? stockArmor.supplyFrequency : setting.defaultSupplyFrequency
          );
        }))
      );
    }

    get id() {
      return this._id;
    }

    /**
     * @param {object} stockItemSaveData セーブデータから取得した在庫情報
     */
    updateCountBySaveData(stockItemSaveData) {
      let indexes = {};
      stockItemSaveData.forEach(object => {
        const items = this._stockItems.filter(item => item.kind === object.kind && item.id === object.id);
        if (!indexes[object.id]) {
          indexes[object.id] = 0;
        }
        const item = items[indexes[object.id]++];
        if (item) {
          item.setCount(object.count);
          if (item.supplyCount !== object.supplyType) { // 補充形式設定が変更されていたらリセットする
            item.resetSupplyCounter();
          } else {
            item.setSupplyCounter(object.counterForSupply);
          }
        }
      });
    }

    /**
     * 在庫を補充する
     */
    supply() {
      this._stockItems.forEach(stock => stock.supply());
    }

    /**
     * 指定したアイテムの在庫情報を取得する
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @return {StockItem}
     */
    getStockItem(item, index) {
      let result = [];
      if (DataManager.isItem(item)) {
        result = this._stockItems.filter(stockItem => stockItem.kind === ITEM_KIND.ITEM && stockItem.id === item.id);
      } else if (DataManager.isWeapon(item)) {
        result = this._stockItems.filter(stockItem => stockItem.kind === ITEM_KIND.WEAPON && stockItem.id === item.id);
      } else if (DataManager.isArmor(item)) {
        result = this._stockItems.filter(stockItem => stockItem.kind === ITEM_KIND.ARMOR && stockItem.id === item.id);
      }
      return result.length > index ? result[index] : null;
    }

    /**
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @return {number|null} 在庫数
     */
    stockCount(item, index) {
      const stockItem = this.getStockItem(item, index);
      return stockItem ? stockItem.count : null;
    }

    /**
     * 指定したアイテムの在庫数を増やす
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @param {number} count 在庫を増やす数
     */
    increaseStockCount(item, index, count) {
      const stockItem = this.getStockItem(item, index);
      if (stockItem) {
        stockItem.increaseCount(count);
      }
    }

    /**
     * 指定したアイテムの在庫数を減らす
     * @param {RPG.Item|RPG.Weapon|RPG.Armor} item アイテムデータ
     * @param {number} index リスト中のアイテム番号
     * @param {number} count 在庫を減らす数
     */
    decreaseStockCount(item, index, count) {
      const stockItem = this.getStockItem(item, index);
      if (stockItem) {
        stockItem.decreaseCount(count);
      }
    }

    /**
     * @return {object}
     */
    toSaveData() {
      return {
        id: this._id,
        stockItems: this._stockItems.map(stockItem => stockItem.toSaveData())
      };
    }
  }

  class StockItem {
    /**
     * @param {number} kind アイテム種別
     * @param {number} id アイテムIDを
     * @param {number} initialCount 初期在庫
     * @param {number} supplyType 補充形式
     * @param {number} supplyCount 補充数
     * @param {number} supplyFrequency 補充間隔
     */
    constructor(kind, id, initialCount, supplyType, supplyCount, supplyFrequency) {
      this._kind = kind;
      this._id = id;
      this._initialCount = initialCount;
      this._count = initialCount;
      this._supplyType = supplyType;
      this._supplyCount = supplyCount;
      this._supplyFrequency = supplyFrequency;
      this._counterForSupply = 0;
    }

    /**
     * @param {object} setting 設定
     * @param {number} kind 種別
     * @param {number} defaultSupplyType 補充形式
     * @param {number} defaultSupplyCount 補充数
     * @param {number} defaultSupplyFrequency 補充間隔
     * @return {StockItem}
     */
    static fromSetting(setting, kind, defaultSupplyType, defaultSupplyCount, defaultSupplyFrequency) {
      return new StockItem(
        kind,
        setting.id,
        setting.stockNum,
        defaultSupplyType,
        defaultSupplyCount,
        defaultSupplyFrequency
      );
    }

    /**
     * @return {object}
     */
    toSaveData() {
      return {
        kind: this._kind,
        id: this._id,
        count: this._count,
        supplyType: this._supplyType,
        counterForSupply: this._counterForSupply
      };
    }

    /**
     * @return {RPG.Item|RPG.Weapon|RPG.Armor|null}
     */
    get data() {
      switch (this._kind) {
        case ITEM_KIND.ITEM:
          return $dataItems[this._id];
        case ITEM_KIND.WEAPON:
          return $dataWeapons[this._id];
        case ITEM_KIND.ARMOR:
          return $dataArmors[this._id];
        default:
          return null;
      }
    }

    /**
     * @return {number}
     */
    get kind() {
      return this._kind;
    }

    /**
     * @return {number}
     */
    get id() {
      return this._id;
    }

    /**
     * @return {number}
     */
    get count() {
      return this._count;
    }

    /**
     * @return {number}
     */
    get supplyType() {
      return this._supplyType;
    }

    /**
     * 在庫を増やす
     * @param {number} count 在庫を増やす数
     */
    increaseCount(count) {
      this.setCount(this.count + count);
    }

    /**
     * 在庫を減らす
     * @param {number} count 在庫を減らす数
     */
    decreaseCount(count) {
      if (this.count === this.maxCount() && count > 0) {

      }
      this.setCount(this.count - count);
    }

    /**
     * 在庫数を設定する
     * @param {number} count 在庫数
     */
    setCount(count) {
      this._count = count;
      if (this._count > this.maxCount()) {
        this._count = this.maxCount();
      } else if (this._count < 0) {
        this._count = 0;
      }
    }

    /**
     * 在庫を補充する
     */
    supply() {
      if (this._supplyType === SUPPLY_TYPE.NONE || this._supplyFrequency <= 0 || this._count >= this.maxCount()) {
        return;
      }
      let supplyTimes = 0;
      switch (this._supplyType) {
        case SUPPLY_TYPE.BATTLE_COUNT:
          supplyTimes = Math.floor(($gameSystem.battleCount() - this._counterForSupply)/this._supplyFrequency);
          break;
        case SUPPLY_TYPE.PLAY_TIME:
          supplyTimes = Math.floor(($gameSystem.playtime() - this._counterForSupply)/this._supplyFrequency);
          break;
        default:
          return;
      }
      for (let i = 0; i < supplyTimes; i++) {
        this.increaseCount(this._supplyCount);
      }
      // 補充用カウンターの更新
      this._counterForSupply += supplyTimes * this._supplyFrequency;
    }

    setSupplyCounter(counter) {
      this._counterForSupply = counter;
    }

    /**
     * 補充用カウンターをリセットする
     */
    resetSupplyCounter() {
      switch (this._supplyType) {
        case SUPPLY_TYPE.BATTLE_COUNT:
          this._counterForSupply = $gameSystem.battleCount();
          break;
        case SUPPLY_TYPE.PLAY_TIME:
          this._counterForSupply = $gameSystem.playtime();
          break;
      }
    }

    /**
     * @return {number} 最大在庫数
     */
    maxCount() {
      // 将来的に最大数を別に設定できるようにしても良い
      return this._initialCount;
    }
  }

  /**
   * @type {ShopStockManager}
   */
  const shopStockManager = new ShopStockManager();

  const _Game_System_initialize = Game_System.prototype.initialize;
  Game_System.prototype.initialize = function() {
    _Game_System_initialize.call(this);
    /**
     * ゲーム開始時に初期化する
     */
    shopStockManager.initialize();
  };

  const _Game_System_onBeforeSave = Game_System.prototype.onBeforeSave;
  Game_System.prototype.onBeforeSave = function () {
    _Game_System_onBeforeSave.call(this);
    this._shopStock = shopStockManager.toSaveData();
  };

  const _Game_System_onAfterLoad = Game_System.prototype.onAfterLoad;
  Game_System.prototype.onAfterLoad = function () {
    _Game_System_onAfterLoad.call(this);
    if (this._shopStock && Array.isArray(this._shopStock)) {
      shopStockManager.updateCountBySaveData(this._shopStock);
    } else {
      /**
       * セーブデータになければとりあえず初期化する
       */
      shopStockManager.initialize();
    }
  };

  const _Scene_Shop_create = Scene_Shop.prototype.create;
  Scene_Shop.prototype.create = function (){
    _Scene_Shop_create.call(this);
    // ショップを開いたタイミングで補充する
    shopStockManager.supplyCurrentShopStock();
  };

  const _SceneShop_doBuy = Scene_Shop.prototype.doBuy;
  Scene_Shop.prototype.doBuy = function (number) {
    _SceneShop_doBuy.call(this, number);
    shopStockManager.decreaseCurrentStockCount(this._item, this._buyWindow.indexOfSameItem(), number);
  };

  const _SceneShop_doSell = Scene_Shop.prototype.doSell;
  Scene_Shop.prototype.doSell = function (number) {
    _SceneShop_doSell.call(this, number);
    if (settings.supplyWhenSell) {
      shopStockManager.increaseCurrentStockCount(this._item, 0, number);
    }
  };

  const _SceneShop_maxBuy = Scene_Shop.prototype.maxBuy;
  Scene_Shop.prototype.maxBuy = function () {
    const currentStock = shopStockManager.stockCount(this._item, this._buyWindow.indexOfSameItem());
    return currentStock > 0 ? Math.min(
      currentStock,
      _SceneShop_maxBuy.call(this)
    ) : _SceneShop_maxBuy.call(this);
  };

  const _WindowShopStatus_refresh = Window_ShopStatus.prototype.refresh;
  Window_ShopStatus.prototype.refresh = function () {
    _WindowShopStatus_refresh.call(this);
    // 在庫数表記
    const currentStock = this.stockCount();
    if (currentStock || currentStock === 0) {
      this.drawStock(this.textPadding(), this.lineHeight());
    }
  };

  Window_ShopStatus.prototype.drawStock = function (x, y) {
    const width = this.contents.width - this.textPadding() - x;
    const stockWidth = this.textWidth('0000');
    this.changeTextColor(this.systemColor());
    this.drawText(settings.stockNumberLabel, x, y, width - stockWidth);
    this.resetTextColor();
    this.drawText(this.stockCount(), x, y, width, 'right');
  };

  Window_ShopStatus.prototype.stockCount = function () {
    return shopStockManager.stockCount(this._item, this.indexOfSameItem());
  };

  Window_ShopStatus.prototype.indexOfSameItem = function () {
    return this._indexOfSameItem ? this._indexOfSameItem : 0;
  };

  Window_ShopStatus.prototype.setIndexOfSameItem = function (index) {
    this._indexOfSameItem = index;
  }

  const _Window_ShopBuy_updateHelp = Window_ShopBuy.prototype.updateHelp;
  Window_ShopBuy.prototype.updateHelp = function() {
    if (this._statusWindow) {
        this._statusWindow.setIndexOfSameItem(this.indexOfSameItem());
    }
    _Window_ShopBuy_updateHelp.call(this);
  };

  /**
   * 指定したアイテムの在庫数を取得する
   * @param {RPG.Item} item アイテムデータ
   * @param {number} index 在庫リストの中での順番
   * @return {number}
   */
  Window_ShopBuy.prototype.stockCount = function (item, index) {
    return shopStockManager.stockCount(item, index);
  };

  /**
   * 指定したアイテムが売り切れであるかどうか
   * @param {RPG.Item} item アイテムデータ
   * @param {number} index 在庫リストの中での順番
   * @return {boolean}
   */
  Window_ShopBuy.prototype.soldOut = function (item, index) {
    return this.stockCount(item, index) === 0;
  };

  const _WindowShopBuy_price = Window_ShopBuy.prototype.price;
  Window_ShopBuy.prototype.price = function (item) {
    return this.soldOut(item, this.indexOfSameItem()) ? settings.soldOutLabel : _WindowShopBuy_price.call(this, item);
  };

  const _WindowShopBuy_isEnabled = Window_ShopBuy.prototype.isEnabled;
  Window_ShopBuy.prototype.isEnabled = function (item) {
    return _WindowShopBuy_isEnabled.call(this, item) &&
      !this.soldOut(item, this.indexOfSameItem());
  };

  const _Window_ShopBuy_drawItem = Window_ShopBuy.prototype.drawItem;
  Window_ShopBuy.prototype.drawItem = function(index) {
    this._indexForDrawing = index;
    this._isDrawing = true;
    _Window_ShopBuy_drawItem.call(this, index);
    this._isDrawing = false;
  };

  /**
   * このショップの同一アイテムで何番目のアイテムを選択しているか
   * アイテムを選択していない場合や、存在しない場合は -1 を返す
   * 描画中は描画対象を選択しているとみなす
   * @return {number}
   */
  Window_ShopBuy.prototype.indexOfSameItem = function () {
    const index = this._isDrawing ? this._indexForDrawing : this.index();
    if (this._extendedData.length <= index || index < 0) {
      return -1;
    }
    return this._extendedData[index].indexOfSameItem;
  };

  /**
   * ソート順を上書き
   */
  Window_ShopBuy.prototype.makeItemList = function () {
    this._data = [];
    this._price = [];
    /**
     * 拡張データ
     */
    this._extendedData = [];
    /**
     * 同名アイテムで何番目か
     */
    let indexesOfSameItem = {};
    this._shopGoods.map((goods, index) => {
      let item = null;
      switch (goods[0]) {
        case 0:
          item = $dataItems[goods[1]];
          break;
        case 1:
          item = $dataWeapons[goods[1]];
          break;
        case 2:
          item = $dataArmors[goods[1]];
          break;
      }
      if (item) {
        /**
         * 売り切れを一番下に表示する
         */
        const key = `${goods[0]}_${item.id}`;
        if (!indexesOfSameItem[key]) {
          indexesOfSameItem[key] = 0;
        }
        return {
          item: item,
          price: goods[2] === 0 ? item.price : goods[3],
          indexOfSameItem: indexesOfSameItem[key],
          soldOut: this.soldOut(item, indexesOfSameItem[key]++),
          goodsIndex: index,
        };
      } else {
        return {};
      }
    }, this).filter(goods => goods.item).sort((a, b) => {
      if (settings.soldOutAtBottom) {
        if (a.soldOut && !b.soldOut) return 1;
        if (!a.soldOUt && b.soldOut) return -1;
      }
      /**
       * Array.prototype.sort が stable になるのは NW.js 0.34.0以降
       * RPGツクールMV1.6系は NW.js 0.29.4 のため、元々のindexで比較してやる
       */
      return a.goodsIndex - b.goodsIndex;
    }).forEach(goods => {
      this._data.push(goods.item);
      this._price.push(goods.price);
      this._extendedData.push({
        indexOfSameItem: goods.indexOfSameItem
      });
    }, this);
  };
})();


code:
/*
DependStateEscape.js

2016/11/10 version 1.0

  利用規約
  ・MITライセンス
  ・商用・非商用問わず使えます。
　・改変公開可能
*/

/*:ja
 * @plugindesc パーティの誰かが特定ステート時にバトル中の逃げるコマンドを封印
 * @author さうと
 *
 *
 * @param stateNo
 * @desc 逃走不可状態にするステートのデータ番号
 * @default 0
 *
 * @help ステート番号を指定
 */

(function () {
var parameters = PluginManager.parameters('DependStateEscape');

//rpg_manager.js
	BattleManager.canEscape = function() {
	var ret = this._canEscape;
	for(i=0;i<$dataActors.length;i++)
	{
		if($gameActors.actor(i)==null)
			continue;

		//パラは文字列なのでint変換する
		if($gameActors.actor(i).isStateAffected(parseInt(parameters['stateNo'])))
		{
			ret = false;
		}
	}
	return ret;
	};

})();

repo_name: onms/sister

license: unknown


code:
//=============================================================================
// DevToolsManage.js
// ----------------------------------------------------------------------------
// (C)2015-2018 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 2.6.0 2018/10/08 戦闘強制敗北、戦闘強制中断の機能を追加。
// 2.5.1 2018/03/25 BGMが演奏されていないときに高速モードを切り替えるとエラーになる問題を修正
// 2.5.0 2018/03/17 最前面に表示しているとき、画面がフォーカスを失うと画面自動で右寄せになる機能を追加
//                  最新のNW.jsで使用できなくなる一部機能を削除
// 2.4.5 2018/03/06 各種ファンクションキーにCtrlおよびAltの同時押し要否の設定を追加しました。
// 2.4.4 2018/03/03 ブレークポイントから再開したときにキー入力が押しっぱなし扱いになってしまう場合があるMVの仕様を考慮。
// 2.4.3 2018/02/18 2.4.1の機能に加えて他のエディタから起動したとき全般で戦闘テストができるようにしました。
// 2.4.2 2018/02/18 ヘルプの記述を追加
// 2.4.1 2018/02/18 ブラウザ起動時に戦闘テストができる機能を追加しました。オプションのbtestは利用者が付与してください。
// 2.4.0 2018/02/15 ブラウザ起動時もテストプレーと判断した場合は一部の機能が使えるようにしました。
// 2.3.4 2017/12/02 リファクタリング
// 2.3.2 2017/11/23 Fix missing menu bar height at SceneManager.setWindowSizeForMenuBar
// 2.3.1 2017/11/11 画面キャプチャ管理プラグインとの連携による修正
// 2.3.0 2017/09/25 競合対策でマップとデータのリロード機能を無効にする設定を追加
//                  最新のNW.jsかつメニューバーを表示しない場合にエラーになる問題を修正
// 2.2.2 2017/09/25 ヘルプに注意事項を追記
// 2.2.1 2017/09/01 スクリプトエラー発生時に警告を出力する機能を追加
// 2.2.0 2017/08/09 パラメータ型指定機能に対応。一部パラメータは再設定が必要です。
//                  起動時に自動で最前面に表示するパラメータを追加。
//                  NW.jsを最新化したときに発生するエラーに対処（ただし一部機能は使用できません）
// 2.1.1 2017/03/10 コミュニティ版でロード時にエンジン名称を出力するよう修正。
// 2.1.0 2016/12/25 スローモーション機能を追加しました。一時的にゲーム速度を最低1/60倍速まで低下できます。
// 2.0.0 2016/12/16 ES2015向けにリファクタリングしました。
//                  JSON形式でのセーブ＆ロードに対応しました。
//                  ゲーム画面にフォーカスを戻した際に、マップとデータベースの再読込を自動で行うようにしました。
//                  「最前面に表示」機能をゲーム中に切り替えられるようにしました。
//                  一部、余分な機能を無効化しました。
// 1.5.1 2016/08/14 コアスクリプトのバージョンを出力するようにしました。
// 1.5.0 2016/06/25 スクリプトの常駐実行機能を追加
// 1.4.2 2016/06/14 SceneManager.initializeでエラーが発生した際にエラー内容を確認できない問題を修正
// 1.4.1 2016/06/14 YEP_CoreEngine.jsと併用したときにウィンドウ高さ補正が効かなくなる問題を修正
// 1.4.0 2016/06/03 メニューバーからデバッグ用のコマンドを実行できる機能を追加
//                  ゲーム画面の更新を停止し、一時的にフリーズする機能を追加
//                  セーブデータのエンコード・デコード機能を追加
// 1.3.0 2016/05/04 スクリプトのその場実行機能を追加
//                  モバイル偽装機能を追加
//                  コードのリファクタリング
// 1.2.2 2016/03/19 ゲームのスピード高速化を8倍速まで対応
// 1.2.1 2016/02/27 タイトルカットしたときにイベントテスト、戦闘テストが正しく開始されない不具合を修正
// 1.2.0 2016/02/24 ゲームのスピードを高速(2倍速)化する機能を追加
//                  戦闘を勝利扱いで即終了する機能を追加
// 1.1.0 2016/01/11 ゲーム開始時にFPS表示（FPS表示/MS表示に対応）を有効にする機能を追加
//                  タイトル画面を飛ばして最新のセーブファイルをロードする機能を追加
// 1.0.2 2016/01/02 繰り返しリセットすると警告が出る問題の解消
//                  ゲームウィンドウを端に寄せる機能(笑)を追加
// 1.0.1 2015/12/19 F12キーでリセットする機能を追加（F5と同様の動作）
// 1.0.0 2015/12/12 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc Developer tools management plugin
 * @author triacontane
 *
 * @param StartupDevTool
 * @desc It will start the developer tools at the start of the game.
 * @default true
 * @type boolean
 *
 * @param FuncKeyReload
 * @desc 画面のリロードを行うキーです(F1～F12)。デフォルトF5キーと同様の役割を持ちます。
 * @default F12
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyOnTop
 * @desc ゲーム画面を最前面に表示するキーです。(F1～F12)。
 * @default
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyRapidGame
 * @desc ゲームを高速化するキーです。(F1～F12)。
 * @default F10
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyForceVictory
 * @desc 戦闘を勝利扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyForceDefeat
 * @desc 戦闘を敗北扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyForceAbort
 * @desc 戦闘を中断扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyScript
 * @desc スクリプト実行用のウィンドウをポップアップするキーです。(F1～F12)。
 * @default F6
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param FuncKeyFreeze
 * @desc 画面の更新を一時停止するキーです。(F1～F12)。
 * @default
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param SimultaneousCtrl
 * @desc 各機能を利用する際にCtrlキーの同時押しが必要かどうかです。他のプラグインと対象キーが競合する場合に利用します。
 * @default false
 * @type boolean
 *
 * @param SimultaneousAlt
 * @desc 各機能を利用する際にAltキーの同時押しが必要かどうかです。他のプラグインと対象キーが競合する場合に利用します。
 * @default false
 * @type boolean
 *
 * @param ShowFPS
 * @desc 初期状態で画面左上にFPSもしくはMSを表示します（FPS/MS/OFF）。
 * @default OFF
 * @type select
 * @option FPS
 * @option MS
 * @option OFF
 *
 * @param CutTitle
 * @desc タイトル画面をとばして最新のセーブファイルをロードします。
 * 起動時にCTRLを押し続けるとカットが無効になります。
 * @default false
 * @type boolean
 *
 * @param RapidStart
 * @desc 高速化された状態でゲームを開始します。（ON/OFF）
 * @default false
 * @type boolean
 *
 * @param RapidSpeed
 * @desc 高速化を実行した際のスピード倍率です。
 * @default 2
 * @type number
 * @min -60
 * @max 16
 *
 * @param FakeMobile
 * @desc モバイル実行を偽装します。(ON/OFF)
 * モバイル版で異なるUIを使用する場合の表示確認ができます。
 * @default false
 * @type boolean
 *
 * @param MenuBarVisible
 * @desc メニューバーを表示し各種デバッグコマンドを実行できます。(ON/OFF)
 * @default true
 * @type boolean
 *
 * @param ClickMenu
 * @desc クリックメニューから各種デバッグコマンドを実行できます。(-1:無効 0:左 1:ホイール 2:右)
 * @default 1
 * @type select
 * @option 無効
 * @value -1
 * @option 左
 * @value 0
 * @option ホイール
 * @value 1
 * @option 右
 * @value 2
 *
 * @param JsonSave
 * @desc JSON形式でセーブ＆ロードできます。テキストエディタ等でセーブファイルを自由に編集できるようになります。
 * @default false
 * @type boolean
 *
 * @param OutputStartupInfo
 * @desc 起動時に様々な情報をログ出力します。
 * @default true
 * @type boolean
 *
 * @param StartupOnTop
 * @desc 起動時にゲーム画面が最前面に固定されます。
 * @default false
 * @type boolean
 *
 * @param UseReloadData
 * @desc オンフォーカスでマップとデータを再読込します。競合等で動作に問題がある場合は無効にしてください。
 * @default true
 * @type boolean
 *
 * @param ShiftRightOnBlur
 * @desc ゲーム画面が最前面に表示されているときにフォーカスを失うと、指定した座標分画面を右寄せします。
 * @default 640
 * @type number
 *
 * @help デベロッパツールの挙動を調整する制作支援プラグインです。
 * このプラグインはローカル環境でのテストプレー時のみ有効となります。
 * また、UserAgentやコアスクリプトのバージョン等役立つ情報をログに出力します。
 * 快適な開発支援のために以下の機能を提供します。
 *
 * 1. ゲーム開始時にデベロッパツールが自動で立ち上がります。(通常はF8で起動)
 *    OFFにしていた場合でもエラーが発生すると自動で立ち上がります。
 *
 * 2. ゲーム画面を常に最前面に表示してくれます。画面を見ながら作業をする場合に
 *    便利です。ゲーム中にメニューバーから切り替えできます。
 *
 * 3. マップやイベントを修正して再保存すると、ゲーム画面にフォーカスを戻した
 *    瞬間にマップとデータベースを自動でリロードしてくれます。
 *
 * 4. タイトル画面を飛ばして最新のセーブファイルをロードできます。
 *
 * 5. マップ上でのゲームのスピードを高速化(16倍速まで)できます。
 *    (マップ上で高速、フェードアウト、メッセージ表示の高速スキップ)
 *    逆に低速化(1/60倍速まで)することもできます。速度倍率に負の値を設定して
 *    ください。ゲームスピードが変化した場合は、BGMのピッチが変化します。
 *
 * 6. 強制的に敵を全滅させて勝利することができます。報酬も取得できます。
 *    強制敗北、強制中断も可能です。
 *
 * 7. 任意のスクリプトを毎フレーム実行させることができます。
 *    スクリプトの戻り値が変化したときのみ結果をコンソールに出力します。
 *
 * 8. ゲーム画面の更新を一時的に止めて画面をフリーズさせることができます。
 *
 * 9. セーブデータを保存する際にエンコーディングせずjson形式のまま
 *    セーブ・ロードできます。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 画面キャプチャ管理プラグインと連携すると画面キャプチャ用の
 * メニューバーが表示されます。
 * https://raw.githubusercontent.com/triacontane/RPGMakerMV/master/MakeScreenCapture.js
 *
 * ・高度な設定
 * ブラウザ起動時でも当プラグインのデバッグ機能を有効にしたい場合は、
 * 起動URLのオプションに「test」もしくは「best」を付与してください。
 * ただしNW.js由来のいくつかの機能は無効になります。
 *
 * 通常、ブラウザなど外部からゲーム起動の場合、戦闘テストは実行できませんが、
 * 当プラグインを有効にしていると戦闘テストが可能です。
 *
 * 外部エディタから起動する場合、以下の起動パラメータが指定可能です。
 * この指定はプラグインパラメータより優先されます。
 * devToolOff : デベロッパツールが起動しなくなります。
 * onTop : 最前面に画面を表示します。
 *
 * This plugin is released under the MIT License.
 */

/*:ja
 * @plugindesc 総合開発支援プラグイン
 * @author トリアコンタン
 *
 * @param 開始時に起動
 * @desc ゲーム開始時に同時にデベロッパツールを起動します。
 * @default true
 * @type boolean
 *
 * @param リロードキー
 * @desc 画面のリロードを行うキーです(F1～F12)。デフォルトF5キーと同様の役割を持ちます。
 * 以前のツクールとの後方互換性を持たせるための機能です。
 * @default F12
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param 最前面に表示キー
 * @desc ゲーム画面を最前面表示を切り替えるキーです。(F1～F12)。
 * @default F11
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param 高速化切替キー
 * @desc ゲームの高速化を切り替えるキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param 強制戦闘勝利キー
 * @desc 戦闘を勝利扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param 強制戦闘敗北キー
 * @desc 戦闘を敗北扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param 強制戦闘中断キー
 * @desc 戦闘を中断扱いで強制終了するキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param スクリプト実行キー
 * @desc スクリプト常駐実行用のキーです。(F1～F12)。
 * @default
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param フリーズキー
 * @desc 画面の更新を一時停止するキーです。(F1～F12)。
 * @default F10
 * @type select
 * @option none
 * @option F1
 * @option F2
 * @option F3
 * @option F4
 * @option F5
 * @option F6
 * @option F7
 * @option F8
 * @option F9
 * @option F10
 * @option F11
 * @option F12
 *
 * @param Ctrl同時押し
 * @desc 各機能を利用する際にCtrlキーの同時押しが必要かどうかです。他のプラグインと対象キーが競合する場合に利用します。
 * @default false
 * @type boolean
 *
 * @param Alt同時押し
 * @desc 各機能を利用する際にAltキーの同時押しが必要かどうかです。他のプラグインと対象キーが競合する場合に利用します。
 * @default false
 * @type boolean
 *
 * @param FPS表示
 * @desc 初期状態で画面左上にFPSを表示します。（FPS/MS/OFF）
 * @default OFF
 * @type select
 * @option FPS
 * @option MS
 * @option OFF
 *
 * @param タイトルカット
 * @desc タイトル画面をとばして最新のセーブファイルをロードします。
 * 起動時にCTRLを押し続けるとカットが無効になります。
 * @default false
 * @type boolean
 *
 * @param 高速開始
 * @desc 高速化された状態でゲームを開始します。（ON/OFF）
 * @default false
 * @type boolean
 *
 * @param 高速スピード
 * @desc 高速化を実行した際のスピード倍率です。(-60...16)
 * 負の値を設定するとスローモーションになります。
 * @default 2
 * @type number
 * @min -60
 * @max 16
 *
 * @param モバイル偽装
 * @desc モバイル実行を偽装します。(ON/OFF)
 * モバイル版で異なるUIを使用する場合の表示確認ができます。
 * @default false
 * @type boolean
 *
 * @param メニューバー表示
 * @desc メニューバーを表示し各種デバッグコマンドを実行できます。(ON/OFF)
 * @default true
 * @type boolean
 *
 * @param クリックメニュー
 * @desc クリックメニューから各種デバッグコマンドを実行できます。(-1:無効 0:左 1:ホイール 2:右)
 * @default 1
 * @type select
 * @option 無効
 * @value -1
 * @option 左
 * @value 0
 * @option ホイール
 * @value 1
 * @option 右
 * @value 2
 *
 * @param JSON形式セーブ
 * @desc JSON形式でセーブ＆ロードできます。テキストエディタ等でセーブファイルを自由に編集できるようになります。
 * @default false
 * @type boolean
 *
 * @param 起動時情報出力
 * @desc 起動時に様々な情報をログ出力します。
 * @default true
 * @type boolean
 *
 * @param 最前面で起動
 * @desc 起動時にゲーム画面が最前面に固定されます。
 * @default false
 * @type boolean
 *
 * @param リロード機能を使う
 * @desc オンフォーカスでマップとデータを再読込します。競合等で動作に問題がある場合は無効にしてください。
 * @default true
 * @type boolean
 *
 * @param 右寄せ座標
 * @desc ゲーム画面が最前面に表示されているときにフォーカスを失うと、指定した座標分画面を右寄せします。
 * @default 640
 * @type number
 *
 * @help デベロッパツールの挙動を調整する制作支援プラグインです。
 * このプラグインはローカル環境でのテストプレー時のみ有効となります。
 * また、UserAgentやコアスクリプトのバージョン等役立つ情報をログに出力します。
 * 快適な開発支援のために以下の機能を提供します。
 *
 * 1. ゲーム開始時にデベロッパツールが自動で立ち上がります。(通常はF8で起動)
 *    OFFにしていた場合でもエラーが発生すると自動で立ち上がります。
 *
 * 2. ゲーム画面を常に最前面に表示してくれます。画面を見ながら作業をする場合に
 *    便利です。ゲーム中にメニューバーから切り替えできます。
 *
 * 3. マップやイベントを修正して再保存すると、ゲーム画面にフォーカスを戻した
 *    瞬間にマップとデータベースを自動でリロードしてくれます。
 *
 * 4. タイトル画面を飛ばして最新のセーブファイルをロードできます。
 *
 * 5. マップ上でのゲームのスピードを高速化(16倍速まで)できます。
 *    (マップ上で高速、フェードアウト、メッセージ表示の高速スキップ)
 *    逆に低速化(1/60倍速まで)することもできます。速度倍率に負の値を設定して
 *    ください。ゲームスピードが変化した場合は、BGMのピッチが変化します。
 *
 * 6. 強制的に敵を全滅させて勝利することができます。報酬も取得できます。
 *    強制敗北、強制中断も可能です。
 *
 * 7. 任意のスクリプトを毎フレーム実行させることができます。
 *    スクリプトの戻り値が変化したときのみ結果をコンソールに出力します。
 *
 * 8. ゲーム画面の更新を一時的に止めて画面をフリーズさせることができます。
 *
 * 9. セーブデータを保存する際にエンコーディングせずjson形式のまま
 *    セーブ・ロードできます。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 画面キャプチャ管理プラグインと連携すると画面キャプチャ用の
 * メニューバーが表示されます。
 * https://raw.githubusercontent.com/triacontane/RPGMakerMV/master/MakeScreenCapture.js
 *
 * ・高度な設定
 * ブラウザ起動時でも当プラグインのデバッグ機能を有効にしたい場合は、
 * 起動URLのオプションに「test」もしくは「best」を付与してください。
 * ただしNW.js由来のいくつかの機能は無効になります。
 *
 * 通常、ブラウザなど外部からゲーム起動の場合、戦闘テストは実行できませんが、
 * 当プラグインを有効にしていると戦闘テストが可能です。
 *
 * 外部エディタから起動する場合、以下の起動パラメータが指定可能です。
 * この指定はプラグインパラメータより優先されます。
 * devToolOff : デベロッパツールが起動しなくなります。
 * onTop : 最前面に画面を表示します。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

var p = null;

/**
 * Controller_NwJs
 * NW.jsのウィンドウを操作します。
 * @constructor
 */
function Controller_NwJs() {
    this.initialize.apply(this, arguments);
}

(function() {
    'use strict';
    const pluginName = 'DevToolsManage';

    //=============================================================================
    // p
    //  ログ出力をより短い関数名で実現します。(RGSS互換)
    //=============================================================================
    p = function() {
        console.log.apply(console, arguments);
        SceneManager.getNwJs().showDevTools();
    };

    //=============================================================================
    // ローカル関数
    //  プラグインパラメータやプラグインコマンドパラメータの整形やチェックをします
    //=============================================================================
    const getParamString = function(paramNames, upperFlg) {
        const value = getParamOther(paramNames);
        return value === null ? '' : upperFlg ? value.toUpperCase() : value;
    };

    const getParamNumber = function(paramNames, min, max) {
        const value = getParamOther(paramNames);
        if (arguments.length < 2) min = -Infinity;
        if (arguments.length < 3) max = Infinity;
        return (parseInt(value, 10) || 0).clamp(min, max);
    };

    const getParamBoolean = function(paramNames) {
        const value = getParamOther(paramNames);
        return (value || '').toUpperCase() === 'ON' || (value || '').toUpperCase() === 'TRUE';
    };

    const getParamOther = function(paramNames) {
        if (!Array.isArray(paramNames)) paramNames = [paramNames];
        for (let i = 0; i < paramNames.length; i++) {
            const name = PluginManager.parameters(pluginName)[paramNames[i]];
            if (name) return name;
        }
        return null;
    };

    const paramStartupDevTool    = getParamBoolean(['StartupDevTool', '開始時に起動']);
    const paramFuncKeyReload     = getParamString(['FuncKeyReload', 'リロードキー']);
    const paramFuncKeyOnTop      = getParamString(['FuncKeyOnTop', '最前面に表示キー']);
    const paramFuncKeyRapidGame  = getParamString(['FuncKeyRapidGame', '高速化切替キー']);
    const paramFuncKeyVictory    = getParamString(['FuncKeyForceVictory', '強制戦闘勝利キー']);
    const paramFuncKeyDefeat     = getParamString(['FuncKeyForceDefeat', '強制戦闘敗北キー']);
    const paramFuncKeyAbort      = getParamString(['FuncKeyForceAbort', '強制戦闘中断キー']);
    const paramFuncKeyScript     = getParamString(['FuncKeyScript', 'スクリプト実行キー']);
    const paramFuncKeyFreeze     = getParamString(['FuncKeyFreeze', 'フリーズキー']);
    const paramShowFPS           = getParamString(['ShowFPS', 'FPS表示'], true);
    const paramCutTitle          = getParamBoolean(['CutTitle', 'タイトルカット']);
    const paramRapidStart        = getParamBoolean(['RapidStart', '高速開始']);
    const paramRapidSpeed        = getParamNumber(['RapidSpeed', '高速スピード'], -60, 16);
    const paramFakeMobile        = getParamBoolean(['FakeMobile', 'モバイル偽装']);
    const paramMenuBarVisible    = getParamBoolean(['MenuBarVisible', 'メニューバー表示']);
    const paramClickMenu         = getParamNumber(['ClickMenu', 'クリックメニュー'], -1);
    const paramJsonSave          = getParamBoolean(['JsonSave', 'JSON形式セーブ']);
    const paramOutputStartupInfo = getParamBoolean(['OutputStartupInfo', '起動時情報出力']);
    const paramStartupOnTop      = getParamBoolean(['StartupOnTop', '最前面で起動']);
    const paramUseReloadData     = getParamBoolean(['UseReloadData', 'リロード機能を使う']);
    const paramSimultaneousCtrl  = getParamBoolean(['SimultaneousCtrl', 'Ctrl同時押し']);
    const paramSimultaneousAlt   = getParamBoolean(['SimultaneousAlt', 'Alt同時押し']);
    const paramShiftRightOnBlur  = getParamNumber(['ShiftRightOnBlur', '右寄せ座標'], -1000, 1000);

    //=============================================================================
    // Graphics
    //  FPSの表示を設定します。
    //=============================================================================
    Graphics.setFPSMeter = function(type) {
        switch (type) {
            case 'FPS':
                this.hideFps();
                this._switchFPSMeter();
                break;
            case 'MS':
                this.hideFps();
                this._switchFPSMeter();
                this._switchFPSMeter();
                break;
        }
    };

    const _Graphics__createAllElements = Graphics._createAllElements;
    Graphics._createAllElements        = function() {
        _Graphics__createAllElements.apply(this, arguments);
        if (paramOutputStartupInfo) {
            this.outputStartUpLog();
        }
    };

    Graphics.outputStartUpLog = function() {
        console.log('********************************');
        console.log('***   User Agent             ***');
        console.log('********************************');
        console.log(navigator.userAgent);
        console.log('********************************');
        console.log('***   Core Version           ***');
        console.log('********************************');
        console.log('RPG Maker Name    : ' + Utils.RPGMAKER_NAME);
        console.log('RPG Maker Version : ' + Utils.RPGMAKER_VERSION);
        console.log('RPG Maker Engine  : ' + (Utils.RPGMAKER_ENGINE || 'Official Version'));
        console.log('********************************');
        console.log('***   Audio                  ***');
        console.log('********************************');
        console.log('Can Play OGG : ' + WebAudio.canPlayOgg());
        console.log('Can Play M4A : ' + WebAudio.canPlayM4a());

        if (Utils.isNwjs()) {
            console.log('********************************');
            console.log('***   Environment            ***');
            console.log('********************************');
            console.log('Platform : ' + process.platform);
            console.log(process.env);
            console.log('*********************************');
            console.log('***   document head           ***');
            console.log('*********************************');
            console.log(document.head);
            console.log('*********************************');
            console.log('***   document body           ***');
            console.log('*********************************');
            console.log(document.body);
        }
    };

    // テストプレー時以外は以降の機能を無効
    if (!Utils.isOptionValid('test') && !DataManager.isBattleTest()) {
        console.log(pluginName + ' is valid only test play!');
        return;
    }

    //=============================================================================
    // Utils
    //  モバイルモードを偽装します。
    //=============================================================================
    const _Utils_isMobileDevice = Utils.isMobileDevice;
    Utils.isMobileDevice        = function() {
        return _Utils_isMobileDevice.apply(this, arguments) || paramFakeMobile;
    };

    Utils.isNwjsNormal = function() {
        return this.isNwjs() && navigator.userAgent.match(/Chrome\/4/);
    };

    //=============================================================================
    // Input
    //  promptを再定義します。
    //=============================================================================
    const _Input_wrapNwjsAlert = Input._wrapNwjsAlert;
    Input._wrapNwjsAlert       = function() {
        _Input_wrapNwjsAlert.apply(this, arguments);
        const _window_prompt = window.prompt;
        window.prompt        = function(value, defaultValue) {
            return SceneManager.getNwJs().prompt(value, defaultValue, _window_prompt);
        };
    };

    Input.functionReverseMapper = {
        F1 : 112,
        F2 : 113,
        F3 : 114,
        F4 : 115,
        F5 : 116,
        F6 : 117,
        F7 : 118,
        F8 : 119,
        F9 : 120,
        F10: 121,
        F11: 122,
        F12: 123
    };

    var _Input__shouldPreventDefault = Input._shouldPreventDefault;
    Input._shouldPreventDefault      = function(keyCode) {
        return _Input__shouldPreventDefault.apply(this, arguments) || keyCode === 9; // Tab
    };

    //=============================================================================
    // SceneManager
    //  状況に応じてデベロッパツールを自動制御します。
    //=============================================================================
    SceneManager.originalTitle     = null;
    SceneManager._rapidGame        = false;
    SceneManager._lastScriptString = null;
    SceneManager._lastScriptResult = null;

    const _SceneManager_initialize = SceneManager.initialize;
    SceneManager.initialize        = function() {
        _SceneManager_initialize.apply(this, arguments);
        this.initDevCommand();
        Graphics.setFPSMeter(paramShowFPS);
        if (!Utils.isNwjs()) {
            return;
        }
        this._freeze  = false;
        this._nwJsGui = new Controller_NwJs();
        if (this.isAlwaysOnTop()) {
            this._nwJsGui.toggleAlwaysOnTop();
        }
    };

    SceneManager.initDevCommand = function() {
        this.devCommands = [
            {code: 102, use: true, name: '最前面に表示', key: paramFuncKeyOnTop, type: 'checkbox'},
            {code: 103, use: true, name: 'リロード', key: paramFuncKeyReload, type: 'normal'},
            {code: 104, use: true, name: '高速モード切替', key: paramFuncKeyRapidGame, type: 'checkbox'},
            {code: 105, use: true, name: '戦闘強制勝利', key: paramFuncKeyVictory, type: 'normal'},
            {code: 106, use: true, name: '常駐スクリプト実行', key: paramFuncKeyScript, type: 'normal'},
            {code: 107, use: true, name: '画面フリーズ', key: paramFuncKeyFreeze, type: 'checkbox'},
            {code: 108, use: !!SceneManager.takeCapture, name: '画面キャプチャ', key: null, type: 'normal'},
            {code: 109, use: true, name: '戦闘強制敗北', key: paramFuncKeyDefeat, type: 'normal'},
            {code: 110, use: true, name: '戦闘強制中断', key: paramFuncKeyAbort, type: 'normal'},
        ];
    };

    SceneManager.getNwJs = function() {
        return this._nwJsGui;
    };

    SceneManager.isAlwaysOnTop = function() {
        return paramStartupOnTop || Utils.isOptionValid('onTop');
    };

    SceneManager.toggleFreeze = function() {
        Input.clear();
        this._freeze = !this._freeze;
        this.updateDocumentTitle();
        return this._freeze;
    };

    const _SceneManager_catchException = SceneManager.catchException;
    SceneManager.catchException        = function(e) {
        if (this._nwJsGui) this._nwJsGui.showDevTools(false);
        _SceneManager_catchException.apply(this, arguments);
    };

    const _SceneManager_onError = SceneManager.onError;
    SceneManager.onError        = function(e) {
        if (this._nwJsGui) this._nwJsGui.showDevTools(false);
        _SceneManager_onError.apply(this, arguments);
    };

    const _SceneManager_onKeyDown = SceneManager.onKeyDown;
    SceneManager.onKeyDown        = function(event) {
        _SceneManager_onKeyDown.apply(this, arguments);
        if (paramSimultaneousCtrl === event.ctrlKey && paramSimultaneousAlt === event.altKey) {
            this.onKeyDownForDevToolManage(event);
        }
    };

    SceneManager.onKeyDownForDevToolManage = function(event) {
        let commandCode = null;
        this.devCommands.some(function(commandInfo) {
            if (Input.functionReverseMapper[commandInfo.key] === event.keyCode) {
                commandCode = commandInfo.code;
                return true;
            }
            return false;
        });
        if (commandCode && this._nwJsGui) this.executeDevCommand(commandCode, event);
    };

    SceneManager.executeDevCommand = function(code, event) {
        const command = this['executeDevCommand' + code];
        return command ? command.call(SceneManager, event) : null;
    };

    SceneManager.executeDevCommand102 = function() {
        return this._nwJsGui.toggleAlwaysOnTop();
    };

    SceneManager.executeDevCommand103 = function() {
        location.reload();
    };

    SceneManager.executeDevCommand104 = function() {
        return this.toggleRapid();
    };

    SceneManager.executeDevCommand105 = function() {
        BattleManager.forceVictory();
    };

    SceneManager.executeDevCommand106 = function() {
        this.showScriptDialog();
    };

    SceneManager.executeDevCommand107 = function() {
        return this.toggleFreeze();
    };

    SceneManager.executeDevCommand108 = function() {
        return this.takeCapture();
    };

    SceneManager.executeDevCommand109 = function() {
        BattleManager.forceDefect();
    };

    SceneManager.executeDevCommand110 = function() {
        BattleManager.forceAbort();
    };

    SceneManager.isRapid = function() {
        return SceneManager._rapidGame && paramRapidSpeed > 1;
    };

    SceneManager.isSlow = function() {
        return SceneManager._rapidGame && paramRapidSpeed < -1;
    };

    SceneManager.toggleRapid = function() {
        this._rapidGame = !this._rapidGame;
        const bgm       = AudioManager.saveBgm();
        if (bgm.name) {
            AudioManager.playBgm(bgm, bgm.pos);
            AudioManager._bgmBuffer.play(true, bgm.pos);
        }
        this.updateDocumentTitle();
        return this.isRapid();
    };

    SceneManager.updateDocumentTitle = function() {
        if (!this.originalTitle) {
            this.originalTitle = document.title;
        }
        document.title = this.originalTitle + this.addDocumentTitleRapidOrSlow() +
            this.addDocumentTitleAlwaysOnTop() +
            this.addDocumentTitleFreeze();
    };

    SceneManager.addDocumentTitleRapidOrSlow = function() {
        if (this.isRapid()) {
            return ' [!!!Rapid!!!] * ' + paramRapidSpeed;
        } else if (this.isSlow()) {
            return ' [!!!Slow!!!] / ' + -paramRapidSpeed;
        } else {
            return '';
        }
    };

    SceneManager.addDocumentTitleAlwaysOnTop = function() {
        return this._nwJsGui.isOnTop() ? ' [!!!Always On Top!!!]' : '';
    };

    SceneManager.addDocumentTitleFreeze = function() {
        return this._freeze ? ' [!!!Freeze!!!]' : '';
    };

    SceneManager.isCurrentScene = function(sceneClass) {
        return this._scene && this._scene.constructor === sceneClass;
    };

    SceneManager.showScriptDialog = function() {
        const promptValue  = '常駐実行したいスクリプトを入力してください。';
        const scriptString = window.prompt(promptValue, this._nwJsGui.readClipboard());
        if (scriptString !== null && scriptString !== '') {
            this._nwJsGui.showDevTools();
            this._nwJsGui.writeClipboard(scriptString);
            this.executeScript(scriptString);
            this._lastScriptString = scriptString;
        }
    };

    SceneManager.executeScript = function(scriptString) {
        let result = null;
        try {
            result = eval(scriptString);
            if (!this._lastScriptString) {
                SoundManager.playOk();
                console.log('Execute Script : ' + scriptString);
                console.log('Execute Result : ');
            }
        } catch (e) {
            if (!this._lastScriptString) {
                SoundManager.playBuzzer();
                console.log('Error Script : ' + scriptString);
                console.error(e.stack);
            }
            result = e.toString();
        }
        if (!this._lastScriptString || result !== this._lastScriptResult) {
            console.log(result);
        }
        this._lastScriptResult = result;
    };

    const _SceneManager_initNwjs = SceneManager.initNwjs;
    SceneManager.initNwjs        = function() {
        _SceneManager_initNwjs.apply(this, arguments);
        if (Utils.isNwjs()) {
            this.addMenuBar();
        }
    };

    SceneManager.addMenuBar = function() {
        if (!paramMenuBarVisible) {
            this._needAdjustScreen = false;
            return;
        }
        const gui        = require('nw.gui');
        const gameWindow = gui.Window.get();
        if (!gameWindow.menu || gameWindow.menu.type !== 'menubar') {
            this._needAdjustScreen = true;
        }
        gameWindow.menu = new gui.Menu({type: 'menubar'});
    };

    const _SceneManager_run = SceneManager.run;
    SceneManager.run        = function(sceneClass) {
        _SceneManager_run.apply(this, arguments);
        this.setWindowSizeForMenuBar();
    };

    SceneManager.setWindowSizeForMenuBar = function() {
        if (!this._needAdjustScreen) {
            return;
        }
        const gui        = require('nw.gui');
        const gameWindow = gui.Window.get();
        var height       = this.getMenuBarHeight();
        gameWindow.moveBy(0, -height);
        gameWindow.resizeBy(0, height);

        setTimeout(function() { // Fix missing menu bar height
            var style_height = parseInt(Graphics._canvas.style.height, 10);
            var height_diff  = SceneManager._screenHeight - style_height;
            if (height_diff !== 0) {
                gameWindow.moveBy(0, -height_diff);
                gameWindow.resizeBy(0, height_diff);
            }
        }, 500);
    };

    SceneManager.getMenuBarHeight = function() {
        return Utils.isNwjsNormal() ? 20 : 25;
    };

    const _SceneManager_requestUpdate = SceneManager.requestUpdate;
    SceneManager._updateRateCount     = 0;
    SceneManager.requestUpdate        = function() {
        if (this.isSlow()) {
            this._updateRateCount++;
            if (this._updateRateCount >= -paramRapidSpeed) {
                this.updateInputData();
                _SceneManager_requestUpdate.apply(this, arguments);
                this._updateRateCount = 0;
            } else if (!this._stopped) {
                requestAnimationFrame(this.requestUpdate.bind(this));
            }
        } else {
            this._updateRateCount = 0;
            _SceneManager_requestUpdate.apply(this, arguments);
        }
    };

    const _SceneManager_updateMain = SceneManager.updateMain;
    SceneManager.updateMain        = function() {
        if (this.isSlow()) {
            this.changeScene();
            this.updateScene();
            this.renderScene();
            this.requestUpdate();
        } else {
            const newTime = this._getTimeInMsWithoutMobileSafari();
            // for break point
            if (newTime - this._currentTime > 1000) {
                Input.clear();
            }
            _SceneManager_updateMain.apply(this, arguments);
        }
    };

    const _SceneManager_updateScene = SceneManager.updateScene;
    SceneManager.updateScene        = function() {
        this.updateScript();
        if (this.isUsingReload()) {
            this.updateDataReload();
        }
        if (this._freeze || this.isReloading()) {
            return;
        }
        _SceneManager_updateScene.apply(this, arguments);
    };

    SceneManager.isUsingReload = function() {
        return paramUseReloadData && !DataManager.isBattleTest() && !DataManager.isEventTest() && Utils.isNwjs();
    };

    SceneManager.updateScript = function() {
        if (this._lastScriptString) {
            this.executeScript(this._lastScriptString);
        }
    };

    SceneManager.updateDataReload = function() {
        if (this.isOnFocusGameWindow() && !this._reloadGenerator) {
            this._reloadGenerator = this.reloadGenerator();
        }
        if (this._reloadGenerator && DataManager.isDatabaseLoaded()) {
            if (!this._reloadGenerator.next().value) {
                this._reloadGenerator = null;
            }
        }
    };

    SceneManager.reloadGenerator = function* () {
        this._preVersionId = DataManager.reloadSystemData();
        yield true;
        if (this._preVersionId !== $dataSystem.versionId) {
            this.reloadMapData();
            DataManager.loadDatabase();
            console.log('Database Reload');
            yield true;
        }
        return false;
    };

    SceneManager.isOnFocusGameWindow = function() {
        if (this.getNwJs().isOnFocus()) {
            this.getNwJs().setOnFocus(false);
            return true;
        }
        return false;
    };

    SceneManager.reloadMapData = function() {
        if (this._scene instanceof Scene_Map && $gamePlayer.canMove()) {
            $gamePlayer.reserveTransfer(
                $gameMap.mapId(), $gamePlayer.x, $gamePlayer.y, $gamePlayer.direction(), 2);
            $gamePlayer.requestMapReload();
            console.log('Map Reload');
        }
    };

    SceneManager.isReloading = function() {
        return !!this._reloadGenerator;
    };

    //=============================================================================
    // BattleManager
    //  強制勝利を追加定義します。
    //=============================================================================
    BattleManager.forceVictory = function() {
        if (this.canExecuteBattleEndProcess()) {
            $gameTroop.members().forEach(function(enemy) {
                enemy.addNewState(enemy.deathStateId());
            });
            this.processVictory();
        }
    };

    BattleManager.forceDefect = function() {
        if (this.canExecuteBattleEndProcess()) {
            $gameParty.members().forEach(function(actor) {
                actor.addNewState(actor.deathStateId());
            });
            this.processDefeat();
        }
    };

    BattleManager.forceAbort = function() {
        if (this.canExecuteBattleEndProcess()) {
            $gameParty.performEscape();
            SoundManager.playEscape();
            this.displayEscapeSuccessMessage();
            this._escaped = true;
            this.processAbort();
        }
    };

    BattleManager.canExecuteBattleEndProcess = function() {
        return SceneManager.isCurrentScene(Scene_Battle) && this._phase !== 'battleEnd';
    };

    //=============================================================================
    // AudioManager
    //  高速化実行時にBGMのピッチを強制的に最大にします。
    //=============================================================================
    const _AudioManager_playBgm = AudioManager.playBgm;
    AudioManager.playBgm        = function(bgm, pos) {
        const originalPitch = bgm.pitch;
        if (SceneManager.isRapid()) {
            arguments[0].pitch = 150;
        }
        if (SceneManager.isSlow()) {
            arguments[0].pitch = 50;
        }
        _AudioManager_playBgm.apply(this, arguments);
        this._currentBgm.pitch = originalPitch;
    };

    //=============================================================================
    // DataManager
    //  セーブデータのエンコード・デコードを実施します。
    //=============================================================================
    DataManager.decodeSaveGame = function(savefileId) {
        if (this.isThisGameFile(savefileId)) {
            const json = StorageManager.load(savefileId);
            StorageManager.saveToLocalFileJson(savefileId, json);
        }
    };

    DataManager.encodeSaveGame = function(savefileId) {
        const json = StorageManager.loadFromLocalFileJson(savefileId);
        if (json) {
            StorageManager.save(savefileId, json);
        }
    };

    const _DataManager_saveGameWithoutRescue = DataManager.saveGameWithoutRescue;
    DataManager.saveGameWithoutRescue        = function(savefileId) {
        const result = _DataManager_saveGameWithoutRescue.apply(this, arguments);
        if (paramJsonSave) this.decodeSaveGame(savefileId);
        return result;
    };

    const _DataManager_loadGameWithoutRescue = DataManager.loadGameWithoutRescue;
    DataManager.loadGameWithoutRescue        = function(savefileId) {
        this.encodeSaveGame(savefileId);
        return _DataManager_loadGameWithoutRescue.apply(this, arguments);
    };

    const _DataManager_loadDatabase = DataManager.loadDatabase;
    DataManager.loadDatabase        = function() {
        if (this.isNeedSuppressBtest()) {
            this._suppressBattleTest = true;
        }
        _DataManager_loadDatabase.apply(this, arguments);
        this._suppressBattleTest = false;
    };

    const _DataManager_isBattleTest = DataManager.isBattleTest;
    DataManager.isBattleTest        = function() {
        return this._suppressBattleTest ? false : _DataManager_isBattleTest.apply(this, arguments);
    };

    DataManager.isNeedSuppressBtest = function() {
        if (!this.isBattleTest()) {
            return false;
        }
        if (Utils.isNwjs()) {
            return this._databaseFiles.some(function(databaseFile) {
                return !StorageManager.isExistTestData(databaseFile.src);
            });
        } else {
            return true;
        }
    };

    DataManager.reloadSystemData = function() {
        for (let i = 0; i < this._databaseFiles.length; i++) {
            const name = this._databaseFiles[i].name;
            if (name === '$dataSystem') {
                var preVersionId = $dataSystem.versionId;
                this.loadDataFile(name, this._databaseFiles[i].src);
                return preVersionId;
            }
        }
        return null;
    };

    //=============================================================================
    // StorageManager
    //  jsonセーブファイルの読み込みと書き込みを行います。
    //=============================================================================
    StorageManager.saveToLocalFileJson = function(savefileId, json) {
        const fs       = require('fs');
        const dirPath  = this.localFileDirectoryPath();
        const filePath = dirPath + 'file%1.json'.format(savefileId);
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath);
        }
        fs.writeFileSync(filePath, json);
    };

    StorageManager.loadFromLocalFileJson = function(savefileId) {
        const fs       = require('fs');
        const filePath = this.localFileDirectoryPath() + 'file%1.json'.format(savefileId);
        if (fs.existsSync(filePath)) {
            return fs.readFileSync(filePath, {encoding: 'utf8'});
        }
        return null;
    };

    StorageManager.isExistTestData = function(fileName) {
        const fs       = require('fs');
        const path     = require('path');
        const filePath = path.join(path.dirname(process.mainModule.filename), 'data/Test_' + fileName);
        return fs.existsSync(filePath);
    };

    //=============================================================================
    // Scene_Base
    //  マップの高速化を提供します。
    //=============================================================================
    const _Scene_Base_fadeSpeed    = Scene_Base.prototype.fadeSpeed;
    Scene_Base.prototype.fadeSpeed = function() {
        return SceneManager.isRapid() ? 1 : _Scene_Base_fadeSpeed.apply(this, arguments);
    };

    const _Scene_Base_startFadeIn    = Scene_Base.prototype.startFadeIn;
    Scene_Base.prototype.startFadeIn = function(duration, white) {
        if (SceneManager.isRapid()) arguments[0] = 1;
        _Scene_Base_startFadeIn.apply(this, arguments);
    };

    const _Scene_Base_startFadeOut    = Scene_Base.prototype.startFadeOut;
    Scene_Base.prototype.startFadeOut = function(duration, white) {
        if (SceneManager.isRapid()) arguments[0] = 1;
        _Scene_Base_startFadeOut.apply(this, arguments);
    };

    //=============================================================================
    // Scene_Boot
    //  タイトル画面をとばしてマップ画面に遷移します。
    //=============================================================================
    const _Scene_Boot_start    = Scene_Boot.prototype.start;
    Scene_Boot.prototype.start = function() {
        _Scene_Boot_start.apply(this, arguments);
        if (paramRapidStart) SceneManager.toggleRapid();
        this.cutSceneTitle();
    };

    Scene_Boot.prototype.cutSceneTitle = function() {
        if (paramCutTitle && !DataManager.isBattleTest() && !DataManager.isEventTest() && !Input.isPressed('control')) {
            if (!this.goToLatestContinue()) {
                this.goToNewGame();
            }
        }
    };

    Scene_Boot.prototype.goToNewGame = function() {
        DataManager.setupNewGame();
        SceneManager.goto(Scene_Map);
    };

    Scene_Boot.prototype.goToLatestContinue = function() {
        if (DataManager.isAnySavefileExists()) {
            if (DataManager.loadGame(DataManager.latestSavefileId())) {
                this.reloadMapIfUpdated();
                SceneManager.goto(Scene_Map);
                $gameSystem.onAfterLoad();
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    };
    Scene_Boot.prototype.reloadMapIfUpdated = Scene_Load.prototype.reloadMapIfUpdated;

    //=============================================================================
    // Scene_Map
    //  高速モードを実装します。
    //=============================================================================
    const _Scene_Map_isFastForward    = Scene_Map.prototype.isFastForward;
    Scene_Map.prototype.isFastForward = function() {
        return _Scene_Map_isFastForward.apply(this, arguments) || SceneManager.isRapid();
    };

    const _Scene_Map_updateMainMultiply    = Scene_Map.prototype.updateMainMultiply;
    Scene_Map.prototype.updateMainMultiply = function() {
        _Scene_Map_updateMainMultiply.apply(this, arguments);
        if (this.isFastForward() && SceneManager.isRapid()) {
            for (let i = 2; i <= paramRapidSpeed; i++) {
                this.updateMain();
            }
        }
    };

    //=============================================================================
    // Window_Base
    //  ウィンドウの高速開閉を提供します。
    //=============================================================================
    const _Window_Base_updateOpen    = Window_Base.prototype.updateOpen;
    Window_Base.prototype.updateOpen = function() {
        if (SceneManager.isRapid() && this._opening) this.openness = 255;
        _Window_Base_updateOpen.apply(this, arguments);
    };

    const _Window_Base_updateClose    = Window_Base.prototype.updateClose;
    Window_Base.prototype.updateClose = function() {
        if (SceneManager.isRapid() && this._closing) this.openness = 0;
        _Window_Base_updateClose.apply(this, arguments);
    };

    //=============================================================================
    // Window_Message
    //  メッセージの高速化を提供します。
    //=============================================================================
    const _Window_Message_isTriggered    = Window_Message.prototype.isTriggered;
    Window_Message.prototype.isTriggered = function() {
        return _Window_Message_isTriggered.apply(this, arguments) || SceneManager.isRapid();
    };

    const _Window_Message_startPause    = Window_Message.prototype.startPause;
    Window_Message.prototype.startPause = function() {
        _Window_Message_startPause.apply(this, arguments);
        if (SceneManager.isRapid()) this.startWait(1);
    };

    var _Game_Interpreter_command355      = Game_Interpreter.prototype.command355;
    Game_Interpreter.prototype.command355 = function() {
        try {
            return _Game_Interpreter_command355.apply(this, arguments);
        } catch (e) {
            console.warn(`Script Error!! Event ID:${this._eventId}`);
            throw e;
        }
    };

    var _Game_Interpreter_command111      = Game_Interpreter.prototype.command111;
    Game_Interpreter.prototype.command111 = function() {
        if (this._params[0] === 12) {
            try {
                return _Game_Interpreter_command111.apply(this, arguments);
            } catch (e) {
                if (this._params[0] === 12) {
                    console.warn(`Script Error!! Event ID:${this._eventId}`);
                }
                throw e;
            }
        } else {
            return _Game_Interpreter_command111.apply(this, arguments);
        }
    };

    var _Game_Character_processMoveCommand      = Game_Character.prototype.processMoveCommand;
    Game_Character.prototype.processMoveCommand = function(command) {
        try {
            _Game_Character_processMoveCommand.apply(this, arguments);
        } catch (e) {
            if (command.code === Game_Character.ROUTE_SCRIPT) {
                console.warn(`Script Error!! ${this.getInformation()}`);
            }
            throw e;
        }
    };

    Game_Character.prototype.getInformation = function() {
        return `X:${this._x} Y:${this._y}`;
    };

    Game_Event.prototype.getInformation = function() {
        return `ID:${this._eventId} Name:${this.event().name}` + Game_Character.prototype.getInformation.call(this);
    };

    Game_Player.prototype.getInformation = function() {
        return 'Player' + Game_Character.prototype.getInformation.call(this);
    };

    //=============================================================================
    // Controller_NwJs
    //  Nw.jsのAPI呼び出しを管理します。
    //=============================================================================
    Controller_NwJs.prototype.constructor = Controller_NwJs;
    Controller_NwJs.prototype.initialize  = function() {
        this._nwGui     = require('nw.gui');
        this._onFocus   = false;
        this._menuBar   = this.getWindow().menu;
        this._menuClick = null;
        this._onTop     = false;
        this.setOnFocus(false);
        this.initSetting();
    };

    Controller_NwJs.prototype.initSetting = function() {
        this.addEventListener();
        if (paramMenuBarVisible) {
            this.makeMenu(this._menuBar);
            this.setMenuBar(this._menuBar);
        }
        this.initClickMenu();
        if (this.isStartUpDevTool()) {
            this.showDevTools();
        }
        setTimeout(function() {
            this.addOnFocusListener();
            SceneManager.updateDocumentTitle();
        }.bind(this), 1000);
    };

    Controller_NwJs.prototype.isStartUpDevTool = function() {
        return paramStartupDevTool && !Utils.isOptionValid('devToolOff');
    };

    Controller_NwJs.prototype.initClickMenu = function() {
        this._menuClick      = new this._nwGui.Menu();
        this._clickMenuItems = {};
        this.makeMenu(this._menuClick);
    };

    Controller_NwJs.prototype.setMenuBar = function(menu) {
        this.getWindow().menu = menu;
    };

    Controller_NwJs.prototype.makeMenu = function(menuObject) {
        this.getSortedDevCommands().forEach(function(commandInfo) {
            if (commandInfo.use) this.makeMenuItem(commandInfo, menuObject);
        }, this);
    };

    Controller_NwJs.prototype.makeMenuItem = function(commandInfo, menuObject) {
        const ctrl     = paramSimultaneousCtrl ? 'Ctrl+' : '';
        const alt      = paramSimultaneousAlt ? 'Alt+' : '';
        const key      = commandInfo.key && commandInfo.key[0] === 'F' ? commandInfo.key : '';
        const menuItem = new this._nwGui.MenuItem({
            label: commandInfo.name + (key ? `(${ctrl}${alt}${key})` : ''),
            type : commandInfo.type,
        });
        if (menuObject === this._menuClick) {
            this._clickMenuItems[commandInfo.name] = menuItem;
        }
        menuItem.click = function() {
            const result = SceneManager.executeDevCommand(commandInfo.code);
            if (commandInfo.type === 'checkbox') this._clickMenuItems[commandInfo.name].checked = result;
        }.bind(this);
        menuObject.append(menuItem);
    };

    Controller_NwJs.prototype.getSortedDevCommands = function() {
        return SceneManager.devCommands.filter(command => command.key && command.key !== 'none').sort(function(a, b) {
            if (a.key && b.key) {
                return Input.functionReverseMapper[a.key] - Input.functionReverseMapper[b.key];
            } else if (a.key || b.key) {
                return a.key ? -1 : 1;
            } else {
                return a.code - b.code;
            }
        });
    };

    Controller_NwJs.prototype.setOnFocus = function(value) {
        this._onFocus = value;
    };

    Controller_NwJs.prototype.isOnFocus = function() {
        return this._onFocus;
    };

    Controller_NwJs.prototype.addEventListener = function() {
        document.addEventListener('mousedown', function(event) {
            if (event.button === paramClickMenu) {
                this._menuClick.popup(event.pageX, event.pageY);
            }
        }.bind(this));
    };

    Controller_NwJs.prototype.addOnFocusListener = function() {
        const currentWin = this.getWindow();
        currentWin.removeAllListeners();
        currentWin.on('focus', this.onFocus.bind(this));
        currentWin.on('blur', this.onBlur.bind(this));
    };

    Controller_NwJs.prototype.onFocus = function() {
        if (this._disableScreenShift) {
            return;
        }
        this._onFocus = true;
        setTimeout(function() {
            if (this._moveOnBlur) {
                this.getWindow().moveBy(-paramShiftRightOnBlur, 0);
                this._moveOnBlur = false;
            }
        }.bind(this), 100);
    };

    Controller_NwJs.prototype.onBlur = function() {
        if (this._disableScreenShift) {
            return;
        }
        setTimeout(function() {
            if (!this._moveOnBlur && this._onTop) {
                this.getWindow().moveBy(paramShiftRightOnBlur, 0);
                this._moveOnBlur = true;
            }
        }.bind(this), 100);
    };

    Controller_NwJs.prototype.getWindow = function() {
        return this._nwGui.Window.get();
    };

    Controller_NwJs.prototype.getClipboard = function() {
        return this._nwGui.Clipboard.get();
    };

    Controller_NwJs.prototype.toggleAlwaysOnTop = function() {
        this._onTop = !this._onTop;
        this.getWindow().setAlwaysOnTop(this._onTop);
        SceneManager.updateDocumentTitle();
        return this._onTop;
    };

    Controller_NwJs.prototype.isOnTop = function() {
        return this._onTop;
    };

    Controller_NwJs.prototype.prompt = function(value, defaultValue, aliasPrompt) {
        const win    = this.getWindow();
        const result = aliasPrompt.call(window, value, defaultValue);
        win.focus();
        Input.clear();
        return result;
    };

    Controller_NwJs.prototype.showDevTools = function() {
        this._disableScreenShift = true;
        this.getWindow().showDevTools();
        setTimeout(function() {
            this.focus();
        }.bind(this), 1000);
    };

    Controller_NwJs.prototype.focus = function() {
        this._disableScreenShift = true;
        this.getWindow().focus();
        setTimeout(function() {
            this._disableScreenShift = false;
        }.bind(this), 500);
    };

    Controller_NwJs.prototype.readClipboard = function(mode) {
        if (!mode) mode = 'text';
        return this.getClipboard().get(mode);
    };

    Controller_NwJs.prototype.writeClipboard = function(copyValue, mode) {
        if (!mode) mode = 'text';
        if (mode === 'text') copyValue = copyValue.toString();
        this.getClipboard().set(copyValue, mode);
    };
})();


code:
//=============================================================================
// DisableFastForward.js
// ----------------------------------------------------------------------------
// (C)2016 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.0.2 2021/01/31 ヘルプの文言を変更
// 1.0.1 2020/01/05 不要な記述を削除
// 1.0.0 2016/02/22 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc イベント高速化禁止プラグイン
 * @author トリアコンタン
 *
 * @help イベント中に決定ボタンやマウスを長押しした場合の
 * イベントの倍速処理を無効化できます。
 * メッセージ送りや戦闘画面の高速化には影響を与えません。
 *
 * プラグインコマンドから禁止/許可の設定ができます。
 * 設定値はセーブデータに保存されます。
 *
 * プラグインコマンド詳細
 *  イベントコマンド「プラグインコマンド」から実行。
 *  （パラメータの間は半角スペースで区切る）
 *
 *  イベント高速化禁止 or DISABLE_FAST_FORWARD
 *   イベントの高速スキップを禁止します。
 *
 *  イベント高速化許可 or ENABLE_FAST_FORWARD
 *   イベントの高速スキップを許可します。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

(function () {
    'use strict';

    var getCommandName = function (command) {
        return (command || '').toUpperCase();
    };

    //=============================================================================
    // Game_Interpreter
    //  プラグインコマンドを追加定義します。
    //=============================================================================
    var _Game_Interpreter_pluginCommand      = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function (command, args) {
        _Game_Interpreter_pluginCommand.apply(this, arguments);
        this.pluginCommandDisableFastForward(command, args);
    };

    Game_Interpreter.prototype.pluginCommandDisableFastForward = function (command, args) {
        switch (getCommandName(command)) {
            case 'イベント高速化禁止' :
            case 'DISABLE_FAST_FORWARD' :
                $gameSystem.disableFastForward = true;
                break;
            case 'イベント高速化許可' :
            case 'ENABLE_FAST_FORWARD' :
                $gameSystem.disableFastForward = false;
                break;
        }
    };

    var _Game_System_initialize = Game_System.prototype.initialize;
    Game_System.prototype.initialize = function() {
        _Game_System_initialize.apply(this, arguments);
        this.disableFastForward = false;
    };

    var _Scene_Map_isFastForward = Scene_Map.prototype.isFastForward;
    Scene_Map.prototype.isFastForward = function() {
        return !$gameSystem.disableFastForward && _Scene_Map_isFastForward.apply(this, arguments);
    };
})();


code:
//=============================================================================
// EasingPicture.js
// PUBLIC DOMAIN
//=============================================================================

/*:
 * @plugindesc ピクチャーの移動パターンを増やします。
 * @author くらむぼん
 *
 * @help
 * 「ピクチャの移動」の際の、位置・拡大率・不透明度の
 * 変化の仕方のパターンを変更できます。
 *
 * 使い方：
 * ◆プラグインコマンド：easing 動き方
 * ◆ピクチャの移動：（普通に設定する）
 * →指定した「動き方」でピクチャが移動します。
 *
 *
 * 以下にいくつかおすすめの「動き方」を載せておきます。
 *
 * linear
 * 　同じ速度でツーっと移動します。プラグイン無しの時の動きと同じ。
 * easeInQuad
 * 　じわ～っと動き出して、だんだん加速していきます。
 * easeOutQuad
 * 　出だしはシュッと素早く、最後の方はゆったり減速します。
 * easeInOutQuad
 * 　前半はだんだん加速していき、後半はゆったり減速します。
 * easeOutBack
 * 　ひゅっと移動した後、少し行き過ぎてから目的の値にバックしてきます。
 * easeOutElastic
 * 　びよよ～んと目的の値を前後してから最終的に目的の値で止まります。
 * easeOutBounce
 * 　目的の値でぼよ～んと何回か跳ね返ってから目的の値で止まります。
 *
 * その他の「動き方」が知りたい方は以下のサイトを参照してください。
 * http://easings.net/ja
 * このサイトには本プラグインで使用できるすべての「動き方」の
 * 名前と図が載っています。（図の見方：横軸が時間、縦軸が変化量）
 *
 *
 * 応用編：
 * ◆プラグインコマンド：easing 動き方 x
 * と指定することで、「横方向への位置の動き方」のみ個別に変更できます。
 * 同様に「y」で縦方向、「sx」で幅、「sy」で高さ、
 * 「op」で透明度の動き方を個別に指定できます。
 *
 * ライセンス：
 * このプラグインの利用法に制限はありません。お好きなようにどうぞ。
 */

(function(jQueryEasing) {
	'use strict';
	var _Game_Picture_move = Game_Picture.prototype.move;
	Game_Picture.prototype.move = function() {
		_Game_Picture_move.apply(this, arguments);
		this._time = 0;
		this._easingX = {f: $gameTemp._easingX, b: this._x, c: this._targetX - this._x};
		this._easingY = {f: $gameTemp._easingY, b: this._y, c: this._targetY - this._y};
		this._easingSx = {f: $gameTemp._easingSx, b: this._scaleX, c: this._targetScaleX - this._scaleX};
		this._easingSy = {f: $gameTemp._easingSy, b: this._scaleY, c: this._targetScaleY - this._scaleY};
		this._easingOp = {f: $gameTemp._easingOp, b: this._opacity, c: this._targetOpacity - this._opacity};
	};

	Game_Picture.prototype.updateMove = function() {
		if (this._time < this._duration) {
			this._time++;
			this._x = this.updateEasing(this._easingX);
			this._y = this.updateEasing(this._easingY);
			this._scaleX = this.updateEasing(this._easingSx);
			this._scaleY = this.updateEasing(this._easingSy);
			this._opacity = this.updateEasing(this._easingOp);
		}
	};

	Game_Picture.prototype.updateEasing = function(easing) {
		if (jQueryEasing[easing.f]) {
			return jQueryEasing[easing.f](this._time, easing.b, easing.c, this._duration);
		} else {
			return jQueryEasing.linear(this._time, easing.b, easing.c, this._duration);
		}
	};

	var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
	Game_Interpreter.prototype.pluginCommand = function(command, args) {
		_Game_Interpreter_pluginCommand.apply(this, arguments);
		if (command.toLowerCase() === 'easing') {
			if (!args[1]) {
				$gameTemp._easingX = args[0];
				$gameTemp._easingY = args[0];
				$gameTemp._easingSx = args[0];
				$gameTemp._easingSy = args[0];
				$gameTemp._easingOp = args[0];
				return;
			}
			switch (args[1].toLowerCase()) {
				case 'x':
					$gameTemp._easingX = args[0];
					break;
				case 'y':
					$gameTemp._easingY = args[0];
					break;
				case 'sx':
					$gameTemp._easingSx = args[0];
					break;
				case 'sy':
					$gameTemp._easingSy = args[0];
					break;
				case 'op':
					$gameTemp._easingOp = args[0];
					break;
				default:
					break;
			}
		}
	};
})(

/* ============================================================
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Open source under the BSD License.
 *
 * Copyright © 2008 George McGinley Smith
 * All rights reserved.
 * https://raw.github.com/danro/jquery-easing/master/LICENSE
 * ======================================================== */

{
	// t: current time, b: begInnIng value, c: change In value, d: duration
	linear: function(t, b, c, d) {
		return c*(t/d) + b;
	},
	easeInQuad: function (t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (c==0||t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (c==0||t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (c==0||t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (t, b, c, d) {
		return c - this.easeOutBounce(d-t, 0, c, d) + b;
	},
	easeOutBounce: function (t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (t, b, c, d) {
		if (t < d/2) return this.easeInBounce(t*2, 0, c, d) * .5 + b;
		return this.easeOutBounce(t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 *
 * Open source under the BSD License.
 *
 * Copyright © 2001 Robert Penner
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 *
 * Neither the name of the author nor the names of contributors may be used to endorse
 * or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */