//=============================================================================
// 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.08;

//=============================================================================
/*:
* @plugindesc v1.08 (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.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.isNwjs() && Utils.isOptionValid('test')) {
      if (!require('nw.gui').window.isDevToolsOpen()) {
        require('nw.gui').window.showDevTools();
      }
    }
  };

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