code:
//=============================================================================
// TriggerOnEquipAndState.js
// ----------------------------------------------------------------------------
// Copyright (c) 2015 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.4.1 2017/01/12 メモ欄の値が空で設定された場合にエラーが発生するかもしれない問題を修正
// 1.4.0 2016/07/27 スイッチにも設定値タグを付けられるよう修正
// 1.3.2 2016/07/14 「武器(防具)の増減」によって装備が外れた場合に対応
// 1.3.1 2016/07/14 1.3.0で敵を倒した際にエラーになる現象の修正
// 1.3.0 2016/07/14 対象アクターがパーティから外れた場合にスイッチをOFFにする仕様を追加
// 1.2.1 2016/07/07 1.2.0が初期装備に対応していなかった問題を修正
// 1.2.0 2016/07/06 戦闘メンバーのみ有効になる設定を追加
// 1.1.0 2016/06/08 一つの装備で複数のスイッチ、変数を操作できるよう修正
// 1.0.1 2016/06/03 スクリプトに「>」「<」を使えるように修正
// 1.0.0 2016/04/03 初版
// ----------------------------------------------------------------------------
// [Blog]   : http://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc 装備変更時の変数操作プラグイン
 * @author トリアコンタン
 *
 * @param 戦闘メンバーのみ
 * @desc 変数やスイッチが変動する対象となるアクターが戦闘メンバーに限定されます。
 * @default OFF
 *
 * @help 装備またはステートの着脱時に、変数およびスイッチを操作できるようになります。
 * 着脱時に、スイッチの場合はON/OFFが切り替わり、変数の場合は値が増減します。
 * 操作対象および設定値には制御文字およびJavaScript計算式を利用できます。
 *
 * 対象となっているアクターがパーティから外れた場合、スイッチはOFFになります。
 * (ver1.3.0以降の仕様)
 *
 * [アイテム]および[ステート]のメモ欄に以下の通り記述してください。
 *
 * ・操作されるスイッチ番号や変数番号です。
 *  <TOESスイッチ対象:3>        // 3番のスイッチが操作対象
 *  <TOES変数対象:20 + actorId> // 20番 + アクターIDの変数が操作対象
 *
 * 通常の制御文字やJavaScript計算式に加えて「actorId」と記述すると、
 * 装備やステートが変更された対象のアクターIDに変換されます。
 *
 * ・スイッチに設定される値です。（省略時はONになります）
 *  <TOESスイッチ設定値:ON>  // 装備時にスイッチがONになり、解除するとOFFになります。
 *  <TOESスイッチ設定値:OFF> // 装備時にスイッチがOFFになり、解除するとONになります。
 *
 * ・変数に設定される値です。
 *  <TOES変数設定値:3> // 装備時に3加算され、解除すると3減算されます。
 *
 * 一度に二つ以上のスイッチや変数を操作したい場合は項目名の後ろに
 * 　数字を追加してください。(3以降も同様)
 *  <TOESスイッチ対象:3>   // 3番のスイッチが操作対象
 *  <TOESスイッチ対象2:5>  // 5番のスイッチも操作対象
 *
 *  注意！
 *  指定する際は、番号に歯抜けがないようにしてください。
 *  以下はダメです。
 *  <TOESスイッチ対象:3>   // 3番のスイッチが操作対象
 *  <TOESスイッチ対象3:5>  // 動作しない
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

(function () {
    'use strict';
    var pluginName = 'TriggerOnEquipAndState';
    var metaTagPrefix = 'TOES';

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

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

    var getMetaValue = function (object, name) {
        var metaTagName = metaTagPrefix + (name ? name : '');
        return object.meta.hasOwnProperty(metaTagName) ? object.meta[metaTagName] : undefined;
    };

    var getMetaValues = function (object, names) {
        if (!Array.isArray(names)) return getMetaValue(object, names);
        for (var i = 0, n = names.length; i < n; i++) {
            var value = getMetaValue(object, names[i]);
            if (value !== undefined) return value;
        }
        return undefined;
    };

    var getArgString = function (arg, upperFlg) {
        arg = convertEscapeCharactersAndEval(arg, false);
        return upperFlg ? arg.toUpperCase() : arg;
    };

    var getArgNumber = function (arg, min, max) {
        if (arguments.length < 2) min = -Infinity;
        if (arguments.length < 3) max = Infinity;
        return (parseInt(convertEscapeCharactersAndEval(arg, true), 10) || 0).clamp(min, max);
    };

    var getArgBoolean = function (arg) {
        return (arg || '').toUpperCase() === 'ON';
    };

    var convertEscapeCharactersAndEval = function (text, evalFlg) {
        if (text == null || text === true) text = '';
        text = text.replace(/>?/gi, '>');
        text = text.replace(/<?/gi, '<');
        text = text.replace(/\\/g, '\x1b');
        text = text.replace(/\x1b\x1b/g, '\\');
        text = text.replace(/\x1bV\[(\d+)\]/gi, function () {
            return $gameVariables.value(parseInt(arguments[1], 10));
        }.bind(this));
        text = text.replace(/\x1bV\[(\d+)\]/gi, function () {
            return $gameVariables.value(parseInt(arguments[1], 10));
        }.bind(this));
        text = text.replace(/\x1bN\[(\d+)\]/gi, function () {
            var actor = parseInt(arguments[1], 10) >= 1 ? $gameActors.actor(parseInt(arguments[1], 10)) : null;
            return actor ? actor.name() : '';
        }.bind(this));
        text = text.replace(/\x1bP\[(\d+)\]/gi, function () {
            var actor = parseInt(arguments[1], 10) >= 1 ? $gameParty.members()[parseInt(arguments[1], 10) - 1] : null;
            return actor ? actor.name() : '';
        }.bind(this));
        text = text.replace(/\x1bG/gi, TextManager.currencyUnit);
        return evalFlg ? eval(text) : text;
    };

    //=============================================================================
    // パラメータの取得と整形
    //=============================================================================
    var paramBattleMemberOnly = getParamBoolean(['BattleMemberOnly', '戦闘メンバーのみ']);

    //=============================================================================
    // Game_Party
    //  変数設定が必要かどうかを返します。
    //=============================================================================
    Game_Party.prototype.isNeedControlVariable = function (actor) {
        return !paramBattleMemberOnly || (this.battleMembers().contains(actor) || this.size() < this.maxBattleMembers());
    };

    Game_Party.prototype.getMembersNeedControl = function () {
        return paramBattleMemberOnly ? this.battleMembers() : this.members();
    };

    Game_Party.prototype.getReserveMembers = function () {
        var battleMembers = this.battleMembers();
        return this.members().filter(function (actor) {
            return !battleMembers.contains(actor);
        });
    };

    var _Game_Party_setupStartingMembers = Game_Party.prototype.setupStartingMembers;
    Game_Party.prototype.setupStartingMembers = function () {
        _Game_Party_setupStartingMembers.apply(this, arguments);
        this.getMembersNeedControl().forEach(function (actor) {
            actor.onChangeMember(true);
        });
    };

    var _Game_Party_addActor = Game_Party.prototype.addActor;
    Game_Party.prototype.addActor = function (actorId) {
        _Game_Party_addActor.apply(this, arguments);
        var actor = $gameActors.actor(actorId);
        if (this.getMembersNeedControl().contains(actor)) {
            actor.onChangeMember(true);
        }
    };

    var _Game_Party_removeActor = Game_Party.prototype.removeActor;
    Game_Party.prototype.removeActor = function (actorId) {
        var actor = $gameActors.actor(actorId);
        if (this.getMembersNeedControl().contains(actor)) {
            actor.onChangeMember(false);
        }
        var reserveMembers = this.getReserveMembers();
        _Game_Party_removeActor.apply(this, arguments);
        if (paramBattleMemberOnly) {
            var members = this.battleMembers();
            reserveMembers.forEach(function (actor) {
                if (members.contains(actor)) {
                    actor.onChangeMember(true);
                }
            });
        }
    };

    var _Game_Party_swapOrder = Game_Party.prototype.swapOrder;
    Game_Party.prototype.swapOrder = function (index1, index2) {
        var prevMembers = this.getMembersNeedControl();
        var actors = [$gameActors.actor(this._actors[index1]), $gameActors.actor(this._actors[index2])];
        _Game_Party_swapOrder.apply(this, arguments);
        var members = this.getMembersNeedControl();
        actors.forEach(function (actor) {
            if (prevMembers.contains(actor) && !members.contains(actor)) {
                actor.onChangeMember(false);
            }
            if (members.contains(actor) && !prevMembers.contains(actor)) {
                actor.onChangeMember(true);
            }
        });
    };

    //=============================================================================
    // Game_Actor
    //  ステートが変更された際のスイッチ、変数制御を追加定義します。
    //=============================================================================
    var _Game_BattlerBase_addNewState = Game_BattlerBase.prototype.addNewState;
    Game_BattlerBase.prototype.addNewState = function (stateId) {
        if (this instanceof Game_Actor && !this._states.contains(stateId)) {
            this.onChangeEquipAndState($dataStates[stateId], true);
        }
        _Game_BattlerBase_addNewState.apply(this, arguments);
    };

    var _Game_BattlerBase_eraseState = Game_BattlerBase.prototype.eraseState;
    Game_BattlerBase.prototype.eraseState = function (stateId) {
        if (this instanceof Game_Actor && this._states.contains(stateId)) {
            this.onChangeEquipAndState($dataStates[stateId], false);
        }
        _Game_BattlerBase_eraseState.apply(this, arguments);
    };

    var _Game_BattlerBase_clearStates = Game_BattlerBase.prototype.clearStates;
    Game_BattlerBase.prototype.clearStates = function () {
        if (this instanceof Game_Actor && this._states) {
            this._states.forEach(function (stateId) {
                this.onChangeEquipAndState($dataStates[stateId], false);
            }.bind(this));
        }
        _Game_BattlerBase_clearStates.apply(this, arguments);
    };

    //=============================================================================
    // Game_Actor
    //  装備が変更された際のスイッチ、変数制御を追加定義します。
    //=============================================================================
    var _Game_Actor_changeEquip = Game_Actor.prototype.changeEquip;
    Game_Actor.prototype.changeEquip = function (slotId, item) {
        var prevItem = new Game_Item(this._equips[slotId].object());
        _Game_Actor_changeEquip.apply(this, arguments);
        var newItem = this._equips[slotId];
        if (prevItem._itemId !== 0 && prevItem._itemId !== newItem._itemId) {
            this.onChangeEquipAndState(prevItem.object(), false);
        }
        if (newItem._itemId !== 0 && newItem._itemId !== prevItem._itemId) {
            this.onChangeEquipAndState(newItem.object(), true);
        }
    };

    var _Game_Actor_discardEquip = Game_Actor.prototype.discardEquip;
    Game_Actor.prototype.discardEquip = function (item) {
        var slotId = this.equips().indexOf(item);
        if (slotId >= 0) {
            this.onChangeEquipAndState(item, false);
        }
        _Game_Actor_discardEquip.apply(this, arguments);
    };

    Game_Actor.prototype.onChangeMember = function (addedSign) {
        this.equips().forEach(function (equip) {
            if (equip && equip.id !== 0) {
                this.onChangeEquipAndState(equip, addedSign, true);
            }
        }.bind(this));
        this._states.forEach(function (stateId) {
            this.onChangeEquipAndState($dataStates[stateId], addedSign, true);
        }.bind(this));
    };

    Game_Actor.prototype.onChangeEquipAndState = function (item, addedSign, force) {
        if (!$gameParty.isNeedControlVariable(this) && !force) return;
        var index = 1;
        while (index) {
            if (this.controlVariable(item, addedSign, index === 1 ? '' : String(index))) {
                index++;
            } else {
                index = 0;
            }
        }
    };

    Game_Actor.prototype.controlVariable = function (item, addedSign, indexString) {
        var switchTarget = getMetaValues(item, ['スイッチ対象' + indexString, 'SwitchTarget' + indexString]);
        var result = false;
        if (switchTarget) {
            var switchId = this.getVariableIdForToes(switchTarget, $dataSystem.switches.length - 1);
            var switchValue = getMetaValues(item, ['スイッチ設定値' + indexString, 'SwitchValue' + indexString]);
            $gameSwitches.setValue(switchId, switchValue ? !(getArgBoolean(switchValue) ^ addedSign) : addedSign);
            result = true;
        }
        var variableTarget = getMetaValues(item, ['変数対象' + indexString, 'VariableTarget' + indexString]);
        if (variableTarget) {
            var variableId = this.getVariableIdForToes(variableTarget, $dataSystem.variables.length - 1);
            var variableValue = getMetaValues(item, ['変数設定値' + indexString, 'VariableValue' + indexString]);
            var resultValue = (variableValue ? getArgNumber(variableValue) : 1) * (addedSign ? 1 : -1);
            $gameVariables.setValue(variableId, $gameVariables.value(variableId) + resultValue);
            result = true;
        }
        return result;
    };

    Game_Actor.prototype.getVariableIdForToes = function (target, max) {
        var actorId = this._actorId; // used in eval
        var result = 0;
        try {
            result = eval(getArgString(target)).clamp(1, max);
        } catch (e) {
            console.error(e.toString());
            console.log(e.stack);
        }
        return result;
    };
})();


code:
//=============================================================================
// TsumioGatheringSystem.js
// ----------------------------------------------------------------------------
// Copyright (c) 2017-2019 Tsumio
// 当ファイルの複製・改変・再配布を固く禁じます。
// No reproduction or republication without written permission.
// ----------------------------------------------------------------------------
// Version
// 1.0.9 2019/11/17 アニメーションの仕様を変更。
// 1.0.8 2019/11/16 アニメーションの枚数を増やせるように変更。アニメーションスピードも変更可能にした。
// 1.0.7 2019/11/12 ロード時に道具アイコンが表示されない不具合を修正。
// 1.0.6 2019/11/12 ロード時にスタミナの幅が必ずMAX値になる不具合を修正。
// 1.0.5 2019/11/11 スタミナのコモンイベントが不要なときに実行される不具合の修正。
// 1.0.4 2019/11/11 スタミナが足りないときコモンイベントを実行するよう変更。
// 1.0.3 2019/11/10 採集ゲージシステムを追加。
// 1.0.2 2019/11/06 収集システムの開始・停止できるようにした。
// 1.0.1 2019/11/05 アイコンの仕様を変更。
// 1.0.0 2019/11/05 公開。
// ----------------------------------------------------------------------------
// [GitHub] : https://github.com/Tsumio/rmmv-plugins
// [Twitter]: https://twitter.com/TsumioNtGame
//=============================================================================

/*:ja
 * @plugindesc 採集システムを実装します。
 * @author ツミオ
 *
 * @param ----基本的な設定----
 * @desc
 * @default
 *
 * @param 追加アクションのリスト
 * @type struct<ActionSettings>[]
 * @desc 追加するアクションについての設定。
 * @default
 *
 * @param 通行不能でも収集を許可するリージョンID
 * @desc 通行不能な場所をまたいでも収集を許可するリージョンIDを指定します。釣り時の池などを想定しています。
 * @type number
 * @default 85
 *
 * @param 収集アクションウィンドウのX座標
 * @desc 現在の収集アクションを示すウィンドウのX座標
 * @type number
 * @default 50
 *
 * @param 収集アクションウィンドウのY座標
 * @desc 現在の収集アクションを示すウィンドウのY座標
 * @type number
 * @default 50
 *
 * @param アイコンのイメージセット
 * @desc 採集アイコンとして使うイメージのセット。
 * @type file
 * @dir img/system/
 * @require 1
 *
 * @param アイコンイメージの幅
 * @desc 採集アイコンとして使うイメージの幅
 * @type number
 * @default 48
 *
 * @param アイコンイメージの高さ
 * @desc 採集アイコンとして使うイメージの高さ
 * @type number
 * @default 48
 *
 * @param スタミナとして使用する変数番号
 * @desc スタミナとして使用する変数番号指定します。
 * @type variable
 * @default 1
 *
 * @param 最大スタミナとして使用する変数番号
 * @desc 最大スタミナとして使用する変数番号指定します。
 * @type variable
 * @default 2
 *
 * @param スタミナゲージの外枠
 * @desc スタミナゲージの外枠画像名
 * @type file
 * @dir img/pictures/
 * @require 1
 * @default
 *
 * @param スタミナゲージの中身
 * @desc スタミナゲージの中身画像名
 * @type file
 * @dir img/pictures/
 * @require 1
 * @default
 *
 * @param スタミナゲージの座標
 * @type struct<Position>
 * @desc スタミナゲージの座標を指定します。
 * @default {"x":"100","y":"100"}
 *
 * @param アクション不可時に実行するコモンイベント番号
 * @type number
 * @desc スタミナが足りないときに実行するコモンイベント番号を設定します。
 * @default 1
 *
 * @param 採集アニメーションのパターン数
 * @type number
 * @desc 採集アニメーションのパターン数を設定します。右側に伸びます
 * @default 5
 *
 * @help 採集システムを実装します。
 *
 * 23/01/08 村人Aによって追加された箇所について分離
 *
 *
 *
 * このプラグインは冬空橙様向けに制作されたプラグインです。
 * いかなる理由があろうとも、許可されていない場合の複製・改変・再配布を認めません。
 *
 * 【特徴】
 * ・採集システムを実装します
 * ・任意個数だけアクションを登録できます
 * ・アクションが発火した場合、セルフスイッチがONになります
 *
 * 【使用方法】
 * プラグインの導入後、プラグインパラメーターを設定してください。
 *
 * 【追加アクションのリスト】
 * プラグインパラメータの追加アクションのリストは任意個数だけ追加することができます。
 * ただし1番目のパラメータはダミー用のアクションとなり、実際には実行できません。
 * 実行させたいアクションは2番め以降に登録してください。
 *
 * 【アクションのsymbol】
 * プラグインパラメータのsymbolはイベントのメモ欄で使用します。
 * 例えばsymbolに「ハンマー」を指定した場合、イベントのメモ欄に以下のように記述することで
 * アクションを実行した際にイベントのセルフスイッチがONになります。
 * <ハンマー:true>
 *
 * 【アイコンの仕様】
 * アイコンはツクールMVのアイコンの仕様に従います。
 * すなわち
 * ・横は16個並ぶ
 * ・縦に無限に並ぶ
 * ・インデックスは0始め
 * となります。
 * 幅と高さはプラグインパラメータの設定に従います。
 *
 * 【収集システムのスクリプト】
 * 以下のスクリプトをスクリプトコマンドで実行することにより、各種動作を実行することができます。
 * valueは任意の数値に置き換えてください。
 *
 * //収集システムの開始
 * $gamePlayer.startGatheringSystem();
 * //収集システムの停止
 * $gamePlayer.stopGatheringSystem();
 * //収集スタミナの回復(最大値以上を指定した場合は最大値まで回復します）
 * $gamePlayer.recoverGatheringStamina(value);
 * //収集スタミナの最大値を設定する
 * $gamePlayer.setMaxGatheringStamina(value);
 *
 * 【アニメーションの仕様】
 * アニメーションは通常の歩行グラフィックを用います。
 * この際、プラグインパラメータの「採集アニメーションのパターン数」で設定した分だけ右側にアニメーションしていきます。
 * アニメーションの再生速度については数字が大きいほど早くなります。
 *
 * 画像名は「現在の歩行グラフィック名」＋「-」＋「animationImageで設定した名前」となります。
 * 例えば現在の歩行グラフィックが「Actor1」でanimationImageが「sword」なら「Actor1-sword」の画像を参照します。
 * この仕様の特性上、複数のキャラクターで収集アクションを実行したい場合
 * 1つの歩行グラフィックセット（Actor1など）に1つのキャラクターだけを置くことを推奨します。
 * もしそうしなければ、画像の管理が煩雑になるかもしれません。
 *
 * 【その他の仕様】
 * ・ゲーム開始時、収集システムは開始しています。
 * ・現在のスタミナを操作したい場合、必ずrecoverGatheringStaminaを利用してください。
 *  直接変数の値を操作した場合、動作は保証されません。
 * ・最大スタミナを操作したい場合、必ずsetMaxGatheringStaminaを利用してください。
 *  直接変数の値を操作した場合、動作は保証されません。
 *
 * 【更新履歴】
 * 1.0.9 2019/11/17 アニメーションの仕様を変更。
 * 1.0.8 2019/11/16 アニメーションの枚数を増やせるように変更。アニメーションスピードも変更可能にした。
 * 1.0.7 2019/11/12 ロード時に道具アイコンが表示されない不具合を修正。
 * 1.0.6 2019/11/12 ロード時にスタミナの幅が必ずMAX値になる不具合を修正。
 * 1.0.5 2019/11/11 スタミナのコモンイベントが不要なときに実行される不具合の修正。
 * 1.0.4 2019/11/11 スタミナが足りないときコモンイベントを実行するよう変更。
 * 1.0.3 2019/11/10 採集ゲージシステムを追加。
 * 1.0.2 2019/11/06 収集システムの開始・停止できるようにした。
 * 1.0.1 2019/11/05 アイコンの仕様を変更。
 * 1.0.0 2019/11/05 公開。
 *
 * 【備考】
 * 当プラグインを利用したことによるいかなる損害に対しても、制作者は一切の責任を負わないこととします。
 *
 * 【利用規約】
 * いかなる理由があろうとも、許可されていない場合の複製・改変・再配布を認めません。
 */
/*~struct~ActionSettings:
 *
 * @param symbol
 * @desc アクション名（イベントのメモタグで指定）。重複不可。
 * @type string
 * @default
 *
 * @param switchId
 * @desc ONにするセルフスイッチの番号。A、B、C、Dのいずれかを指定
 * @type string
 * @default D
 *
 * @param iconId
 * @desc 画面上部に表示するアクション用のアイコン番号
 * @type number
 * @default 1
 *
 * @param se
 * @desc アクション時に使用するSE
 * @type file
 * @dir audio/se/
 * @require 1
 * @default
 *
 * @param missSe
 * @desc 空振り時に使用するSE
 * @type file
 * @dir audio/se/
 * @require 1
 * @default
 *
 * @param endurance
 * @desc 耐久力（使用可能回数）。使用回数無限にしたい場合は-999を指定する。
 * @type number
 * @min -999
 * @default 10
 *
 * @param missEndurance
 * @desc 空振り時の耐久性を減らすかどうかを指定します。trueで減らし、falseで減らしません
 * @type boolean
 * @default true
 *
 * @param throwImage
 * @desc アクション時に矢等の投擲物を表示する場合、こちらに使用するキャラチップの画像名と位置を半角カンマを挟んで指定します。
 * @default !Other1,0
 *
 * @param collisionDistance
 * @desc 当たり判定のマス目。例えば3を指定すると3マス先まで当たる。
 * @default 1
 *
 * @param waitPatternNum
 * @desc イベントに当たった際に止めるパターン番号を指定します。左から0,1,2…となります。必要ない場合-1としてください
 * @default -1
 * @type number
 * @min -1
 *
 * @param correspondItemId
 * @desc 対応するアイテムID。プレイヤーがこのアイテムを保持していない場合、アクションは実行されない。
 * @type item
 * @default
 *
 * @param animationImage
 * @desc アニメーションで使用する画像を設定
 * @type string
 * @require
 *
 * @param animationImageIndex
 * @desc アニメーションで使用する画像のインデックスを設定。
 * 左上から0で右端が3。次は左下に行って4で右下が7。
 * @type number
 * @default 1
 *
 * @param animationTimeFrame
 * @desc アニメーションの再生フレーム。60フレーム＝約1秒
 * @type number
 * @default 60
 *
 * @param consumingMissStamina
 * @desc 空振り時の消費するスタミナ量
 * @type number
 * @default 1
 *
 * @param consumingStamina
 * @desc 消費するスタミナ量
 * @type number
 * @default 2
 *
 * @param animSpeed
 * @desc アニメーション速度。値が大きいほど早い。
 * @type number
 * @default 4
 *
 */
/*~struct~Position:
 *
 * @param x
 * @desc X座標
 * @type number
 * @max 9999
 * @min -9999
 * @default 0
 *
 * @param y
 * @desc Y座標
 * @type number
 * @max 9999
 * @min -9999
 * @default 0
 *
 */

(function () {
    'use strict';
    var pluginName = 'TsumioGatheringSystem';

    ////=============================================================================
    //// Local function
    ////  These functions checks & formats pluguin's command parameters.
    ////  I borrowed these functions from Triacontane.Thanks!
    ////=============================================================================
    var getParamString = function (paramNames) {
        if (!Array.isArray(paramNames)) paramNames = [paramNames];
        for (var i = 0; i < paramNames.length; i++) {
            var name = PluginManager.parameters(pluginName)[paramNames[i]];
            if (name) return name;
        }
        return '';
    };

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

    //This function is not written by Triacontane.Tsumio wrote this function !
    var convertParam = function (param) {
        if (param !== undefined) {
            try {
                return JSON.parse(param);
            } catch (e) {
                console.group();
                console.error('%cParameter is invalid ! You should check the following parameter !', 'background-color: #5174FF');
                console.error('Parameter:' + eval(param));
                console.error('Error message :' + e);
                console.groupEnd();
            }
        }
    };

    //This function is not written by Triacontane.Tsumio wrote this function !
    var convertArrayParam = function (param) {
        if (param !== undefined) {
            try {
                const array = JSON.parse(param);
                for (let i = 0; i < array.length; i++) {
                    array[i] = JSON.parse(array[i]);
                }
                return array;
            } catch (e) {
                console.group();
                console.error('%cParameter is invalid ! You should check the following parameter !', 'background-color: #5174FF');
                console.error('Parameter:' + eval(param));
                console.error('Error message :' + e);
                console.groupEnd();
            }
        }
    };

    /**
     * Convert to number.Receive converted JSON object.
     * @param {Object} obj
     *
     */
    //This function is not written by Triacontane.Tsumio wrote this function !
    var convertToNumber = function (obj) {
        for (var prop in obj) {
            obj[prop] = Number(obj[prop]);
        }
        return obj;
    }

    //////=============================================================================
    ///// 共通の便利関数
    /////
    //////=============================================================================

    const convertActionSettings = function (settings) {
        settings.forEach(x => {
            x.iconId = Number(x.iconId);
            x.endurance = Number(x.endurance);
            x.collisionDistance = Number(x.collisionDistance);
            x.correspondItemId = Number(x.correspondItemId);
            x.animationImageIndex = Number(x.animationImageIndex);
            x.animationTimeFrame = Number(x.animationTimeFrame);
            x.consumingStamina = Number(x.consumingStamina);
            x.animSpeed = Number(x.animSpeed);
        });
    };

    //前方を取得する
    const getForwardPosition = function (x, y, dir) {
        return {
            x: $gameMap.xWithDirection(x, dir),
            y: $gameMap.yWithDirection(y, dir)
        };
    };

    //aを0、bを1としたときのvalueの値を返す。値は0-1に制限する。
    const inverseLerp = function (a, b, value) {
        let answer = 0;
        if (a != b) {
            answer = (value - a) / (b - a);
        }
        answer = answer > 1 ? 1 : answer;
        answer = answer < 0 ? 0 : answer;

        return answer;
    };

    ////=============================================================================
    //// Get and set pluguin parameters.
    ////=============================================================================
    var param = {};
    //実績取得の設定
    param.addedActionList = getParamString(['追加アクションのリスト', '追加アクションのリスト']);
    param.addedActionList = convertArrayParam(param.addedActionList);
    convertActionSettings(param.addedActionList);
    //通行不能でも収集を許可するリージョンID
    param.canGetheringRegionId = getParamNumber(['通行不能でも収集を許可するリージョンID', '通行不能でも収集を許可するリージョンID']);
    //収集アクションウィンドウのX座標
    param.gatheringActionWindowX = getParamNumber(['収集アクションウィンドウのX座標', '収集アクションウィンドウのX座標']);
    //収集アクションウィンドウのY座標
    param.gatheringActionWindowY = getParamNumber(['収集アクションウィンドウのY座標', '収集アクションウィンドウのY座標']);
    //アイコンのイメージセット
    param.iconImageSet = getParamString(['アイコンのイメージセット', 'アイコンのイメージセット']);
    //アイコンイメージの幅
    param.iconWidth = getParamNumber(['アイコンイメージの幅', 'アイコンイメージの幅']);
    //アイコンイメージの高さ
    param.iconHeight = getParamNumber(['アイコンイメージの高さ', 'アイコンイメージの高さ']);
    //スタミナとして使用する変数番号
    param.staminaVariableId = getParamNumber(['スタミナとして使用する変数番号', 'スタミナとして使用する変数番号']);
    //最大スタミナとして使用する変数番号
    param.maxStaminaVariableId = getParamNumber(['最大スタミナとして使用する変数番号', '最大スタミナとして使用する変数番号']);
    //スタミナゲージの外枠
    param.staminaGaugeFrameName = getParamString(['スタミナゲージの外枠', 'スタミナゲージの外枠']);
    //スタミナゲージの中身
    param.staminaGaugeContentName = getParamString(['スタミナゲージの中身', 'スタミナゲージの中身']);
    //スタミナゲージの座標
    param.staminaGaugePos = getParamString(['スタミナゲージの座標', 'スタミナゲージの座標']);
    param.staminaGaugePos = convertParam(param.staminaGaugePos);
    param.staminaGaugePos = convertToNumber(param.staminaGaugePos);
    //アクション不可時に実行するコモンイベント番号
    param.diableStaminaEventId = getParamNumber(['アクション不可時に実行するコモンイベント番号', 'アクション不可時に実行するコモンイベント番号']);
    //採集アニメーションのパターン数
    param.gatheringAnimPatternNum = getParamNumber(['採集アニメーションのパターン数', '採集アニメーションのパターン数']);

    //////=============================================================================
    ///// Scene_Boot
    /////  システムイメージに道具のアイコンも予約しておく
    //////=============================================================================

    //Note:道具のアイコンも追加しておかないと、最初表示したときに何も表示されなくなる
    const _Scene_Boot_loadSystemImages = Scene_Boot.loadSystemImages;
    Scene_Boot.loadSystemImages = function () {
        _Scene_Boot_loadSystemImages.call(this);
        ImageManager.reserveSystem(param.iconImageSet);
    };

    //////=============================================================================
    ///// Game_Player
    /////  アクションを実行する本体
    //////=============================================================================

    //メンバを追加
    const _Game_Player_initMembers = Game_Player.prototype.initMembers;
    Game_Player.prototype.initMembers = function () {
        _Game_Player_initMembers.call(this);
        this._gatheringAction = new Game_GatheringAction();
        this._isEnableGatheringSystem = true;
    };

    //収集システムが開始可能かどうか
    Game_Player.prototype.isEnableGatheringSystem = function () {
        return this._isEnableGatheringSystem;
    };

    //行動中は動き不可にする
    const _Game_Player_canMove = Game_Player.prototype.canMove;
    Game_Player.prototype.canMove = function () {
        const result = _Game_Player_canMove.apply(this, arguments);
        return result && !this.gatheringAction().isActing();
    };

    //アクションのキー入力受付を更新する
    const _Game_Player_update = Game_Player.prototype.update;
    Game_Player.prototype.update = function (sceneActive) {
        this.updateGatheringActions(sceneActive);
        _Game_Player_update.apply(this, arguments);
    };

    //アクションの実態を返す
    Game_Player.prototype.gatheringAction = function () {
        if (!this._gatheringAction) {
            this._gatheringAction = new Game_GatheringAction();
        }
        return this._gatheringAction;
    };

    //アクションを更新する
    Game_Player.prototype.updateGatheringActions = function (sceneActive) {
        if (!this.isEnableGatheringSystem()) {
            return;
        }

        if (sceneActive && this.canMove()) {
            this.gatheringAction().updateInput();
        }
        this.gatheringAction().updateActing();
    };

    //前方を取得する
    Game_Player.prototype.getForwardPosition = function () {
        return getForwardPosition(this.x, this.y, this.direction());
    };

    //収集システムを開始する
    Game_Player.prototype.startGatheringSystem = function () {
        this._isEnableGatheringSystem = true;
    };

    //収集システムを全て停止する
    Game_Player.prototype.stopGatheringSystem = function () {
        this._isEnableGatheringSystem = false;
    };

    //採集スタミナを取得する
    Game_Player.prototype.getGatheringStamina = function () {
        return $gameVariables.value(param.staminaVariableId);
    };

    //採集スタミナをセットする
    Game_Player.prototype.setGatheringStamina = function (value) {
        $gameVariables.setValue(param.staminaVariableId, value);
    };

    //最大採集スタミナを取得する
    Game_Player.prototype.getMaxGatheringStamina = function () {
        return $gameVariables.value(param.maxStaminaVariableId);
    };

    //最大採集スタミナを取得する
    Game_Player.prototype.setMaxGatheringStamina = function (value) {
        $gameVariables.setValue(param.maxStaminaVariableId, value);
        ActionSettings.staminaGaugeSprite.refresh();
    };

    //採集スタミナを回復する。最大値を超えた場合、clampされる
    Game_Player.prototype.recoverGatheringStamina = function (value) {
        const recoverValue = value.clamp(0, this.getMaxGatheringStamina());
        this.setGatheringStamina(recoverValue);
        ActionSettings.staminaGaugeSprite.refresh();
    };

    //////=============================================================================
    ///// Game_CaharacterBase
    /////  パターンを採集用に変える
    //////=============================================================================

    //パターンの最大値を変更した
    const _Game_CharacterBase_pattern = Game_CharacterBase.prototype.pattern;
    Game_CharacterBase.prototype.pattern = function () {
        if (this.isGatheringActor()) {
            return this._pattern < param.gatheringAnimPatternNum ? this._pattern : 1;
        } else {
            return _Game_CharacterBase_pattern.call(this);
        }
    };

    //収集行動をするべきアクターかどうかを決定する。すなわちPTで1番目のアクター
    Game_CharacterBase.prototype.isGatheringActor = function () {
        return $gamePlayer.gatheringAction().isActing() && $gamePlayer === this;
    };

    //最大パターンを収集時にのみ変える
    const _Game_CharacterBase_maxPattern = Game_CharacterBase.prototype.maxPattern;
    Game_CharacterBase.prototype.maxPattern = function () {
        if (this.isGatheringActor()) {
            return param.gatheringAnimPatternNum;
        } else {
            return _Game_CharacterBase_maxPattern.call(this);
        }
    };

    Game_CharacterBase.prototype.canGathering = function () {
        const x = this.x;
        const y = this.y;
        const d = this.direction();
        const x2 = $gameMap.roundXWithDirection(x, d);
        const y2 = $gameMap.roundYWithDirection(y, d);
        if ($gameMap.regionId(x2, y2) == param.canGetheringRegionId) {
            return true;
        }
        if (!$gameMap.isValid(x2, y2)) {
            return false;
        }
        if (this.isThrough() || this.isDebugThrough()) {
            return true;
        }
        if (!this.isMapPassable(x, y, d)) {
            return false;
        }
        return true;
    };

    //////=============================================================================
    ///// Scene_Map
    /////  アクションウィンドウを生成する。ゲージも生成
    //////=============================================================================

    //収集ウィンドウを表示するようにする
    const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
    Scene_Map.prototype.createAllWindows = function () {
        this.createGatheringActionWindow();
        this.createGatheringGaugeSprite();
        _Scene_Map_createAllWindows.call(this);
    };

    //収集ウィンドウを実際に生成
    Scene_Map.prototype.createGatheringActionWindow = function () {
        const x = param.gatheringActionWindowX;
        const y = param.gatheringActionWindowY;
        this._gatheringActionWindow = new Window_GatheringAction(x, y, 100, 100);
        this._gatheringActionWindow.refresh();
        this.addWindow(this._gatheringActionWindow);
        ActionSettings.setGatheringActionWindow(this._gatheringActionWindow);
    };

    //収集ゲージを実際に生成する
    Scene_Map.prototype.createGatheringGaugeSprite = function () {
        const x = param.staminaGaugePos.x;
        const y = param.staminaGaugePos.y;
        this._gatheringGaugeSprite = new Sprite_GatheringGauge(x, y);
        this._gatheringGaugeSprite.refresh();
        this.addWindow(this._gatheringGaugeSprite);
        ActionSettings.setStaminaGaugeSprite(this._gatheringGaugeSprite);
    };

    //////=============================================================================
    ///// Game_Map
    /////  プレイヤーの前方Xマスを調べる
    //////=============================================================================

    //現在装備中の収集アイテムを使って、対象を探す
    Game_Map.prototype.searchGatheringTargetByCurrentItem = function () {
        const currentModel = $gamePlayer.gatheringAction().fetchCurrentModel();
        return this._searchGatheringTarget(currentModel);
    };

    //actionSymbolを使って、対象を探す。引数にはプラグインパラメータで設定したものを渡す。
    Game_Map.prototype._searchGatheringTarget = function (actionModel) {
        if (!$gamePlayer.canGathering()) { return null }
        const forwardPos = $gamePlayer.getForwardPosition();
        const recursionCount = actionModel.collisionDistance;
        const x = forwardPos.x;
        const y = forwardPos.y;
        const targetEvent = this._searchGatheringTargetTo(actionModel, recursionCount, x, y);
        return targetEvent || null;
    };

    //actionModelが反応するイベントが、指定のx座標y座標にあるかどうか調べる
    Game_Map.prototype._searchGatheringTargetTo = function (actionModel, recursionCount, x, y) {
        const targetMeta = actionModel.symbol;
        const coordinateEvents = $gameMap.eventsXy(x, y);
        const targetEvent = coordinateEvents.find(x => x.event().meta[targetMeta]);
        const nextRecursionCount = recursionCount - 1;
        if (targetEvent) {
            return targetEvent;
        } else if (nextRecursionCount > 0) {
            const nextPos = getForwardPosition(x, y, $gamePlayer.direction());
            const nextX = nextPos.x;
            const nextY = nextPos.y;
            return this._searchGatheringTargetTo(actionModel, nextRecursionCount, nextX, nextY) || null;
        } else {
            return null;
        }
    };

    //////=============================================================================
    ///// Window_GatheringAction
    /////  現在の収集アクションを示すウィンドウ
    //////=============================================================================

    class Window_GatheringAction extends Window_Base {
        constructor(x, y, width, height) {
            super(x, y, width, height);
        }

        update() {
            super.update();
            this.updateVisible();
        }

        updateVisible() {
            if ($gamePlayer.isEnableGatheringSystem()) {
                this.visible = true;
            } else {
                this.visible = false;
            }
        }

        refresh() {
            const curerntModel = $gamePlayer.gatheringAction().fetchCurrentModel();
            const countObj = $gamePlayer.gatheringAction().fetchCurrentRemainingCountObj();
            this.contents.clear();
            this._drawCurrentActionIcon(curerntModel.iconId);
            this._drawEndurance(countObj.remainingEnduranceCount);
        }

        /**
         * 現在のアクションのアイコンを描画
         */
        _drawCurrentActionIcon(iconId) {
            //中央に表示
            const x = (this.contentsWidth() - param.iconWidth) / 2;
            const y = (this.contentsHeight() - param.iconHeight) / 2;
            this._drawGatheringIcon(iconId, x, y);
        }

        /**
         * 残りの耐久力を描画
         */
        _drawEndurance(enduranceNum) {
            if (!this._shouldDrawEndurance(enduranceNum)) {
                return;
            }

            const text = String(enduranceNum);
            const x = 0;
            const y = this.contentsHeight() - 33;
            this.drawText(text, x, y, this.contentsWidth(), 'right');
        }

        _shouldDrawEndurance(enduranceNum) {
            return enduranceNum >= 0;
        }

        _drawGatheringIcon(iconIndex, x, y) {
            const bitmap = ImageManager.loadSystem(param.iconImageSet);
            const pw = param.iconWidth;
            const ph = param.iconHeight;
            const sx = iconIndex % 16 * pw;
            const sy = Math.floor(iconIndex / 16) * ph;
            this.contents.blt(bitmap, sx, sy, pw, ph, x, y);
        }
    }

    //////=============================================================================
    ///// Game_GatheringAction
    /////  アクションの現在データを返す用のクラス。Game_Playerから参照されることが多い
    //////=============================================================================

    class Game_GatheringAction {
        constructor() {
            this._initializeMembers();
            this._refreshCurrentSymbol();
        }

        _initializeMembers() {
            this._currentSymbol = '';
            this._currentIndex = 0;
            this._isActing = false;
            this._actingFrame = 0;
            this._endFrame = 0;
            this._prevAnimationData = null;
            this._initializeRemainingCountList();
        }

        _initializeRemainingCountList() {
            this._remainingCountList = [];
            ActionSettings.actionList.forEach((action, index) => {
                this._remainingCountList.push(new Game_RemainingCount(index, action.correspondItemId, action.endurance));
            });
        }

        /**
         * 現在のシンボルの番号。
         * 0だと何もアクション実行しない。実際のアクションは1から。
         * @return {Number}
         */
        get currentIndex() {
            return this._currentIndex || 0;
        }

        /**
         * 現在のアクションのシンボル名
         * @return {String}
         */
        get currentSymbol() {
            return this._currentSymbol || '';
        }

        /**
         * 残り耐久力のこと。それのリスト
         * @return {Game_RemainingCount[]}
         */
        get remainingCountList() {
            return this._remainingCountList || [];
        }

        updateInput() {
            if (Input.isTriggered('ok')) {
                this.tryInvokeAction();
            }

            if (Input.isTriggered('pagedown')) {
                this.setNextAction();
            } else if (Input.isTriggered('pageup')) {
                this.setPrevAction();
            }
        }

        updateActing() {
            if (!this.isActing()) {
                return;
            }

            this._actingFrame++;
            if (this._shouldEndAnimation()) {
                this._endAnimation();
            }
        }

        /**
         * 現在アクション中かどうかを示す
         * @return {Boolean}
         */
        isActing() {
            return this._isActing;
        }

        tryInvokeAction() {
            //インデックスチェック
            if (!this._canInvokeAction()) {
                return;
            }
            //スタミナ十分かチェック
            if (!this._hasEnoughStamina()) {
                this._invokeDisableActionEvent();
                return;
            }
            //イベント発火
            const event = $gameMap.searchGatheringTargetByCurrentItem();
            this._invokeAction(event);
        }

        setNextAction() {
            this._currentIndex = (this.currentIndex + 1) % ActionSettings.actionCount;
            this._refreshCurrentSymbol();
            if (this._isActionValid()) {
                this.refreshActionItemWindow();
            } else {
                this.setNextAction();
            }
        }

        setPrevAction() {
            this._currentIndex = (this.currentIndex + (ActionSettings.actionCount - 1)) % ActionSettings.actionCount;
            this._refreshCurrentSymbol();
            if (this._isActionValid()) {
                this.refreshActionItemWindow();
            } else {
                this.setPrevAction();
            }
        }

        /**
         * @return {ActionSettings}
         */
        fetchCurrentModel() {
            return ActionSettings.fetchActionSetting(this.currentSymbol);
        }

        /**
         * @return {Game_RemainingCount}
         */
        fetchCurrentRemainingCountObj() {
            return this.remainingCountList.find(x => x._symbolIndex === this.currentIndex);
        }

        refreshActionItemWindow() {
            ActionSettings.gatheringActionWindow.refresh();
        }

        /**
         * スタミナが足りないことを示すイベント発火
         */
        _invokeDisableActionEvent() {
            $gameTemp.reserveCommonEvent(param.diableStaminaEventId);
        }

        /**
         * イベント発火
         * @param {Game_Event} event
         */
        _invokeAction(event) {
            const miss = !!!event
            if (event && this._fireSelfSwitchEvent(event)) { return }
            this._playSe(miss);
            this._playAnimation();
            this._consumeStamina(miss);
            //アイテムの個数や耐久力を調整
            this._decreaseEnduranceCount(miss);
            this._refreshCurrentSymbol();
            this.refreshActionItemWindow();
            if (!this._isActionValid()) {
                this.setNextAction();
            }
            this._startActing();
            this._refreshGauge();
        }

        /**
         * アクションの開始
         */
        _startActing() {
            this._actingFrame = 0;
            this._isActing = true;
            this._endFrame = this.fetchCurrentModel().animationTimeFrame;
        }

        /**
         * イベントのセルフスイッチを発火する
         * @param {Game_Event} event
         */
        _fireSelfSwitchEvent(event) {
            const selfSwithId = this.fetchCurrentModel().switchId;
            const key = [event._mapId, event._eventId, selfSwithId];
            const selfSwitchValue = $gameSelfSwitches.value(key);
            $gameSelfSwitches.setValue(key, true);
            event.refresh();
            return selfSwitchValue
        }

        _canInvokeAction() {
            return this._currentIndex !== 0;
        }

        _hasEnoughStamina() {
            const hasEnoughStamina = $gamePlayer.getGatheringStamina() >= this.fetchCurrentModel().consumingStamina;
            return hasEnoughStamina;
        }

        /**
         * アクションを選択可能かどうかを返す。
         * 選択不可なときというのは、特定のアイテムを何も持っていない場合。
         */
        _canActionSelected() {
            return ActionSettings.actionList.some(x => {
                const targetItemId = x.correspondItemId;
                const targetItem = $dataItems[targetItemId];
                return $gameParty.hasItem(targetItem, false);
            });
        }

        /**
         * 現在のシンボルを更新する
         */
        _refreshCurrentSymbol() {
            this._currentSymbol = ActionSettings.actionList[this.currentIndex].symbol;
            const remainingCountObj = this.fetchCurrentRemainingCountObj();
            remainingCountObj.adjustEnduranceNum();
        }

        /**
         * 現在のアクションのために使うアイテムを持っているかどうかを判定
         */
        _isActionValid() {
            if (this.currentIndex === 0) {
                return true;
            }

            const actionSetting = ActionSettings.fetchActionSetting(this.currentSymbol);
            if (!actionSetting) {
                return false;
            }

            const targetItemId = actionSetting.correspondItemId;
            const targetItem = $dataItems[targetItemId];
            return $gameParty.hasItem(targetItem, false);
        }

        _decreaseEnduranceCount(miss) {
            if (miss && (!this.fetchCurrentModel().missEndurance || this.fetchCurrentModel().missEndurance == "false")) { return }
            const remainingCountObj = this.fetchCurrentRemainingCountObj();
            remainingCountObj.decreaseEnduranceCount();
        }

        _playSe(miss) {
            const model = this.fetchCurrentModel();
            const se = miss ? model.missSe : model.se;
            const seObj = { 'name': se, 'volume': 100, 'pitch': 100, 'pan': 0 };
            AudioManager.playSe(seObj);
        }

        _playAnimation() {
            const firstMember = $gameParty.members()[0];
            const model = this.fetchCurrentModel();
            const imgName = model.animationImage;
            const imgIndex = model.animationImageIndex;
            const animSpeed = model.animSpeed;
            const walkModelName = firstMember.actor().characterName;
            const gatheringWalkModelName = `${walkModelName}-${imgName}`;
            this._storeAnimationData(firstMember);
            firstMember.setCharacterImage(gatheringWalkModelName, imgIndex);
            //足踏みする
            $gamePlayer._pattern = 0;//Note:resetPatternでは1からになるのでダメ
            $gamePlayer.setMoveSpeed(animSpeed);
            $gamePlayer.setStepAnime(true);
            $gamePlayer.refresh();
        }

        _storeAnimationData(targetMember) {
            const isStepping = $gamePlayer.hasStepAnime();
            const imgName = targetMember.characterName();
            const imgIndex = targetMember.characterIndex();
            const animSpeed = $gamePlayer.moveSpeed();
            this._prevAnimationData = new Game_GatheringAnimation(isStepping, imgName, imgIndex, animSpeed);
        }

        _restoreAnimationData() {
            const firstMember = $gameParty.members()[0];
            const imgName = this._prevAnimationData.imgName;
            const imgIndex = this._prevAnimationData.imgIndex;
            const isStepping = this._prevAnimationData.isStepping;
            const animSpeed = this._prevAnimationData.animSpeed;
            firstMember.setCharacterImage(imgName, imgIndex);
            $gamePlayer.setStepAnime(isStepping);
            $gamePlayer.setMoveSpeed(animSpeed);
            $gamePlayer.refresh();
        }

        _shouldEndAnimation() {
            return this._actingFrame >= this._endFrame;
        }

        _endAnimation() {
            this._isActing = false;
            this._restoreAnimationData();
        }

        _consumeStamina(miss) {
            const m = this.fetchCurrentModel()
            const cosumValue = miss ? m.consumingMissStamina : m.consumingStamina
            const nextStamina = $gamePlayer.getGatheringStamina() - cosumValue;
            $gamePlayer.setGatheringStamina(nextStamina);
        }

        _refreshGauge() {
            ActionSettings.staminaGaugeSprite.refresh();
        }
    }
    window[Game_GatheringAction.name] = Game_GatheringAction;

    //////=============================================================================
    ///// Game_GatheringAnimation
    /////  アニメーション用のデータを保持するクラス
    //////=============================================================================

    class Game_GatheringAnimation {
        constructor(isStepping, imgName, imgIndex, animSpeed) {
            this._isStepping = isStepping || false;
            this._imgName = imgName || '';
            this._imgIndex = imgIndex || 0;
            this._animSpeed = animSpeed || 3;
        }

        get isStepping() {
            return this._isStepping;
        }

        get imgName() {
            return this._imgName;
        }

        get imgIndex() {
            return this._imgIndex;
        }

        get animSpeed() {
            return this._animSpeed;
        }
    }
    window[Game_GatheringAnimation.name] = Game_GatheringAnimation;

    //////=============================================================================
    ///// Game_RemainingCount
    /////  残り使用回数を保存するためのクラス
    //////=============================================================================

    class Game_RemainingCount {
        constructor(index, itemId, maxEnduranceCount) {
            this._symbolIndex = index;
            this._itemId = itemId;
            this.MAX_ENDURANCE_COUNT = maxEnduranceCount;
            this._remainingEnduranceCount = maxEnduranceCount;
            this._prevItemCount = 0;
        }

        /**
         * 対象アイテム
         * @return {RPG.Item}
         */
        get item() {
            return $dataItems[this._itemId];
        }

        /**
         * 残りの耐久力
         * @return {Number}
         */
        get remainingEnduranceCount() {
            return this._remainingEnduranceCount;
        }

        /**
         * 残りの耐久力を1下げる
         */
        decreaseEnduranceCount() {
            this.adjustEnduranceNum();
            this._remainingEnduranceCount--;
            this._tryDiscardItem();
        }

        /**
         * アイテムの数が減ってるか増えてるかチェックして、残り回数を調整する
         */
        adjustEnduranceNum() {
            const realItemCount = $gameParty.numItems(this.item);
            const diff = realItemCount - this._prevItemCount;
            const isItemLosted = diff < 0;
            if (isItemLosted) {
                this._remainingEnduranceCount = this.MAX_ENDURANCE_COUNT;
            }
            this._prevItemCount = $gameParty.numItems(this.item);
        }

        /**
         * 耐久力が0以下になっていればアイテムを捨てようとする
         */
        _tryDiscardItem() {
            if (this._remainingEnduranceCount <= 0 && ActionSettings.INFINITE_NUM !== this.MAX_ENDURANCE_COUNT) {
                this._discardItem();
            }
        }

        /**
         * 耐久力が0になっていればアイテムを捨てる
         */
        _discardItem() {
            $gameParty.loseItem(this.item, 1, false);
            this.adjustEnduranceNum();
        }
    }
    window[Game_RemainingCount.name] = Game_RemainingCount;

    //////=============================================================================
    ///// Sprite_GatheringGauge
    /////  収集システムのゲージ用スプライト
    //////=============================================================================

    class Sprite_GatheringGauge extends Sprite {
        /**
         * @param {Number} x
         * @param {Number} y
         */
        constructor(x, y) {
            super(null);
            this._initializeMembers();
            this._initializePosition(x, y);
            this._createDisplayObjects();
        }

        /**
         * 枠の最大幅。要はフレームの幅を返す
         * @return {Number}
         */
        get maxWidth() {
            return this._frameSprite.width;
        }

        _initializeMembers() {
            this._frameSprite = null;
            this._contentSprite = null;
        }

        _initializePosition(x, y) {
            this.position.set(x, y);
        }

        /**
         * 表示オブジェクトを一括生成
         */
        _createDisplayObjects() {
            this._createFrameSprite();
            this._createContentSprite();
        }

        /**
         * 枠スプライトを生成
         */
        _createFrameSprite() {
            const bitmap = ImageManager.loadPicture(param.staminaGaugeFrameName);
            this._frameSprite = new Sprite(bitmap);
            this.addChild(this._frameSprite);
        }

        /**
         * 中身スプライトを生成
         */
        _createContentSprite() {
            const bitmap = ImageManager.loadPicture(param.staminaGaugeContentName);
            this._contentSprite = new Sprite(bitmap);
            this._frameSprite.addChild(this._contentSprite);
            //Note:ビットマップロード完了後、リフレッシュしておく。
            //     こうしないと、ロード時にwidthが取得できず、リフレッシュを正常に実行できない。
            //     不細工だが、これが最も単純かつ軽い書き方。
            bitmap.addLoadListener(() => {
                this._frameSprite.bitmap.addLoadListener(() => {
                    this.refresh();
                });
            });
        }

        update() {
            super.update();
            this.updateVisible();
        }

        updateVisible() {
            if ($gamePlayer.isEnableGatheringSystem()) {
                this.visible = true;
            } else {
                this.visible = false;
            }
        }

        /**
         * ゲージを更新する
         */
        refresh() {
            this._refreshContentWidth();
        }

        /**
         * 中身の幅を適切に調整する
         */
        _refreshContentWidth() {
            this._contentSprite.width = this._calculateContentWidth();
        }

        /**
         * 最大スタミナと現在スタミナから、中身の幅を計算して返す。
         * 幅は右から左に減る。
         * @return {Number}
         */
        _calculateContentWidth() {
            const maxStamina = $gamePlayer.getMaxGatheringStamina();
            const currentStamina = $gamePlayer.getGatheringStamina();
            const maxWidth = this.maxWidth;
            const point = inverseLerp(0, maxStamina, currentStamina);
            const contentWidth = maxWidth * point;
            return contentWidth;
        }
    }

    //////=============================================================================
    ///// ActionSettings
    /////  各アクションを表すクラス。主に補完用
    //////=============================================================================

    class ActionSettings {
        /**
         * 一致するシンボルのアクションを返す
         * @param {String} symbol
         * @return {ActionSettings} 一致するシンボルのアクション
         */
        static fetchActionSetting(symbol) {
            return param.addedActionList.find(x => x.symbol === symbol);
        }

        /**
         * アクションリストの合計数を返す
         * @return {Number}
         */
        static get actionCount() {
            return param.addedActionList.length;
        }

        /**
         * アクションのリストを返す
         * @return {ActionSettings[]}
         */
        static get actionList() {
            return param.addedActionList;
        }

        /**
         * 収集アクションのウィンドウを返却
         * @return {Window_GatheringAction}
         **/
        static get gatheringActionWindow() {
            return this._gatheringActionWindow || null;
        }

        /**
         * 収集アクションのウィンドウをセット
         * @param {Window_GatheringAction} symbol
         **/
        static setGatheringActionWindow(window) {
            this._gatheringActionWindow = window;
        }

        /**
         * スタミナゲージのスプライト
         * @return {Sprite_GatheringGauge}
         */
        static get staminaGaugeSprite() {
            return this._staminaGaugeSprite || null;
        }

        /**
         * スタミナゲージのスプライトをセット
         * @param {Sprite_GatheringGauge} sprite
         */
        static setStaminaGaugeSprite(sprite) {
            this._staminaGaugeSprite = sprite;
        }

        /**
         * -999を無限として扱う
         */
        static get INFINITE_NUM() {
            return -999;
        }

        /**
         * アクション名（イベントのメモタグで使用）。重複不可。
         * @return {String}
         */
        get symbol() {
            return null;
        }

        /**
         * ONにするセルフスイッチの番号。A,B,C,Dのいずれかを指定。
         * @return {String}
         */
        get switchId() {
            return null;
        }

        /**
         * 画面上部に表示するアクション用のアイコン番号
         * @return {Number}
         */
        get iconId() {
            return null;
        }

        /**
         * アクション時に再生するSE名
         * @return {String}
         */
        get se() {
            return null;
        }

        /**
         * 耐久力（使用可能回数）。負の値を指定した場合、壊れない。
         * @return {Number}
         */
        get endurance() {
            return null;
        }

        /**
         * 当たり判定のマス目。例えば3を指定すると3マス先まで当たる。
         * @return {Number}
         */
        get collisionDistance() {
            return null;
        }

        /**
         * 対応するアイテムID.
         * プレイヤーがこのアイテムを保持していない場合、アクションは実行されない。
         * @return {Number}
         */
        get correspondItemId() {
            return null;
        }

        /**
         * アニメーションに使用するイメージの名前
         * @return {String}
         */
        get animationImage() {
            return null;
        }

        /**
         * アニメーションに使用するイメージのインデックス
         * @return {Number}
         */
        get animationImageIndex() {
            return null;
        }

        /**
         * アニメーションの再生フレーム数
         * @return {Number}
         */
        get animationTimeFrame() {
            return null;
        }

        /**
         * 消費スタミナ量
         * @return {Number}
         */
        get consumingStamina() {
            return null;
        }

        /**
         * アニメーションの速度
         * @return {Number}
         */
        get animSpeed() {
            return null;
        }
    }

})();

repo_name: dazed/translations

license: unknown


code:
//=============================================================================
// UTA_CommonSave.js
//=============================================================================
// Version    : 1.20
// LastUpdate : 2016.05.11
// Author     : T.Akatsuki
// Website    : http://utakata-no-yume.net/
// License    : MIT License(http://opensource.org/licenses/mit-license.php)
//=============================================================================

//=============================================================================
/*:
 * @plugindesc Create a share save data. Share state of game switch and variables between each save data.
 * @author T.Akatsuki
 *
 * @param Target Switches
 * @desc Set target switch index number.
 * If you want to set multiple, put comma between indexes.
 * Using "-", the range can be specified.
 * (ex) 1-3,11,12,13,20,21
 * @default
 *
 * @param Target Variables
 * @desc Set target variable index number.
 * If you want to set multiple, put comma between indexes.
 * Using "-", the range can be specified.
 * (ex) 1-5,11,12,13,23,24
 * @default
 *
 * @param Is Auto
 * @desc Automatically to set save / load common save data on save / load timing.
 * true  : Automatically common save data is saved/loaded on save / load timing.
 * false : Save and load of common save data you do manually.
 * @default true
 *
 * @param Auto on Gameover
 * @desc If auto saving common save data is enabled , it automatically saves common save data on gameover.
 * true  : It save common save data on game over.
 * false : It do nothing on game over.
 * @default false
 *
 * @param Show Trace
 * @desc Set state traces display.
 * true  : Show trace., false : Don't show trace.
 * @default false
 *
 * @help # Overview
 * Create a share save data. Share state of game switch and variables between each save data.
 * If you use plugin command, you can operate common save data at any time.
 *
 * Common save data will be saved with a name "common.rpgsave" below save directory.
 *
 * # Parameters
 *   Target Switches [Switch No, ...]
 *     Set target switch index number.
 *     If you want to set multiple, put comma between indexes.
 *     Using "-", the range can be specified.
 *     (Ex) 1-3,11,12,13,20,21
 *            => number 1,2,3,11,12,13,20,21 switches are target.
 *
 *   Target Variables [Valiable No, ...]
 *     Set target variable index number.
 *     If you want to set multiple, put comma between indexes.
 *     Using "-", the range can be specified.
 *     (Ex) 1-3,11,12,13,14,15
 *            => number 1,2,3,11,12,13,14,15 variables are target.
 *
 *   Is Auto [true|false]
 *     Automatically to set save / load common save data on save / load timing.
 *
 *   Auto on Gameover [true|false]
 *     If auto saving common save data is enabled , it automatically saves common save data on gameover.
 *
 *   Show Trace [true|false]
 *     Set whether the issue a trace for debugging.
 *
 * # Plugin Command
 *   CommonSave load                  # Read GameSwitches and GameVariables from common save data and apply game data.
 *                                    # This command is used when you want to load common save data at any time.
 *   CommonSave save                  # Save target GameSwitches and GameVariables common save data.
 *                                    # This command is used when you want to save in common save data at any time.
 *   CommonSave remove                # Remove common save data file.
 *
 *   CommonSave setTrace [true|false] # Setting to enabled trace on console.
 *
 *   CommonSave check                 # Show sharing target switches and variables to console.
 *
 * # Change Log
 *   ver 1.20 (2016.05.11)
 *     Using "-", the range can be specified.
 *     Add setTrace plugin command.
 *     Add check plugin command.
 *
 *   ver 1.10 (Fed 17, 2016)
 *     Rename to UTA_CommonSave.js.
 *     To be able to set trace in the parameter.
 *
 *   ver1.01 (Jan 14, 2016)
 *     Fixed bug where could not share variables.
 *
 *   ver1.00 (Dec 31, 2015)
 *     To ver1.00 according to the Japanese version of RPG Maker MV.
 *
 *   ver0.01 (Nov 05, 2015)
 *     Initial release.
 */

/*:ja
 * @plugindesc 共有のセーブデータを作成し、指定したスイッチ・変数の状態をセーブデータ間で共有します。
 * @author 赤月 智平
 *
 * @param Target Switches
 * @desc 対象となるスイッチの番号を指定します。
 * カンマ区切りで複数指定、ハイフンで範囲指定が可能です。
 * (例) 1-3,11,12,13,20,21
 * @default
 *
 * @param Target Variables
 * @desc 対象となる変数の番号を指定します。
 * カンマ区切りで複数指定、ハイフンで範囲指定が可能です。
 * (例) 1-5,11,12,13,23,24
 * @default
 *
 * @param Is Auto
 * @desc セーブ・ロード時に自動的に共通セーブデータのセーブ・ロードを行うかを設定します。
 *       true  : セーブ・ロード時に自動的に共有セーブデータにデータのセーブ・ロードを行う。
 *       false : 共通セーブデータのセーブ・ロードは手動で行う。
 * @default true
 *
 * @param Auto on Gameover
 * @desc 自動セーブ設定がONの時にゲームオーバー時に共有セーブデータの自動セーブを行うかを設定します。
 *       true  : ゲームオーバー時に自動共有セーブを行う。
 *       false : ゲームオーバー時は自動共有セーブを行わない。
 * @default false
 *
 * @param Show Trace
 * @desc デバッグ用のトレースを出すかを設定します。
 * true: トレースを表示, false: トレースを表示しない
 * @default false
 *
 * @help ■概要
 * CommonSaveプラグインは共有セーブデータを作成し、指定したスイッチ・変数の状態をセーブデータ間で共有する事ができます。
 * 基本的にはセーブ・ロード時に設定に応じて自動的に処理してくれます。
 * プラグインコマンドを利用すると、任意のタイミングで共有セーブデータの操作を行う事ができます。
 *
 * 共有セーブデータはsaveディレクトリ以下に「common.rpgsave」という名前で保存されます。
 *
 * ■パラメータの説明
 *   Target Switches [スイッチ番号, ...]
 *     共有したいスイッチの番号を指定します。「,」で区切り複数の値を指定する事ができます。
 *     「-」で数値の範囲を指定する事も可能です。
 *     (例) 1-3,11,12,13,20,21
 *            => 1,2,3,11,12,13,20,21番のスイッチが対象になる。
 *
 *   Target Variables [変数番号, ...]
 *     共有したい変数の番号を指定します。「,」で区切り複数の値を指定する事ができます。
 *     「-」で数値の範囲を指定する事も可能です。
 *     (例) 1-3,11,12,13,14,15
 *            => 1,2,3,11,12,13,14,15番の変数が対象になる。
 *
 *   Is Auto [true|false]
 *     セーブ・ロード時に自動的に共通セーブデータのセーブ・ロードを行うかを設定します。
 *     デフォルトではtrue(セーブ・ロード時に自動的に共有セーブ・ロードを行う)になっています。
 *
 *   Auto on Gameover [true|false]
 *     自動セーブ設定がONの時にゲームオーバー時に共有セーブデータの自動セーブを行うかを設定します。
 *     デフォルトではfalse(ゲームオーバー時に自動共有セーブを行わない)になっています。
 *
 *   Show Trace [true|false]
 *     デバッグ用のトレースを出すかを設定します。
 *
 * ■プラグインコマンド
 *   CommonSave load                  # 共有セーブデータからスイッチ・変数を読み込み反映させます。
 *                                    # 任意のタイミングで共有セーブデータのロードを行いたい場合に使用します。
 *   CommonSave save                  # 共有セーブデータにスイッチ・変数の状態を記録します。
 *                                    # 任意のタイミングで共有セーブデータのセーブを行いたい場合に使用します。
 *   CommonSave remove                # 共有セーブデータを削除します。
 *                                    # 共有セーブデータをリセットしたい場合に使用します。
 *   CommonSave setTrace [true|false] # コンソールにトレースを出力するかを設定します。
 *                                    # ゲーム実行中にトレース状態を切り替えたい場合に使用します。
 *   CommonSave check                 # 共有状態になっているスイッチ・変数番号をコンソールに表示し確認します。
 *
 * ■更新履歴
 *   ver 1.20 (2016.05.11)
 *     スイッチ・変数の指定の際に「-」を使う事で範囲指定を可能に。
 *     トレース状態を動的に変えられるように setTrace プラグインコマンドを追加。
 *     共有対象になっているスイッチ・変数を確認できる check プラグインコマンドを追加。
 *
 *   ver 1.10 (2016.02.17)
 *     ファイル名をUTA_CommonSave.jsに変更。
 *     トレース出力の有無をパラメータで設定できるように。
 *
 *   ver 1.01 (2016.01.14)
 *     変数の共有が上手くできていないバグの修正。
 *
 *   ver 1.00 (2015.12.31)
 *     日本語版RPGツクールMVの発売に合わせver1.00に。
 *
 *   ver 0.01 (2015.11.05)
 *     初版。
 */
//=============================================================================

//name space
var utakata = utakata || (utakata = {});

//-----------------------------------------------------------------------------
// class CommonSaveManager
//-----------------------------------------------------------------------------
(function (utakata) {
    var CommonSaveManager = (function () {
        //constructor
        function CommonSaveManager() {
            //member variables
            this._targetSwitchList = [];
            this._targetVariableList = [];

            this._showTrace = false;
            this._tr = null;

            this.initialize();
        }

        //private methods
        CommonSaveManager.prototype.initialize = function () {
            this.parameters = PluginManager.parameters('UTA_CommonSave');

            this._showTrace = (String(this.parameters['Show Trace']) === "true");

            this._tr = function (s) {
                if (!this._showTrace) { return; }
                var str = "CommonSaveManager: " + s; console.log(str);
            };

            var targetSwitchStr = String(this.parameters['Target Switches']);
            var targetVariableStr = String(this.parameters['Target Variables']);

            this._targetSwitchList = this._parseTargetNumber(targetSwitchStr);
            this._targetVariableList = this._parseTargetNumber(targetVariableStr);
        };

        CommonSaveManager.prototype._parseTargetNumber = function (str) {
            if (typeof str === "undefined" || str === "undefined") { return []; }

            str = str.replace(/\s+/g, "");

            var split_str = ',';
            var rgn_split_str = '-';

            var indexes = str.split(split_str, -1);
            var ret = [];

            var parseTargetNumbersFromRegion = function (rgn_target) {
                var ret = [];
                var target_number = rgn_target.split(rgn_split_str, -1);
                if (typeof target_number[0] === "undefined" || typeof target_number[1] === "undefined") { return []; }

                var s_number = parseInt(target_number[0]);
                if (s_number !== s_number) {
                    this._tr("_parseTargetNumber getTargetNumbersFromRegion: Setting parameter is invalid.");
                    return [];
                }

                var e_number = parseInt(target_number[1]);
                if (e_number !== e_number) {
                    this._tr("_parseTargetNumber getTargetNumbersFromRegion: Setting parameter is invalid.");
                    return [];
                }

                for (var i = s_number; i <= e_number; i++) {
                    ret.push(i);
                }

                return ret;
            };

            for (var i = 0; i < indexes.length; i++) {
                var rgn_target = indexes[i].match(/\d+-\d+/g);
                if (!!rgn_target) {
                    var parseIndexes = parseTargetNumbersFromRegion(rgn_target[0]);
                    if (parseIndexes.length <= 0) { continue; }

                    ret = ret.concat(parseIndexes);
                } else {
                    var parseIndex = parseInt(indexes[i]);
                    if (parseIndex !== parseIndex) {
                        this._tr("_parseTargetNumber: Setting parameter is invalid.");
                        continue;
                    }
                    ret.push(parseIndex);
                }
            }
            return ret;
        };

        CommonSaveManager.prototype._getTargetSwitchJson = function () {
            var json = {};

            for (var i = 0; i < this._targetSwitchList.length; i++) {
                var idx = this._targetSwitchList[i].toString();
                var value = $gameSwitches.value(idx);
                json[idx] = value;
            }

            return json;
        };

        CommonSaveManager.prototype._getTargetVariableJson = function () {
            var json = {};

            for (var i = 0; i < this._targetVariableList.length; i++) {
                var idx = this._targetVariableList[i].toString();
                var value = $gameVariables.value(idx);
                json[idx] = value;
            }

            return json;
        };

        CommonSaveManager.prototype.setLoadSwitches = function (switches) {
            for (var key in switches) {
                var idx = parseInt(key);
                var value = switches[key];
                $gameSwitches.setValue(idx, value);
            }
        };

        CommonSaveManager.prototype.setLoadVariables = function (variables) {
            for (var key in variables) {
                var idx = parseInt(key);
                var value = variables[key];
                $gameVariables.setValue(idx, value);
            }
        };

        //public methods
        CommonSaveManager.prototype.load = function () {
            this._tr("load common save data.");

            if (!this.exists()) { return false; }

            var loadData = DataManager.loadCommonSave();

            if ("gameSwitches" in loadData) {
                this.setLoadSwitches(loadData["gameSwitches"]);
            }
            if ("gameVariables" in loadData) {
                this.setLoadVariables(loadData["gameVariables"]);
            }
        };

        CommonSaveManager.prototype.save = function () {
            this._tr("save common save data.");

            var switchesJson = this._getTargetSwitchJson();
            var variablesJson = this._getTargetVariableJson();

            var json = {
                "gameSwitches": switchesJson,
                "gameVariables": variablesJson
            };

            DataManager.saveCommonSave(json);
        };

        CommonSaveManager.prototype.exists = function () {
            this._tr("check exists common save data.");
            return StorageManager.existsCommonSave();
        };

        CommonSaveManager.prototype.remove = function () {
            this._tr("remove common save data.");
            StorageManager.removeCommonSave();
        };

        CommonSaveManager.prototype.setShowTrace = function (args) {
            if (!args) { return; }
            var enabled = String(args[1]) === "true" ? true : false;
            this._tr("setShowTrace: " + this._showTrace);
            this._showTrace = enabled;
        };

        CommonSaveManager.prototype.check = function () {
            var str = "CommonSaveManager: \n";
            str += " # Share switch targets : \n";
            str += JSON.stringify(this._targetSwitchList);

            str += "\n # Share variable targets : \n";
            str += JSON.stringify(this._targetVariableList);

            console.log(str);
        };

        CommonSaveManager.prototype.isAuto = function () {
            return String(this.parameters['Is Auto']) === "true";
        };

        CommonSaveManager.prototype.isAutoOnGameOver = function () {
            return String(this.parameters['Auto on Gameover']) === "true";
        };

        return CommonSaveManager;
    })();
    utakata.CommonSaveManager = new CommonSaveManager();

})(utakata || (utakata = {}));

(function () {
    //parse and dispatch plugin command
    var _Game_Interpreter_pluginCommand =
        Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function (command, args) {
        _Game_Interpreter_pluginCommand.call(this, command, args);
        if (command === 'CommonSave') {
            switch (args[0]) {
                case 'load':
                    utakata.CommonSaveManager.load();
                    break;
                case 'save':
                    utakata.CommonSaveManager.save();
                    break;
                case 'exists':
                    utakata.CommonSaveManager.exists();
                    break;
                case 'remove':
                    utakata.CommonSaveManager.remove();
                    break;
                case 'setTrace':
                    utakata.CommonSaveManager.setShowTrace(args);
                    break;
                case 'check':
                    utakata.CommonSaveManager.check();
                    break;
                default:
                    break;
            }
        }
    };

    //-----------------------------------------------------------------------------
    // DataManager
    //-----------------------------------------------------------------------------
    // # extension
    var _Data_Manager_loadGame = DataManager.loadGame;
    DataManager.loadGame = function (savefileId) {
        var ret = _Data_Manager_loadGame.call(this, savefileId);

        if (utakata.CommonSaveManager.isAuto()) { utakata.CommonSaveManager.load(); }
        return ret;
    };

    var _Data_Manager_saveGame = DataManager.saveGame;
    DataManager.saveGame = function (savefileId) {
        var ret = _Data_Manager_saveGame.call(this, savefileId);

        if (utakata.CommonSaveManager.isAuto()) { utakata.CommonSaveManager.save(); }
        return ret;
    };

    var _Data_Manager_setupNewGame = DataManager.setupNewGame;
    DataManager.setupNewGame = function () {
        _Data_Manager_setupNewGame.call(this);
        if (utakata.CommonSaveManager.isAuto()) { utakata.CommonSaveManager.load(); }
    };

    // # add methods
    DataManager.loadCommonSave = function () {
        var json;
        try {
            json = StorageManager.loadCommonSave();
        } catch (e) {
            console.error(e);
            return [];
        }
        if (json) {
            var commonInfo = JSON.parse(json);
            return commonInfo;
        } else {
            return [];
        }
    };

    DataManager.saveCommonSave = function (json) {
        var jsonStr = JsonEx.stringify(json);
        if (jsonStr.length >= 200000) {
            console.warn('Common Save too big!');
        }
        StorageManager.saveCommonSave(jsonStr);
        return true;
    };

    //-----------------------------------------------------------------------------
    // StorageManager
    //-----------------------------------------------------------------------------
    //load methods ----------------------------------------------------------------
    StorageManager.loadCommonSave = function () {
        if (this.isLocalMode()) {
            return this.loadFromLocalFileCommonSave();
        } else {
            return this.loadFromWebStorageCommonSave();
        }
    };

    StorageManager.loadFromLocalFileCommonSave = function () {
        var data = null;
        var fs = require('fs');
        var filePath = this.localFilePathCommonSave();
        if (fs.existsSync(filePath)) {
            data = fs.readFileSync(filePath, { encoding: 'utf8' });
        }
        return LZString.decompressFromBase64(data);
    };

    StorageManager.loadFromWebStorageCommonSave = function () {
        var key = this.webStorageKeyCommonSave();
        var data = localStorage.getItem(key);
        return LZString.decompressFromBase64(data);
    };

    // save methods ---------------------------------------------------------------
    StorageManager.saveCommonSave = function (json) {
        if (this.isLocalMode()) {
            this.saveToLocalFileCommonSave(json);
        } else {
            this.saveToWebStorageCommonSave(json);
        }
    };

    StorageManager.saveToLocalFileCommonSave = function (json) {
        var data = LZString.compressToBase64(json);
        var fs = require('fs');
        var dirPath = this.localFileDirectoryPath();
        var filePath = this.localFilePathCommonSave();
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath);
        }
        fs.writeFileSync(filePath, data);
    };

    StorageManager.saveToWebStorageCommonSave = function (json) {
        var key = this.webStorageKeyCommonSave();
        var data = LZString.compressToBase64(json);
        localStorage.setItem(key, data);
    };

    //check exists ----------------------------------------------------------------
    StorageManager.existsCommonSave = function () {
        if (this.isLocalMode()) {
            return this.localFileExistsCommonSave();
        } else {
            return this.webStorageExistsCommonSave();
        }
    };

    StorageManager.localFileExistsCommonSave = function () {
        var fs = require('fs');
        return fs.existsSync(this.localFilePathCommonSave());
    };

    StorageManager.webStorageExistsCommonSave = function () {
        var key = this.webStorageKeyCommonSave();
        return !!localStorage.getItem(key);
    };

    //remove ----------------------------------------------------------------------
    StorageManager.removeCommonSave = function () {
        if (this.isLocalMode()) {
            this.removeLocalFileCommonSave();
        } else {
            this.removeWebStorageCommonSave();
        }
    };

    StorageManager.removeLocalFileCommonSave = function () {
        var fs = require('fs');
        var filePath = this.localFilePathCommonSave();
        if (fs.existsSync(filePath)) {
            fs.unlinkSync(filePath);
        }
    };

    StorageManager.removeWebStorageCommonSave = function () {
        var key = this.webStorageKeyCommonSave();
        localStorage.removeItem(key);
    };

    StorageManager.localFilePathCommonSave = function () { return this.localFileDirectoryPath() + 'common.rpgsave'; };
    StorageManager.webStorageKeyCommonSave = function () { return 'RPG Common'; };

    //-----------------------------------------------------------------------------
    // Scene_Gameover
    //-----------------------------------------------------------------------------
    var _Scene_Gameover_Start = Scene_Gameover.prototype.start;
    Scene_Gameover.prototype.start = function () {
        if (utakata.CommonSaveManager.isAutoOnGameOver()) {
            utakata.CommonSaveManager.save();
        }
        _Scene_Gameover_Start.call(this);
    };

})();


code:
//=============================================================================
// VanguardAndRearguard.js
// ----------------------------------------------------------------------------
// Copyright (c) 2015-2016 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.1.1 2016/06/18 アクター加入時に前衛ステートを強制設定する処理を追加
// 1.1.0 2016/06/06 戦闘不能時に隊列ステートが解除される不具合を修正
//                  前衛メンバーが生存している限り、後衛メンバーが狙われなくなる機能を追加
// 1.0.0 2016/06/05 初版
// ----------------------------------------------------------------------------
// [Blog]   : http://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc Vanguard and rearguard
 * @author triacontane
 *
 * @param VanguardStateId
 * @desc State ID of vanguard.
 * @default 2
 *
 * @param RearguardStateId
 * @desc State ID of rearguard.
 * @default 3
 *
 * @param ChangeInMenu
 * @desc Changeable formation in menu screen.
 * @default ON
 *
 * @param RearDefense
 * @desc Rearguard member never targeting, if vanguard member alive.
 * @default OFF
 *
 * @param SkillIdChange
 * @desc Skill ID of formation change.
 * @default 0
 *
 * @param RearguardOffsetX
 * @desc Offset X Position of rearguard.
 * @default 48
 *
 * @param RearguardOffsetY
 * @desc Offset Y Position of rearguard.
 * @default 0
 *
 * @param ChangeSpeed
 * @desc Move speed of formation change.
 * @default 8
 *
 * @help 戦闘に「前衛」「後衛」の概念を追加します。
 * 「前衛」時のステートと「後衛」時のステートを指定したうえで
 * 「特徴」欄などを使って「前衛」と「後衛」それぞれの特殊効果を設定してください。
 *
 * 「前衛」「後衛」に指定されたステートは、解除条件を満たしても解除されません。
 * 変更するには以下のいずれかの方法を選択します。
 *
 * ・メニュー画面の「並び替え」で同じキャラクターを選択する。
 * ・戦闘画面で「チェンジ」コマンドを実行する。
 * ・イベントから対象ステートを付与する。
 *
 * 戦闘中のチェンジを有効した場合、チェンジ用スキルの対象を「使用者」にして
 * さらにメモ欄に以下の通り設定してください。
 * (既存スキル「防御」をコピーすることをオススメします)
 * <VARChange>
 *
 * 上記メモ欄はチェンジ以外のスキルでも有効です。
 * スキル使用者をチェンジ対象にしたい場合はメモ欄に以下の通り設定してください。
 * <VARUserChange>
 *
 * 前衛のみ、後衛のみを対象にしたスキルを作成したい場合、
 * スキルのメモ欄に以下の通り設定してください。
 * <VAROnlyVanguard> // 前衛のみ対象スキル
 * <VAROnlyRearguard> // 後衛のみ対象スキル
 *
 * ただし、メニュー画面から使用する場合は無効です。
 *
 * 敵キャラの初期配置を後衛にしたい場合、メモ欄に以下の通り設定してください。
 * <VARRearguard>
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * This plugin is released under the MIT License.
 */
/*:ja
 * @plugindesc 前衛後衛プラグイン
 * @author トリアコンタン
 *
 * @param 前衛ステートID
 * @desc 前衛のステートIDです。
 * @default 2
 *
 * @param 後衛ステートID
 * @desc 後衛のステートIDです。
 * @default 3
 *
 * @param メニューチェンジ可能
 * @desc メニュー画面で前衛・後衛の切り替えが可能になります。
 * @default ON
 *
 * @param 後衛防御
 * @desc 前衛メンバーが生存している限り、後衛メンバーが狙われなくなります。
 * @default OFF
 *
 * @param チェンジスキルID
 * @desc 戦闘中の前衛・後衛切り替えコマンドで実行されるスキルIDです。0を指定すると戦闘中はチェンジ不可となります。
 * @default 0
 *
 * @param 後衛時X補正
 * @desc 後衛時のX座標を前衛時に対する相対値で指定します。サイドビューかつ敵キャラの場合は反転します。
 * @default 48
 *
 * @param 後衛時Y補正
 * @desc 後衛時のX座標を前衛時に対する相対値で指定します。
 * @default 0
 *
 * @param チェンジ速度
 * @desc 戦闘中にチェンジした場合のグラフィックの移動速度です。
 * @default 8
 *
 * @help 戦闘に「前衛」「後衛」の概念を追加します。
 * 「前衛」時のステートと「後衛」時のステートを指定したうえで
 * 「特徴」欄などを使って「前衛」と「後衛」それぞれの特殊効果を設定してください。
 *
 * 「前衛」「後衛」に指定されたステートは、解除条件を満たしても解除されません。
 * 変更するには以下のいずれかの方法を選択します。
 *
 * ・メニュー画面の「並び替え」で同じキャラクターを選択する。
 * ・戦闘画面で「チェンジ」コマンドを実行する。
 * ・イベントから対象ステートを付与する。
 *
 * 戦闘中のチェンジを有効した場合、チェンジ用スキルの対象を「使用者」にして
 * さらにメモ欄に以下の通り設定してください。
 * (既存スキル「防御」をコピーすることをオススメします)
 * <VARチェンジ>
 *
 * 上記メモ欄はチェンジ以外のスキルでも有効です。
 * スキル使用者をチェンジ対象にしたい場合はメモ欄に以下の通り設定してください。
 * <VAR使用者チェンジ>
 *
 * 前衛のみ、後衛のみを対象にしたスキルを作成したい場合、
 * スキルのメモ欄に以下の通り設定してください。
 * <VAR前衛のみ> // 前衛のみ対象スキル
 * <VAR後衛のみ> // 後衛のみ対象スキル
 *
 * ただし、メニュー画面から使用する場合は無効です。
 *
 * 敵キャラの初期配置を後衛にしたい場合、メモ欄に以下の通り設定してください。
 * <VAR後衛>
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

(function () {
    'use strict';
    var pluginName = 'VanguardAndRearguard';
    var metaTagPrefix = 'VAR';

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

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

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

    var getMetaValue = function (object, name) {
        var metaTagName = metaTagPrefix + (name ? name : '');
        return object.meta.hasOwnProperty(metaTagName) ? object.meta[metaTagName] : undefined;
    };

    var getMetaValues = function (object, names) {
        if (!Array.isArray(names)) return getMetaValue(object, names);
        for (var i = 0, n = names.length; i < n; i++) {
            var value = getMetaValue(object, names[i]);
            if (value !== undefined) return value;
        }
        return undefined;
    };

    //=============================================================================
    // パラメータの取得と整形
    //=============================================================================
    var paramVanguardStateId = getParamNumber(['VanguardStateId', '前衛ステートID'], 2);
    var paramRearguardStateId = getParamNumber(['RearguardStateId', '後衛ステートID'], 2);
    var paramChangeInMenu = getParamBoolean(['ChangeInMenu', 'メニューチェンジ可能'], 1);
    var paramSkillIdChange = getParamNumber(['SkillIdChange', 'チェンジスキルID'], 0);
    var paramRearguardOffsetX = getParamNumber(['RearguardOffsetX', '後衛時X補正'], 0);
    var paramRearguardOffsetY = getParamNumber(['RearguardOffsetY', '後衛時Y補正'], 0);
    var paramChangeSpeed = getParamNumber(['ChangeSpeed', 'チェンジ速度'], 1);
    var paramRearDefense = getParamBoolean(['RearDefense', '後衛防御']);

    //=============================================================================
    // Game_BattlerBase
    //  前衛・後衛の概念を追加定義します。
    //=============================================================================
    var _Game_BattlerBase_refresh = Game_BattlerBase.prototype.refresh;
    Game_BattlerBase.prototype.refresh = function () {
        _Game_BattlerBase_refresh.apply(this, arguments);
        if (!this.isVanguard() && !this.isRearguard()) {
            this.setFormationState(true);
        }
    };

    var _Game_BattlerBase_die = Game_BattlerBase.prototype.die;
    Game_BattlerBase.prototype.die = function () {
        var prevVanguard = this.isVanguard();
        _Game_BattlerBase_die.apply(this, arguments);
        this.setFormationState(prevVanguard);
    };

    Game_BattlerBase.prototype.setFormationState = function (vanguardFlg) {
        var additionalStateId = (vanguardFlg ? paramVanguardStateId : paramRearguardStateId);
        var removeStateId = (vanguardFlg ? paramRearguardStateId : paramVanguardStateId);
        if (!this.isStateAffected(additionalStateId)) {
            this.addNewState(additionalStateId);
        }
        if (this.isStateAffected(removeStateId)) {
            this.eraseState(removeStateId);
        }
    };

    Game_BattlerBase.prototype.changeFormationState = function () {
        this.setFormationState(!this.isVanguard());
    };

    Game_BattlerBase.prototype.isVanguard = function () {
        return this.isStateAffected(paramVanguardStateId);
    };

    Game_BattlerBase.prototype.isRearguard = function () {
        return this.isStateAffected(paramRearguardStateId);
    };

    Game_BattlerBase.prototype.isVanguardStateOf = function (stateId) {
        return stateId === paramVanguardStateId;
    };

    Game_BattlerBase.prototype.isRearguardStateOf = function (stateId) {
        return stateId === paramRearguardStateId;
    };

    Game_BattlerBase.prototype.changeSkillId = function () {
        return paramSkillIdChange;
    };

    Game_BattlerBase.prototype.getFormationOffsetX = function () {
        return this.isRearguard() ? paramRearguardOffsetX : 0;
    };

    Game_BattlerBase.prototype.getFormationOffsetY = function () {
        return this.isRearguard() ? paramRearguardOffsetY : 0;
    };

    //=============================================================================
    // Game_Battler
    //  前衛・後衛ステートの解除を無効にします。
    //=============================================================================
    var _Game_Battler_removeState = Game_Battler.prototype.removeState;
    Game_Battler.prototype.removeState = function (stateId) {
        if (!this.isVanguardStateOf(stateId) && !this.isVanguardStateOf(stateId)) {
            _Game_Battler_removeState.apply(this, arguments);
        }
    };

    var _Game_Battler_addState = Game_Battler.prototype.addState;
    Game_Battler.prototype.addState = function (stateId) {
        if (this.isVanguardStateOf(stateId)) {
            this.setFormationState(true);
        } else if (this.isRearguardStateOf(stateId)) {
            this.setFormationState(false);
        } else {
            _Game_Battler_addState.apply(this, arguments);
        }
    };

    var _Game_Battler_performActionStart = Game_Battler.prototype.performActionStart;
    Game_Battler.prototype.performActionStart = function (action) {
        if (!action.isChange()) {
            _Game_Battler_performActionStart.apply(this, arguments);
        }
    };

    //=============================================================================
    // Game_Actor
    //  チェンジ用のモーションを定義します。
    //=============================================================================
    var _Game_Actor_setup = Game_Actor.prototype.setup;
    Game_Actor.prototype.setup = function (actorId) {
        _Game_Actor_setup.apply(this, arguments);
        if (!this.isVanguard() && !this.isRearguard()) {
            this.setFormationState(true);
        }
    };

    var _Game_Actor_performAction = Game_Actor.prototype.performAction;
    Game_Actor.prototype.performAction = function (action) {
        _Game_Actor_performAction.apply(this, arguments);
        if (action.isChange()) {
            this.requestMotion('guard');
        }
    };

    //=============================================================================
    // Game_Enemy
    //  前衛・後衛ステートの初期値を設定します。
    //=============================================================================
    var _Game_Enemy_setup = Game_Enemy.prototype.setup;
    Game_Enemy.prototype.setup = function (enemyId, x, y) {
        _Game_Enemy_setup.apply(this, arguments);
        if (getMetaValues(this.enemy(), ['Rearguard', '後衛'])) {
            this.setFormationState(false);
        }
    };

    Game_Enemy.prototype.getFormationOffsetX = function () {
        return Game_BattlerBase.prototype.getFormationOffsetX.call(this) * ($gameSystem.isSideView() ? -1 : 1);
    };

    //=============================================================================
    // Game_Unit
    //  前衛メンバーのみを生存メンバーとして扱う仕様に変更します。
    //=============================================================================
    if (paramRearDefense) {
        var _Game_Unit_aliveMembers = Game_Unit.prototype.aliveMembers;
        Game_Unit.prototype.aliveMembers = function () {
            var members = this.vanguardMembers();
            return members.length > 0 ? members : _Game_Unit_aliveMembers.apply(this, arguments);
        };

        Game_Unit.prototype.vanguardMembers = function () {
            return _Game_Unit_aliveMembers.apply(this, arguments).filter(function (member) {
                return member.isVanguard();
            });
        };
    }

    //=============================================================================
    // Game_Action
    //  チェンジスキルを設定します。
    //=============================================================================
    Game_Action.prototype.setChange = function () {
        this.setSkill(this.subject().changeSkillId());
    };

    Game_Action.prototype.isChange = function () {
        return this.item() === $dataSkills[this.subject().changeSkillId()];
    };

    var _Game_Action_applyItemUserEffect = Game_Action.prototype.applyItemUserEffect;
    Game_Action.prototype.applyItemUserEffect = function (target) {
        _Game_Action_applyItemUserEffect.apply(this, arguments);
        if (getMetaValues(this.item(), ['Change', 'チェンジ'])) {
            target.changeFormationState();
            this.makeSuccess(target);
        }
        if (getMetaValues(this.item(), ['UserChange', '使用者チェンジ'])) {
            this.subject().changeFormationState();
            this.makeSuccess(target);
        }
    };

    var _Game_Action_repeatTargets = Game_Action.prototype.repeatTargets;
    Game_Action.prototype.repeatTargets = function (targets) {
        if (getMetaValues(this.item(), ['OnlyVanguard', '前衛のみ'])) {
            arguments[0] = targets.filter(function (target) {
                return target.isVanguard();
            });
        }
        if (getMetaValues(this.item(), ['OnlyRearguard', '後衛のみ'])) {
            arguments[0] = targets.filter(function (target) {
                return target.isRearguard();
            });
        }
        return _Game_Action_repeatTargets.apply(this, arguments);
    };

    //=============================================================================
    // Scene_Battle
    //  前衛・後衛のチェンジを追加定義します。
    //=============================================================================
    var _Scene_Battle_createActorCommandWindow = Scene_Battle.prototype.createActorCommandWindow;
    Scene_Battle.prototype.createActorCommandWindow = function () {
        _Scene_Battle_createActorCommandWindow.apply(this, arguments);
        this._actorCommandWindow.setHandler('change', this.commandChange.bind(this));
    };

    Scene_Battle.prototype.commandChange = function () {
        BattleManager.inputtingAction().setChange();
        this.selectNextCommand();
    };

    //=============================================================================
    // Scene_Menu
    //  前衛・後衛のチェンジを追加定義します。
    //=============================================================================
    var _Scene_Menu_onFormationOk = Scene_Menu.prototype.onFormationOk;
    Scene_Menu.prototype.onFormationOk = function () {
        if (paramChangeInMenu) {
            var pendingIndex = this._statusWindow.pendingIndex();
            var index = this._statusWindow.index();
            var actor = $gameParty.members()[index];
            if (pendingIndex >= 0 && index === pendingIndex) {
                actor.changeFormationState();
            }
        }
        _Scene_Menu_onFormationOk.apply(this, arguments);
    };

    //=============================================================================
    // Window_MenuStatus
    //  前衛・後衛で描画位置を変更します。
    //=============================================================================
    Window_MenuStatus.shiftWidth = 24;
    Window_MenuStatus.prototype.drawActorFace = function (actor, x, y, width, height) {
        if (actor.isRearguard()) {
            arguments[1] += Window_MenuStatus.shiftWidth;
        }
        Window_Base.prototype.drawActorFace.apply(this, arguments);
    };

    Window_MenuStatus.prototype.drawActorSimpleStatus = function (actor, x, y, width) {
        if (actor.isRearguard()) {
            arguments[1] += Window_MenuStatus.shiftWidth;
            arguments[3] -= Window_MenuStatus.shiftWidth;
        }
        Window_Base.prototype.drawActorSimpleStatus.apply(this, arguments);
    };

    //=============================================================================
    // Window_ActorCommand
    //  チェンジコマンドを追加定義します。
    //=============================================================================
    var _Window_ActorCommand_makeCommandList = Window_ActorCommand.prototype.makeCommandList;
    Window_ActorCommand.prototype.makeCommandList = function () {
        _Window_ActorCommand_makeCommandList.apply(this, arguments);
        if (this._actor && paramSkillIdChange) {
            this.addChangeCommand();
        }
    };

    Window_ActorCommand.prototype.addChangeCommand = function () {
        var skill = $dataSkills[paramSkillIdChange];
        this.addCommand(skill ? skill.name : 'Change', 'change', true);
    };

    //=============================================================================
    // Sprite_Battler
    //  前衛・後衛によって位置を変動させます。
    //=============================================================================
    var _Sprite_Battler_initMembers = Sprite_Battler.prototype.initMembers;
    Sprite_Battler.prototype.initMembers = function () {
        _Sprite_Battler_initMembers.apply(this, arguments);
        this._formationX = 0;
        this._formationY = 0;
    };

    var _Sprite_Battler_setHome = Sprite_Battler.prototype.setHome;
    Sprite_Battler.prototype.setHome = function (x, y) {
        _Sprite_Battler_setHome.apply(this, arguments);
        this._formationX = this._battler.getFormationOffsetX();
        this._formationY = this._battler.getFormationOffsetY();
    };

    var _Sprite_Battler_updatePosition = Sprite_Battler.prototype.updatePosition;
    Sprite_Battler.prototype.updatePosition = function () {
        _Sprite_Battler_updatePosition.apply(this, arguments);
        this.updateFormation();
        this.x += this._formationX;
        this.y += this._formationY;
    };

    Sprite_Battler.prototype.updateFormation = function () {
        var targetFormationX = this._battler.getFormationOffsetX();
        var targetFormationY = this._battler.getFormationOffsetY();
        if (targetFormationX > this._formationX) {
            this._formationX = Math.min(this._formationX + paramChangeSpeed, targetFormationX);
        }
        if (targetFormationX < this._formationX) {
            this._formationX = Math.max(this._formationX - paramChangeSpeed, targetFormationX);
        }
        if (targetFormationY > this._formationY) {
            this._formationY = Math.min(this._formationY + paramChangeSpeed, targetFormationY);
        }
        if (targetFormationY < this._formationY) {
            this._formationY = Math.max(this._formationY - paramChangeSpeed, targetFormationY);
        }
    };
})();