code:
//=============================================================================
// Yanfly Engine Plugins - Battle Engine Extension - Counter Control
// YEP_X_CounterControl.js
//=============================================================================

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

var Yanfly = Yanfly || {};
Yanfly.Counter = Yanfly.Counter || {};
Yanfly.Counter.version = 1.09;

//=============================================================================
 /*:
 * @plugindesc v1.09 (Requires YEP_BattleEngineCore.js) Gives you more
 * control over how counters work in RPG Maker MV!
 * @author Yanfly Engine Plugins
 *
 * @param ---General---
 * @default
 *
 * @param Queue Max
 * @parent ---General---
 * @type number
 * @min 1
 * @desc What's the maximum size for the counter queue?
 * @default 20
 *
 * @param ---Default Traits---
 * @default
 *
 * @param Counter Skill
 * @parent ---Default Traits---
 * @type number
 * @min 1
 * @desc This is the default skill used for counterattacks.
 * Insert the skill ID here. Use 0 for the MV default.
 * @default 1
 *
 * @param Evade Counter
 * @parent ---Default Traits---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Counter skills to evade, then counter by default?
 * NO - false     YES - true
 * @default false
 *
 * @param Counter Name
 * @parent ---Default Traits---
 * @desc The default counter skill name used per skill.
 * %1 - Skill Name
 * @default Counter-%1
 *
 * @param Counter Icon
 * @parent ---Default Traits---
 * @type number
 * @min 0
 * @desc The icon ID used for counter attacks. Leave at 0
 * to use the default skill's icon.
 * @default 78
 *
 * @param Counter Total
 * @parent ---Default Traits---
 * @desc Default amount of counters per actor and enemy.
 * @default 1
 *
 * @param Ally Counter
 * @parent ---Default Traits---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Allow allies to counter the actions of other allies?
 * NO - false     YES - true
 * @default false
 *
 * @param ---Default Conditions---
 * @default
 *
 * @param Physical
 * @parent ---Default Conditions---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Require the countered action to be physical?
 * NO - false     YES - true
 * @default true
 *
 * @param Single Target
 * @parent ---Default Conditions---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Require the countered action scope to be single target?
 * NO - false     YES - true
 * @default true
 *
 * @param Not Counter
 * @parent ---Default Conditions---
 * @type boolean
 * @on YES
 * @off NO
 * @desc Require the countered action to not be a counter?
 * NO - false     YES - true
 * @default false
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * This plugin requires YEP_BattleEngineCore. Make sure this plugin is located
 * under YEP_BattleEngineCore in the plugin list.
 *
 * If you are using Imported.YEP_X_BattleSysATB or Imported.YEP_X_BattleSysCTB,
 * place this plugin under those plugins in the plugin list.
 *
 * The default counterattack trait in RPG Maker MV doesn't give many options
 * for the developer. It's a skill that flatout cancels out the physical skill
 * of the attacker by evading it and then producing a normal attack from the
 * counterattacker. This plugin will give you more control over how counters
 * work in the sense that you can choose to have the counter connect first
 * before allowing the counter skill to proc.
 *
 * ============================================================================
 * Instructions - How Advanced Counters Work
 * ============================================================================
 *
 * A counterattack is an action that serves as a reaction to an action used by
 * an opposing battler unless the action is marked as able of being countered
 * by allied members.
 *
 * Now begins a clash between the attacker's anti-counter stat (newly added)
 * against the target's counter stat plus any of the action's modifiers. Once
 * the finalized counter rate is decided, a random number check is made to see
 * if the counter will pass. If it doesn't, no counter will occur. If it does,
 * the next step occurs.
 *
 * The target will then generate a pool of skills it can use as counters. It
 * will go in a priority list mentioned in the next section below. The battle
 * system will then go through the pool of skills in order and select the first
 * counter skill that meets all of the conditions required. If no skill is
 * selected, no skill will be used as a counter. All skills have a mandatory
 * requirement of being able to pay the skill's cost and can use it.
 *
 * Once the skill is selected, the counter skill is placed in the counter queue
 * and waits for the current attacker's turn to be over. Once over, the actions
 * in the counter queue will begin. The counterattacker will perform counter
 * actions without conflicting with their own turns. This process will repeat
 * itself until the counter queue is emptied.
 *
 * During the counter queue process, counter skills can trigger counter skills,
 * too. For that reason, there is a maximum queue size determined by the plugin
 * parameters. Once the queue count reaches this size, no more counter skills
 * will be added to the counter queue.
 *
 * ============================================================================
 * Instructions - Counter Skill Priority List
 * ============================================================================
 *
 * When the pool of counter skills is being generated, they will be generated
 * in the following order:
 *
 * 1. States - Highest Priority Notebox
 * 2. States - Lowest Priority Notebox
 * 3. Equipment - Weapons Notebox
 * 4. Equipment - Armors Notebox
 * 5. Actor - Current Class Notebox
 * 6. Actor - Actor Notebox
 * 7. Enemy - Enemy Notebox
 *
 * The order of the pool of counter skills matter in that when going through
 * the conditions of the counter skill to be used, the first counter skill
 * whose condition is met will be the one used.
 *
 * ============================================================================
 * Notetags
 * ============================================================================
 *
 * You can use the following notetags to alter counters in your game. Each of
 * these notetags will alter counters in a particular way.
 *
 * Actor and Enemy Notetags:
 *
 *   <Default Counter: x>
 *   <Default Counter: name>
 *   Sets the default counter skill to x. If it is left as 0, then the counter
 *   skill will be RPG Maker MV's default counter skill. If you are using the
 *   name of the skill, and there are multiple skills in the database with the
 *   same name, then priority will be given to the skill with the highest ID.
 *   *Note: Use 0 for x if you wish to add RPG Maker MV's default counter.
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *
 *   <Counter Skills: x>
 *   <Counter Skills: x, x, x>
 *   <Counter Skills: x to y>
 *   This will add to the list of possible counter skills for the battler.
 *   If multiple skill ID's are listed, then they're all added. Priority will
 *   be given to the counter skills listed earlier.
 *   *Note: Use 0 for x if you wish to add RPG Maker MV's default counter.
 *   *Note2: See the Counter List priority to see which skills will be given
 *   priority on the counter skill list.
 *
 *   <Counter Skill: name>
 *   This will add the named skill to the list of possible counter skills for
 *   the battler. If there are multiple skills in the database with the same
 *   name, then priority will be given to the skill with the highest ID.
 *   *Note: See the Counter List priority to see which skills will be given
 *   priority on the counter skill list.
 *
 *   <Counter Total: +x>
 *   <Counter Total: -x>
 *   Alters the number of times the battler can counter by x. This is the
 *   amount of times the battler can counter until the battler's turn comes up
 *   at which, the number of times is reset.
 *
 *   <Target Counter: x%>
 *   When this battler attacks an opponent target, this will cause the target
 *   counter rate to be altered by x% rate. If a target has 10% CNT, then a
 *   notetag of 50% will cause the counter rate to become 5%.
 *
 *   <Target Counter: +x%>
 *   <Target Counter: -x%>
 *   When this battler attacks an opponent target, this will cause the target
 *   counter rate to increase or decrease by x%. If a target has 10% CNT, then
 *   a notetag of +50% will cause the counter rate to become +60%.
 *
 *   <Evade Counter>
 *   This will change all counter skills used by the related battler to become
 *   evade counters regardless of their default nature. However, if the battler
 *   is affected by a trait that is <Hit Counter>, then priority will be given
 *   to the <Hit Counter> trait instead.
 *
 *   <Hit Counter>
 *   This will change all counter skills used by the related battler to become
 *   hit counters regardless of their default nature. If the battler is also
 *   affected by <Evade Counter>, this effect will take priority.
 *
 * Skill and Item Notetags:
 *
 *   <Ally Counter>
 *   Makes this action able to proc counter skills by allied members.
 *
 *   <Ally Cannot Counter>
 *   Makes this action unable to proc counter skills by allied members.
 *
 *   <Cannot Counter>
 *   Causes this action to be un-counterable. This means that it will always
 *   return a 0% counterattack possibility.
 *
 *   <Counter Rate: x%>
 *   This will cause this action to proc a counter from the target by x% rate.
 *   This means if the target has a 10% chance to counter and this notetag is
 *   50%, then the target will have a 5% chance to counter.
 *
 *   <Counter Rate: +x%>
 *   <Counter Rate: -x%>
 *   This will cause this action to proc a counter from the target by an
 *   additive x%. This means if the target has a 10% chance to counter and this
 *   notetag is +50%, then the target has a 60% chance to counter.
 *
 * Skill Notetags:
 *
 *   <Evade Counter>
 *   If this skill is being used as the counter skill, the battler will evade
 *   the current action and then counter.
 *
 *   <Hit Counter>
 *   If this skill is being used as the counter skill, the battler will take
 *   the hit against the current action and then counter.
 *
 *   <Counter Name: text>
 *   This changes the displayed name of the skill when used as a counter skill
 *   to 'text'.
 *
 *   <Counter Icon: x>
 *   This changes the displayed icon of the skill when used as a counter skill
 *   to x icon.
 *
 * ============================================================================
 * Notetags - Counter Conditions
 * ============================================================================
 *
 * When making your counter skills, you can have those counter skills respond
 * only to specific conditions. If all conditions are met, the counter skill
 * will occur. If a single condition isn't met, that counter skill will then be
 * skipped and the next one will be checked. To add counter conditions, use the
 * following notetags:
 *
 * Skill Notetags:
 *
 *   <Counter Condition>
 *    condition
 *    condition
 *   </Counter Condition>
 *   Replace the 'condition' text in between the notetags with the listed in
 *   the conditions list below to best fit what you want.
 *
 * --- Example ---
 *
 *   <Counter Condition>
 *    physical hit
 *    single target
 *   </Counter Condition>
 *   This skill will only be used as a counter skill if the current action is
 *   a physical hit that's single target.
 *
 * ============================================================================
 * Counter Condition List
 * ============================================================================
 *
 * Here is a list of all the counter conditions that come with this plugin that
 * you can use. Keep in mind that all of the counter conditions must be met
 * before a counter will take effect. If even a single counter condition fails
 * to be met, the counter skill will not proc.
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * ATTACKER param eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'param' with 'level', 'maxhp', 'hp', 'maxmp', 'mp', 'atk', 'def',
 * 'mat', 'mdf', 'agi', or 'luk'. This will run a check against the attacker's
 * parameter. If the check returns 'true', the counter condition is met. If it
 * returns 'false', the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Attacker level > 50
 *            Attacker hp <= attacker.mhp * 0.50
 *            Attacker atk > defender.def
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * CERTAIN HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a certain hit, the counter
 * condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Certain Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * COUNTER HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a counter skill, the
 * counter condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Counter Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * DEFENDER param eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Replace 'param' with 'level', 'maxhp', 'hp', 'maxmp', 'mp', 'atk', 'def',
 * 'mat', 'mdf', 'agi', or 'luk'. This will run a check against the defender's
 * parameter. If the check returns 'true', the counter condition is met. If it
 * returns 'false', the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Defender level > 50
 *            Defender hp <= defender.mhp * 0.50
 *            Defender atk > attacker.def
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * ELEMENT: x
 * ELEMENT: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target has element X attached to it,
 * the counter condition is met. If it isn't, the counter condition isn't met.
 * Replace 'x' with the element ID or the element name in the database system
 * tab. If multiple elements share the same name, priority will be given to the
 * element with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Element: 4
 *            Element: Fire
 *            Element: Ice
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * EVAL: code
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * For those with JavaScript experience, you can use the above line to perform
 * an eval check to see if the conditions are met for the counter skill. If the
 * eval check returns 'true', the condition is met. If it returns 'false', the
 * condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Eval: attacker.name() === 'Harold'
 *            Eval: defender.hpRate() <= 0.50
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * ITEM: x
 * ITEM: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current aciton used against the target is item x, the counter
 * condition is met. If it isn't, the counter condition isn't met. Replace 'x'
 * with the item ID. If you choose to use the item name, and your database
 * has multiple items with the same name, priority will be given to the item
 * with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Item: 30
 *            Item: Bomb
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * MAGICAL HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a magical hit, the counter
 * condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Magical Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * MULTI TARGET
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a multi target action, the
 * counter condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Multi Target
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT CERTAIN HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is NOT certain hit, the
 * counter condition is met. If it is, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Certain Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT COUNTER HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is NOT a counter skill, the
 * counter condition is met. If it is, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Counter Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT ELEMENT: x
 * NOT ELEMENT: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target has element X attached to it,
 * the counter condition is NOT met. If it isn't, the counter condition is met.
 * Replace 'x' with the element ID or the element name in the database system
 * tab. If multiple elements share the same name, priority will be given to the
 * element with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Element: 4
 *            Not Element: Fire
 *            Not Element: Ice
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT ITEM: x
 * NOT ITEM: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current aciton used against the target is NOT item x, the counter
 * condition is met. If it is, the counter condition isn't met. Replace 'x'
 * with the item ID. If you choose to use the item name, and your database
 * has multiple items with the same name, priority will be given to the item
 * with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Item: 30
 *            Not Item: Bomb
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT MAGICAL HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is NOT a magical hit, the
 * counter condition is met. If it is, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Magical Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT PHYSICAL HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is NOT a physical hit, the
 * counter condition is met. If it is, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Physical Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT SKILL: x
 * NOT SKILL: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current aciton used against the target is NOT skill x, the counter
 * condition is met. If it is, the counter condition isn't met. Replace 'x'
 * with the skill ID. If you choose to use the skill name, and your database
 * has multiple skills with the same name, priority will be given to the skill
 * with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Skill: 50
 *            Not Skill: Firaga
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * NOT STYPE: x
 * NOT STYPE: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is NOT a skill and skill type
 * x, the counter condition is met. If it isn't the counter condition isn't
 * met. Replace 'x' with the Skill Type ID. If you choose to use the skill type
 * name and your database has multiple skill types with the same name, priority
 * will be given to the skill type with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Not Stype: 1
 *            Not Stype: Magic
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * PHYSICAL HIT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a physical hit, the counter
 * condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Physical Hit
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * RANDOM: x%
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will run a random check. There is a x% chance that this counter check
 * will pass. If it passes, the counter condition is met. If it doesn't, the
 * counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Random: 30%
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * SINGLE TARGET
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a single target action, the
 * counter condition is met. If it isn't, the counter condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Single Target
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * SKILL: x
 * SKILL: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current aciton used against the target is skill x, the counter
 * condition is met. If it isn't, the counter condition isn't met. Replace 'x'
 * with the skill ID. If you choose to use the skill name, and your database
 * has multiple skills with the same name, priority will be given to the skill
 * with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Skill: 50
 *            Skill: Firaga
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * STYPE: x
 * STYPE: name
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If the current action used against the target is a skill and skill type x,
 * the counter condition is met. If it isn't the counter condition isn't met.
 * Replace 'x' with the Skill Type ID. If you choose to use the skill type name
 * and your database has multiple skill types with the same name, priority will
 * be given to the skill type with the highest ID.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Stype: 1
 *            Stype: Magic
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * SWITCH x OFF
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If switch x is OFF (false) prior to the current action being used against
 * the target, the counter condition is met. If it is ON (true), the counter
 * condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Switch 10 Off
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * SWITCH x ON
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * If switch x is ON (ftrue) prior to the current action being used against
 * the target, the counter condition is met. If it is OFF (false), the counter
 * condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Switch 10 On
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 * VARIABLE x eval
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This runs an eval check against variable x. If the eval check returns 'true'
 * the condition is met. If it returns 'false' then the condition isn't met.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Example:   Variable 15 >= 15
 *            Variable 16 <= 20
 *            Variable 17 === $gameParty.aliveMembers().length
 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 *
 * ============================================================================
 * Lunatic Mode - Custom Counter Skills
 * ============================================================================
 *
 * For those with JavaScript proficiency, you can use the following Lunatic
 * Mode notetags to give a dynamic set of skills granted for counter usage.
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *
 *   <Custom Counter Skills>
 *    if (user.name() === 'Harold') {
 *      skills.push(50, 51, 52);
 *    } else if (user.name() === 'Therese') {
 *      skills.push(53, 54, 55);
 *    } else if (user.name() === 'Marsha') {
 *      skills.push(56, 57, 58);
 *    } else if (user.name() === 'Lucius') {
 *      skills.push(59, 60, 61);
 *    }
 *   </Custom Counter Skills>
 *   The 'skills' variable is an array that will contain all the counter skills
 *   that will be added to the list of potential skills the battler can counter
 *   actions with provided that their requirements are met.
 *
 * ============================================================================
 * Lunatic Mode - Custom Counter Total
 * ============================================================================
 *
 * For those with JavaScript proficiency, you can use the following Lunatic
 * Mode notetags to give a dynamic counter total bonus:
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *
 *   <Custom Counter Total>
 *    value = user.level;
 *   </Custom Counter Total>
 *   The 'value' variable is the total amount of counters is increased or
 *   decreased by. If the total counter value reaches 0 or less than 0 for the
 *   battler, the battler is unable to use counter skills.
 *
 * ============================================================================
 * Lunatic Mode - Custom Target Counter Rate
 * ============================================================================
 *
 * For those with JavaScript proficiency, you can use the following Lunatic
 * Mode notetags to make the attacker's traits alter the target's CNT rate.
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *
 *   <Custom Target Counter Rate>
 *    rate -= user.hpRate();
 *   </Custom Target Counter Rate>
 *   The 'rate' variable is the final rate used to determine the counter rate
 *   the target has. It is already given the value calculated from the target's
 *   CNT value. This is calculated before the skill's custom counter rate.
 *
 * ============================================================================
 * Lunatic Mode - Custom Counter Rates
 * ============================================================================
 *
 * For those with JavaScript proficiency, you can use the following Lunatic
 * Mode notetags to give skills a dynamic chance for the target to counter.
 *
 * Skill and Item Notetags:
 *
 *   <Custom Counter Rate>
 *    rate += target.hpRate();
 *   </Custom Counter Rate>
 *   The 'rate' variable is the final rate used to determine the counter rate
 *   the target has. It is already given the value calculated from the target's
 *   CNT value plus any additional counter rate modifiers from the skill. This
 *   is calculated after the attacker's custom target counter rate.
 *
 * ============================================================================
 * Lunatic Mode - Custom Counter Condition
 * ============================================================================
 *
 * For those with JavaScript proficiency, you can use the following Luantic
 * Mode notetags to give counter skills a custom counter condition. While you
 * can do the same with an Eval condition, this notetag is for those who prefer
 * to take control over everything at once.
 *
 * Skill Notetags:
 *
 *   <Custom Counter Condition>
 *    if (attacker.name() === 'Harold') {
 *      condition = true;
 *    } else if (defender.name() === 'Therese') {
 *      condition = true;
 *    } else {
 *      condition = false;
 *    }
 *   </Custom Counter Condition>
 *   The 'condition' variable determines whether or not the counter skill will
 *   pass or fail. If the 'condition' variable returns 'true', the condition is
 *   met. If the 'condition' variable returns 'false', the condition fails to
 *   be met. Once the condition is met, the rest of the <Counter Condition>
 *   conditions will be checked.
 *
 * ============================================================================
 * Changelog
 * ============================================================================
 *
 * Version 1.09:
 * - Bypass the isDevToolsOpen() error when bad code is inserted into a script
 * call or custom Lunatic Mode code segment due to updating to MV 1.6.1.
 *
 * Version 1.08:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.07a:
 * - Lunatic Mode fail safes added.
 * - Optimization update
 *
 * Version 1.06:
 * - Updated for RPG Maker MV version 1.3.2.
 *
 * Version 1.05:
 * - Fixed a bug that caused the Eval: condition to not work and crash.
 * - Fixed an issue that caused default counter attacks to trigger upon magical
 * actions.
 *
 * Version 1.04:
 * - Fixed a bug that caused counter-countered actions to no longer disappear
 * from queue.
 *
 * Version 1.03:
 * - Fixed a bug that caused enemies to get a free action after a counter in
 * the DTB engine.
 *
 * Version 1.02:
 * - Fixed a bug that didn't replace the proper skill for the countered battler
 * appropriate causing some action effects to not proc correctly.
 *
 * Version 1.01:
 * - Fixed a bug that caused the <Counter Skills: 0> notetag to not work.
 *
 * Version 1.00:
 * - Finished Plugin!
 */
//=============================================================================

if (Imported.YEP_BattleEngineCore) {

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

Yanfly.Parameters = PluginManager.parameters('YEP_X_CounterControl');
Yanfly.Param = Yanfly.Param || {};

Yanfly.Param.CounterMaxQueue = Number(Yanfly.Parameters['Queue Max']);

Yanfly.Param.CounterDefault = Number(Yanfly.Parameters['Counter Skill']);
Yanfly.Param.CounterEvade = eval(String(Yanfly.Parameters['Evade Counter']));
Yanfly.Param.CounterFmt = String(Yanfly.Parameters['Counter Name']);
Yanfly.Param.CounterIcon = Number(Yanfly.Parameters['Counter Icon']);
Yanfly.Param.CounterTotal = Number(Yanfly.Parameters['Counter Total']);
Yanfly.Param.CounterAllyCnt = eval(String(Yanfly.Parameters['Ally Counter']));

Yanfly.Param.CounterConditions = [];
if (eval(String(Yanfly.Parameters['Physical']))) {
  Yanfly.Param.CounterConditions.push('PHYSICAL HIT');
};
if (eval(String(Yanfly.Parameters['Single Target']))) {
  Yanfly.Param.CounterConditions.push('SINGLE TARGET');
};
if (eval(String(Yanfly.Parameters['Not Counter']))) {
  Yanfly.Param.CounterConditions.push('NOT COUNTER HIT');
};

//=============================================================================
// DataManager
//=============================================================================

Yanfly.Counter.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
  if (!Yanfly.Counter.DataManager_isDatabaseLoaded.call(this)) return false;
  if (!Yanfly._loaded_YEP_X_CounterControl) {
    this.processCounterNotetagsI($dataItems);
    this.processCounterNotetagsW($dataWeapons);
    this.processCounterNotetagsA($dataArmors);
    this.processCounterNotetagsS($dataSkills);
    this.processCounterNotetagsT($dataStates);
    this.processCounterNotetagsSys($dataSystem);
    this.processCounterNotetags1($dataActors);
    this.processCounterNotetags1($dataEnemies);
    this.processCounterNotetags2($dataActors);
    this.processCounterNotetags2($dataClasses);
    this.processCounterNotetags2($dataEnemies);
    this.processCounterNotetags2($dataWeapons);
    this.processCounterNotetags2($dataArmors);
    this.processCounterNotetags2($dataStates);
    this.processCounterNotetags3($dataSkills);
    this.processCounterNotetags4($dataSkills);
    this.processCounterNotetags4($dataItems);
    Yanfly._loaded_YEP_X_CounterControl = true;
  }
  return true;
};

DataManager.processCounterNotetagsI = function(group) {
  if (Yanfly.ItemIdRef) return;
  Yanfly.ItemIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.ItemIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCounterNotetagsW = function(group) {
  if (Yanfly.WeaponIdRef) return;
  Yanfly.WeaponIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.WeaponIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCounterNotetagsA = function(group) {
  if (Yanfly.ArmorIdRef) return;
  Yanfly.ArmorIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.ArmorIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCounterNotetagsS = function(group) {
  if (Yanfly.SkillIdRef) return;
  Yanfly.SkillIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.SkillIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCounterNotetagsT = function(group) {
  if (Yanfly.StateIdRef) return;
  Yanfly.StateIdRef = {};
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    if (obj.name.length <= 0) continue;
    Yanfly.StateIdRef[obj.name.toUpperCase()] = n;
  }
};

DataManager.processCounterNotetagsSys = function(group) {
  Yanfly.STypeIdRef = {};
  for (var i = 1; i < group.skillTypes.length; ++i) {
    var name = group.skillTypes[i].toUpperCase();
    name = name.replace(/\\I\[(\d+)\]/gi, '');
    Yanfly.STypeIdRef[name] = i;
  }
  Yanfly.ElementIdRef = {};
  for (var i = 1; i < group.elements.length; ++i) {
    var name = group.elements[i].toUpperCase();
    name = name.replace(/\\I\[(\d+)\]/gi, '');
    Yanfly.ElementIdRef[name] = i;
  }
};

DataManager.processCounterNotetags1 = function(group) {
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.defaultCounter = Yanfly.Param.CounterDefault;
    obj.counterTotal = Yanfly.Param.CounterTotal;

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(/<DEFAULT COUNTER:[ ](\d+)>/i)) {
        obj.defaultCounter = parseInt(RegExp.$1);
      } else if (line.match(/<DEFAULT COUNTER:[ ](.*)>/i)) {
        var name = String(RegExp.$1).toUpperCase();
        var id = Yanfly.SkillIdRef[name];
        if (id) obj.defaultCounter = id;
      }
    }
  }
};

DataManager.processCounterNotetags2 = function(group) {
  var noteA1 = /<(?:COUNTER SKILLS):[ ]*(\d+(?:\s*,\s*\d+)*)>/i;
  var noteA2 = /<(?:COUNTER SKILLS):[ ](\d+)[ ](?:THROUGH|to)[ ](\d+)>/i;
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.counterSkills = [];
    obj.counterTotal = 0;
    obj.targetCounterRate = 1;
    obj.targetCounterFlat = 0;
    obj.evadeCounter = false;
    obj.hitCounter = false;
    var evalMode = 'none';
    obj.counterTotalEval = '';
    obj.counterSkillsEval = '';
    obj.targetCounterRateEval = '';

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(noteA1)) {
        var array = JSON.parse('[' + RegExp.$1.match(/\d+/g) + ']');
        obj.counterSkills = obj.counterSkills.concat(array);
      } else if (line.match(noteA2)) {
        var range = Yanfly.Util.getRange(parseInt(RegExp.$1),
          parseInt(RegExp.$2));
        obj.counterSkills = obj.counterSkills.concat(range);
      } else if (line.match(/<(?:COUNTER SKILL):[ ](.*)>/i)) {
        var name = String(RegExp.$1).toUpperCase();
        var id = Yanfly.SkillIdRef[name];
        if (id) obj.counterSkills.push(id);
      } else if (line.match(/<(?:COUNTER TOTAL):[ ]([\+\-]\d+)>/i)) {
        obj.counterTotal = parseInt(RegExp.$1);
      } else if (line.match(/<(?:TARGET COUNTER):[ ](\d+)([%％])>/i)) {
        obj.targetCounterRate = parseFloat(RegExp.$1) * 0.01;
      } else if (line.match(/<(?:TARGET COUNTER):[ ]([\+\-]\d+)([%％])>/i)) {
        obj.targetCounterFlat = parseFloat(RegExp.$1) * 0.01;
      } else if (line.match(/<(?:EVADE COUNTER|COUNTER EVADE)>/i)) {
        obj.evadeCounter = true;
      } else if (line.match(/<(?:HIT COUNTER|COUNTER HIT)>/i)) {
        obj.hitCounter = true;
      } else if (line.match(/<CUSTOM COUNTER TOTAL>/i)) {
        evalMode = 'custom counter total';
      } else if (line.match(/<\/CUSTOM COUNTER TOTAL>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'custom counter total') {
        obj.counterTotalEval = obj.counterTotalEval + line + '\n';
      } else if (line.match(/<CUSTOM COUNTER SKILLS>/i)) {
        evalMode = 'custom counter skills';
      } else if (line.match(/<\/CUSTOM COUNTER SKILLS>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'custom counter skills') {
        obj.counterSkillsEval = obj.counterSkillsEval + line + '\n';
      } else if (line.match(/<CUSTOM TARGET COUNTER RATE>/i)) {
        evalMode = 'custom target counter rate';
      } else if (line.match(/<\/CUSTOM TARGET COUNTER RATE>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'custom target counter rate') {
        obj.targetCounterRateEval = obj.targetCounterRateEval + line + '\n';
      }
    }
  }
};

DataManager.processCounterNotetags3 = function(group) {
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.evadeCounter = Yanfly.Param.CounterEvade;
    obj.counterName = Yanfly.Param.CounterFmt.format(obj.name);
    obj.counterIcon = Yanfly.Param.CounterIcon || obj.iconIndex;
    obj.cannotCounter = false;
    var evalMode = 'none';
    obj.counterConditions = Yanfly.Param.CounterConditions.slice();
    obj.counterConditionEval = '';

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(/<(?:EVADE COUNTER|COUNTER EVADE)>/i)) {
        obj.evadeCounter = true;
      } else if (line.match(/<(?:HIT COUNTER|COUNTER HIT)>/i)) {
        obj.evadeCounter = false;
      } else if (line.match(/<(?:COUNTER NAME):[ ](.*)>/i)) {
        obj.counterName = String(RegExp.$1);
      } else if (line.match(/<(?:COUNTER ICON):[ ](\d+)>/i)) {
        obj.counterIcon = String(RegExp.$1);
      } else if (line.match(/<CANNOT COUNTER>/i)) {
        obj.cannotCounter = true;
      } else if (line.match(/<(?:COUNTER CONDITION|COUNTER CONDITIONS)>/i)) {
        evalMode = 'counter condition';
        obj.counterConditions = [];
      } else if (line.match(/<\/(?:COUNTER CONDITION|COUNTER CONDITIONS)>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'counter condition') {
        obj.counterConditions.push(line);
      } else if (line.match(/<CUSTOM COUNTER CONDITION>/i)) {
        evalMode = 'custom counter condition';
      } else if (line.match(/<\/CUSTOM COUNTER CONDITION>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'custom counter condition') {
        obj.counterConditionEval = obj.counterConditionEval + line + '\n';
      }
    }
  }
};

DataManager.processCounterNotetags4 = function(group) {
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.allyCounter = Yanfly.Param.CounterAllyCnt;
    obj.cannotCounter = false;
    obj.counterRate = 1;
    obj.counterMod = 0;
    var evalMode = 'none';
    obj.counterRateEval = '';

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(/<ALLY COUNTER>/i)) {
        obj.allyCounter = true;
      } else if (line.match(/<ALLY CANNOT COUNTER>/i)) {
        obj.allyCounter = false;
      } else if (line.match(/<CANNOT COUNTER>/i)) {
        obj.cannotCounter = true;
      } else if (line.match(/<COUNTER RATE:[ ](\d+)([%％])>/i)) {
        obj.counterRate = parseFloat(RegExp.$1) * 0.01;
      } else if (line.match(/<COUNTER RATE:[ ]([\+\-]\d+)([%％])>/i)) {
        obj.counterMod = parseFloat(RegExp.$1) * 0.01;
      } else if (line.match(/<CUSTOM COUNTER RATE>/i)) {
        evalMode = 'custom counter rate';
      } else if (line.match(/<\/CUSTOM COUNTER RATE>/i)) {
        evalMode = 'none';
      } else if (evalMode === 'custom counter rate') {
        obj.counterRateEval = obj.counterRateEval + line + '\n';
      }
    }
  }
};

//=============================================================================
// BattleManager
//=============================================================================

Yanfly.Counter.BattleManager_initMembers = BattleManager.initMembers;
BattleManager.initMembers = function() {
    Yanfly.Counter.BattleManager_initMembers.call(this);
    this._counterQueue = [];
    this._counterSequence = 0;
    this._counterOriginalSubject = undefined;
    this._counterOriginalAction = undefined;
    this._countering = false;
};

BattleManager.isCountering = function() {
    return this._countering;
};

Yanfly.Counter.BattleManager_invokeCounter =
    BattleManager.invokeCounterAttack;
BattleManager.invokeCounterAttack = function(subject, target) {
  this._counterQueue = this._counterQueue || [];
  if (this.isValidCounterAction(target)) this._counterSequence += 1;
  if (!this.isMaxCounterQueue()) {
    if (this.isValidCounterAction(target)) target.payCounter();
  }
  if (target.canCounter()) {
    this._counterSkill = this.getCounterSkill(subject, target);
    this._counterQueue = this._counterQueue || [];
    if (this._counterSkill === null) {
      if (this._action.isPhysical() && target.canMove()) {
        Yanfly.Counter.BattleManager_invokeCounter.call(this, subject, target);
      } else {
        this.invokeNormalAction(subject, target);
      }
      return;
    } else if (this.evadeAndCounter(subject, target)) {
      target.performEvasion();
      target.forceEvadePopup();
      this.addCounterQueue(subject, target);
    } else if (this._counterSkill !== undefined) {
      this.invokeNormalAction(subject, target);
      this.addCounterQueue(subject, target);
    } else {
      if (this.isValidCounterAction(target)) {
        target.payCounter(-1);
        this._counterSequence -= 1;
      }
      this.invokeNormalAction(subject, target);
      return;
    }
    this._logWindow.displayActionResults(target, subject);
    if (subject.isDead()) subject.performCollapse();
  } else {
    this.invokeNormalAction(subject, target);
  }
};

BattleManager.getCounterSkill = function(subject, target) {
    target.makeCounterSkills();
    var skills = target.counterSkills();
    var length = skills.length;
    for (var i = 0; i < length; ++i) {
      var skill = skills[i];
      if (this.meetCounterConditions(skill, subject, target)) return skill;
    }
    return undefined;
};

BattleManager.meetCounterConditions = function(skill, subject, target) {
    if (skill === null) return true;
    if (!target.canUse(skill)) return false;
    if (!this.meetCounterConditionsEval(skill, subject, target));
    var condition = this.getCounterCondition(skill, subject, target);
    return condition;
};

BattleManager.meetCounterConditionsEval = function(skill, subject, target) {
    var action = this._action;
    if (this._action.item().counterConditionEval === '') return true;
    var condition = true;
    var a = subject;
    var user = subject;
    var attacker = subject;
    var b = target;
    var defender = target;
    var item = skill;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = this._action.item().counterConditionEval;
    try {
      eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'COUNTER CONDITIONS EVAL ERROR');
    }
    return condition;
};

BattleManager.getCounterCondition = function(skill, subject, target) {
    var conditions = skill.counterConditions;
    var length = conditions.length;
    for (var i = 0; i < length; ++i) {
      var line = conditions[i];
      if (!this.checkCounterLine(line, skill, subject, target)) return false;
    }
    return true;
};

BattleManager.checkCounterLine = function(line, skill, subject, target) {
    // EVAL
    if (line.match(/EVAL:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return this.checkCounterEval(value, skill, subject, target);
    // CERTAIN HIT
    } else if (line.toUpperCase() === 'CERTAIN HIT') {
      return this.checkCounterHitType(Game_Action.HITTYPE_CERTAIN);
    // PHYSICAL HIT
    } else if (line.toUpperCase() === 'PHYSICAL HIT') {
      return this.checkCounterHitType(Game_Action.HITTYPE_PHYSICAL);
    // MAGICAL HIT
    } else if (line.toUpperCase() === 'MAGICAL HIT') {
      return this.checkCounterHitType(Game_Action.HITTYPE_MAGICAL);
    // NOT CERTAIN HIT
    } else if (line.toUpperCase() === 'NOT CERTAIN HIT') {
      return !this.checkCounterHitType(Game_Action.HITTYPE_CERTAIN);
    // NOT PHYSICAL HIT
    } else if (line.toUpperCase() === 'NOT PHYSICAL HIT') {
      return !this.checkCounterHitType(Game_Action.HITTYPE_PHYSICAL);
    // NOT MAGICAL HIT
    } else if (line.toUpperCase() === 'NOT MAGICAL HIT') {
      return !this.checkCounterHitType(Game_Action.HITTYPE_MAGICAL);
    // SINGLE TARGET
    } else if (line.toUpperCase() === 'SINGLE TARGET') {
      return this.checkCounterSingleTarget();
    // MULTI TARGET
    } else if (line.toUpperCase() === 'MULTI TARGET') {
      return !this.checkCounterSingleTarget();
    // COUNTER HIT
    } else if (line.toUpperCase() === 'COUNTER HIT') {
      return !this.checkCounterCounterHit();
    // NOT COUNTER HIT
    } else if (line.toUpperCase() === 'NOT COUNTER HIT') {
      return !this.checkCounterCounterHit();
    // RANDOM
    } else if (line.match(/RANDOM:[ ](\d+)([%％])/i)) {
      var value = parseFloat(RegExp.$1) * 0.01;
      return !this.checkCounterRandom(value);
    // NOT ELEMENT
    } else if (line.match(/NOT ELEMENT:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return !this.checkCounterElement(value);
    // ELEMENT
    } else if (line.match(/ELEMENT:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return this.checkCounterElement(value);
    // SWITCH ON
    } else if (line.match(/SWITCH[ ](\d+)[ ]ON/i)) {
      var value = parseInt(RegExp.$1);
      return this.checkCounterSwitch(value);
    // SWITCH OFF
    } else if (line.match(/SWITCH[ ](\d+)[ ]OFF/i)) {
      var value = parseInt(RegExp.$1);
      return !this.checkCounterSwitch(value);
    // VARIABLE
    } else if (line.match(/VARIABLE[ ](\d+)[ ](.*)/i)) {
      var varId = parseInt(RegExp.$1);
      var eval = String(RegExp.$2);
      eval = '$gameVariables.value(' + varId + ') ' + eval;
      return this.checkCounterEval(eval, skill, subject, target);
    // NOT SKILL
    } else if (line.match(/NOT SKILL:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return !this.checkCounterSkill(value);
    // SKILL
    } else if (line.match(/SKILL:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return this.checkCounterSkill(value);
    // NOT STYPE
    } else if (line.match(/NOT STYPE:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return !this.checkCounterStype(value);
    // STYPE
    } else if (line.match(/STYPE:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return this.checkCounterStype(value);
    // NOT ITEM
    } else if (line.match(/NOT ITEM:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return !this.checkCounterItem(value);
    // ITEM
    } else if (line.match(/ITEM:[ ](.*)/i)) {
      var value = String(RegExp.$1);
      return this.checkCounterItem(value);
    // ATTACKER PARAM
    } else if (line.match(/ATTACKER[ ](.*)[ ](.*)/i)) {
      var value1 = String(RegExp.$1);
      var value2 = String(RegExp.$1);
      return this.checkCounterAttacker(value1, value2, skill, subject, target);
    // DEFENDER PARAM
    } else if (line.match(/DEFENDER[ ](.*)[ ](.*)/i)) {
      var value1 = String(RegExp.$1);
      var value2 = String(RegExp.$1);
      return this.checkCounterDefender(value1, value2, skill, subject, target);
    // ELSE - NOTHING LISTED
    } else {
      return true;
    }
};

BattleManager.checkCounterEval = function(code, skill, subject, target) {
    var action = this._action;
    var a = subject;
    var user = subject;
    var attacker = subject;
    var b = target;
    var defender = target;
    var item = skill;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = code;
    try {
      return eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'COUNTER CHECK ERROR');
      return false;
    }
};

BattleManager.checkCounterHitType = function(value) {
    return this._action.item().hitType === value;
};

BattleManager.checkCounterCounterHit = function() {
    return this._action.isCounter();
};

BattleManager.checkCounterRandom = function(value) {
    return Math.random() < value;
};

BattleManager.checkCounterSingleTarget = function() {
    return this._action.isForOne();
};

BattleManager.checkCounterElement = function(value) {
    if (value.match(/(\d+)/i)) {
      var elementId = parseInt(RegExp.$1);
    } else {
      var elementId = Yanfly.ElementIdRef[value.toUpperCase()];
      if (!elementId) return true;
    }
    var actionElement = this._action.item().damage.elementId;
    if (actionElement < 0) {
      return this._subject.attackElements().contains(elementId);
    } else {
      return elementId === actionElement;
    }
};

BattleManager.checkCounterSwitch = function(value) {
    return $gameSwitches.value(value);
};

BattleManager.checkCounterSkill = function(value) {
    if (!this._action.isSkill()) return false;
    if (value.match(/(\d+)/i)) {
      var skillId = parseInt(RegExp.$1);
    } else {
      var skillId = Yanfly.SkillIdRef[value.toUpperCase()];
    }
    var skill = $dataSkills[skillId];
    return this._action.item() === skill;
};

BattleManager.checkCounterStype = function(value) {
    if (!this._action.isSkill()) return false;
    if (value.match(/(\d+)/i)) {
      var stypeId = parseInt(RegExp.$1);
    } else {
      var stypeId = Yanfly.STypeIdRef[value.toUpperCase()];
    }
    var skill = this._action.item();
    return skill.stypeId === stypeId;
};

BattleManager.checkCounterItem = function(value) {
    if (!this._action.isItem()) return false;
    if (value.match(/(\d+)/i)) {
      var itemId = parseInt(RegExp.$1);
    } else {
      var itemId = Yanfly.SkillIdRef[value.toUpperCase()];
    }
    var skill = $dataSkills[skillId];
    return this._action.item() === skill;
};

BattleManager.checkCounterAttacker = function(v1, v2, skill, subject, target) {
    var eval = 'subject.';
    if (['LEVEL', 'LV', 'LVL'].contains(v1.toUpperCase())) {
      eval += 'level';
    } else if (['MAX HP', 'MAXHP', 'MHP'].contains(v1.toUpperCase())) {
      eval += 'mhp';
    } else if (['HP', 'CURRENT HP'].contains(v1.toUpperCase())) {
      eval += 'hp';
    } else if (['MAX MP', 'MAXMP', 'MMP'].contains(v1.toUpperCase())) {
      eval += 'mmp';
    } else if (['MP', 'CURRENT MP'].contains(v1.toUpperCase())) {
      eval += 'mp';
    } else if (['ATK', 'STR'].contains(v1.toUpperCase())) {
      eval += 'atk';
    } else if (['DEF'].contains(v1.toUpperCase())) {
      eval += 'def';
    } else if (['MAT', 'INT', 'SPI'].contains(v1.toUpperCase())) {
      eval += 'mat';
    } else if (['MDF', 'RES'].contains(v1.toUpperCase())) {
      eval += 'mdf';
    } else if (['AGI', 'SPD'].contains(v1.toUpperCase())) {
      eval += 'agi';
    } else if (['LUK'].contains(v1.toUpperCase())) {
      eval += 'luk';
    } else {
      return false;
    }
    eval += ' ' + v2;
    return this.checkCounterEval(eval, skill, subject, target);
};

BattleManager.checkCounterDefender = function(v1, v2, skill, subject, target) {
    var eval = 'target.';
    if (['LEVEL', 'LV', 'LVL'].contains(v1.toUpperCase())) {
      eval += 'level';
    } else if (['MAX HP', 'MAXHP', 'MHP'].contains(v1.toUpperCase())) {
      eval += 'mhp';
    } else if (['HP', 'CURRENT HP'].contains(v1.toUpperCase())) {
      eval += 'hp';
    } else if (['MAX MP', 'MAXMP', 'MMP'].contains(v1.toUpperCase())) {
      eval += 'mmp';
    } else if (['MP', 'CURRENT MP'].contains(v1.toUpperCase())) {
      eval += 'mp';
    } else if (['ATK', 'STR'].contains(v1.toUpperCase())) {
      eval += 'atk';
    } else if (['DEF'].contains(v1.toUpperCase())) {
      eval += 'def';
    } else if (['MAT', 'INT', 'SPI'].contains(v1.toUpperCase())) {
      eval += 'mat';
    } else if (['MDF', 'RES'].contains(v1.toUpperCase())) {
      eval += 'mdf';
    } else if (['AGI', 'SPD'].contains(v1.toUpperCase())) {
      eval += 'agi';
    } else if (['LUK'].contains(v1.toUpperCase())) {
      eval += 'luk';
    } else {
      return false;
    }
    eval += ' ' + v2;
    return this.checkCounterEval(eval, skill, subject, target);
};

BattleManager.evadeAndCounter = function(subject, target) {
    if (this._counterSkill === undefined) return false;
    if (this._counterSkill === null) return false;
    if (target.forceHitCounter()) return false;
    if (target.forceEvadeCounter()) return true;
    return this._counterSkill.evadeCounter;
};

BattleManager.isValidCounterAction = function(target) {
    if (this._counterQueue.length <= 0) return true;
    return this._counterQueue[0].subject() !== target;
};

BattleManager.addCounterQueue = function(subject, target) {
    if (!target.canCounter()) return;
    if (!this.isValidCounterAction(target)) return;
    var action = new Game_Action(target);
    action.setSkill(this._counterSkill.id);
    action.setCounter();
    if (action.isForOpponent()) {
      action.setTarget(subject.index());
    } else {
      action.setTarget(target.index());
    }
    this._counterQueue = this._counterQueue || [];
    this._counterQueue.push(action);
    this._counterOriginalSubject = this._counterOriginalSubject || subject;
    this._counterOriginalAction = this._counterOriginalAction || this._action;
};

Yanfly.Counter.BattleManager_createFinishActions =
    BattleManager.createFinishActions;
BattleManager.createFinishActions = function() {
    Yanfly.Counter.BattleManager_createFinishActions.call(this);
    this._actionList.push(['START COUNTER PHASE']);
};

Yanfly.Counter.BattleManager_processActionSequence =
    BattleManager.processActionSequence;
BattleManager.processActionSequence = function(actionName, actionArgs) {
    // START COUNTER PHASE
    if (actionName === 'START COUNTER PHASE') {
      return this.actionStartCounterPhase();
    }
    return Yanfly.Counter.BattleManager_processActionSequence.call(this,
      actionName, actionArgs);
};

BattleManager.isMaxCounterQueue = function() {
    return this._counterSequence > Yanfly.Param.CounterMaxQueue;
};

BattleManager.actionStartCounterPhase = function() {
    this._counterQueue = this._counterQueue || [];
    var action = this._counterQueue.shift();
    if (this.isMaxCounterQueue() || !action) {
      this.actionEndCounterPhase();
    } else if (action && action.subject().isDead()) {
      this.actionStartCounterPhase();
    } else {
      this.actionPerformCounterPhase(action);
    }
    this._counterQueue = this._counterQueue || [];
};

BattleManager.actionEndCounterPhase = function() {
  this._countering = false;
  if (this._counterOriginalSubject) {
    this._subject = this._counterOriginalSubject;
    this._action = this._counterOriginalAction;
    if (this.isSetCounterOriginal()) {
      this._subject.setCounterAction(this._action);
    }
  }
  this._counterOriginalSubject = undefined;
  this._counterOriginalAction = undefined;
  this._counterSequence = 0;
  this._counterQueue = [];
};

BattleManager.isSetCounterOriginal = function() {
    if (this.isDTB()) return false;
    return true;
};

BattleManager.actionPerformCounterPhase = function(action) {
    this._countering = true;
    this._subject.removeCounterAction();
    this._subject = action.subject();
    this._action = action;
    var subject = this._subject;
    var targets = action.makeTargets();
    this.setTargets(targets);
    this._allTargets = targets.slice();
    this._individualTargets = targets.slice();
    this._phase = 'phaseChange';
    this._phaseSteps = ['setup', 'whole', 'target', 'follow', 'finish'];
    this._returnPhase = '';
    this._actionList = [];
    subject.useItem(this._action.item());
    this._action.applyGlobal();
    this._logWindow.startAction(this._subject, this._action, this._targets);
};

//=============================================================================
// Game_BattlerBase
//=============================================================================

Yanfly.Counter.Game_BattlerBase_updateStateActionStart =
    Game_BattlerBase.prototype.updateStateActionStart;
Game_BattlerBase.prototype.updateStateActionStart = function() {
    if (BattleManager.isCountering()) return;
    Yanfly.Counter.Game_BattlerBase_updateStateActionStart.call(this);
};

//=============================================================================
// Game_Battler
//=============================================================================

Yanfly.Counter.Game_Battler_refresh = Game_Battler.prototype.refresh;
Game_Battler.prototype.refresh = function() {
    this._counterTotalCache = undefined;
    Yanfly.Counter.Game_Battler_refresh.call(this);
};

Game_Battler.prototype.counterSkills = function() {
    if (this._counterSkills === undefined || this._counterSkills === []) {
      return [];
    }
    var array = [];
    var length = this._counterSkills.length;
    for (var i = 0; i < length; ++i) {
      var skillId = this._counterSkills[i];
      var skill = $dataSkills[skillId];
      array.push(skill);
    }
    return array;
};

Game_Battler.prototype.makeCounterSkills = function() {
    this._counterSkills = [];
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      if (obj.counterSkills) {
        Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
      }
      this.extendCounterSkillsEval(obj);
    }
};

Game_Battler.prototype.extendCounterSkillsEval = function(obj) {
    if (!obj) return;
    if (!obj.counterSkillsEval) return;
    if (obj.counterSkillsEval === '') return;
    var skills = [];
    var a = this;
    var user = this;
    var subject = this;
    var b = this;
    var target = this;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = obj.counterSkillsEval;
    try {
      eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'COUNTER SKILLS ERROR');
    }
    Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
};

Yanfly.Counter.Game_Battler_onBattleStart =
    Game_Battler.prototype.onBattleStart;
Game_Battler.prototype.onBattleStart = function() {
    Yanfly.Counter.Game_Battler_onBattleStart.call(this);
    this._counters = 0;
};

Yanfly.Counter.Game_Battler_onTurnStart = Game_Battler.prototype.onTurnStart;
Game_Battler.prototype.onTurnStart = function() {
    Yanfly.Counter.Game_Battler_onTurnStart.call(this);
    this._counters = 0;
};

Game_Battler.prototype.counterTotal = function() {
    var value = Yanfly.Param.CounterTotal;
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      if (obj.counterTotal) value += obj.counterTotal;
      value += this.getCounterTotalEval(obj);
    }
    return value;
};

Game_Battler.prototype.getCounterTotalEval = function(obj) {
    if (!obj) return 0;
    if (!obj.counterTotalEval) return 0;
    if (obj.counterTotalEval === '') return 0;
    var value = 0;
    var a = this;
    var user = this;
    var subject = this;
    var b = this;
    var target = this;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = obj.counterTotalEval;
    try {
      eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'COUNTER TOTAL ERROR');
    }
    return value;
};

Game_Battler.prototype.payCounter = function(value) {
    value = value || 1;
    this._counters += value;
};

Game_Battler.prototype.canCounter = function() {
    if (!this.canMove()) return false;
    return this.counterTotal() >= this._counters;
};

Game_Battler.prototype.targetCounterRate = function() {
    var rate = 1;
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      rate *= obj.targetCounterRate;
    }
    return rate;
};

Game_Battler.prototype.targetCounterFlat = function() {
    var value = 0;
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      value += obj.targetCounterFlat;
    }
    return value;
};

Game_Battler.prototype.targetCounterRateEval = function(rate, target, item) {
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      rate = this.getTargetCntRateEval(obj, rate, target, item);
    }
    return rate;
};

Game_Battler.prototype.getTargetCntRateEval = function(obj, rate, trg, item) {
    if (!obj) return rate;
    if (!obj.targetCounterRateEval) return rate;
    if (obj.targetCounterRateEval === '') return rate;
    var skill = item;
    var a = this;
    var user = this;
    var subject = this;
    var b = trg;
    var target = trg;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = obj.targetCounterRateEval;
    try {
      eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'TARGET COUNTER RATE ERROR');
    }
    return rate;
};

Game_Battler.prototype.forceEvadeCounter = function() {
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      if (obj.evadeCounter) return true;
    }
    return false;
};

Game_Battler.prototype.forceHitCounter = function() {
    var length = this.states().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.states()[i];
      if (!obj) continue;
      if (obj.hitCounter) return true;
    }
    return false;
};

Game_Battler.prototype.forceEvadePopup = function() {
    this._result = new Game_ActionResult();
    this._result.evaded = true;
    this.startDamagePopup();
    this.clearResult();
};

Game_Battler.prototype.setCounterAction = function(action) {
    if (!this._originalPreCounterActions) {
      this._originalPreCounterActions = JsonEx.makeDeepCopy(this._actions);
    }
    action.setCounter();
    this.setAction(0, action);
};

Game_Battler.prototype.removeCounterAction = function() {
    if (this._originalPreCounterActions) {
      this._actions = JsonEx.makeDeepCopy(this._originalPreCounterActions);
    }
    this._originalPreCounterActions = undefined;
};

//=============================================================================
// Game_Actor
//=============================================================================

Game_Actor.prototype.makeCounterSkills = function() {
    Game_Battler.prototype.makeCounterSkills.call(this);
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      if (obj.counterSkills) {
        Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
      }
      this.extendCounterSkillsEval(obj);
    }
    if (this.currentClass().counterSkills) {
      obj = this.currentClass();
      Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
      this.extendCounterSkillsEval(obj);
    }
    if (this.actor().counterSkills) {
      obj = this.actor();
      Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
      this.extendCounterSkillsEval(obj);
    }
    if (this._counterSkills.length <= 0) {
      this._counterSkills.push(obj.defaultCounter);
    }
};

Game_Actor.prototype.counterTotal = function() {
    if (this._counterTotalCache !== undefined) return this._counterTotalCache;
    var value = Game_Battler.prototype.counterTotal.call(this);
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      if (obj.counterTotal) value += obj.counterTotal;
      value += this.getCounterTotalEval(obj);
    }
    value += this.currentClass().counterTotal;
    value += this.getCounterTotalEval(this.currentClass());
    value += this.actor().counterTotal;
    value += this.getCounterTotalEval(this.actor());
    this._counterTotalCache = value;
    return this._counterTotalCache;
};

Game_Actor.prototype.targetCounterRate = function() {
    var rate = Game_Battler.prototype.targetCounterRate.call(this);
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      rate *= obj.targetCounterRate;
    }
    rate *= this.currentClass().targetCounterRate;
    rate *= this.actor().targetCounterRate;
    return rate;
};

Game_Actor.prototype.targetCounterFlat = function() {
    var value = Game_Battler.prototype.targetCounterFlat.call(this);
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      value += obj.targetCounterFlat;
    }
    value += this.currentClass().targetCounterFlat;
    value += this.actor().targetCounterFlat;
    return value;
};

Game_Actor.prototype.targetCounterRateEval = function(rate, target, item) {
    var rate = Game_Battler.prototype.targetCounterRateEval.call(this, rate,
      target, item);
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      rate = this.getTargetCntRateEval(obj, rate, target, item);
    }
    rate = this.getTargetCntRateEval(this.currentClass(), rate, target, item);
    rate = this.getTargetCntRateEval(this.actor(), rate, target, item);
    return rate;
};

Game_Actor.prototype.forceEvadeCounter = function() {
    if (Game_Battler.prototype.forceEvadeCounter.call(this)) return true;
    if (this.actor().evadeCounter) return true;
    if (this.currentClass().evadeCounter) return true;
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      if (obj.evadeCounter) return true;
    }
    return false;
};

Game_Actor.prototype.forceHitCounter = function() {
    if (Game_Battler.prototype.forceHitCounter.call(this)) return true;
    if (this.actor().hitCounter) return true;
    if (this.currentClass().hitCounter) return true;
    var length = this.equips().length;
    for (var i = 0; i < length; ++i) {
      var obj = this.equips()[i];
      if (!obj) continue;
      if (obj.hitCounter) return true;
    }
    return false;
};

//=============================================================================
// Game_Enemy
//=============================================================================

Game_Enemy.prototype.makeCounterSkills = function() {
    Game_Battler.prototype.makeCounterSkills.call(this);
    if (this.enemy().counterSkills) {
      obj = this.enemy();
      Yanfly.Util.extend(this._counterSkills, obj.counterSkills);
      this.extendCounterSkillsEval(obj);
    }
    if (this._counterSkills.length <= 0) {
      this._counterSkills.push(obj.defaultCounter);
    }
};

Game_Enemy.prototype.counterTotal = function() {
    if (this._counterTotalCache !== undefined) return this._counterTotalCache;
    var value = Game_Battler.prototype.counterTotal.call(this);
    value += this.enemy().counterTotal;
    value += this.getCounterTotalEval(this.enemy());
    this._counterTotalCache = value;
    return this._counterTotalCache;
};

Game_Enemy.prototype.targetCounterRate = function() {
    var rate = Game_Battler.prototype.targetCounterRate.call(this);
    rate *= this.enemy().targetCounterRate;
    return rate;
};

Game_Enemy.prototype.targetCounterFlat = function() {
    var value = Game_Battler.prototype.targetCounterFlat.call(this);
    value += this.enemy().targetCounterFlat;
    return value;
};

Game_Enemy.prototype.targetCounterRateEval = function(rate, target, item) {
    var rate = Game_Battler.prototype.targetCounterRateEval.call(this, rate,
      target, item);
    rate = this.getTargetCntRateEval(this.enemy(), rate, target, item);
    return rate;
};

Game_Enemy.prototype.forceEvadeCounter = function() {
    if (Game_Battler.prototype.forceEvadeCounter.call(this)) return true;
    if (this.enemy().evadeCounter) return true;
    return false;
};

Game_Enemy.prototype.forceHitCounter = function() {
    if (Game_Battler.prototype.forceHitCounter.call(this)) return true;
    if (this.enemy().hitCounter) return true;
    return false;
};

//=============================================================================
// Game_Action
//=============================================================================

Game_Action.prototype.itemCnt = function(target) {
    if (this.item().cannotCounter) return 0;
    if (!this.item().allyCounter) {
      if (target.isActor() === this.subject().isActor()) return 0;
    }
    if (!target.canMove()) return 0;
    if (!target.canCounter()) return 0;
    var rate = target.cnt;
    rate *= this.subject().targetCounterRate();
    rate *= this.item().counterRate;
    rate += this.subject().targetCounterFlat();
    rate += this.item().counterMod;
    rate = this.subject().targetCounterRateEval(rate, target, this.item());
    rate = this.customCounterRateEval(rate, target);
    return rate;
};

Game_Action.prototype.customCounterRateEval = function(rate, target) {
    if (this.item().counterRateEval === '') return rate;
    var item = this.item();
    var skill = this.item();
    var a = this.subject();
    var user = this.subject();
    var subject = this.subject();
    var b = target;
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var code = this.item().counterRateEval;
    try {
      eval(code);
    } catch (e) {
      Yanfly.Util.displayError(e, code, 'COUNTER RATE ERROR');
    }
    return rate;
};

if (Imported.YEP_X_BattleSysCTB) {

Yanfly.Counter.Game_Action_rebalanceCTBSpeed =
    Game_Action.prototype.rebalanceCTBSpeed;
Game_Action.prototype.rebalanceCTBSpeed = function(target) {
    if (BattleManager.isCountering()) return;
    Yanfly.Counter.Game_Action_rebalanceCTBSpeed.call(this, target);
};

}; // Imported.YEP_X_BattleSysCTB

Yanfly.Counter.Game_Action_clear = Game_Action.prototype.clear;
Game_Action.prototype.clear = function() {
    Yanfly.Counter.Game_Action_clear.call(this);
    this._isCounter = false;
    this._isForceMiss = false;
};

Game_Action.prototype.setCounter = function() {
    this._isCounter = true;
};

Game_Action.prototype.isCounter = function() {
    return this._isCounter;
};

//=============================================================================
// Window_BattleLog
//=============================================================================

Yanfly.Counter.Window_BattleLog_displayIcon =
    Window_BattleLog.prototype.displayIcon;
Window_BattleLog.prototype.displayIcon = function(item) {
    if (BattleManager.isCountering()) return item.counterIcon;
    return Yanfly.Counter.Window_BattleLog_displayIcon.call(this, item);
};

Yanfly.Counter.Window_BattleLog_displayText =
    Window_BattleLog.prototype.displayText;
Window_BattleLog.prototype.displayText = function(item) {
    if (BattleManager.isCountering()) return item.counterName;
    return Yanfly.Counter.Window_BattleLog_displayText.call(this, item);
};

//=============================================================================
// Utilities
//=============================================================================

Yanfly.Util = Yanfly.Util || {};

Yanfly.Util.extend = function (mainArray, otherArray) {
    otherArray.forEach(function(i) {
      mainArray.push(i)
    }, this);
}

Yanfly.Util.displayError = function(e, code, message) {
  console.log(message);
  console.log(code || 'NON-EXISTENT');
  console.error(e);
  if (Utils.RPGMAKER_VERSION && Utils.RPGMAKER_VERSION >= "1.6.0") return;
  if (Utils.isNwjs() && Utils.isOptionValid('test')) {
    if (!require('nw.gui').Window.get().isDevToolsOpen()) {
      require('nw.gui').Window.get().showDevTools();
    }
  }
};

//=============================================================================
// End of File
//=============================================================================
};