code:
//
//  条件付き○○用ベース ver1.07
//
// ------------------------------------------------------
// Copyright (c) 2016 Yana
// Released under the MIT license
// http://opensource.org/licenses/mit-license.php
// ------------------------------------------------------
//
// author Yana
//

var Imported = Imported || {};
Imported['ConditionallyCore'] = 1.07;

/*:
 * @plugindesc ver1.07/条件付き○○のプラグインを使用するのに、必要となる条件をまとめたベースプラグインです。
 * @author Yana
 *
 * @help プラグインコマンドはありません。
 *
 * このプラグインは、条件ドロップ、条件変化スキル、条件付き追加効果を使用するときに必要となります。
 *
 * 条件の設定方法
 *
 * ○○××
 *
 * というように組み合わせて設定します。
 *
 * ○○=対象
 * ××=基本条件
 * ◇◇=条件設定
 *
 * となります。
 *
 * ======================================================
 * ○○で設定可能な対象
 * 対象
 * 味方
 * 敵
 * 討伐者
 * (何もなし)
 * ・対象=行動の対象が条件を満たすか判定します。
 * ・味方=仲間全体のうちいずれかが条件を満たすか判定します。
 * ・敵=敵全体のうちいずれかが条件を満たすか判定します。
 * ・討伐者=トドメを刺した者が条件を満たすか判定します。(条件ドロップ用)
 * ・(何もなし)=使用者または倒された対象が条件を満たすか判定します。
 * ------------------------------------------------------
 * ××で設定可能な条件
 * ターン条件:○以上
 * ターン条件:○以下
 * ・現在(または倒した時)のターン数が○以上(以下)の時、条件を満たします。
 * ターン条件:○-○以内
 * ターン条件:○-○以外
 * ・現在(または倒した時)のターン数が○-○以内(以外)の時、条件を満たします。
 * ターン条件:○,○,○,○・・・・
 * ・現在(または倒した時)のターン数が○,○,○,○・・・のいずれかの時、条件を満たします。
 *
 * 例:
 * ターン条件:5以上
 * ターン条件:5以下
 * ターン条件:5-7以内
 * ターン条件:5-8以外
 * ターン条件:5,6,7,8,9,10
 * ------------------------------------------------------
 * ステート条件:×:○,○,○,○・・・・
 * ・現在(または倒した時)にステート○,○,○,○・・・のうち×個にかかっている時、条件を満たします。
 *
 * 例:
 * ステート条件:×:○,○,○,○・・・・
 * 対象ステート条件:×:○,○,○,○・・・・
 * 討伐者ステート条件:×:○,○,○,○・・・・
 * 敵ステート条件:×:○,○,○,○・・・・
 * 味方ステート条件:×:○,○,○,○・・・・
 * -----------------------------------------------------
 * ID条件
 * ID条件:○,○,○,○・・・
 * ・対象のIDが○のいずれかの時に条件を満たします。
 *
 * 例:
 * ID条件:4,5,6,7,8
 * 対象ID条件:4,5,6,7,8
 * 討伐者ID条件:4,5,6,7,8
 * 敵ID条件:4,5,6,7,8
 * 味方ID条件:4,5,6,7,8
 * ------------------------------------------------------
 * ステータス条件:××○以上
 * ステータス条件:××○%以上
 * ステータス条件:××○以下
 * ステータス条件:××○%以下
 * ・現在(または倒した時)に××のステータスが○(%)以上(以下)の時、条件を満たします。
 * ステータス条件:××○-○以内
 * ステータス条件:××○%-○%以内
 * ステータス条件:××○-以外
 * ステータス条件:××○%-○%以外
 * ・現在(または倒した時)に××のステータスが○(%)-○(%)以内(以外)の時、条件を満たします。
 * ステータス条件:××○の倍数
 * ・現在(または倒した時)に××のステータスが○の倍数の時、条件を満たします。
 *
 * 例:
 * ステータス条件:HP40%以上
 * ステータス条件:HP40%-80%以内
 * 対象ステータス条件:攻撃力30以上
 * 敵ステータス条件:運4の倍数
 * 味方ステータス条件:回避率100%以上
 * 討伐者ステータス条件:HP再生率10%以下
 * ------------------------------------------------------
 * 属性有効度条件:×,○：◇%以上,○：◇%以下,○：◇%以上・・・
 * ・現在(または倒した時)に○の属性の有効度が◇%以上(以下)のうちx個を満たす時、条件を満たします。
 *
 * 例：
 * 属性有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 対象属性有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 討伐者属性有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 敵属性有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 味方属性有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * ------------------------------------------------------
 * ステート有効度条件:×,○：◇%以上,○：◇%以下,○：◇%以上・・・
 * ・現在(または倒した時)に○番のステートの有効度が◇%以上(以下)のうちx個を満たす時、条件を満たします。
 *
 * 例：
 * ステート有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 対象ステート有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 討伐者ステート有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 敵ステート有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * 味方ステート有効度条件：2,1:40%以上,2:60%以上,3:70%以下
 * ------------------------------------------------------
 * 人数条件:○以上
 * 人数条件:○以下
 * ・現在(または倒した時)の戦闘参加人数が○以上(以下)の時、条件を満たします。
 * 人数条件:○-○以内
 * 人数条件:○-○以外
 * ・現在(または倒した時)の戦闘参加人数が○-○以内(以外)の時、条件を満たします。
 * 人数条件:○,○,○,○・・・・
 * ・現在(または倒した時)の戦闘参加人数が○,○,○,○・・・のいずれかの時、条件を満たします。
 *
 * 例:
 * 人数条件:5以上
 * 人数条件:5以下
 * 人数条件:5-7以内
 * 人数条件:5-8以外
 * 人数条件:5,6,7,8,9,10
 * 対象人数条件:5以上
 * 対象人数条件:5以下
 * 対象人数条件:5-7以内
 * 対象人数条件:5-8以外
 * 対象人数条件:5,6,7,8,9,10
 * ------------------------------------------------------
 * ××で設定可能な条件
 * 生存人数条件:○以上
 * 生存人数条件:○以下
 * ・現在の生存人数が○以上(以下)の時、条件を満たします。
 * 生存人数条件:○-○以内
 * 生存人数条件:○-○以外
 * ・現在の生存人数が○-○以内(以外)の時、条件を満たします。
 * 生存人数条件:○,○,○,○・・・・
 * ・現在の生存人数が○,○,○,○・・・のいずれかの時、条件を満たします。
 *
 * 例:
 * 生存人数条件:5以上
 * 生存人数条件:5以下
 * 生存人数条件:5-7以内
 * 生存人数条件:5-8以外
 * 生存人数条件:5,6,7,8,9,10
 * 対象生存人数条件:5以上
 * 対象生存人数条件:5以下
 * 対象生存人数条件:5-7以内
 * 対象生存人数条件:5-8以外
 * 対象生存人数条件:5,6,7,8,9,10
 * ------------------------------------------------------
 * 変数条件:×が○以上
 * 変数条件:×が○以下
 * ・変数×の値が○以上(以下)の時、条件を満たします。
 *
 * 例:
 * 変数条件:5が10以上
 * 変数条件:5が10以下
 * ------------------------------------------------------
 * スイッチ条件:×がON
 * スイッチ条件:×がOFF
 * ・スイッチ×の値がON(OFF)の時、条件を満たします。
 *
 * 例:
 * スイッチ条件:5がON
 * スイッチ条件:5がOFF
 * ------------------------------------------------------
 * 装備条件:×:○◇,○◇,○◇・・・
 * ・対象がW(A)◇番の装備を×個装備している時、条件を満たします。
 *
 * 例:
 * 装備条件:2:W1,A2,W3,A1
 * ------------------------------------------------------
 * 装備タイプ条件:×:○◇,○◇,○◇・・・
 * ・対象がW(A)◇番の装備タイプの装備をぞれぞれ×個装備している時、条件を満たします。
 *
 * 例:
 * 装備タイプ条件:2:W1,A2,W3,A1
 * ------------------------------------------------------
 * クラス条件:○,○,○,○・・・
 * ・対象のクラスが○,○,○,○・・・のいずれかの時、条件を満たします。対象がエネミーの場合条件を満たしません。
 *
 * 例:
 * クラス条件:1,2,3,4,5
 * ------------------------------------------------------
 * 特徴メモ条件:○○
 * ・対象の持っている特徴に○○をメモに含むものがある時、条件を満たします。
 *
 * 例:
 * 特徴メモ条件:<テスト>
 * ------------------------------------------------------
 * メンバー条件:×:○,○,○・・・
 * ・待機メンバーを含むパーティメンバーの中にIDが○のメンバーがx人以上いる場合、条件を満たします。
 * 敵を対象とした場合、隠れているエネミーを含む敵メンバーで判定されます。
 *
 * 例:
 * メンバー条件:2:1,2,3,4
 * ------------------------------------------------------
 * 戦闘メンバー条件:x:○,○,○・・・
 * ・戦闘メンバーの中にIDが○のメンバーがx人以上いる場合、条件を満たします。
 * 敵を対象とした場合、隠れているエネミーを含まない敵メンバーで判定されます。
 *
 * 例:
 * 戦闘メンバー条件:2:1,2,3,4
 * ------------------------------------------------------
 * スクリプト条件:○
 * ・○をevalで判定します。
 * スクリプト条件は、複数行にわたって記述することができます。
 *
 * 例:
 * スクリプト条件:$gameVairbales.value[10] === 10
 * ------------------------------------------------------
 * 以下は基本的に条件ドロップ専用です
 * ------------------------------------------------------
 * 属性条件:○,○,○,○・・・
 * ・トドメに使用した属性が○,○,○,○のうちいずれかの時、条件を満たします。
 *
 * 例:
 * 属性条件:1,2,3,4,5
 * ------------------------------------------------------
 * ダメージ条件:○以上
 * ダメージ条件:○以下
 * ・トドメを刺した時に与えたダメージが、○以上(以下)の時、条件を満たします。
 * ダメージ条件:○-○以内
 * ダメージ条件:○-○以外
 * ・トドメを刺した時に与えたダメージが、○-○以内(以外)の時、条件を満たします。
 *
 * 例:
 * ダメージ条件:120以上
 * ダメージ条件:120-240以内
 * ------------------------------------------------------
 * レベル条件:最大○以上
 * レベル条件:最低○以上
 * レベル条件:平均○以下
 * レベル条件:討伐者○以上
 * ・(パーティの)レベルが最大(最低、平均または討伐者)○以上(以下)の時、条件を満たします。
 * レベル条件:最大○-○以内
 * レベル条件:最低○-○以外
 * レベル条件:平均○-○以内
 * レベル条件:討伐者○-○以外
 * ・(パーティの)レベルが最大(最低、平均または討伐者)○-○以内(以外)の時、条件を満たします。
 *
 * 例:
 * レベル条件:最大5以上
 * レベル条件:最低3以下
 * レベル条件:平均4-8以内
 * レベル条件:討伐者5-10以外
 * ------------------------------------------------------
 * 通常攻撃
 * スキル攻撃
 * アイテム攻撃
 * 通常攻撃以外
 * スキル攻撃以外
 * アイテム攻撃以外
 * ・トドメを刺した攻撃が上記いずれかの時、条件を満たします。
 *
 * 例:
 * 通常攻撃
 * 通常攻撃以外
 * スキル攻撃
 * スキル攻撃以外
 * アイテム攻撃
 * アイテム攻撃以外
 * ------------------------------------------------------
 * スキル条件:○,○,○,○・・・
 * アイテム条件:○,○,○,○・・・
 * ・トドメを刺した攻撃が○,○,○,○番のいずれかのスキル(アイテム)の時、条件を満たします。
 *
 * 例:
 * スキル条件:1,2,3,4,5
 * アイテム条件:1,2,3,4,5
 *
 * ------------------------------------------------------
 * 利用規約
 * ------------------------------------------------------
 * 当プラグインはMITライセンスで公開されています。
 * 使用に制限はありません。商用、アダルト、いずれにも使用できます。
 * 二次配布も制限はしませんが、サポートは行いません。
 * 著作表示は任意です。行わなくても利用できます。
 * 要するに、特に規約はありません。
 * バグ報告や使用方法等のお問合せはネ実ツクールスレ、または、Twitterにお願いします。
 * https://twitter.com/yanatsuki_
 * 素材利用は自己責任でお願いします。
 * ------------------------------------------------------
 * 更新履歴:
 * ver1.07:
 * dieStatus周りの処理を修正。
 * ver1.06:
 * 利用規約をMITライセンスに変更。
 * スクリプト条件で、v=変数,s=スイッチが使えるように変更。
 * 装備条件、装備タイプ条件が正常に機能していなかったバグを修正。
 * ver1.05:
 * スクリプト条件が正常に機能していないバグを修正。
 * ver1.04:
 * 生存人数条件を使用した際、エラーが発生するバグを修正。
 * ver1.03:
 * メンバー条件を追加。
 * 戦闘メンバー条件を追加。
 * スクリプト条件を追加。
 * ver1.02:
 * スイッチ条件が正常に機能していないバグを修正。
 * ver1.01:
 * ステータス条件に最大HPと最大MPの条件を追加。
 * ver1.00:
 * 公開
 */

function ConditionallyManager() {
    throw new Error('This is a static class');
}

(function(){

    'use strict';

    ConditionallyManager.initConditions = function(){
        this._targetCheck = ['^(?:t|対象)','^(?:p|味方)','^(?:tr|敵)','^(?:f|討伐者)','^'];

        this._preCheck = [
            ['(?:turn|ターン条件)','value1','value2','value3'],
            ['(?:state|ステート条件)','value3'],
            ['(?:id|ID条件)','value3'],
            ['(?:status|ステータス条件)','value4','value5','value6'],
            ['(?:eEfficacy|属性有効度条件)','value7'],
            ['(?:sEfficacy|ステート有効度条件)','value7'],
            ['(?:size|人数条件)','value1','value2','value3'],
            ['(?:aSize|生存人数条件)','value1','value2','value3'],
            ['(?:variable|変数条件)','value8'],
            ['(?:switch|スイッチ条件)','value9'],
            ['(?:equip|装備条件)','value10'],
            ['(?:eType|装備タイプ条件)','value10'],
            ['(?:class|クラス条件)','value3'],
            ['(?:memo|特徴メモ条件)','value11'],
            ['(?:element|属性条件)','value3'],
            ['(?:damage|ダメージ条件)','value1','value2'],
            ['(?:level|レベル条件)','value12'],
            ['(?:normalA|通常攻撃)','value13'],
            ['(?:skillA|スキル攻撃)','value13'],
            ['(?:itemA|アイテム攻撃)','value13'],
            ['(?:skill|スキル条件)','value3'],
            ['(?:item|アイテム条件)','value3'],
            ['(?:member|メンバー条件)','value3'],
            ['(?:battleMember|戦闘メンバー条件)','value3'],
            ['(?:script|スクリプト条件)','value13']
        ];

        this._valueCheck = {
            'value1':/(\d+)以([上下])/,
            'value2':/(\d+)\-(\d+)以([内外])/,
            'value3':/(?:(\d+):)*(\d+(?:,\d+)*)/,
            'value4':/(.+?)(\d+)+([%％])?以([上下])/,
            'value5':/(.+?)(\d+)+([%％])?\-+(\d+)+([%％])?以([内外])/,
            'value6':/(.+?)(\d+)+の倍数/,
            'value7':/(?:(\d+),)*(\d+:\d+[%％]以[上下]?)(?:[,，]\d+:\d+[%％]以[上下]?)*/,
            'value8':/(\d+)が(\d+)以([上下])?/,
            'value9':/(\d+)が(O[NF])/,
            'value10':/(?:(\d+):)*([WAwa])(\d+)(?:[,，]([WAwa])(\d+))*/,
            'value11':/(.+)/,
            'value12':/(.+)(\d+)以([上下])/,
            'value13':/(.+)*/
        }
    };

    ConditionallyManager.makeCondition = function(text){
        if(!this._targetCheck){ this.initConditions() }

        for(var i=0;i<this._targetCheck.length;i++){
            for(var j=0;j<this._preCheck.length;j++){
                var reg1 = this._targetCheck[i] + this._preCheck[j][0] + '[:：]*';
                if (text.match(new RegExp(reg1+'(.+)*'))){
                    var reg = RegExp.$1;
                    var ary = this._preCheck[j].slice(1);
                    for(var t=0;t<ary.length;t++){
                        var result = null;
                        if(reg.match(this._valueCheck[ary[t]])){
                            switch (ary[t]){
                            case 'value1':
                                return [i,j,[parseInt(RegExp.$1),RegExp.$2]];
                            case 'value2':
                                return [i,j,[parseInt(RegExp.$1),parseInt(RegExp.$2),RegExp.$3]];
                            case 'value3':
                                result = RegExp.$2.split(',');
                                result.unshift(RegExp.$1);
                                for(var u=0;u<result.length;u++){
                                    result[u] = parseInt(result[u]);
                                }
                                return [i,j,result];
                            case 'value4':
                                result = [RegExp.$1,parseInt(RegExp.$2),RegExp.$3,RegExp.$4];
                                return [i,j,result];
                            case 'value5':
                                result = [RegExp.$1,parseInt(RegExp.$2),RegExp.$3,
                                          parseInt(RegExp.$4),RegExp.$5,RegExp.$6];
                                return [i,j,result];
                            case 'value6':
                                return [i,j,[RegExp.$1,parseInt(RegExp.$2)]];
                            case 'value7':
                                var num = null;
                                result = reg.split(',');
                                if (result[0].match(/(\d+):(\d+):(\d+)[%％]以([上下])/)){
                                    num = (parseInt(RegExp.$1));
                                }
                                for (var u=0;u<result.length;u++){
                                    result[u].match(/(\d+):(\d+)[%％]以([上下])/);
                                    result[u] = [parseInt(RegExp.$1),parseInt(RegExp.$2),RegExp.$3];
                                }
                                result.unshift(num);
                                return [i,j,result];
                            case 'value8':
                                return [i,j,[parseInt(RegExp.$1),parseInt(RegExp.$2)]];
                            case 'value9':
                                return [i,j,[parseInt(RegExp.$1),RegExp.$2]];
                            case 'value10':
                                var num = null;
                                result = reg.split(',');
                                if (result[0].match(/(\d+):([WAwa])(\d+)/)){
                                    num = parseInt(RegExp.$1);
                                }
                                for(var u=0;u<result.length;u++){
                                    result[u].match(/([WAwa])(\d+)/);
                                    result[u] = [RegExp.$1,parseInt(RegExp.$2)];
                                }
                                result.unshift(num);
                                return [i,j,result];
                            case 'value11':
                                result = reg.replace(/\r/,'');
                                result = result.replace(/\n/,'');
                                return [i,j,result];
                            case 'value12':
                                return [i,j,[RegExp.$1,parseInt(RegExp.$2),RegExp.$3]];
                            case 'value13':
                                return [i,j,[RegExp.$1]];
                            default:
                                break;
                            }
                        }
                    }
                }
            }
        }
        return [-1,-1,text];
    };

    ConditionallyManager.setTargets = function(user,target,type){
        switch(type){
        case 0:
        case 3:
            return [target];
        case 1:
            return user.isActor() ? $gameParty.members() : $gameTroop.members();
        case 2:
            return user.isActor() ? $gameTroop.members() : $gameParty.members();
        case 4:
            return [user];
        default:
        }
    };

    ConditionallyManager.checkConditions = function(user, target, conditions, dieStatus){
        this._dieStatus = dieStatus;
        var fcns = [
                    this.checkTurn,
                    this.checkState,
                    this.checkId,
                    this.checkStatus,
                    this.checkEleEff,
                    this.checkStaEff,
                    this.checkSize,
                    this.checkASize,
                    this.checkVariable,
                    this.checkSwitch,
                    this.checkEquip,
                    this.checkEType,
                    this.checkClass,
                    this.checkMemo,
                    this.checkElement,
                    this.checkDamage,
                    this.checkLevel,
                    this.checkNormalA,
                    this.checkSkillA,
                    this.checkItemA,
                    this.checkSkill,
                    this.checkItem,
                    this.checkMember,
                    this.checkBattleMember,
                    this.checkScript
                   ];
        for(var i=0;i<conditions.length;i++){
            if (conditions[i][1] === -1 && conditions[i-1][1] === 24) {
                for(var j=i;j<conditions.length;j++){
                    if (conditions[j][1] === -1 && conditions[i][2]){
                        conditions[i-1][2] += '\n' + conditions[j][2];
                        conditions[j][1] = -2;
                    } else {
                        i = j - 1;
                        break;
                    }
                }
            }
        }
        for(var i=0;i<conditions.length;i++){
            if (!conditions[i]){ continue }
            if (conditions[i][1] < 0){ continue }
            var targets = this.setTargets(user,target,conditions[i][0]);
            if (!fcns[conditions[i][1]].call(this,targets,conditions[i][2])){
                return false;
            }
        }
        return true;
    };

    ConditionallyManager.checkValues = function(code,num,value1,value2){
        switch (code){
        case '上':
            return num >= value1;
        case '下':
            return num <= value1;
        case '内':
            return num >= value1 && num <= value2;
        case '外':
            return num <= value1 && num >= value2;
        default:
            return (value1 % num) === 0;
        }
    };

    ConditionallyManager.checkTurn = function(targets,conditions){
        var turn = this._dieStatus ? this._dieStatus['turn'] : BattleManager.ccTurnCount();
        var code = conditions[conditions.length-1];
        switch (code){
        case '上':
        case '下':
        case '内':
        case '外':
            return this.checkValues(code,turn,conditions[0],conditions[1]);
        default:
            return conditions.indexOf(turn) !== -1;
        }
    };
    ConditionallyManager.checkState = function(targets,conditions){
        conditions = conditions.clone();
        var num = conditions.shift();
        num = isNaN(num) ? 1 : num;
        for(var i=0;i<targets.length;i++){
            var count = 0;
            for(var j=0;j<conditions.length;j++){
                if (targets[i].isStateAffected(conditions[j])){
                    count++;
                    if (count === num){ return true }
                }
            }
        }
        return false;
    };
    ConditionallyManager.checkId = function(targets,conditions){
        conditions = conditions.clone();
        conditions.shift();
        for(var i=0;i<targets.length;i++){
            var id = targets[i].isActor() ? targets[i]._actorId : targets[i]._enemyId;
            if (conditions.indexOf(id) >= 0){ return true }
        }
        return false;
    };
    ConditionallyManager.checkStatus = function(targets,conditions){
        var code = conditions[conditions.length-1];
        var value1 = conditions[1];
        var value2 = (conditions.length - 1) > 3 ? conditions[3] : null ;
        for(var i=0;i<targets.length;i++){
            var num = this.getStatusValue(targets[i],conditions);
            if (this.checkValues(code, num, value1, value2)){ return true }
        }
        return false;
    };
    ConditionallyManager.checkEleEff = function(targets,conditions){
        conditions = conditions.clone();
        var num = conditions.shift();
        num = num ? num : 1;
        for(var i=0;i<targets.length;i++){
            var count = 0;
            for(var j=0;j<conditions.length;j++){
                var code = conditions[j][2];
                var value = targets[i].elementRate(conditions[j][0]) * 100;
                var value1 = conditions[j][1];
                if (this.checkValues(code,value,value1)){
                    count++;
                    if (count === num){ return true }
                }
            }
        }
        return false;
    };
    ConditionallyManager.checkStaEff = function(targets,conditions){
        conditions = conditions.clone();
        var num = conditions.shift();
        num = num ? num : 1;
        for(var i=0;i<targets.length;i++){
            var count = 0;
            for(var j=0;j<conditions.length;j++){
                var code = conditions[j][2];
                var value = targets[i].stateRate(conditions[j][0]) * 100;
                var value1 = conditions[j][1];
                if (this.checkValues(code,value,value1)){
                    count++;
                    if (count === num){ return true }
                }
            }
        }
        return false;

    };
    ConditionallyManager.checkSize = function(targets,conditions){
        var code = conditions[conditions.length-1];
        var num = null;
        var value1 = conditions[0];
        var value2 = conditions[1];
        if (targets[0].isActor()){
            num = $gameParty.members().length;
        }else{
            num = $gameTroop.members().length;
        }
        switch (code){
        case '上':
        case '下':
        case '内':
        case '外':
            return this.checkValues(code,num,value1,value2);
        default:
            return conditions.indexOf(num) !== -1;
        }
    };
    ConditionallyManager.checkASize = function(targets,conditions){
        var code = conditions[conditions.length-1];
        var num = null;
        var value1 = conditions[0];
        var value2 = conditions[1];
        if (targets[0].isActor()){
            num = $gameParty.aliveMembers().length;
        }else{
            num = $gameTroop.aliveMembers().length;
        }
        switch (code){
        case '上':
        case '下':
        case '内':
        case '外':
            return this.checkValues(code,num,value1,value2);
        default:
            return conditions.indexOf(num) !== -1;
        }

    };
    ConditionallyManager.checkVariable = function(targets,conditions){
        var value = $gameVariables.value(conditions[0]);
        var code = conditions[2];
        return this.checkValues(code,value,conditions[1]);
    };
    ConditionallyManager.checkSwitch = function(targets,conditions){
        var flag = $gameSwitches.value(conditions[0]);
        return flag === (conditions[1] === 'ON');
    };
    ConditionallyManager.checkEquip = function(targets,conditions){
        if(!targets[0]){ return false }
        if(targets[0].isEnemy()){ return false }
        conditions = conditions.clone();
        var num = conditions.shift();
        num = num ? num : 1;
        for (var i=0;i<targets.length;i++){
            var count = 0;
            for (var j=0;j<conditions.length;j++){
                var eVar = conditions[j][0];
                var eId = conditions[j][1];
                switch(eVar){
                    case 'W':
                    case 'w':
                        if (targets[i].isEquipped($dataWeapons[eId])){ count++ }
                        break;
                    case 'A':
                    case 'a':
                        if (targets[i].isEquipped($dataArmors[eId])){ count++ }
                        break;
                }
                if (count >= num){ return true }
            }
        }
        return false;
    };
    ConditionallyManager.checkEType = function(targets,conditions){
        if(!targets[0]){ return false }
        if(targets[0].isEnemy()){ return false }
        var result = [];
        conditions = conditions.clone();
        var num = conditions.shift();
        num = num ? num : 1;
        for (var i=0;i<targets.length;i++){
            var count = 0;
            for (var j=0;j<conditions.length;j++){
                var eVar = conditions[j][0];
                var eId = conditions[j][1];
                switch(eVar){
                    case 'W':
                    case 'w':
                        result = targets[i].weapons().filter(function(equip){
                            return equip.wtypeId === eId;
                        });
                        break;
                    case 'A':
                    case 'a':
                        result = targets[i].armors().filter(function(equip){
                            return equip.atypeId === eId;
                        });
                }
                if (result.length > 0){ count++ }
                if (count >= num){ return true }
            }
        }
        return false;

    };
    ConditionallyManager.checkClass = function(targets,conditions){
        conditions = conditions.clone();
        var num = conditions.shift();
        num = num ? num : 1;
        var count = 0;
        for(var i=0;i<targets.length;i++){
            if (conditions.indexOf(targets[i]._classId) !== -1){ count++ }
            if (count >= num){ return true }
        }
        return false;
    };
    ConditionallyManager.checkMemo = function(targets,conditions){
        var result = [];
        for(var i=0;i<targets.length;i++){
            result = targets[i].traitObjects().filter(function(object){
                return object.note.match('^' + conditions);
            });
            if (result.length > 0){ return true }
        }
        return false;
    };
    ConditionallyManager.checkElement = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var finisher = this.decodeFinisher(this._dieStatus['finisher']);
        var item = this._dieStatus['item'];
        var elements = item.damage.elementId < 0 ? finisher.attackElements() : [item.damage.elementId];
        conditions = conditions.clone();
        var num = conditions.shift();
        var count = 0;
        num = isNaN(num) ? 1 : num;
        for(var i=0;i<conditions.length;i++){
            if (elements.indexOf(conditions[i]) >= 0){
                count++;
                if (num === count){ return true }
            }
        }
        return false;
    };
    ConditionallyManager.decodeFinisher = function(finisher) {
        if (finisher[0]) return $gameParty.members()[finisher[1]];
        return $gameTroop.members()[finisher[1]];
    };
    ConditionallyManager.checkDamage = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var code = conditions[1];
        var num = this._dieStatus['damage'];
        var value1 = conditions[0];
        return this.checkValues(code,num,value1);
    };
    ConditionallyManager.checkLevel = function(targets,conditions){
        var num = 0;
        var code = conditions[2];
        var value = conditions[1];
        switch(conditions[0]){
        case '平均':
            $gameParty.members().forEach(function(member){ num += member._level });
            num /= $gameParty.members().length;
            break;
        case '最大':
            $gameParty.members().forEach(function(member){ if (num < member._level){ num = member._level} });
            break;
        case '最低':
            num = 999999;
            $gameParty.members().forEach(function(member){ if (num > member._level){ num = member._level} });
            break;
        case '討伐者':
            if (!this._dieStatus){ return false }
            num = this._dieStatus['finisher']._level;
            break;
        default:
        }
        return this.checkValues(code,num,value);
    };
    ConditionallyManager.checkNormalA = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var item = this._dieStatus['item'];
        var finisher = this.decodeFinisher(this._dieStatus['finisher']);
        var attackSkill = $dataSkills[finisher.attackSkillId()];
        var result = item === attackSkill;
        return conditions[0] === '以外' ? !result : result;
    };
    ConditionallyManager.checkSKillA = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var item = this._dieStatus['item'];
        var result = DataManager.isSkill(item);
        return conditions[0] === '以外' ? !result : result;
    };
    ConditionallyManager.checkItemA = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var item = this._dieStatus['item'];
        var result = DataManager.isItem(item);
        return conditions[0] === '以外' ? !result : result;
    };
    ConditionallyManager.checkSkill = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var skill = this._dieStatus['item'];
        conditions = conditions.clone();
        conditions.shift();
        if (DataManager.isItem(skill)){ return false }
        return conditions.indexOf(skill.id) >= 0;
    };
    ConditionallyManager.checkItem = function(targets,conditions){
        if (!this._dieStatus){ return false }
        var item = this._dieStatus['item'];
        conditions = conditions.clone();
        conditions.shift();
        if (DataManager.isSkill(item)){ return false }
        return conditions.indexOf(item.id) >= 0;

    };
    ConditionallyManager.checkMember = function(targets,conditions){
        conditions = conditions.clone();
        var members = $gameParty.allMembers();
        if (targets[0].isEnemy()){ members = $gameTroop.members() }
        var num = conditions.shift();
        num = num ? num : 1;
        var count = 0;
        for(var i=0;i<members.length;i++){
            var id = members[i].isActor() ? members[i]._actorId : members[i]._enemyId;
            if (conditions.indexOf(id) !== -1){ count++ }
            if (count >= num){ return true }
        }
        return false;
    };
    ConditionallyManager.checkBattleMember = function(targets,conditions){
        conditions = conditions.clone();
        var members = $gameParty.battleMembers();
        if (targets[0].isEnemy()){ members = $gameTroop.members().filter(function(enemy){ return enemy.isAppeared()}) }
        var num = conditions.shift();
        num = num ? num : 1;
        var count = 0;
        for(var i=0;i<members.length;i++){
            var id = members[i].isActor() ? members[i]._actorId : members[i]._enemyId;
            if (conditions.indexOf(id) !== -1){ count++ }
            if (count >= num){ return true }
        }
        return false;
    };
    ConditionallyManager.checkScript = function(targets,conditions){
        conditions = [conditions].clone();
        var a = targets;
        var v = $gameVariables._data;
        var s = $gameSwitches._data;
        return !!eval(conditions[0][0]);
    };
    ConditionallyManager.getStatusValue = function(subject, conditions){
        switch(conditions[0]){
        case 'Level':
        case 'Lv':
        case TextManager.basic(0):
        case TextManager.basic(1):
            return subject.isActor() ? subject._level : 0;
        case 'HP':
        case 'ＨＰ':
        case TextManager.basic(2):
        case TextManager.basic(3):
            if (conditions[2] === ''){
                return subject.hp;
            }else{
                return Math.floor((subject.hp / subject.mhp) * 100);
            }
        case 'MP':
        case 'ＭＰ':
        case TextManager.basic(4):
        case TextManager.basic(5):
            if (conditions[2] === ''){
                return subject.mp;
            }else{
                return Math.floor((subject.mp / subject.mmp) * 100);
            }
        case 'TP':
        case 'ＴＰ':
        case TextManager.basic(6):
        case TextManager.basic(7):
            return subject.tp;
        case 'EXP':
        case 'ＥＸＰ':
        case '経験値':
        case TextManager.basic(8):
        case TextManager.basic(9):
            return subject.isActor() ? subject.currentExp() : 0;
        case '最大HP':
        case '最大ＨＰ':
        case TextManager.param[0]:
            return subject.mhp;
        case '最大MP':
        case '最大ＭＰ':
        case TextManager.param[1]:
            return subject.mmp;
        case 'ATK':
        case 'ＡＴＫ':
        case '攻撃力':
        case TextManager.param[2]:
            return subject.atk;
        case 'DEF':
        case 'ＤＥＦ':
        case '防御力':
        case TextManager.param[3]:
            return subject.def;
        case 'MAT':
        case 'ＭＡＴ':
        case '魔法力':
        case TextManager.param[4]:
            return subject.mat;
        case 'MDF':
        case 'ＭＤＦ':
        case '魔法防御':
        case TextManager.param[5]:
            return subject.mdf;
        case 'AGI':
        case 'ＡＧＩ':
        case '敏捷性':
        case TextManager.param[6]:
            return subject.agi;
        case 'LUK':
        case 'ＬＵＫ':
        case '運':
        case TextManager.param[7]:
            return subject.luk;
        case 'HIT':
        case 'ＨＩＴ':
        case '命中率':
        case TextManager.param[8]:
            return Math.floor(subject.hit * 100);
        case 'EVA':
        case 'ＥＶＡ':
        case '回避率':
        case TextManager.param[9]:
            return Math.floor(subject.eva * 100);
        case 'CRI':
        case 'ＣＲＩ':
        case '会心率':
            return Math.floor(subject.cri * 100);
        case 'CEV':
        case 'ＣＥＶ':
        case '会心回避率':
            return Math.floor(subject.cev * 100);
        case 'MEV':
        case 'ＭＥＶ':
        case '魔法回避率':
            return Math.floor(subject.mev * 100);
        case 'MRF':
        case 'ＭＲＦ':
        case '魔法反射率':
            return Math.floor(subject.mrf * 100);
        case 'CNT':
        case 'ＣＮＴ':
        case '反撃率':
            return Math.floor(subject.cnt * 100);
        case 'HRG':
        case 'ＨＲＧ':
        case 'HP再生率':
        case 'ＨＰ再生率':
            return Math.floor(subject.hrg * 100);
        case 'MRG':
        case 'ＭＲＧ':
        case 'MP再生率':
        case 'ＭＰ再生率':
            return Math.floor(subject.mrg * 100);
        case 'TRG':
        case 'ＴＲＧ':
        case 'TP再生率':
        case 'ＴＰ再生率':
            return Math.floor(subject.trg * 100);
        case 'TGR':
        case 'ＴＧＲ':
        case '狙われ率':
            return Math.floor(subject.tgr * 100);
        case 'GRD':
        case 'ＧＲＤ':
        case '防御効果率':
            return Math.floor(subject.grd * 100);
        case 'REC':
        case 'ＲＥＣ':
        case '回復効果率':
            return Math.floor(subject.rec * 100);
        case 'PHA':
        case 'ＰＨＡ':
        case '薬の知識':
            return Math.floor(subject.pha * 100);
        case 'MCR':
        case 'ＭＣＲ':
        case 'MP消費率':
        case 'ＭＰ消費率':
            return Math.floor(subject.mcr * 100);
        case 'TCR':
        case 'ＴＣＲ':
        case 'TPチャージ率':
        case 'ＴＰチャージ率':
            return Math.floor(subject.tcr * 100);
        case 'PDR':
        case 'ＰＤＲ':
        case '物理ダメージ率':
            return Math.floor(subject.pdr * 100);
        case 'MDR':
        case 'ＭＤＲ':
        case '魔法ダメージ率':
            return Math.floor(subject.mdr * 100);
        case 'FDR':
        case 'ＦＤＲ':
        case '床ダメージ率':
            return Math.floor(subject.fdr * 100);
        case 'EXR':
        case 'ＥＸＲ':
        case '経験値獲得率':
            return Math.floor(subject.exr * 100);
        default:
            return 0;
        }
    };

    BattleManager.ccTurnCount = function(turn){
        return this.isInputting() ? ($gameTroop._turnCount + 1) : $gameTroop._turnCount;
    };

    var _CnC_GAction_repeatTargets = Game_Action.prototype.repeatTargets;
    Game_Action.prototype.repeatTargets = function(targets){
        BattleManager._lastTargets = targets;
        return _CnC_GAction_repeatTargets.call(this,targets);
    };

    var _CnC_GAction_executeDamage = Game_Action.prototype.executeDamage;
    Game_Action.prototype.executeDamage = function(target, value){
        this.subject()._lastDamage += value;
        _CnC_GAction_executeDamage.call(this,target,value);
    };

    var _CnC_BManager_startAction = BattleManager.startAction;
    BattleManager.startAction = function(){
        _CnC_BManager_startAction.call(this);
        this._subject._lastDamage = 0;
        this._lastAction = this._subject.currentAction();
    };

}());


code:
//=============================================================================
// CounterExtend.js
// ----------------------------------------------------------------------------
// (C)2016 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.10.1 2021/03/03 1.10.0のタグが命中タイプ「必中」の場合にも作動していた問題を修正
// 1.10.0 2021/03/03 特徴の「反撃率」の代わりになるタグ<CE_反撃:n>を定義
// 1.9.5 2021/01/25 ヘルプの文言を修正
// 1.9.4 2020/04/07 NRP_CountTimeBattle.jsと併用したとき、戦闘行動の強制による反撃でコマンド入力が回ってきてしまう競合を修正
// 1.9.3 2019/12/30 スキルの属性を指定してからタイプを「なし」にした場合でも、スクリプト「action.hasElement」が元々指定していた属性を返してしまう問題を修正
// 1.9.2 2019/06/09 戦闘行動の強制による反撃を行わない設定のとき、反撃後の自動ステート解除で反撃を有効にするステートを解除した場合、
//                  反撃による敵の行動キャンセルが行われない問題を修正
// 1.9.1 2019/05/02 クロスカウンターで、相手の攻撃が当たった場合のみ反撃する場合は、身代わりによる肩代わりも除外するよう仕様変更
// 1.9.0 2019/04/30 クロスカウンターで、相手の攻撃が当たった場合のみ反撃、もしくは外れた場合のみ反撃できる設定を追加
// 1.8.3 2019/01/29 クロスカウンターによって敵を全滅された後の戦闘で、一部の反撃エフェクトが表示される場合がある問題を修正
// 1.8.2 2019/01/13 クロスカウンター有効時、反撃可能かどうかの再チェックを行うよう修正
//                  「コスト不足で失敗」パラメータ有効時、スキル封印についても考慮するよう修正
// 1.8.1 2018/12/19 クロスカウンター有効時、攻撃によって戦闘不能になったバトラーの反撃が実行される問題を修正
// 1.8.0 2018/12/07 相手が攻撃してきたスキルで反撃する機能を追加
// 1.7.1 2018/09/26 「戦闘行動の強制」を使用しない反撃方法でスキルアニメーションとコモンイベントが呼ばれない問題を修正
//                  反撃が失敗しなかった場合も任意のステートを解除できる機能を追加
// 1.7.0 2018/09/26 「戦闘行動の強制」を使用しない反撃方法を追加しました。動作に若干の違いがあります
// 1.6.0 2018/08/19 コスト不足で反撃が失敗した場合に任意のステートを解除できる機能を追加
//                  魔法反撃に対してコスト不足時発動失敗する機能が正常に動いていなかった問題を修正
// 1.5.0 2018/04/25 スキルに対して個別に反撃されやすさを設定できるようになりました。
// 1.4.4 2018/03/10 反撃条件にスクリプトを使用する際、攻撃してきた相手の情報をtargetで正しく取得できていなかった問題を修正
// 1.4.3 2017/08/09 反撃条件に属性を指定する際に「通常攻撃」を指定した場合も考慮する関数を追加
// 1.4.2 2017/07/12 複数のバトラーが同時に反撃を行った場合に全員分の反撃が正常に行われない問題を修正
// 1.4.1 2017/07/11 1.4.0の機能追加以降、スキル反撃を行うとアクター本来の行動がキャンセルされる場合がある問題を修正
// 1.4.0 2017/06/13 反撃スキルに指定した効果範囲と連続回数が適用されるようになりました。
//                  攻撃を受けてから反撃するクロスカウンター機能を追加
// 1.3.3 2017/06/10 CustumCriticalSoundVer5.jsとの競合を解消
// 1.3.2 2017/05/20 BattleEffectPopup.jsとの併用でスキルによる反撃が表示されない問題を修正。
// 1.3.1 2017/04/22 1.3.0の機能がBattleEngineCoreで動作するよう修正
// 1.3.0 2017/04/09 反撃に成功した時点で相手の行動をキャンセルできる機能を追加
// 1.2.2 2017/02/07 端末依存の記述を削除
// 1.2.1 2017/01/12 メモ欄の値が空で設定された場合にエラーが発生するかもしれない問題を修正
// 1.2.0 2016/11/27 反撃スキルIDを複数設定できる機能を追加。条件に応じたスキルで反撃します。
// 1.1.0 2016/11/20 特定のスキルによる反撃や反撃条件を細かく指定できる機能を追加
// 1.0.0 2016/11/15 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc CounterExtendPlugin
 * @author triacontane
 *
 * @param PayCounterCost
 * @desc 固有スキルによる反撃がコスト消費するかどうかを設定します。(ON/OFF)
 * @default false
 * @type boolean
 *
 * @param FailureCostShortage
 * @desc 固有スキルによる反撃がコスト不足もしくは封印されている場合、反撃は行いません。(ON/OFF)
 * @default false
 * @type boolean
 *
 * @param UsingForceAction
 * @desc 戦闘行動の強制によって反撃を実現します。通常はONで、希望通りの動作をしない場合は試してみてください。
 * @default true
 * @type boolean
 *
 * @param EraseStateTiming
 * @desc 反撃後にメモ欄で指定したステートを解除できます。メモ欄の指定がなければ解除されません。
 * @default 0
 * @type select
 * @option Failure
 * @value 0
 * @option Success
 * @value 1
 * @option Always
 * @value 2
 *
 * @help 反撃の仕様を拡張します。
 * 魔法に対する反撃や、特定のスキルを使った反撃、
 * 特定の条件下でのみ発動する反撃などが作成できます。
 *
 * 前提として魔法反撃以外は特徴「反撃率」の設定が必須です。
 * そのうえで、反撃の仕様を以下の通り拡張します。
 *
 * 機能詳細
 *
 * 1. 魔法攻撃を受けた場合もカウンターが発動するようになります。
 * 特徴を有するメモ欄(※1)に以下の通り入力してください。
 *
 * <CE_魔法反撃:50>      # 魔法攻撃を受けた場合に50%の確率で反撃します。
 * <CE_MagicCounter:50>  # 同上
 *
 * ※1 アクター、職業、武器、防具、ステート、敵キャラが該当します。
 *
 * 数値を指定しない場合は、物理攻撃と同様の反撃率が適用されます。
 *
 * <CE_魔法反撃>     # 魔法攻撃を受けた場合にもともとの反撃率で反撃します。
 * <CE_MagicCounter> # 同上
 *
 * 2. 反撃時のスキルを個別に設定することができます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 *
 * <CE_反撃スキルID:\v[1]>    # 反撃時に変数[1]のIDのスキルを使用します。
 * <CE_CounterSkillId:\v[1]>  # 同上
 * <CE_魔法反撃スキルID:3>    # 魔法反撃時にID[3]のスキルを使用します。
 * <CE_MagicCounterSkillId:3> # 同上
 *
 * 反撃スキルは、通常は攻撃してきた相手をターゲットとしますが
 * 味方対象のスキルなどは自分や味方に対して使用します。
 *
 * IDに[0]を指定すると、相手が攻撃してきたスキルで反撃します。
 *
 * 3. 反撃条件をJavaScript計算式の評価結果を利用できます。
 * 反撃条件を満たさない場合は反撃は実行されません。
 * 特定のスキルに対してのみ反撃したり、特定の条件下でのみ反撃したりできます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 *
 * <CE_反撃条件:v(1) < 100>    # 変数[1]が100より小さければ反撃します。
 * <CE_CounterCond:v(1) < 100> # 同上(※2)
 * <CE_魔法反撃条件:s(1)>         # スイッチ[1]がONなら魔法反撃します。
 * <CE_MagicCounterCond:s(1)>     # 同上
 *
 * 実行したスキルの情報はローカル変数「skill」で参照できます。
 * また、アクションオブジェクトはローカル変数「action」で参照できます。
 * <CE_反撃条件:skill.id === 10>      # スキルIDが[10]なら反撃します。
 * <CE_反撃条件:action.hasElement(3)> # スキル属性IDが[3]なら反撃します。
 *
 * 対象のバトラー情報は「this」で、相手のバトラー情報は「target」で参照できます。
 * <CE_反撃条件:this.hpRate() < 0.5> # 自分のHPが50%を下回ると反撃します。
 *
 * ※2 文章、スクリプト中で不等号を使いたい場合、以下のように記述してください。
 * < → <
 * > → >
 *
 * 4. 複数の反撃スキルおよび反撃条件を設定できます。
 * 複数定義する場合は、末尾に「_n」を付与してください。
 * <CE_反撃スキルID:4>   # スイッチ[1]がONならスキルID[4]で反撃
 * <CE_反撃条件:s(1)>    #
 * <CE_反撃スキルID_1:5> # スイッチ[2]がONならスキルID[5]で反撃
 * <CE_反撃条件_1:s(2)>  #
 * <CE_反撃スキルID_2:6> # スイッチ[3]がONならスキルID[6]で反撃
 * <CE_反撃条件_2:s(3)>  #
 *
 * 複数の条件を一度に満たした場合は、インデックスの小さい方が
 * 優先して使用されます。
 *
 * 5. 反撃実行時に専用のアニメーションIDを再生します。
 * 特徴を有するメモ欄に以下の通り入力してください。
 * <CE_反撃アニメID:20>       # 反撃実行前にアニメーション[20]を再生します。
 * <CE_CounterAnimationId:20> # 同上
 *
 * 6. 反撃成功時に相手の行動をキャンセル（中断）できます。
 * 相手が全体攻撃のスキルを使った場合に、反撃成功時点で以降の相手には
 * 当たらなくなります。特徴を有するメモ欄に以下の通り入力してください。
 * <CE_キャンセル> # 反撃成功時に相手の行動をキャンセル
 * <CE_Cancel>     # 同上
 *
 * 7. デフォルトの反撃とは異なり、攻撃を受けてから反撃させることができます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 * <CE_クロスカウンター> # 相手の攻撃を受けてから反撃します。
 * <CE_CrossCounter>     # 同上
 * ※クロスカウンターはスキルによる反撃の場合のみ有効です。
 *
 * クロスカウンターの発動条件を設定できます。
 * <CE_クロスカウンター条件:1> # 相手の行動が当たった場合のみ反撃
 * <CE_CrossCounterCond:1>     # 同上
 * 0 : 常に
 * 1 : 相手の行動が当たった場合のみ
 * 2 : 相手の行動が外れた場合のみ
 *
 * 8. スキルに対して個別に反撃のされやすさを設定できます。
 * スキルのメモ欄に以下の通り入力してください。
 * <CE_反撃増減:50> # 相手の反撃確率が50%増加する
 * <CE_反撃増減:-100> # 相手の反撃確率が100%減少する
 * ※確率は元の値に乗算ではなく加算(減算)となります。
 *
 * 9. コスト不足で反撃が失敗した場合、任意のステートを解除できます。
 * <CE_ステート解除:1> # コスト不足で反撃が失敗するとステート[1]が解除
 * <CE_StateClear:1>   # 同上
 *
 * ※  パラメータ「戦闘行動強制による反撃」による動作の違い
 * パラメータ「戦闘行動強制による反撃」を有効にすると以下の動作となります。
 * 1. クロスカウンターが動作する
 * 2. 反撃スキルに設定した「効果範囲」および「連続回数」が有効になる
 * 3. 複数回行動する敵の場合、敵の全行動が終わってから反撃する
 * 4. 複数人が反撃しかつ反撃スキルにコモンイベントを設定していた場合、
 *    反撃したバトラー全員のコモンイベントが実行される。
 *
 * 逆に無効にすると以下の動作となります。
 * 1. クロスカウンターが動作しない
 * 2. 反撃スキルに設定した「効果範囲」および「連続回数」が無視される
 * 3. 複数回行動する敵の場合、敵の行動ごとに反撃を実行する
 * 4. 複数人が反撃しかつ反撃スキルにコモンイベントを設定していた場合、
 *    最後に反撃したバトラーのコモンイベントのみが実行される。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * This plugin is released under the MIT License.
 */
/*:ja
 * @plugindesc 反撃拡張プラグイン
 * @author トリアコンタン
 *
 * @param PayCounterCost
 * @text 反撃コスト消費
 * @desc 固有スキルによる反撃がコスト消費するかどうかを設定します。(ON/OFF)
 * @default false
 * @type boolean
 *
 * @param FailureCostShortage
 * @text コスト不足で失敗
 * @desc 固有スキルによる反撃がコスト不足もしくは封印されている場合、反撃は行いません。(ON/OFF)
 * @default false
 * @type boolean
 *
 * @param UsingForceAction
 * @text 戦闘行動強制による反撃
 * @desc 戦闘行動の強制によって反撃を実現します。通常はONで、希望通りの動作をしない場合は試してみてください。
 * @default true
 * @type boolean
 *
 * @param EraseStateTiming
 * @text ステート解除タイミング
 * @desc 反撃後にメモ欄で指定したステートを解除できます。メモ欄の指定がなければ解除されません。
 * @default 0
 * @type select
 * @option 失敗した場合(コスト不足により)
 * @value 0
 * @option 成功した場合
 * @value 1
 * @option 常に
 * @value 2
 *
 * @help 反撃の仕様を拡張します。
 * 魔法に対する反撃や、特定のスキルを使った反撃、
 * 特定の条件下でのみ発動する反撃などが作成できます。
 *
 * 具体的な機能詳細は以下の通りです。
 *
 * 機能詳細
 *
 * 1. 魔法攻撃を受けた場合もカウンターが発動するようになります。
 * 特徴を有するメモ欄(※1)に以下の通り入力してください。
 *
 * <CE_魔法反撃:50>      # 魔法攻撃を受けた場合に50%の確率で反撃します。
 * <CE_MagicCounter:50>  # 同上
 *
 * ※1 アクター、職業、武器、防具、ステートが該当します。
 *
 * 数値を指定しない場合は、物理攻撃と同様の反撃率が適用されます。
 *
 * <CE_魔法反撃>     # 魔法攻撃を受けた場合にもともとの反撃率で反撃します。
 * <CE_MagicCounter> # 同上
 *
 * 2. 反撃時のスキルを個別に設定することができます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 *
 * <CE_反撃スキルID:\v[1]>    # 反撃時に変数[1]のIDのスキルを使用します。
 * <CE_CounterSkillId:\v[1]>  # 同上
 * <CE_魔法反撃スキルID:3>    # 魔法反撃時にID[3]のスキルを使用します。
 * <CE_MagicCounterSkillId:3> # 同上
 *
 * 反撃スキルは、通常は攻撃してきた相手をターゲットとしますが
 * 味方対象のスキルなどは自分や味方に対して使用します。
 *
 * IDに[0]を指定すると、相手が攻撃してきたスキルで反撃します。
 *
 * 3. 反撃条件をJavaScript計算式の評価結果を利用できます。
 * 反撃条件を満たさない場合は反撃は実行されません。
 * 特定のスキルに対してのみ反撃したり、特定の条件下でのみ反撃したりできます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 *
 * <CE_反撃条件:v(1) < 100>    # 変数[1]が100より小さければ反撃します。
 * <CE_CounterCond:v(1) < 100> # 同上(※2)
 * <CE_魔法反撃条件:s(1)>         # スイッチ[1]がONなら魔法反撃します。
 * <CE_MagicCounterCond:s(1)>     # 同上
 *
 * 実行したスキルの情報はローカル変数「skill」で参照できます。
 * また、アクションオブジェクトはローカル変数「action」で参照できます。
 * <CE_反撃条件:skill.id === 10>      # スキルIDが[10]なら反撃します。
 * <CE_反撃条件:action.hasElement(3)> # スキル属性IDが[3]なら反撃します。
 *
 * 対象のバトラー情報は「this」で、相手のバトラー情報は「target」で参照できます。
 * <CE_反撃条件:this.hpRate() < 0.5> # 自分のHPが50%を下回ると反撃します。
 *
 * ※2 文章、スクリプト中で不等号を使いたい場合、以下のように記述してください。
 * < → <
 * > → >
 *
 * 4. 複数の反撃スキルおよび反撃条件を設定できます。
 * 複数定義する場合は、末尾に「_n」を付与してください。
 * <CE_反撃スキルID:4>   # スイッチ[1]がONならスキルID[4]で反撃
 * <CE_反撃条件:s(1)>    #
 * <CE_反撃スキルID_1:5> # スイッチ[2]がONならスキルID[5]で反撃
 * <CE_反撃条件_1:s(2)>  #
 * <CE_反撃スキルID_2:6> # スイッチ[3]がONならスキルID[6]で反撃
 * <CE_反撃条件_2:s(3)>  #
 *
 * 複数の条件を一度に満たした場合は、インデックスの小さい方が
 * 優先して使用されます。
 *
 * 5. 反撃実行時に専用のアニメーションIDを再生します。
 * 特徴を有するメモ欄に以下の通り入力してください。
 * <CE_反撃アニメID:20>       # 反撃実行前にアニメーション[20]を再生します。
 * <CE_CounterAnimationId:20> # 同上
 *
 * 6. 反撃成功時に相手の行動をキャンセル（中断）できます。
 * 相手が全体攻撃のスキルを使った場合に、反撃成功時点で以降の相手には
 * 当たらなくなります。特徴を有するメモ欄に以下の通り入力してください。
 * <CE_キャンセル> # 反撃成功時に相手の行動をキャンセル
 * <CE_Cancel>     # 同上
 *
 * 7. デフォルトの反撃とは異なり、攻撃を受けてから反撃させることができます。
 * 特徴を有するメモ欄に以下の通り入力してください。
 * <CE_クロスカウンター> # 相手の攻撃を受けてから反撃します。
 * <CE_CrossCounter>     # 同上
 * ※クロスカウンターはスキルによる反撃の場合のみ有効です。
 *
 * クロスカウンターの発動条件を設定できます。
 * <CE_クロスカウンター条件:1> # 相手の行動が当たった場合のみ反撃
 * <CE_CrossCounterCond:1>     # 同上
 * 0 : 常に
 * 1 : 相手の行動が当たった場合のみ
 * 2 : 相手の行動が外れた場合のみ
 *
 * 8. スキルに対して個別に反撃のされやすさを設定できます。
 * スキルのメモ欄に以下の通り入力してください。
 * <CE_反撃増減:50> # 相手の反撃確率が50%増加する
 * <CE_反撃増減:-100> # 相手の反撃確率が100%減少する
 * ※確率は元の値に乗算ではなく加算(減算)となります。
 *
 * 9. コスト不足で反撃が失敗した場合、任意のステートを解除できます。
 * <CE_ステート解除:1> # コスト不足で反撃が失敗するとステート[1]が解除
 * <CE_StateClear:1>   # 同上
 *
 * ※  パラメータ「戦闘行動強制による反撃」による動作の違い
 * パラメータ「戦闘行動強制による反撃」を有効にすると以下の動作となります。
 * 1. クロスカウンターが動作する
 * 2. 反撃スキルに設定した「効果範囲」および「連続回数」が有効になる
 * 3. 複数回行動する敵の場合、敵の全行動が終わってから反撃する
 *
 * 逆に無効にすると以下の動作となります。
 * 1. クロスカウンターが動作しない
 * 2. 反撃スキルに設定した「効果範囲」および「連続回数」が無視される
 * 3. 複数回行動する敵の場合、敵の行動ごとに反撃を実行する
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

var Imported = Imported || {};

(function() {
    'use strict';
    var metaTagPrefix = 'CE_';

    var getArgEval = function(arg, min, max) {
        if (arguments.length < 2) min = -Infinity;
        if (arguments.length < 3) max = Infinity;
        return (eval(convertEscapeCharacters(arg)) || 0).clamp(min, max);
    };

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

    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 convertEscapeCharacters = function(text) {
        if (isNotAString(text)) text = '';
        text            = text.replace(/>?/gi, '>');
        text            = text.replace(/<?/gi, '<');
        var windowLayer = SceneManager._scene._windowLayer;
        return windowLayer ? windowLayer.children[0].convertEscapeCharacters(text) : text;
    };

    var isNotAString = function(args) {
        return String(args) !== args;
    };

    //=============================================================================
    // パラメータの取得と整形
    //=============================================================================
    /**
     * Create plugin parameter. param[paramName] ex. param.commandPrefix
     * @param pluginName plugin name(EncounterSwitchConditions)
     * @returns {Object} Created parameter
     */
    var createPluginParameter = function(pluginName) {
        var paramReplacer = function(key, value) {
            if (value === 'null') {
                return value;
            }
            if (value[0] === '"' && value[value.length - 1] === '"') {
                return value;
            }
            try {
                return JSON.parse(value);
            } catch (e) {
                return value;
            }
        };
        var parameter     = JSON.parse(JSON.stringify(PluginManager.parameters(pluginName), paramReplacer));
        PluginManager.setParameters(pluginName, parameter);
        return parameter;
    };
    var param                 = createPluginParameter('CounterExtend');

    //=============================================================================
    // Game_BattlerBase
    //  行動制約が有効なステートデータを取得します。
    //=============================================================================
    Game_BattlerBase.prototype.isValidMagicCounter = function() {
        return this.traitObjects().some(function(traitObject) {
            return getMetaValues(traitObject, ['魔法反撃', 'MagicCounter']);
        });
    };

    Game_BattlerBase.prototype.isCounterCancel = function() {
        return this.traitObjects().some(function(traitObject) {
            return getMetaValues(traitObject, ['キャンセル', 'Cancel']);
        });
    };

    Game_BattlerBase.prototype.isCrossCounter = function() {
        return this.traitObjects().some(function(traitObject) {
            return getMetaValues(traitObject, ['クロスカウンター', 'CrossCounter']);
        });
    };

    Game_BattlerBase.prototype.getMagicCounterRate = function() {
        return this.traitObjects().reduce(function(prevValue, traitObject) {
            var metaValue = getMetaValues(traitObject, ['魔法反撃', 'MagicCounter']);
            return metaValue ? Math.max(getArgEval(metaValue) / 100, prevValue) : prevValue;
        }, 0);
    };

    Game_BattlerBase.prototype.getCounterAnimationId = function() {
        var counterAnimationId = 0;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, ['反撃アニメID', 'CounterAnimationId']);
            if (metaValue) {
                counterAnimationId = getArgEval(metaValue, 1);
                return true;
            }
            return false;
        }.bind(this));
        return counterAnimationId;
    };

    Game_BattlerBase.prototype.getCrossCounterCondition = function() {
        var crossCounterCondition = 0;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, ['クロスカウンター条件', 'CrossCounterCond']);
            if (metaValue) {
                crossCounterCondition = parseInt(getArgString(metaValue)) || 0;
                return true;
            }
            return false;
        }.bind(this));
        return crossCounterCondition;
    };

    Game_BattlerBase.prototype.reserveCounterSkillId = function(names, originalSkillId) {
        this._reserveCounterSkillId = 0;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, names);
            if (metaValue) {
                this._reserveCounterSkillId = getArgEval(metaValue, 0) || originalSkillId;
                return true;
            }
            return false;
        }.bind(this));
        return this._reserveCounterSkillId;
    };

    Game_BattlerBase.prototype.getCounterCustomRate = function(names, action, target) {
        if (!this.canPaySkillCostForCounter()) {
            if (param.EraseStateTiming !== 1) {
                this.eraseStateCounterFailure();
            }
            return 0;
        }
        var counterCondition;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, names);
            if (metaValue) {
                counterCondition = getArgString(metaValue);
                return true;
            }
            return false;
        }.bind(this));
        return counterCondition ? this.executeCounterScript(counterCondition, action, target) : 1;
    };

    Game_BattlerBase.prototype.eraseStateCounterFailure = function() {
        var stateId = 0;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, ['ステート解除', 'StateClear']);
            if (metaValue) {
                stateId = getArgEval(metaValue, 1);
                return true;
            }
            return false;
        }.bind(this));
        if (stateId > 0) {
            this.eraseState(stateId);
        }
    };

    Game_BattlerBase.prototype.getCounterRate = function() {
        var counter = null;
        this.traitObjects().some(function(traitObject) {
            var metaValue = getMetaValues(traitObject, ['反撃', 'Counter']);
            if (metaValue) {
                counter = getArgEval(metaValue, 1);
                return true;
            }
            return false;
        }.bind(this));
        return counter;
    };

    Game_BattlerBase.prototype.executeCounterScript = function(counterCondition, action, target) {
        var skill     = action.item();
        // use in eval
        var v         = $gameVariables.value.bind($gameVariables);
        var s         = $gameSwitches.value.bind($gameSwitches);
        var elementId = skill.damage.elementId;
        var result;
        try {
            result = !!eval(counterCondition);
            if ($gameTemp.isPlaytest()) {
                console.log('Execute Script:' + counterCondition);
                console.log('Execute Result:' + result);
            }
        } catch (e) {
            console.error(e.toString());
            throw new Error('Failed To Execute Counter Condition Script :' + counterCondition);
        }
        return result ? 1 : 0;
    };

    Game_BattlerBase.prototype.getCounterSkillId = function() {
        return this.isReserveCounterSkill() ? this._reserveCounterSkillId : this.attackSkillId();
    };

    Game_BattlerBase.prototype.isReserveCounterSkill = function() {
        return !!this._reserveCounterSkillId;
    };

    Game_BattlerBase.prototype.canPaySkillCostForCounter = function() {
        return !param.FailureCostShortage || !this._reserveCounterSkillId ||
            this.meetsSkillConditions($dataSkills[this._reserveCounterSkillId]);
    };

    //=============================================================================
    // Game_Battler
    //  カウンター時のスキルコスト消費処理を別途定義します。
    //=============================================================================
    var _Game_Battler_useItem      = Game_Battler.prototype.useItem;
    Game_Battler.prototype.useItem = function(item) {
        if (this.isCounterSubject() && !param.PayCounterCost) return;
        _Game_Battler_useItem.apply(this, arguments);
        this.refresh();
    };

    Game_Battler.prototype.setCounterAction = function(target) {
        var counterSkillId = this.getCounterSkillId();
        var action         = new Game_Action(this);
        action.setSkill(counterSkillId);
        var counterTargetIndex;
        if (action.isForFriend()) {
            counterTargetIndex = this.friendsUnit().members().indexOf(this);
        } else {
            counterTargetIndex = target.friendsUnit().members().indexOf(target);
        }
        this._nativeActions  = this._actions;
        this._counterSubject = true;
        this.forceAction(counterSkillId, counterTargetIndex);
    };

    Game_Battler.prototype.clearCounterAction = function() {
        if (this._nativeActions && this._nativeActions.length > 0) {
            this._actions = this._nativeActions;
        }
        this._nativeActions  = null;
        this._counterSubject = false;
        if (param.EraseStateTiming !== 0) {
            this.eraseStateCounterFailure();
        }
    };

    Game_Battler.prototype.isCounterSubject = function() {
        return this._counterSubject;
    };

    var _Game_Battler_onAllActionsEnd      = Game_Battler.prototype.onAllActionsEnd;
    Game_Battler.prototype.onAllActionsEnd = function() {
        if (this.isCounterSubject()) {
            this.clearResult();
        } else {
            _Game_Battler_onAllActionsEnd.apply(this, arguments);
        }
    };

    var _Game_Battler_onBattleEnd = Game_Battler.prototype.onBattleEnd;
    Game_Battler.prototype.onBattleEnd = function() {
        _Game_Battler_onBattleEnd.apply(this, arguments);
        this.clearCounterAction();
    };

    //=============================================================================
    // Game_Action
    //  魔法反撃を可能にします。
    //=============================================================================
    Game_Action.prototype.getCounterAdditionalRate = function() {
        var rate = getMetaValues(this.item(), ['反撃増減', 'CounterAdditional']);
        return rate ? parseInt(rate) / 100 : 0;
    };

    var _Game_Action_itemCnt      = Game_Action.prototype.itemCnt;
    Game_Action.prototype.itemCnt = function(target) {
        // invalid by user action
        if (this.subject().isCounterSubject()) {
            return 0;
        }
        var cnt           = _Game_Action_itemCnt.apply(this, arguments);
        var additionalCnt = this.getCounterAdditionalRate();
        if (this.isMagical()) {
            return this.itemMagicCnt(target, additionalCnt);
        } else {
            var rate = this.reserveTargetCounterSkillId(target, false, 0);
            if (!cnt && this.isPhysical() && target.canMove()) {
                cnt = target.getCounterRate();
            }
            return rate * (cnt + additionalCnt);
        }
    };

    Game_Action.prototype.itemMagicCnt = function(target, additionalCnt) {
        if (target.isValidMagicCounter() && this.isMagical() && target.canMove()) {
            var rate = this.reserveTargetCounterSkillId(target, true, 0);
            return rate * ((target.getMagicCounterRate() || target.cnt) + additionalCnt);
        } else {
            return 0;
        }
    };

    Game_Action.prototype.reserveTargetCounterSkillId = function(target, magicFlg, depth) {
        var skillMetaNames = this.getMetaNamesForCounterExtend(['反撃スキルID', 'CounterSkillId'], magicFlg, depth);
        var counterSkill   = target.reserveCounterSkillId(skillMetaNames, this.item().id);
        if (counterSkill === 0 && depth > 0) {
            return 0;
        }
        var rateMetaNames = this.getMetaNamesForCounterExtend(['反撃条件', 'CounterCond'], magicFlg, depth);
        var counterRate   = target.getCounterCustomRate(rateMetaNames, this, this.subject());
        if (counterRate > 0 || depth > 100) {
            return counterRate;
        } else {
            return this.reserveTargetCounterSkillId(target, magicFlg, depth + 1);
        }
    };

    Game_Action.prototype.getMetaNamesForCounterExtend = function(names, magicFlg, depth) {
        if (depth > 0) {
            names[0] = names[0] + '_' + String(depth);
            names[1] = names[1] + '_' + String(depth);
        }
        if (magicFlg) {
            names[0] = '魔法' + names[0];
            names[1] = 'Magic' + names[1];
        }
        return names;
    };

    Game_Action.prototype.hasElement = function(elementId) {
        if (this.item().damage.type === 0) {
            return false;
        }
        var skillElementId = this.item().damage.elementId;
        // Normal attack elementID[-1]
        if (skillElementId === -1) {
            return this.subject().attackElements().contains(elementId);
        } else {
            return elementId === skillElementId;
        }
    };

    var _Game_Action_applyItemUserEffect = Game_Action.prototype.applyItemUserEffect;
    Game_Action.prototype.applyItemUserEffect = function(target) {
        _Game_Action_applyItemUserEffect.apply(this, arguments);
        this._targetForCounterExtend = target;
    };

    Game_Action.prototype.isValidCrossCounter = function(target) {
        if (target.isDead() || this.itemCnt(target) <= 0) {
            return false;
        }
        var cond = target.getCrossCounterCondition();
        return (cond === 1 && this._targetForCounterExtend === target) ||
            (cond === 2 && !this._targetForCounterExtend) ||
            cond === 0;
    };

    //=============================================================================
    // BattleManager
    //  スキルによる反撃を実装します。
    //=============================================================================
    var _BattleManager_initMembers = BattleManager.initMembers;
    BattleManager.initMembers      = function() {
        _BattleManager_initMembers.apply(this, arguments);
        this._counterBattlers = [];
    };

    var _BattleManager_startAction = BattleManager.startAction;
    BattleManager.startAction      = function() {
        this._actionCancel = false;
        if (this._subject.isCounterSubject()) {
            this._logWindow.displaySkillCounter(this._subject);
        }
        _BattleManager_startAction.apply(this, arguments);
    };

    var _BattleManager_invokeCounterAttack = BattleManager.invokeCounterAttack;
    BattleManager.invokeCounterAttack      = function(subject, target) {
        if (target.isCounterCancel()) {
            this._actionCancel = true;
        }
        if (!target.isReserveCounterSkill()) {
            _BattleManager_invokeCounterAttack.apply(this, arguments);
        } else if (param.UsingForceAction) {
            if (target.isCrossCounter()) {
                this.invokeNormalAction(subject, target);
                if (!this._action.isValidCrossCounter(target)) {
                    return;
                }
            }
            if (!target.isCounterSubject()) {
                this.prepareCounterSkill(subject, target);
            }
        } else if (subject.isAlive()) {
            var action = new Game_Action(target);
            action.setSkill(target.getCounterSkillId());
            action.apply(subject);
            action.applyGlobal();
            this._logWindow.displaySkillCounterAction(subject, target, action);
            if (param.EraseStateTiming !== 0) {
                target.eraseStateCounterFailure();
            }
        }
    };

    BattleManager.prepareCounterSkill = function(subject, target) {
        target.setCounterAction(subject);
        this._counterBattlers.push(target);
    };

    var _BattleManager_invokeAction = BattleManager.invokeAction;
    BattleManager.invokeAction      = function(subject, target) {
        if (this._actionCancel) return;
        _BattleManager_invokeAction.apply(this, arguments);
    };

    var _BattleManager_getNextSubject = BattleManager.getNextSubject;
    BattleManager.getNextSubject      = function() {
        if (this._subject && this._subject.isCounterSubject()) {
            this._subject.clearCounterAction();
        }
        if (this._counterBattlers.length > 0) {
            return this._counterBattlers.shift();
        }
        return _BattleManager_getNextSubject.apply(this, arguments);
    };

    //=============================================================================
    // Window_BattleLog
    //  スキルによる反撃を演出します。
    //=============================================================================
    Window_BattleLog.prototype.displaySkillCounter = function(subject) {
        var counterAnimation = subject.getCounterAnimationId();
        if (counterAnimation) {
            this.push('showAnimation', subject, [subject], counterAnimation);
            this.push('waitForAnimation');
        }
        if (!Imported.YEP_BattleEngineCore) {
            this.push('addText', TextManager.counterAttack.format(subject.name()));
        }
        // for BattleEffectPopup.js
        if (this.popupCounter) {
            this.popupCounter(subject);
        }
    };

    Window_BattleLog.prototype.displaySkillCounterAction = function(subject, target, action) {
        this.displaySkillCounter(target);
        this.startAction(target, action, [subject]);
        this.push('waitForAnimation');
        this.displayActionResults(target, subject);
        this.endAction(target);
    };

    var _Window_BattleLog_updateWaitMode      = Window_BattleLog.prototype.updateWaitMode;
    Window_BattleLog.prototype.updateWaitMode = function() {
        var waiting = false;
        switch (this._waitMode) {
            case 'animation':
                waiting = this._spriteset.isAnimationPlaying();
                break;
        }
        if (!waiting) {
            waiting = _Window_BattleLog_updateWaitMode.apply(this, arguments);
        }
        return waiting;
    };

    Window_BattleLog.prototype.waitForAnimation = function() {
        this.setWaitMode('animation');
    };

    // Resolve conflict for NRP_CountTimeBattle.js
    if (BattleManager.startInputActor) {
        var _BattleManager_startInput = BattleManager.startInput;
        BattleManager.startInput = function() {
            if (this._counterBattlers.length > 0) {
                this._subject = this.getNextSubject();
                this._phase = 'action';
            } else {
                _BattleManager_startInput.apply(this, arguments);
            }
        };

        var _BattleManager_endTurn = BattleManager.endTurn;
        BattleManager.endTurn = function() {
            if (this._subject.isCounterSubject()) {
                this._phase      = 'turnEnd';
                this._preemptive = false;
                this._surprise   = false;
            } else {
                _BattleManager_endTurn.apply(this, arguments);
            }
        }
    }
})();

repo_name: dazed/translations

license: unknown


code:
//=============================================================================
// CustomizeConfigDefault.js
// ----------------------------------------------------------------------------
// (C)2015 Triacontane
// This plugin is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 1.1.1 2020/09/13 Mano_InputConfig.jsと併用したとき、Option項目を消していると表示不整合が発生する競合を修正
// 1.1.0 2016/08/01 項目自体を非表示にする機能を追加しました。
// 1.0.3 2016/06/22 多言語対応
// 1.0.2 2016/01/17 競合対策
// 1.0.1 2015/11/01 既存コードの再定義方法を修正（内容に変化なし）
// 1.0.0 2015/11/01 初版
// ----------------------------------------------------------------------------
// [Blog]   : https://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc Setting default value for Options
 * @author triacontane
 *
 * @param AlwaysDash
 * @desc Always dash(ON/OFF)
 * @default OFF
 *
 * @param CommandRemember
 * @desc Command remember(ON/OFF)
 * @default OFF
 *
 * @param BgmVolume
 * @desc BGM Volume(0-100)
 * @default 100
 *
 * @param BgsVolume
 * @desc BGS Volume(0-100)
 * @default 100
 *
 * @param MeVolume
 * @desc ME Volume(0-100)
 * @default 100
 *
 * @param SeVolume
 * @desc SE Volume(0-100)
 * @default 100
 *
 * @param EraseAlwaysDash
 * @desc Erase AlwaysDash Option(ON/OFF)
 * @default OFF
 *
 * @param EraseCommandRemember
 * @desc Erase CommandRemember Option(ON/OFF)
 * @default OFF
 *
 * @param EraseBgmVolume
 * @desc Erase BgmVolume Option(ON/OFF)
 * @default OFF
 *
 * @param EraseBgsVolume
 * @desc Erase BgsVolume Option(ON/OFF)
 * @default OFF
 *
 * @param EraseMeVolume
 * @desc Erase MeVolume Option(ON/OFF)
 * @default OFF
 *
 * @param EraseSeVolume
 * @desc Erase SeVolume Option(ON/OFF)
 * @default OFF
 *
 * @help Setting default value for Options.
 *
 * This plugin is released under the MIT License.
 */
/*:ja
 * @plugindesc オプションデフォルト値設定プラグイン
 * @author トリアコンタン
 *
 * @param 常時ダッシュ
 * @desc 常にダッシュする。（Shiftキーを押している場合のみ歩行）(ON/OFF)
 * @default OFF
 *
 * @param コマンド記憶
 * @desc 選択したコマンドを記憶する。(ON/OFF)
 * @default OFF
 *
 * @param BGM音量
 * @desc BGMの音量。0-100
 * @default 100
 *
 * @param BGS音量
 * @desc BGSの音量。0-100
 * @default 100
 *
 * @param ME音量
 * @desc MEの音量。0-100
 * @default 100
 *
 * @param SE音量
 * @desc SEの音量。0-100
 * @default 100
 *
 * @param 常時ダッシュ消去
 * @desc 常時ダッシュの項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @param コマンド記憶消去
 * @desc コマンド記憶の項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @param BGM音量消去
 * @desc BGM音量の項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @param BGS音量消去
 * @desc BGS音量の項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @param ME音量消去
 * @desc ME音量の項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @param SE音量消去
 * @desc SE音量の項目を非表示にする。(ON/OFF)
 * @default OFF
 *
 * @help オプション画面で設定可能な項目のデフォルト値を指定した値に変更します。
 * 例えば、初回から常時ダッシュをONにしておけば
 * プレイヤーが設定を変更する手間を省くことができます。
 * この処理はconfig.rpgsaveが未作成の場合にのみ実行されます。
 *
 * また、項目そのものを消去することもできます。
 * 例えば、戦闘がないゲームでは「コマンド記憶」等は不要なので消去できます。
 *
 * このプラグインにはプラグインコマンドはありません。
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */
(function() {
    'use strict';
    var pluginName = 'CustomizeConfigDefault';

    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 getParamBoolean = function(paramNames) {
        var value = getParamOther(paramNames);
        return (value || '').toUpperCase() === 'ON';
    };

    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 paramAlwaysDash           = getParamBoolean(['AlwaysDash', '常時ダッシュ']);
    var paramCommandRemember      = getParamBoolean(['CommandRemember', 'コマンド記憶']);
    var paramBgmVolume            = getParamNumber(['BgmVolume', 'BGM音量'], 0, 100);
    var paramBgsVolume            = getParamNumber(['BgsVolume', 'BGS音量'], 0, 100);
    var paramMeVolume             = getParamNumber(['MeVolume', 'ME音量'], 0, 100);
    var paramSeVolume             = getParamNumber(['SeVolume', 'SE音量'], 0, 100);
    var paramEraseAlwaysDash      = getParamBoolean(['EraseAlwaysDash', '常時ダッシュ消去']);
    var paramEraseCommandRemember = getParamBoolean(['EraseCommandRemember', 'コマンド記憶消去']);
    var paramEraseBgmVolume       = getParamBoolean(['EraseBgmVolume', 'BGM音量消去']);
    var paramEraseBgsVolume       = getParamBoolean(['EraseBgsVolume', 'BGS音量消去']);
    var paramEraseMeVolume        = getParamBoolean(['EraseMeVolume', 'ME音量消去']);
    var paramEraseSeVolume        = getParamBoolean(['EraseSeVolume', 'SE音量消去']);

    //=============================================================================
    // ConfigManager
    //  それぞれの項目に初期値を与えます。
    //=============================================================================
    var _ConfigManagerApplyData = ConfigManager.applyData;
    ConfigManager.applyData     = function(config) {
        _ConfigManagerApplyData.apply(this, arguments);
        if (config.alwaysDash == null)      this.alwaysDash = paramAlwaysDash;
        if (config.commandRemember == null) this.commandRemember = paramCommandRemember;
        if (config.bgmVolume == null)       this.bgmVolume = paramBgmVolume;
        if (config.bgsVolume == null)       this.bgsVolume = paramBgsVolume;
        if (config.meVolume == null)        this.meVolume = paramMeVolume;
        if (config.seVolume == null)        this.seVolume = paramSeVolume;
    };

    //=============================================================================
    // Window_Options
    //  パラメータを空白にした項目を除去します。
    //=============================================================================
    var _Window_Options_makeCommandList = Window_Options.prototype.makeCommandList;
    Window_Options.prototype.makeCommandList = function() {
        _Window_Options_makeCommandList.apply(this, arguments);
        if (paramEraseAlwaysDash) this.eraseOption('alwaysDash');
        if (paramEraseCommandRemember) this.eraseOption('commandRemember');
        if (paramEraseBgmVolume) this.eraseOption('bgmVolume');
        if (paramEraseBgsVolume) this.eraseOption('bgsVolume');
        if (paramEraseMeVolume) this.eraseOption('meVolume');
        if (paramEraseSeVolume) this.eraseOption('seVolume');
    };

    Window_Options.prototype.eraseOption = function(symbol) {
        for (var i = 0; i < this._list.length; i++) {
            if (this._list[i].symbol === symbol) {
                this._list.splice(i, 1);
                // for Mano_InputConfig.js
                this.adjustIndexManoInputConfig(i);
                break;
            }
        }
    };

    Window_Options.prototype.adjustIndexManoInputConfig = function(index) {
        if (this._gamepadOptionIndex > index) {
            this._gamepadOptionIndex -= 1;
        }
        if (this._keyboardConfigIndex > index) {
            this._keyboardConfigIndex -= 1;
        }
    };
})();


code:
//=============================================================================
// CustomizeConfigItem.js
// ----------------------------------------------------------------------------
// (C) 2015 Triacontane
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// ----------------------------------------------------------------------------
// Version
// 2.1.2 2020/10/13 Mano_InputConfig.jsと併用したとき、項目を末尾以外に追加すると表示不整合が発生する競合を修正
// 2.1.0 2017/12/15 追加項目のデフォルト項目を含めた並び順を自由に設定できる機能を追加
//                  項目名称を日本語化
// 2.0.1 2017/10/15 2.0.0の修正によりスイッチ項目を有効にしたときにゲーム開始するとエラーになる問題を修正
// 2.0.0 2017/09/10 ツクールの型指定機能に対応し、各オプション項目を任意の数だけ追加できる機能を追加
// 1.2.3 2017/06/08 1.2.2の修正により起動できなくなっていた問題を修正
// 1.2.2 2017/05/27 競合の可能性のある記述（Objectクラスへのプロパティ追加）をリファクタリング
// 1.2.1 2016/12/08 1.2.0の機能追加以降、デフォルト項目で決定ボタンを押すとエラーになっていた現象を修正
// 1.2.0 2016/12/02 各項目で決定ボタンを押したときに実行されるスクリプトを設定できる機能を追加
// 1.1.1 2016/08/14 スイッチ項目、音量項目の初期値が無効になっていた問題を修正
// 1.1.0 2016/04/29 項目をクリックしたときに項目値が循環するよう修正
// 1.0.0 2016/01/17 初版
// ----------------------------------------------------------------------------
// [Blog]   : http://triacontane.blogspot.jp/
// [Twitter]: https://twitter.com/triacontane/
// [GitHub] : https://github.com/triacontane/
//=============================================================================

/*:
 * @plugindesc オプション任意項目作成プラグイン
 * @author トリアコンタン
 *
 * @param 数値項目
 * @desc 追加する数値項目のオプション項目情報です。
 * @default
 * @type struct<NumberData>[]
 *
 * @param 文字項目
 * @desc 追加する文字項目のオプション項目情報です。
 * @default
 * @type struct<StringData>[]
 *
 * @param スイッチ項目
 * @desc 追加するスイッチ項目のオプション項目情報です。
 * @default
 * @type struct<BooleanData>[]
 *
 * @param 音量項目
 * @desc 追加する音量項目のオプション項目情報です。
 * @default
 * @type struct<VolumeData>[]
 *
 * @help オプション画面に任意の項目を追加します。
 * 項目の種類は、以下の四種類があります。
 * 不要な項目は値を空に設定してください。
 *
 * ・スイッチ項目：
 * ON/OFFを選択する項目です。指定した番号のスイッチと値が同期されます。
 * オプションから値を設定すれば、それがスイッチに反映され、
 * スイッチを変更すれば、オプションの値に反映されます。
 * さらに、値はセーブデータ間で共有されます。
 * 隠しフラグを設定すると、オプション画面に表示されなくなります。
 * ゲームを進めないと出現しない項目などに利用できます。
 * 隠しフラグはプラグインコマンドから解除できます。
 *
 * スクリプトは上級者向け項目です。対象にカーソルを合わせて決定ボタンを
 * 押下すると指定したJavaScriptを実行できます。
 * 主に専用の設定画面などの遷移に使用します。
 *
 * ・数値項目：
 * 数値を選択する項目です。指定した番号の変数と値が同期されます。
 * スイッチ項目で指定した内容に加えて、
 * 最小値と最大値および一回の入力で変化する値を指定します。
 *
 * ・音量項目：
 * 音量を選択する項目です。BGMボリュームなどと同じ仕様で
 * キャラクターごとのボイス音量等に使ってください。
 *
 * ・文字項目：
 * 文字を選択する項目です。指定した文字の配列から項目を選択します。
 * 選択した文字のインデックス(開始位置は0)が変数に設定されます。
 * 初期値に設定する値もインデックスです。
 *
 * プラグインコマンド詳細
 *  イベントコマンド「プラグインコマンド」から実行。
 *  （パラメータの間は半角スペースで区切る）
 *
 *  CC_UNLOCK or
 *  オプション任意項目の隠し解除 [項目名]
 *  　指定した項目の隠しフラグを解除します。
 *  使用例：CC_UNLOCK 数値項目1
 *
 * 利用規約：
 *  作者に無断で改変、再配布が可能で、利用形態（商用、18禁利用等）
 *  についても制限はありません。
 *  このプラグインはもうあなたのものです。
 */

/*~struct~NumberData:
 * @param Name
 * @text 項目名称
 * @desc 項目の名称です。
 * @default 数値項目1
 *
 * @param DefaultValue
 * @text 初期値
 * @desc 項目の初期値です。
 * @default 0
 * @type number
 *
 * @param VariableID
 * @text 変数番号
 * @desc 項目の設定内容が格納される変数番号です。
 * @default 0
 * @type variable
 *
 * @param HiddenFlag
 * @text 隠しフラグ
 * @desc 項目がデフォルトで隠されるようになります。プラグインコマンドの実行で表示されます。
 * @default false
 * @type boolean
 *
 * @param Script
 * @text スクリプト
 * @desc 項目を決定したときに実行されるスクリプトです。
 * @default
 *
 * @param NumberMin
 * @text 最小値
 * @desc 項目の最小値です。
 * @default 0
 * @type number
 *
 * @param NumberMax
 * @text 最大値
 * @desc 項目の最大値です。
 * @default 100
 * @type number
 *
 * @param NumberStep
 * @text 変化値
 * @desc 項目を操作したときの数値の変化量です。
 * @default 20
 * @type number
 *
 * @param AddPosition
 * @text 追加位置
 * @desc 項目を追加する位置です。指定した項目の上に追加されます。
 * @default
 * @type select
 * @option 末尾に追加
 * @value
 * @option 常時ダッシュ
 * @value alwaysDash
 * @option コマンド記憶
 * @value commandRemember
 * @option BGM 音量
 * @value bgmVolume
 * @option BGS 音量
 * @value bgsVolume
 * @option ME 音量
 * @value meVolume
 * @option SE 音量
 * @value seVolume
 */
/*~struct~BooleanData:
 * @param Name
 * @text 項目名称
 * @desc 項目の名称です。
 * @default スイッチ項目1
 *
 * @param DefaultValue
 * @text 初期値
 * @desc 項目の初期値です。
 * @default false
 * @type boolean
 *
 * @param SwitchID
 * @text スイッチ番号
 * @desc 項目の設定内容が格納されるスイッチ番号です。
 * @default 0
 * @type switch
 *
 * @param HiddenFlag
 * @text 隠しフラグ
 * @desc 項目がデフォルトで隠されるようになります。プラグインコマンドの実行で表示されます。
 * @default false
 * @type boolean
 *
 * @param Script
 * @text スクリプト
 * @desc 項目を決定したときに実行されるスクリプトです。
 * @default
 *
 * @param displayTrue
 * @text Trueの時の表示
 * @desc Trueのときに表示されるテキスト
 * @default ON
 *
 * @param displayFalse
 * @text Falseの時の表示
 * @desc Falseのときに表示されるテキスト
 * @default OFF
 *
 * @param AddPosition
 * @text 追加位置
 * @desc 項目を追加する位置です。指定した項目の上に追加されます。
 * @default
 * @type select
 * @option 末尾に追加
 * @value
 * @option 常時ダッシュ
 * @value alwaysDash
 * @option コマンド記憶
 * @value commandRemember
 * @option BGM 音量
 * @value bgmVolume
 * @option BGS 音量
 * @value bgsVolume
 * @option ME 音量
 * @value meVolume
 * @option SE 音量
 * @value seVolume
 */
/*~struct~StringData:
 * @param Name
 * @text 項目名称
 * @desc 項目の名称です。
 * @default 文字列項目1
 *
 * @param DefaultValue
 * @text 初期値
 * @desc 項目の初期値です。インデックスの数値を指定します。
 * @default 0
 * @type number
 *
 * @param VariableID
 * @text 変数番号
 * @desc 項目の設定内容が格納される変数番号です。
 * @default 0
 * @type variable
 *
 * @param HiddenFlag
 * @text 隠しフラグ
 * @desc 項目がデフォルトで隠されるようになります。プラグインコマンドの実行で表示されます。
 * @default false
 * @type boolean
 *
 * @param Script
 * @text スクリプト
 * @desc 項目を決定したときに実行されるスクリプトです。
 * @default
 *
 * @param StringItems
 * @text 内容の配列
 * @desc 項目の設定内容の配列です。
 * @default
 * @type string[]
 *
 * @param AddPosition
 * @text 追加位置
 * @desc 項目を追加する位置です。指定した項目の上に追加されます。
 * @default
 * @type select
 * @option 末尾に追加
 * @value
 * @option 常時ダッシュ
 * @value alwaysDash
 * @option コマンド記憶
 * @value commandRemember
 * @option BGM 音量
 * @value bgmVolume
 * @option BGS 音量
 * @value bgsVolume
 * @option ME 音量
 * @value meVolume
 * @option SE 音量
 * @value seVolume
 */
/*~struct~VolumeData:
 * @param Name
 * @text 項目名称
 * @desc 項目の名称です。
 * @default 音量項目1
 *
 * @param DefaultValue
 * @text 初期値
 * @desc 項目の初期値です。
 * @default 0
 * @type number
 *
 * @param VariableID
 * @text 変数番号
 * @desc 項目の設定内容が格納される変数番号です。
 * @default 0
 * @type variable
 *
 * @param HiddenFlag
 * @text 隠しフラグ
 * @desc 項目がデフォルトで隠されるようになります。プラグインコマンドの実行で表示されます。
 * @default false
 * @type boolean
 *
 * @param Script
 * @text スクリプト
 * @desc 項目を決定したときに実行されるスクリプトです。
 * @default
 *
 * @param AddPosition
 * @text 追加位置
 * @desc 項目を追加する位置です。指定した項目の上に追加されます。
 * @default
 * @type select
 * @option 末尾に追加
 * @value
 * @option 常時ダッシュ
 * @value alwaysDash
 * @option コマンド記憶
 * @value commandRemember
 * @option BGM 音量
 * @value bgmVolume
 * @option BGS 音量
 * @value bgsVolume
 * @option ME 音量
 * @value meVolume
 * @option SE 音量
 * @value seVolume
 */

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

    var getParamString = function(paramNames) {
        var value = getParamOther(paramNames);
        return value == null ? '' : value;
    };

    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 getParamArrayJson = function(paramNames, defaultValue) {
        var value = getParamString(paramNames) || null;
        try {
            value = JSON.parse(value);
            if (value === null) {
                value = defaultValue;
            } else {
                value = value.map(function(valueData) {
                    return JSON.parse(valueData);
                });
            }
        } catch (e) {
            alert(`!!!Plugin param is wrong.!!!\nPlugin:${pluginName}.js\nName:[${paramNames}]\nValue:${value}`);
            value = defaultValue;
        }
        return value;
    };

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

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

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

    var getArgJson = function(arg, defaultValue) {
        try {
            arg = JSON.parse(arg || null);
            if (arg === null) {
                arg = defaultValue;
            }
        } catch (e) {
            alert(`!!!Plugin param is wrong.!!!\nPlugin:${pluginName}.js\nValue:${arg}`);
            arg = defaultValue;
        }
        return arg;
    };

    var iterate = function(that, handler) {
        Object.keys(that).forEach(function(key, index) {
            handler.call(that, key, that[key], index);
        });
    };

    //=============================================================================
    // パラメータの取得と整形
    //=============================================================================
    var param           = {};
    param.numberOptions = getParamArrayJson(['NumberOptions', '数値項目'], []);
    param.stringOptions = getParamArrayJson(['StringOptions', '文字項目'], []);
    param.switchOptions = getParamArrayJson(['SwitchOptions', 'スイッチ項目'], []);
    param.volumeOptions = getParamArrayJson(['VolumeOptions', '音量項目'], []);

    var localOptionWindowIndex = 0;

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

    Game_Interpreter.prototype.pluginCommandCustomizeConfigItem = function(command, args) {
        switch (getCommandName(command)) {
            case 'CC_UNLOCK' :
            case 'オプション任意項目の隠し解除' :
                ConfigManager.customParamUnlock(args[0]);
                break;
        }
    };

    //=============================================================================
    // ConfigManager
    //  追加項目の設定値や初期値を管理します。
    //=============================================================================
    ConfigManager.customParams   = null;
    ConfigManager.hiddenInfo     = {};
    ConfigManager._symbolNumber  = 'Number';
    ConfigManager._symbolBoolean = 'Boolean';
    ConfigManager._symbolString  = 'String';
    ConfigManager._symbolVolume  = 'Volume';

    ConfigManager.getCustomParams = function() {
        if (this.customParams) {
            return this.customParams;
        }
        this.customParams = {};
        param.numberOptions.forEach(function(optionItem, index) {
            this.makeNumberOption(optionItem, index);
        }, this);
        param.stringOptions.forEach(function(optionItem, index) {
            this.makeStringOption(optionItem, index);
        }, this);
        param.switchOptions.forEach(function(optionItem, index) {
            this.makeSwitchOption(optionItem, index);
        }, this);
        param.volumeOptions.forEach(function(optionItem, index) {
            this.makeVolumeOption(optionItem, index);
        }, this);
        return this.customParams;
    };

    ConfigManager.makeNumberOption = function(optionItem, index) {
        var data    = this.makeCommonOption(optionItem, index, this._symbolNumber);
        data.min    = getArgNumber(optionItem.NumberMin);
        data.max    = getArgNumber(optionItem.NumberMax);
        data.offset = getArgNumber(optionItem.NumberStep);
        this.pushOptionData(data);
    };

    ConfigManager.makeStringOption = function(optionItem, index) {
        var data    = this.makeCommonOption(optionItem, index, this._symbolString);
        data.values = getArgJson(optionItem.StringItems, ['no item']);
        data.min    = 0;
        data.max    = data.values.length - 1;
        this.pushOptionData(data);
    };

    ConfigManager.makeSwitchOption = function(optionItem, index) {
        var data       = this.makeCommonOption(optionItem, index, this._symbolBoolean);
        data.initValue = getArgBoolean(optionItem.DefaultValue);
        data.variable  = getArgNumber(optionItem.SwitchID);
        this.pushOptionData(data);
    };

    ConfigManager.makeVolumeOption = function(optionItem, index) {
        var data = this.makeCommonOption(optionItem, index, this._symbolVolume);
        this.pushOptionData(data);
    };

    ConfigManager.makeCommonOption = function(optionItem, index, type) {
        var data       = {};
        data.symbol    = `${type}${index + 1}`;
        data.name      = optionItem.Name;
        data.hidden    = getArgBoolean(optionItem.HiddenFlag);
        data.script    = optionItem.Script;
        data.initValue = getArgNumber(optionItem.DefaultValue);
        data.variable  = getArgNumber(optionItem.VariableID, 0);
        data.addPotion = optionItem.AddPosition;
        return data;
    };

    ConfigManager.pushOptionData = function(data) {
        this.customParams[data.symbol] = data;
    };

    var _ConfigManager_makeData = ConfigManager.makeData;
    ConfigManager.makeData      = function() {
        var config        = _ConfigManager_makeData.apply(this, arguments);
        config.hiddenInfo = {};
        iterate(this.getCustomParams(), function(symbol) {
            config[symbol]            = this[symbol];
            config.hiddenInfo[symbol] = this.hiddenInfo[symbol];
        }.bind(this));
        return config;
    };

    var _ConfigManager_applyData = ConfigManager.applyData;
    ConfigManager.applyData      = function(config) {
        _ConfigManager_applyData.apply(this, arguments);
        iterate(this.getCustomParams(), function(symbol, item) {
            if (symbol.contains(this._symbolBoolean)) {
                this[symbol] = this.readFlagCustom(config, symbol, item);
            } else if (symbol.contains(this._symbolVolume)) {
                this[symbol] = this.readVolumeCustom(config, symbol, item);
            } else {
                this[symbol] = this.readOther(config, symbol, item);
            }
            this.hiddenInfo[symbol] = (config.hiddenInfo ? config.hiddenInfo[symbol] : item.hidden);
        }.bind(this));
    };

    ConfigManager.customParamUnlock = function(name) {
        iterate(this.getCustomParams(), function(symbol, item) {
            if (item.name === name) this.hiddenInfo[symbol] = false;
        }.bind(this));
        this.save();
    };

    ConfigManager.readOther = function(config, name, item) {
        var value = config[name];
        if (value !== undefined) {
            return Number(value).clamp(item.min, item.max);
        } else {
            return item.initValue;
        }
    };

    ConfigManager.readFlagCustom = function(config, name, item) {
        if (config[name] !== undefined) {
            return this.readFlag(config, name);
        } else {
            return item.initValue;
        }
    };

    ConfigManager.readVolumeCustom = function(config, name, item) {
        if (config[name] !== undefined) {
            return this.readVolume(config, name);
        } else {
            return item.initValue;
        }
    };

    ConfigManager.exportCustomParams = function() {
        if (!$gameVariables || !$gameSwitches) return;
        iterate(this.getCustomParams(), function(symbol, item) {
            if (item.variable > 0) {
                if (symbol.contains(this._symbolBoolean)) {
                    $gameSwitches.setValue(item.variable, !!this[symbol]);
                } else {
                    $gameVariables.setValue(item.variable, this[symbol]);
                }
            }
        }.bind(this));
    };

    ConfigManager.importCustomParams = function() {
        if (!$gameVariables || !$gameSwitches) return;
        iterate(this.getCustomParams(), function(symbol, item) {
            if (item.variable > 0) {
                if (symbol.contains(this._symbolBoolean)) {
                    this[symbol] = $gameSwitches.value(item.variable);
                } else if (symbol.contains(this._symbolVolume)) {
                    this[symbol] = $gameVariables.value(item.variable).clamp(0, 100);
                } else {
                    this[symbol] = $gameVariables.value(item.variable).clamp(item.min, item.max);
                }
            }
        }.bind(this));
    };

    var _ConfigManager_save = ConfigManager.save;
    ConfigManager.save      = function() {
        _ConfigManager_save.apply(this, arguments);
        this.exportCustomParams();
    };

    //=============================================================================
    // Game_Map
    //  リフレッシュ時にオプション値を同期します。
    //=============================================================================
    var _Game_Map_refresh      = Game_Map.prototype.refresh;
    Game_Map.prototype.refresh = function() {
        _Game_Map_refresh.apply(this, arguments);
        ConfigManager.importCustomParams();
    };

    //=============================================================================
    // DataManager
    //  セーブ時とロード時にオプション値を同期します。
    //=============================================================================
    var _DataManager_setupNewGame = DataManager.setupNewGame;
    DataManager.setupNewGame      = function() {
        _DataManager_setupNewGame.apply(this, arguments);
        ConfigManager.exportCustomParams();
    };

    var _DataManager_loadGameWithoutRescue = DataManager.loadGameWithoutRescue;
    DataManager.loadGameWithoutRescue      = function(savefileId) {
        var result = _DataManager_loadGameWithoutRescue.apply(this, arguments);
        ConfigManager.exportCustomParams();
        return result;
    };

    //=============================================================================
    // Window_Options
    //  追加項目を描画します。
    //=============================================================================
    var _Window_Options_initialize      = Window_Options.prototype.initialize;
    Window_Options.prototype.initialize = function() {
        this._customParams = ConfigManager.getCustomParams();
        _Window_Options_initialize.apply(this, arguments);
        this.select(localOptionWindowIndex);
        localOptionWindowIndex = 0;
    };

    var _Window_Options_makeCommandList      = Window_Options.prototype.makeCommandList;
    Window_Options.prototype.makeCommandList = function() {
        _Window_Options_makeCommandList.apply(this, arguments);
        this.addCustomOptions();
    };

    Window_Options.prototype.addCustomOptions = function() {
        iterate(this._customParams, function(key, item) {
            if (!ConfigManager.hiddenInfo[key]) {
                this.addCommand(item.name, key);
                if (item.addPotion) {
                    this.shiftCustomOptions(item.addPotion);
                }
            }
        }.bind(this));
    };

    Window_Options.prototype.shiftCustomOptions = function(addPotion) {
        var targetCommand = this._list.filter(function(command) {
            return command.symbol === addPotion;
        })[0];
        if (!targetCommand) {
            return;
        }
        var targetIndex = this._list.indexOf(targetCommand);
        var newCommand = this._list.pop();
        this.addIndexForManoInputConfig(targetIndex);
        this._list.splice(targetIndex, 0, newCommand);
    };

    Window_Options.prototype.addIndexForManoInputConfig = function(index) {
        if (this._gamepadOptionIndex > index) {
            this._gamepadOptionIndex += 1;
        }
        if (this._keyboardConfigIndex > index) {
            this._keyboardConfigIndex += 1;
        }
    };

    var _Window_Options_statusText      = Window_Options.prototype.statusText;
    Window_Options.prototype.statusText = function(index) {
        var result = _Window_Options_statusText.apply(this, arguments);
        var symbol = this.commandSymbol(index);
        var value  = this.getConfigValue(symbol);

        if (this.isNumberSymbol(symbol)) {
            result = this.numberStatusText(value);
        } else if (this.isStringSymbol(symbol)) {
            result = this.stringStatusText(value, symbol);
        }

        //戦闘軽量化処理
        if (symbol=="Boolean3"){
        	if(value){
        		result = param.switchOptions[2].displayTrue
        	}else{
        		result = param.switchOptions[2].displayFalse
        	}
        }

        //描き文字処理
        if (symbol=="Boolean4"){
        	if(value){
        		result = param.switchOptions[3].displayTrue
        	}else{
        		result = param.switchOptions[3].displayFalse
        	}
        }
        return result;
    };

    Window_Options.prototype.isNumberSymbol = function(symbol) {
        return symbol.contains(ConfigManager._symbolNumber);
    };

    Window_Options.prototype.isStringSymbol = function(symbol) {
        return symbol.contains(ConfigManager._symbolString);
    };

    Window_Options.prototype.isCustomSymbol = function(symbol) {
        return !!this._customParams[symbol];
    };

    Window_Options.prototype.numberStatusText = function(value) {
        return value;
    };

    Window_Options.prototype.stringStatusText = function(value, symbol) {
        return this._customParams[symbol].values[value];
    };

    var _Window_Options_processOk      = Window_Options.prototype.processOk;
    Window_Options.prototype.processOk = function() {
        if (!this._shiftValue(1, true)) _Window_Options_processOk.apply(this, arguments);
        this.execScript();
    };

    var _Window_Options_cursorRight      = Window_Options.prototype.cursorRight;
    Window_Options.prototype.cursorRight = function(wrap) {
        if (!this._shiftValue(1, false)) _Window_Options_cursorRight.apply(this, arguments);
    };

    var _Window_Options_cursorLeft      = Window_Options.prototype.cursorLeft;
    Window_Options.prototype.cursorLeft = function(wrap) {
        if (!this._shiftValue(-1, false)) _Window_Options_cursorLeft.apply(this, arguments);
    };

    Window_Options.prototype._shiftValue = function(sign, loopFlg) {
        var symbol = this.commandSymbol(this.index());
        var value  = this.getConfigValue(symbol);
        if (this.isNumberSymbol(symbol)) {
            value += this.numberOffset(symbol) * sign;
            this.changeValue(symbol, this._clampValue(value, symbol, loopFlg));
            return true;
        }
        if (this.isStringSymbol(symbol)) {
            value += sign;
            this.changeValue(symbol, this._clampValue(value, symbol, loopFlg));
            return true;
        }
        return false;
    };

    Window_Options.prototype.execScript = function() {
        var symbol = this.commandSymbol(this.index());
        if (!this.isCustomSymbol(symbol)) return;
        var script = this._customParams[symbol].script;
        if (script) eval(script);
        localOptionWindowIndex = this.index();
    };

    Window_Options.prototype._clampValue = function(value, symbol, loopFlg) {
        var maxValue = this._customParams[symbol].max;
        var minValue = this._customParams[symbol].min;
        if (loopFlg) {
            if (value > maxValue) value = minValue;
            if (value < minValue) value = maxValue;
        }
        return value.clamp(this._customParams[symbol].min, this._customParams[symbol].max);
    };

    Window_Options.prototype.numberOffset = function(symbol) {
        var value = this._customParams[symbol].offset;
        if (Input.isPressed('shift')) value *= 10;
        return value;
    };

    Window_Options.prototype.windowHeight = function() {
        return this.fittingHeight(Math.min(this.numVisibleRows(), 14));
    };
})();