code:
//=============================================================================
// Yanfly Engine Plugins - Damage Core
// YEP_DamageCore.js
//=============================================================================

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

var Yanfly = Yanfly || {};
Yanfly.DMG = Yanfly.DMG || {};
Yanfly.DMG.version = 1.07;

//=============================================================================
 /*:
 * @plugindesc v1.07 Expand the control you have over the game's damage
 * calculation with more features and effects.
 * @author Yanfly Engine Plugins
 *
 * @param ---Damage Cap---
 * @default
 *
 * @param Enable Cap
 * @parent ---Damage Cap---
 * @type boolean
 * @on Damage Cap
 * @off No Damage Cap
 * @desc Do you wish to put a cap on your damage?
 * NO - false     YES - true     Default: false
 * @default true
 *
 * @param Maximum Damage
 * @parent ---Damage Cap---
 * @type number
 * @min 0
 * @desc If enabled, what is the default maximum damage?
 * @default 9999
 *
 * @param Maximum Healing
 * @parent ---Damage Cap---
 * @type number
 * @min 0
 * @desc If enabled, what is the default maximum healing?
 * @default 9999
 *
 * @param ---Damage Steps---
 * @default
 *
 * @param Damage Step 1
 * @parent ---Damage Steps---
 * @desc This is the step after the base value has been calculated.
 * Previous line: baseDamage = this.evalDamageFormula(target);
 * @default baseDamage = this.modifyBaseDamage(value, baseDamage, target);
 *
 * @param Damage Step 2
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default baseDamage *= this.calcElementRate(target);
 *
 * @param Damage Step 3
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 4
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 5
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 6
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default critical = this.modifyCritical(critical, baseDamage, target);
 *
 * @param Damage Step 7
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default target.result().critical = critical;
 *
 * @param Damage Step 8
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = baseDamage;
 *
 * @param Damage Step 9
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 10
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (baseDamage > 0) {
 *
 * @param Damage Step 11
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyDamageRate(value, baseDamage, target);
 *
 * @param Damage Step 12
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 13
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 14
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 15
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 16
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 17
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 18
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 19
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 20
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (baseDamage < 0) {
 *
 * @param Damage Step 21
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyHealRate(value, baseDamage, target);
 *
 * @param Damage Step 22
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 23
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 24
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 25
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 26
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 27
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 28
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 29
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 30
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (critical) {
 *
 * @param Damage Step 31
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyCriticalRate(value, baseDamage, target);
 *
 * @param Damage Step 32
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 33
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 34
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 35
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 36
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 37
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 38
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 39
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 40
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (this.isPhysical()) {
 *
 * @param Damage Step 41
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyPhysicalRate(value, baseDamage, target);
 *
 * @param Damage Step 42
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 43
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 44
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 45
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 46
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 47
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatPhysical(value, baseDamage, target);
 *
 * @param Damage Step 48
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 49
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 50
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (this.isMagical()) {
 *
 * @param Damage Step 51
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyMagicalRate(value, baseDamage, target);
 *
 * @param Damage Step 52
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 53
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 54
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 55
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 56
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 57
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatMagical(value, baseDamage, target);
 *
 * @param Damage Step 58
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 59
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 60
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (baseDamage > 0) {
 *
 * @param Damage Step 61
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatDamage(value, baseDamage, target);
 *
 * @param Damage Step 62
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 63
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 64
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 65
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 66
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 67
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 68
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 69
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 70
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (baseDamage < 0) {
 *
 * @param Damage Step 71
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatHeal(value, baseDamage, target);
 *
 * @param Damage Step 72
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 73
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 74
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 75
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 76
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 77
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 78
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 79
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 80
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default if (critical) {
 *
 * @param Damage Step 81
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatCritical(value, baseDamage, target);
 *
 * @param Damage Step 82
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 83
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 84
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 85
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 86
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 87
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 88
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default }
 *
 * @param Damage Step 89
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 90
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyVariance(value, item.damage.variance);
 *
 * @param Damage Step 91
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 92
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 93
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 94
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 95
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyGuard(value, target);
 *
 * @param Damage Step 96
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 97
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 98
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default
 *
 * @param Damage Step 99
 * @parent ---Damage Steps---
 * @desc This is the next step in the damage flow.
 * @default value = this.applyFlatGlobal(value, baseDamage, target);
 *
 * @param Damage Step 100
 * @parent ---Damage Steps---
 * @desc This is the final step in the damage flow.
 * Following line: return Math.round(value);
 * @default value = this.applyMinimumDamage(value, baseDamage, target);
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * The game gives a lot of control over the damage formula, but it doesn't give
 * much control for everything else after calculating it. This plugin will give
 * you control over the order the damage formula is calculated in addition to
 * letting you insert your own changes to it at whatever you wish.
 *
 * If you have YEP_BattleEngineCore.js installed, place this plugin under
 * YEP_BattleEngineCore.js if you wish to make use of the extra features this
 * plugin has to offer.
 *
 * ============================================================================
 * Notetags
 * ============================================================================
 *
 * The following are some notetags you can use to modify the damage caps.
 *
 * Skill and Item Notetag:
 *   <Bypass Damage Cap>
 *   This causes the skill/item to ignore the damage cap and go with the
 *   regular value of the calculated damage. This will cancel out any damage
 *   cap effects otherwise. This will take priority over any damage cap
 *   breaking effects.
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *   <Bypass Damage Cap>
 *   This will cause the related battler to bypass any damage capping effects
 *   and its skills/items will go with the uncapped calculated value.
 *
 *   <Damage Cap: x>
 *   <Heal Cap: x>
 *   This will set the skill to have a damage/healing cap of x. This will
 *   cancel out any damage cap bypassers. If a battler has more than one
 *   damage cap, it will go with the highest value. This means if an actor that
 *   has a weapon that brings the damage cap to 99,999 and an accessory that
 *   brings the damage cap to 999,999, then the battler's damage cap will be
 *   the highest value of 999,999.
 *
 * ============================================================================
 * Plugin Commands
 * ============================================================================
 *
 * The following are plugins you can use to set the damage cap rulings for your
 * game. Keep in mind that individual aspects such as equipment traits, skill
 * properties, etc. will take priority over these default caps.
 *
 * Plugin Command:
 *   SetDamageCap 9999     Sets the default damage cap to 9999.
 *   SetHealingCap 9999    Sets the default healing cap to 9999.
 *   EnableDamageCap       Enables default cap for both damage and healing.
 *   DisableDamageCap      Disables default cap for both damage and healing.
 *
 * ============================================================================
 * Lunatic Mode - Damage Formula
 * ============================================================================
 *
 * For those who think the damage formula box is too small and would like to
 * use the notebox instead to declare the damage formula, you can use the
 * notetags below:
 *
 * Skill and Item Notetags:
 *   <damage formula>
 *    value = 500;
 *    value += 2500;
 *   </damage formula>
 *   This will overwrite the damage formula found at the top and use the
 *   strings in the middle as the formula instead. Keep in mind that using
 *   comments here will cancel out anything following after. New variables can
 *   be used, too, to make damage calculations a bit easier.
 *
 *   value   - Refers to the amount that will become the base damage value.
 *   user    - Refers to the actor/enemy using the skill/item.
 *   subject - Refers to the actor/enemy using the skill/item.
 *   target  - Refers to the target actor/enemy on the receiving end of
 *             the skill/item.
 *
 * ============================================================================
 * Lunatic Mode - Damage Steps
 * ============================================================================
 *
 * The damage formula isn't all there is to calculating the damage that appears
 * at the very end. In this plugin's parameters towards the bottom, you'll see
 * a large list of Damage Steps. Each one of these steps is a line of code that
 * the damage count will run through in order to calculate and finalize the
 * damage output.
 *
 * The purpose of those parameters is to allow you ease of access on where you
 * want to insert code that is your own or custom code provided by another
 * plugin. Here's a quick reference on how the original damage flow looked like:
 *
 * Game_Action.prototype.makeDamageValue = function(target, critical) {
 *     var item = this.item();
 *     var baseDamage = this.evalDamageFormula(target);
 *     var value = baseDamage * this.calcElementRate(target);
 *     if (this.isPhysical()) {
 *         value *= target.pdr;
 *     }
 *     if (this.isMagical()) {
 *         value *= target.mdr;
 *     }
 *     if (baseDamage < 0) {
 *         value *= target.rec;
 *     }
 *     if (critical) {
 *         value = this.applyCritical(value);
 *     }
 *     value = this.applyVariance(value, item.damage.variance);
 *     value = this.applyGuard(value, target);
 *     value = Math.round(value);
 *     return value;
 * };
 *
 * In the vein of keeping everything organized, the following lines have been
 * incorporated into new functions:
 *
 * Formula                               New Function
 *   value *= target.pdr                   value = this.applyPhysicalRate
 *   value *= target.mdr                   value = this.applyMagicalRate
 *   value *= target.rec                   value = this.applyHealRate
 *   value = this.applyCritical(value)     value = this.applyCriticalRate
 *
 * ============================================================================
 * Yanfly Engine Plugins - Battle Engine Extension - Action Sequence Commands
 * ============================================================================
 *
 * If you have YEP_BattleEngineCore.js installed with this plugin located
 * underneath it in the Plugin Manager, you can make use of these extra
 * damage related action sequences.
 *
 *=============================================================================
 * BYPASS DAMAGE CAP
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will override all damage caps. This is applied to healing, too.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: bypass damage cap
 *=============================================================================
 *
 *=============================================================================
 * DAMAGE CAP: x
 * HEALING CAP: x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This sets the action's damage cap to x, overriding all over damage caps in
 * play except its own. This will also apply to healing, too.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: damage cap: 999
 *                healing cap: 999999
 *=============================================================================
 *
 *=============================================================================
 * DAMAGE RATE: x%
 * DAMAGE RATE: x.y
 * DAMAGE RATE: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This changes the damage rate across all types of damage (physical, magical,
 * and certain hit). The damage rate is reset at the end of each action
 * sequence. If you use a variable, it is treated as a percentage.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: damage rate: 50%
 *                damage rate: 8.667
 *                damage rate: variable 3
 *=============================================================================
 *
 *=============================================================================
 * FLAT DAMAGE: +x
 * FLAT DAMAGE: -x
 * FLAT DAMAGE: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This adds a flat damage across all types of damage (physical, magical, and
 * certain hit). The flat damage is reset at the end of each action sequence.
 * If you use a variable, it is added onto the damage.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: flat damage: +100
 *                flat damage: -250
 *                flat damage: variable 3
 *=============================================================================
 *
 *=============================================================================
 * FLAT GLOBAL: +x
 * FLAT GLOBAL: -x
 * FLAT GLOBAL: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This adds a flat global damage and heal across all types of damage
 * (physical, magical, and certain hit). The flat damage and heal is reset at
 * the end of each action sequence. If you use a variable, it is added onto the
 * damage and heal.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: flat global: +100
 *                flat global: -250
 *                flat global: variable 3
 *=============================================================================
 *
 *=============================================================================
 * FLAT HEAL: +x
 * FLAT HEAL: -x
 * FLAT HEAL: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This adds a flat heal across all types of damage (physical, magical, and
 * certain hit). The flat heal is reset at the end of each action sequence.
 * If you use a variable, it is added onto the heal.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: flat heal: +100
 *                flat heal: -250
 *                flat heal: variable 3
 *=============================================================================
 *
 *=============================================================================
 * GLOBAL RATE: x%
 * GLOBAL RATE: x.y
 * GLOBAL RATE: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This changes the damage and healing rates across all types of damage
 * (physical, magical, and certain hit). The damage and healing rates are reset
 * at the end of each action sequence. If you use a variable, it is treated as
 * a percentage.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: global rate: 50%
 *                global rate: 8.667
 *                global rate: variable 3
 *=============================================================================
 *
 *=============================================================================
 * HEAL RATE: x%
 * HEAL RATE: x.y
 * HEAL RATE: VARIABLE x
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This changes the healing rate across all types of damage (physical, magical,
 * and certain hit). The healing rate is reset at the end of each action
 * sequence. If you use a variable, it is treated as a percentage.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: heal rate: 50%
 *                heal rate: 8.667
 *                heal rate: variable 3
 *=============================================================================
 *
 *=============================================================================
 * RESET DAMAGE CAP
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will reset the damage cap implemented by the Damage Cap action
 * sequence. This will also reset the effects of the Bypass Damage Cap
 * action sequence.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: reset damage cap
 *=============================================================================
 *
 *=============================================================================
 * RESET DAMAGE MODIFIERS
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will cause all damage and healing modifiers caused by action sequences
 * to reset. While they normally reset at the end of each action sequence, this
 * will allow you to do it manually.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: reset damage modifiers
 *=============================================================================
 *
 * ============================================================================
 * Changelog
 * ============================================================================
 *
 * Version 1.07:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.06:
 * - Lunatic Mode fail safes added.
 *
 * Version 1.05:
 * - Added failsafe for damage cap check in case Lunatic Mode effects of other
 * plugins would push the damage past the capped amount.
 *
 * Version 1.04:
 * - Rewored Damage Steps 1 through 8. If you're updating from an old version,
 * please update the these manually:
 *   Step 1: baseDamage = this.modifyBaseDamage(value, baseDamage, target);
 *   Step 2: baseDamage *= this.calcElementRate(target);
 *   Steps 3 through 5: (empty)
 *   Step 6: critical = this.modifyCritical(critical, baseDamage, target);
 *   Step 7: target.result().critical = critical;
 *   Step 8: value = baseDamage;
 * - This change was made to Element Absorb and Disperse Damage better. This
 * damage step change is also more efficient in calculating damage effects that
 * alters the baseDamage.
 *
 * Version 1.03:
 * - Changed default parameter in Damage Step 4 from
 *   'baseDamage = this.modifyBaseDamage(value, baseDamage, target);' to
 *   'value = this.modifyBaseDamage(value, baseDamage, target);'
 * Be sure to manually change this yourself if you want to get things like the
 * Selection Control's Disperse Damage mechanic to work.
 *
 * Version 1.02:
 * - Updated for RPG Maker MV version 1.1.0.
 * - <Damage Formula> notetag now supports comments.
 *
 * Version 1.01:
 * - Fixed a bug with <Damage Formula> not recording custom formulas correctly.
 *
 * Version 1.00:
 * - Finished plugin!
 */
//=============================================================================

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

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

Yanfly.Param.DMGEnableCap = eval(String(Yanfly.Parameters['Enable Cap']));
Yanfly.Param.DMGMaxDamage = Number(Yanfly.Parameters['Maximum Damage']);
Yanfly.Param.DMGMaxHealing = Number(Yanfly.Parameters['Maximum Healing']);

Yanfly.SetupParameters = function() {
  Yanfly.DMG.DamageFlow = '';
  for (var i = 1; i <= 100; ++i) {
    var param = 'Damage Step ' + i;
    Yanfly.DMG.DamageFlow += String(Yanfly.Parameters[param]) + '\n';
  }
};
Yanfly.SetupParameters();

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

Yanfly.DMG.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
    if (!Yanfly.DMG.DataManager_isDatabaseLoaded.call(this)) return false;
    if (!Yanfly._loaded_YEP_DamageCore) {
      this.processDMGNotetags1($dataSkills);
      this.processDMGNotetags1($dataItems);
      this.processDMGNotetags2($dataActors);
      this.processDMGNotetags2($dataClasses);
      this.processDMGNotetags2($dataEnemies);
      this.processDMGNotetags2($dataWeapons);
      this.processDMGNotetags2($dataArmors);
      this.processDMGNotetags2($dataStates);
      Yanfly._loaded_YEP_DamageCore = true;
    }
		return true;
};

DataManager.processDMGNotetags1 = function(group) {
  var noteD1 = /<(?:DAMAGE CAP|HEAL CAP|HEALING CAP):[ ](\d+)>/i;
  for (var n = 1; n < group.length; n++) {
		var obj = group[n];
		var notedata = obj.note.split(/[\r\n]+/);

    var damageFormulaMode = false;
    obj.damage.custom = false;
    obj.breakDamageCap = false;
    obj.damageCap = undefined;

    for (var i = 0; i < notedata.length; i++) {
			var line = notedata[i];
      if (line.match(/<(?:BREAK DAMAGE CAP|BYPASS DAMAGE CAP)>/i)) {
        obj.breakDamageCap = true;
        obj.damageCap = undefined;
      } else if (line.match(noteD1)) {
        obj.damageCap = parseInt(RegExp.$1);
        obj.breakDamageCap = false;
      } else if (line.match(/<(?:DAMAGE FORMULA)>/i)) {
				damageFormulaMode = true;
        obj.damage.formula = '';
        obj.damage.custom = true;
			} else if (line.match(/<\/(?:DAMAGE FORMULA)>/i)) {
				damageFormulaMode = false;
			} else if (damageFormulaMode) {
        obj.damage.formula = obj.damage.formula + line + '\n';
      }
		}
	}
};

DataManager.processDMGNotetags2 = function(group) {
  var noteD1 = /<(?:BREAK DAMAGE CAP|BYPASS DAMAGE CAP)>/i;
  var noteD2 = /<(?:DAMAGE CAP):[ ](\d+)>/i;
  var noteD3 = /<(?:HEAL CAP|HEALING CAP):[ ](\d+)>/i;
  for (var n = 1; n < group.length; n++) {
		var obj = group[n];
		var notedata = obj.note.split(/[\r\n]+/);

    obj.breakDamageCap = undefined;
    obj.damageCap = undefined;
    obj.healCap = undefined;

		for (var i = 0; i < notedata.length; i++) {
			var line = notedata[i];
			if (line.match(noteD1)) {
				obj.breakDamageCap = true;
        obj.damageCap = undefined;
        obj.healCap = undefined;
      } else if (line.match(noteD2)) {
        obj.damageCap = parseInt(RegExp.$1);
        obj.breakDamageCap = undefined;
      } else if (line.match(noteD3)) {
        obj.healCap = parseInt(RegExp.$1) * -1;
        obj.breakDamageCap = undefined;
      }
		}
	}
};

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

if (Imported.YEP_BattleEngineCore) {
Yanfly.DMG.BattleManager_processActionSequence =
  BattleManager.processActionSequence;
  BattleManager.processActionSequence = function(actionName, actionArgs) {
    // BYPASS DAMAGE CAP
    if (actionName === 'BYPASS DAMAGE CAP') {
      return this.actionBypassDamageCap();
    }
    // DAMAGE CAP, HEALING CAP
    if (actionName === 'DAMAGE CAP' || actionName === 'HEALING CAP') {
      return this.actionDamageCap(actionArgs);
    }
    // DAMAGE RATE
    if (actionName === 'DAMAGE RATE') {
      return this.actionDamageRate(actionArgs);
    }
    // FLAT DAMAGE
    if (actionName === 'FLAT DAMAGE') {
      return this.actionFlatDamage(actionArgs);
    }
    // FLAT GLOBAL
    if (actionName === 'FLAT GLOBAL') {
      return this.actionFlatGlobal(actionArgs);
    }
    // FLAT HEAL
    if (actionName === 'FLAT HEAL') {
      return this.actionFlatHeal(actionArgs);
    }
    // GLOBAL RATE
    if (actionName === 'GLOBAL RATE') {
      return this.actionGlobalRate(actionArgs);
    }
    // HEAL RATE
    if (actionName === 'HEAL RATE') {
      return this.actionHealRate(actionArgs);
    }
    // RESET DAMAGE CAP
    if (actionName === 'RESET DAMAGE CAP') {
      return this.actionResetDamageCap();
    }
    // RESET DAMAGE MODIFIERS
    if (actionName === 'RESET DAMAGE MODIFIERS') {
      return this.actionResetDamageModifiers();
    }
    return Yanfly.DMG.BattleManager_processActionSequence.call(this,
      actionName, actionArgs);
  };
};

BattleManager.actionBypassDamageCap = function() {
    $gameSystem.actSeqBypassDamageCap();
    return true;
};

BattleManager.actionDamageCap = function(actionArgs) {
    if (!actionArgs) return;
    if (actionArgs[0]) {
      var value = parseInt(actionArgs[0]);
      $gameSystem.setActSeqDamageCap(value);
    }
    return true;
};

BattleManager.actionDamageRate = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseFloat($gameVariables.value(parseInt(RegExp.$1)) * 0.01);
    } else if (actionArgs[0].match(/(\d+)([%％])/i)) {
      var value = parseFloat(RegExp.$1 * 0.01);
    } else if (actionArgs[0].match(/(\d+).(\d+)/i)) {
      var value = parseFloat(String(RegExp.$1) + '.' + String(RegExp.$1));
    } else {
      return true;
    }
    $gameSystem._damageRate = value;
    return true;
};

BattleManager.actionFlatDamage = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseInt($gameVariables.value(parseInt(RegExp.$1)));
    } else if (actionArgs[0].match(/([\+\-]\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else if (actionArgs[0].match(/(\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else {
      return true;
    }
    $gameSystem._flatDamage = value;
    return true;
};

BattleManager.actionFlatGlobal = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseInt($gameVariables.value(parseInt(RegExp.$1)));
    } else if (actionArgs[0].match(/([\+\-]\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else if (actionArgs[0].match(/(\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else {
      return true;
    }
    $gameSystem._flatDamage = value;
    $gameSystem._flatHeal = value;
    return true;
};

BattleManager.actionFlatHeal = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseInt($gameVariables.value(parseInt(RegExp.$1)));
    } else if (actionArgs[0].match(/([\+\-]\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else if (actionArgs[0].match(/(\d+)/i)) {
      var value = parseInt(RegExp.$1);
    } else {
      return true;
    }
    $gameSystem._flatHeal = value;
    return true;
};

BattleManager.actionGlobalRate = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseFloat($gameVariables.value(parseInt(RegExp.$1)) * 0.01);
    } else if (actionArgs[0].match(/(\d+)([%％])/i)) {
      var value = parseFloat(RegExp.$1 * 0.01);
    } else if (actionArgs[0].match(/(\d+).(\d+)/i)) {
      var value = parseFloat(String(RegExp.$1) + '.' + String(RegExp.$1));
    } else {
      return true;
    }
    $gameSystem._damageRate = value;
    $gameSystem._healRate = value;
    return true;
};

BattleManager.actionHealRate = function(actionArgs) {
    if (actionArgs[0].match(/(?:VARIABLE|VAR)[ ](\d+)/i)) {
      var value = parseFloat($gameVariables.value(parseInt(RegExp.$1)) * 0.01);
    } else if (actionArgs[0].match(/(\d+)([%％])/i)) {
      var value = parseFloat(RegExp.$1 * 0.01);
    } else if (actionArgs[0].match(/(\d+).(\d+)/i)) {
      var value = parseFloat(String(RegExp.$1) + '.' + String(RegExp.$1));
    } else {
      return true;
    }
    $gameSystem._healRate = value;
    return true;
};

BattleManager.actionResetDamageCap = function() {
    $gameSystem.resetActSeqDamageCap();
    return true;
};

BattleManager.actionResetDamageModifiers = function() {
    $gameSystem.resetDamageSettings();
    return true;
};

//=============================================================================
// Game_System
//=============================================================================

Yanfly.DMG.Game_System_initialize = Game_System.prototype.initialize;
Game_System.prototype.initialize = function() {
    Yanfly.DMG.Game_System_initialize.call(this);
    this.resetActSeqDamageCap();
    this.resetDamageSettings();
};

Game_System.prototype.resetActSeqDamageCap = function() {
    this._actSeqBypassDamageCap = false;
    this._actSeqDamageCap = undefined;
};

Game_System.prototype.actSeqBypassDamageCap = function() {
    this._actSeqBypassDamageCap = true;
};

Game_System.prototype.getActSeqBypassDamageCap = function() {
    return this._actSeqBypassDamageCap;
};

Game_System.prototype.setActSeqDamageCap = function(value) {
    this._actSeqDamageCap = value;
};

Game_System.prototype.getActSeqDamageCap = function() {
    return this._actSeqDamageCap;
};

Game_System.prototype.resetDamageSettings = function() {
    this._damageRate = 1.0;
    this._flatDamage = 0;
    this._healRate = 1.0;
    this._flatHeal = 0;
    this._defaultDamageCap = Yanfly.Param.DMGEnableCap;
};

Game_System.prototype.damageRate = function() {
    if (this._damageRate === undefined) this.resetDamageSettings();
    return this._damageRate;
};

Game_System.prototype.flatDamage = function() {
    if (this._flatDamage === undefined) this.resetDamageSettings();
    return this._flatDamage;
};

Game_System.prototype.healRate = function() {
    if (this._healRate === undefined) this.resetDamageSettings();
    return this._healRate;
};

Game_System.prototype.flatHeal = function() {
    if (this._flatHeal === undefined) this.resetDamageSettings();
    return this._flatHeal;
};

Game_System.prototype.isDamageCapped = function() {
    return this._defaultDamageCap;
};

Game_System.prototype.maximumDamage = function() {
    if (this._newDamageCap !== undefined) return this._newDamageCap;
    return Yanfly.Param.DMGMaxDamage;
};

Game_System.prototype.maximumHealing = function() {
    if (this._newHealingCap !== undefined) return this._newHealingCap;
    return Yanfly.Param.DMGMaxHealing * -1;
};

Game_System.prototype.setNewDamageCap = function(value, damage) {
    if (damage) {
      this._newDamageCap = value;
    } else {
      this._newHealingCap = value * -1;
    }
};

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

Yanfly.DMG.Game_BattlerBase_refresh = Game_BattlerBase.prototype.refresh;
Game_BattlerBase.prototype.refresh = function() {
    Yanfly.DMG.Game_BattlerBase_refresh.call(this);
    this.resetDMGTempValues();
};

Game_BattlerBase.prototype.resetDMGTempValues = function() {
    this._isDMGCapped = undefined;
    this._maximumDamage = undefined;
    this._maximumHealing = undefined;
};

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

Yanfly.DMG.Game_Battler_performActionEnd =
    Game_Battler.prototype.performActionEnd;
Game_Battler.prototype.performActionEnd = function() {
    Yanfly.DMG.Game_Battler_performActionEnd.call(this);
    $gameSystem.resetDamageSettings();
};

Game_Battler.prototype.isDamageCapped = function() {
    for (var i = 0; i < this.states().length; ++i) {
      var state = this.states()[i];
      if (state && state.breakDamageCap) return this._isDMGCapped = false;
    }
    return this._isDMGCapped = $gameSystem.isDamageCapped();
};

Game_Battler.prototype.maximumDamage = function() {
    var value = $gameSystem.maximumDamage();
    for (var i = 0; i < this.states().length; ++i) {
      var state = this.states()[i];
      if (state && state.damageCap) value = Math.max(value, state.damageCap);
    }
    return value;
};

Game_Battler.prototype.maximumHealing = function() {
  var value = $gameSystem.maximumHealing();
  for (var i = 0; i < this.states().length; ++i) {
    var state = this.states()[i];
    if (state && state.healCap) value = Math.min(value, state.healCap);
  }
  return value;
};

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

Game_Actor.prototype.isDamageCapped = function() {
  if (this._isDMGCapped !== undefined) return this._isDMGCapped;
  if (this.actor().breakDamageCap) return this._isDMGCapped = false;
  if (this.currentClass().breakDamageCap) return this._isDMGCapped = false;
  for (var i = 0; i < this.equips().length; ++i) {
    var equip = this.equips()[i];
    if (equip && equip.breakDamageCap) return this._isDMGCapped = false;
  }
  return Game_Battler.prototype.isDamageCapped.call(this);
};

Game_Actor.prototype.maximumDamage = function() {
    if (this._maximumDamage !== undefined) return this._maximumDamage;
    var value = Game_Battler.prototype.maximumDamage.call(this);
    if (this.actor().damageCap) {
      value = Math.max(value, this.actor().damageCap);
    }
    if (this.currentClass().damageCap) {
      value = Math.max(value, this.currentClass().damageCap);
    }
    for (var i = 0; i < this.equips().length; ++i) {
      var equip = this.equips()[i];
      if (equip && equip.damageCap) value = Math.max(value, equip.damageCap);
    }
    return this._maximumDamage = value;
};

Game_Actor.prototype.maximumHealing = function() {
    if (this._maximumHealing !== undefined) return this._maximumHealing;
    var value = Game_Battler.prototype.maximumHealing.call(this);
    if (this.actor().healCap) {
      value = Math.min(value, this.actor().healCap);
    }
    if (this.currentClass().healCap) {
      value = Math.min(value, this.currentClass().healCap);
    }
    for (var i = 0; i < this.equips().length; ++i) {
      var equip = this.equips()[i];
      if (equip && equip.healCap) value = Math.min(value, equip.healCap);
    }
    return this._maximumHealing = value;
};

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

Game_Enemy.prototype.isDamageCapped = function() {
  if (this._isDMGCapped !== undefined) return this._isDMGCapped;
  if (this.enemy().breakDamageCap) return this._isDMGCapped = false;
  return Game_Battler.prototype.isDamageCapped.call(this);
};

Game_Enemy.prototype.maximumDamage = function() {
    if (this._maximumDamage !== undefined) return this._maximumDamage;
    var value = Game_Battler.prototype.maximumDamage.call(this);
    if (this.enemy().damageCap) {
      value = Math.max(value, this.enemy().damageCap);
    }
    return this._maximumDamage = value;
};

Game_Enemy.prototype.maximumHealing = function() {
    if (this._maximumHealing !== undefined) return this._maximumHealing;
    var value = Game_Battler.prototype.maximumHealing.call(this);
    if (this.enemy().healCap) {
      value = Math.min(value, this.enemy().healCap);
    }
    return this._maximumHealing = value;
};

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

Game_Action.prototype.makeDamageValue = function(target, critical) {
  var item = this.item();
  var a = this.subject();
  var b = target;
  var user = this.subject();
  var s = $gameSwitches._data;
  var v = $gameVariables._data;
  var baseDamage = this.evalDamageFormula(target);
  var value = baseDamage;
  try {
    eval(Yanfly.DMG.DamageFlow);
  } catch (e) {
    Yanfly.Util.displayError(e, Yanfly.DMG.DamageFlow, 'DAMAGE FLOW ERROR');
  }
  return Math.round(value);
};

Game_Action.prototype.evalDamageFormula = function(target) {
  try {
    var item = this.item();
    var a = this.subject();
    var b = target;
    var user = this.subject();
    var subject = this.subject();
    var s = $gameSwitches._data;
    var v = $gameVariables._data;
    var sign = ([3, 4].contains(item.damage.type) ? -1 : 1);
    var value = 0;
    if (item.damage.custom) {
		//console.log(item.damage.formula)
      eval(item.damage.formula);
      value = Math.max(value, 0) * sign;
    } else {
      value = Math.max(eval(item.damage.formula), 0) * sign;
    }
    return value;
  } catch (e) {
    if (item.damage.custom) {
      Yanfly.Util.displayError(e, item.damage.custom, 'DAMAGE FORMULA ERROR');
    } else {
      Yanfly.Util.displayError(e, item.damage.formula, 'DAMAGE FORMULA ERROR');
    }
    return 0;
  }
};

Game_Action.prototype.modifyCritical = function(critical, baseDamage, target) {
    return critical;
};

Game_Action.prototype.modifyBaseDamage = function(value, baseDamage, target) {
    return baseDamage;
};

Game_Action.prototype.applyDamageRate = function(value, baseDamage, target) {
    value *= $gameSystem.damageRate();
    value = Math.max(0, value);
    return value;
};

Game_Action.prototype.applyHealRate = function(value, baseDamage, target) {
    value *= $gameSystem.healRate();
    value *= target.rec;
    value = Math.min(0, value);
    return value;
};

Game_Action.prototype.applyCriticalRate = function(value, baseDamage, target) {
    value = this.applyCritical(value);
    return value;
};

Game_Action.prototype.applyPhysicalRate = function(value, baseDamage, target) {
    value *= target.pdr;
    return value;
};

Game_Action.prototype.applyFlatPhysical = function(value, baseDamage, target) {
    return value;
};

Game_Action.prototype.applyMagicalRate = function(value, baseDamage, target) {
    value *= target.mdr;
    return value;
};

Game_Action.prototype.applyFlatMagical = function(value, baseDamage, target) {
    return value;
};

Game_Action.prototype.applyFlatDamage = function(value, baseDamage, target) {
    value += $gameSystem.flatDamage();
    return value;
};

Game_Action.prototype.applyFlatHeal = function(value, baseDamage, target) {
    value -= $gameSystem.flatHeal();
    return value;
};

Game_Action.prototype.applyFlatCritical = function(value, baseDamage, target) {
    return value;
};

Game_Action.prototype.applyFlatGlobal = function(value, baseDamage, target) {
    return value;
};

Game_Action.prototype.applyMinimumDamage = function(value, baseDamage, target) {
    if (baseDamage > 0) {
      value = Math.max(0, value);
    } else if (baseDamage < 0) {
      value = Math.min(0, value);
    }
    if (this.isDamageCapped()) {
      if ($gameSystem.getActSeqDamageCap() !== undefined) {
        var min = $gameSystem.getActSeqDamageCap() * -1;
        var max = $gameSystem.getActSeqDamageCap();
      } else if (this.item().damageCap) {
        var min = this.item().damageCap * -1;
        var max = this.item().damageCap;
      } else {
        var min = this.subject().maximumHealing();
        var max = this.subject().maximumDamage();
      }
      value = value.clamp(min, max);
    }
    return value;
};

Game_Action.prototype.isDamageCapped = function() {
    var item = this.item();
    if ($gameSystem.getActSeqBypassDamageCap()) return false;
    if ($gameSystem.getActSeqDamageCap() !== undefined) return true;
    if (item.damageCap !== undefined) return true;
    if (item.breakDamageCap) return false;
    return this.subject().isDamageCapped();
};

Yanfly.DMG.Game_Action_executeHpDamage = Game_Action.prototype.executeHpDamage;
Game_Action.prototype.executeHpDamage = function(target, value) {
  value = this.applyMinimumDamage(value, value, target);
  Yanfly.DMG.Game_Action_executeHpDamage.call(this, target, value);
};

Yanfly.DMG.Game_Action_executeMpDamage = Game_Action.prototype.executeMpDamage;
Game_Action.prototype.executeMpDamage = function(target, value) {
  value = this.applyMinimumDamage(value, value, target);
  Yanfly.DMG.Game_Action_executeMpDamage.call(this, target, value);
};

//=============================================================================
// Game_Interpreter
//=============================================================================

Yanfly.DMG.Game_Interpreter_pluginCommand =
    Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    Yanfly.DMG.Game_Interpreter_pluginCommand.call(this, command, args)
    if (command === 'SetDamageCap') this.setDamageCap(args);
		if (command === 'SetHealingCap') this.setHealingCap(args);
    if (command === 'EnableDamageCap') this.setDefaultDamageCap(true);
    if (command === 'DisableDamageCap') this.setDefaultDamageCap(false);
};

Game_Interpreter.prototype.setDamageCap = function(args) {
    $gameSystem.setNewDamageCap(parseInt(args[0]), true);
};

Game_Interpreter.prototype.setHealingCap = function(args) {
    $gameSystem.setNewDamageCap(parseInt(args[0]), false);
};

Game_Interpreter.prototype.setDefaultDamageCap = function(value) {
    $gameSystem._defaultDamageCap = value;
};

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

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

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.get().isDevToolsOpen()) {
      require('nw.gui').Window.get().showDevTools();
    }
  }
};

//=============================================================================
// End of File
//=============================================================================

repo_name: Deltras/karryn

license: unknown


code:
//=============================================================================
// Yanfly Engine Plugins - Element Core
// YEP_ElementCore.js
//=============================================================================

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

var Yanfly = Yanfly || {};
Yanfly.Ele = Yanfly.Ele || {};
Yanfly.Ele.version = 1.03;

//=============================================================================
 /*:
 * @plugindesc v1.03 Manage the way elements work in this game from
 * absorbing elements, reflecting elements, and more!
 * @author Yanfly Engine Plugins
 *
 * @param Multi-Element Rulings
 * @type select
 * @option Lowest
 * @value 0
 * @option Add
 * @value 1
 * @option Multiply
 * @value 2
 * @option Highest
 * @value 3
 * @option Average
 * @value 4
 * @desc If a skill/item has multiple elements, then use these rules:
 * 0 - Lowest; 1 - Add; 2 - Multiply; 3 - Highest; 4 - Average
 * @default 2
 *
 * @help
 * ============================================================================
 * Introduction
 * ============================================================================
 *
 * Elemental control in RPG Maker MV is pretty lacking. The calculation of how
 * multiple elements are handled aren't very clear nor are they too intuitive
 * when it comes to certain aspects. This plugin also gives way to skills and
 * items having more than one element, battlers being able to absorb, reflect,
 * amplify elemental damage, and more!
 *
 * * Note: If you are using the Battle Engine Core, place this plugin under the
 * Battle Engine Core in the plugin list for additional features.
 *
 * * Note: If you are using the Damage Core, place this plugin underneath the
 * Damage Core plugin in the list for maximum compatibility.
 *
 * ============================================================================
 * Notetags
 * ============================================================================
 *
 * Use these notetags if you wish to modify various aspects of elements for
 * your database objects.
 *
 * Skill and Item Notetags
 *
 *   <Bypass Element Reflect>
 *   - Allows this skill/item to ignore elemental reflect properties. This will
 *   not bypass reflect properties as a whole, however.
 *
 *   <Multiple Elements: x>
 *   <Multiple Elements: x to y>
 *   <Multiple Elements: x, x, x>
 *   <Multiple Elements: name, name, name>
 *   - This adds elements x (or name) to the skill/item in addition to the
 *   skill/item's current element. Skills and items with multiple elements will
 *   follow the Multi-Element Rule when calculating damage rate. Insert more of
 *   this notetag to insert more elements.
 *
 *   <Multi-Element Rule: Lowest>
 *   <Multi-Element Rule: Add>
 *   <Multi-Element Rule: Multiply>
 *   <Multi-Element Rule: Highest>
 *   <Multi-Element Rule: Average>
 *   - This allows you to set the rule for this skill/item if it has multiple
 *   elements. Either the lowest rate, the additive sum of all rates, the
 *   multiplicative product of all rates, or the highest rate will be used. If
 *   average is used, it will be the average of all element rates.
 *
 * Actor, Class, Enemy, Weapon, Armor, and State Notetags:
 *
 *   <Element Absorb: x>
 *   <Element Absorb: x, x, x>
 *   <Element Absorb: name>
 *   <Element Absorb: name, name, name>
 *   - Causes element x to be absorbed and heals the battler. When an element
 *   is absorbed, the rate goes down by 200% instead of being just an inverse.
 *   This is so that battlers that are originally resistant to the element will
 *   absorb more of the element while battlers that are originally vulnerable
 *   to the element will absorb less of the element. The minimum amount
 *   absorbed is 0.01%.
 *
 *   <Element Reflect x: +y%>
 *   <Element Reflect x: -y%>
 *   <Element Reflect name: +y%>
 *   <Element Reflect name: -y%>
 *   - Increases or decreases the rate to reflect element x by y%. If a skill
 *   or item has multiple elements, the reflect rate is added for each element
 *   used by the skill/item.
 *
 *   <Element Magnify x: +y%>
 *   <Element Magnify x: -y%>
 *   <Element Magnify name: +y%>
 *   <Element Magnify name: -y%>
 *   - If the user performs a skill or item that utilizes element x (or name),
 *   increase or decrease its damage by y%. If a skill or item has multiple
 *   elements, the rate is increased additively for each element and adjusted
 *   multiplicatively with base rate. This bottoms out at 0%.
 *
 *   <Element Amplify x: +y%>
 *   <Element Amplify x: -y%>
 *   <Element Amplify name: +y%>
 *   <Element Amplify name: -y%>
 *   - If the user performs a skill or item that utilizes element x (or name),
 *   increase or decrease its damage by y%. If a skill or item has multiple
 *   elements, the rate is increased additively for each element and adjusted
 *   additively for base rate.
 *
 *   <Element Null>
 *   - This will cause the battler to not have elemental attacks when using
 *   skills and items. However, this will not bypass the 'Force Element' action
 *   sequence effect.
 *
 *   <Force Element x Rate: y%>
 *   <Force Element name Rate: y%>
 *   - This forces the battler's elemental rate for x (or named) to be y%. This
 *   will work in a priority setting of states (highest priority to lowest),
 *   equips (first to last), then class, then actor/enemy if more than one
 *   notetag is used for the same element. If y is negative, the element is
 *   absorbed.
 *
 * ============================================================================
 * Yanfly Engine Plugins - Battle Engine Extension - Action Sequence Commands
 * ============================================================================
 *
 * If you have YEP_BattleEngineCore.js installed with this plugin located
 * underneath it in the Plugin Manager, you can make use of these extra
 * damage related action sequences.
 *
 *=============================================================================
 * ADD ELEMENT: X
 * ADD ELEMENT: X, X, X
 * ADD ELEMENT: NAME
 * ADD ELEMENT: NAME, NAME, NAME
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will add more elements to the currently existing elements. This will
 * not include forced elements.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: add element: 4
 *                add element: 5, 6, 7
 *                add element: fire
 *                add element: ice, wind, water
 *=============================================================================
 *
 *=============================================================================
 * CLEAR ELEMENT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will clear any ___ Element action sequence settings and revert element
 * calculation back to normal.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: clear element
 *=============================================================================
 *
 *=============================================================================
 * FORCE ELEMENT: X
 * FORCE ELEMENT: X, X, X
 * FORCE ELEMENT: NAME
 * FORCE ELEMENT: NAME, NAME, NAME
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will override elemental settings for elemental damage rate calculations
 * except for customized calculations that aim at specific elements.  This will
 * ignore all other elements.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: force element: 4
 *                force element: 5, 6, 7
 *                force element: fire
 *                force element: ice, wind, water
 *=============================================================================
 *
 *=============================================================================
 * NULL ELEMENT
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * This will force the elements to null. Calculated elemental damage will
 * always return neutral rate. Using this will clear the Force Element action
 * sequence effect.
 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * Usage Example: null element
 *=============================================================================
 *
 * ============================================================================
 * Changelog
 * ============================================================================
 *
 * Version 1.03:
 * - Updated for RPG Maker MV version 1.5.0.
 *
 * Version 1.02:
 * - Added <Element x Magnify: +y%>, <Element x Magnify: -y%> notetags. These
 * notetags different from the Amplify counterparts in a way where the Amplify
 * notetags will shift the element rate additively. These will alter the rate
 * multiplicatively.
 *
 * Version 1.01:
 * - Optimized element rate calculation where if no elements are present, then
 * damage rate will default to 100%.
 *
  * Version 1.00:
 * - Finished Plugin!
 */
//=============================================================================

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

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

Yanfly.Param.EleMultiRule = Number(Yanfly.Parameters['Multi-Element Rulings']);

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

Yanfly.Ele.DataManager_isDatabaseLoaded = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function() {
  if (!Yanfly.Ele.DataManager_isDatabaseLoaded.call(this)) return false;

  if (!Yanfly._loaded_YEP_ElementCore) {
    this.processElementNotetagsSys($dataSystem);
    this.processElementNotetags1($dataSkills);
    this.processElementNotetags1($dataItems);
    this.processElementNotetags2($dataActors);
    this.processElementNotetags2($dataClasses);
    this.processElementNotetags2($dataEnemies);
    this.processElementNotetags2($dataWeapons);
    this.processElementNotetags2($dataArmors);
    this.processElementNotetags2($dataStates);
    Yanfly._loaded_YEP_ElementCore = true;
  }

  return true;
};

DataManager.processElementNotetagsSys = function(group) {
  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.processElementNotetags1 = function(group) {
  var noteA1 = /<MULTIPLE ELEMENTS:[ ]*(\d+(?:\s*,\s*\d+)*)>/i;
  var noteA2 = /<MULTIPLE ELEMENTS:[ ](\d+)[ ](?:THROUGH|to)[ ](\d+)>/i;
  var noteA3 = /<MULTIPLE ELEMENTS:[ ](.*)>/i;
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.elementMultiRule = Yanfly.Param.EleMultiRule;
    obj.multipleElements = [];
    obj.bypassElementReflect = false;

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(/<MULTI-ELEMENT RULE:[ ]LOWEST>/i)) {
        obj.elementMultiRule = 0;
      } else if (line.match(/<MULTI-ELEMENT RULE:[ ]ADD>/i)) {
        obj.elementMultiRule = 1;
      } else if (line.match(/<MULTI-ELEMENT RULE:[ ]MULTIPLY>/i)) {
        obj.elementMultiRule = 2;
      } else if (line.match(/<MULTI-ELEMENT RULE:[ ]HIGHEST>/i)) {
        obj.elementMultiRule = 3;
      } else if (line.match(/<MULTI-ELEMENT RULE:[ ]AVERAGE>/i)) {
        obj.elementMultiRule = 4;
      } else if (line.match(/<MULTI-ELEMENT RULE:[ ](.*)>/i)) {
        obj.elementMultiRule = String(RegExp.$1);
      } else if (line.match(noteA1)) {
        var array = JSON.parse('[' + RegExp.$1.match(/\d+/g) + ']');
        obj.multipleElements = obj.multipleElements.concat(array);
      } else if (line.match(noteA2)) {
        var range = Yanfly.Util.getRange(parseInt(RegExp.$1),
          parseInt(RegExp.$2));
        obj.multipleElements = obj.multipleElements.concat(range);
      } else if (line.match(noteA3)) {
        var text = String(RegExp.$1);
        var array = text.split(',');
        var length = array.length;
        for (var j = 0; j < length; ++j) {
          var name = array[j].toUpperCase().trim();
          if (Yanfly.ElementIdRef[name]) {
            var id = Yanfly.ElementIdRef[name];
            obj.multipleElements.push(id);
          }
        }
      } else if (line.match(/<(?:BYPASS ELEMENT REFLECT)>/i)) {
        obj.bypassElementReflect = true;
      }
    }
  }
};

DataManager.processElementNotetags2 = function(group) {
  var noteA1 = /<(?:ELEMENT ABSORB):[ ](\d+)[ ](?:THROUGH|to)[ ](\d+)>/i;
  var noteB1 = /<(?:ELEMENT REFLECT)[ ](\d+):[ ]([\+\-]\d+)([%％])>/i;
  var noteB2 = /<(?:ELEMENT REFLECT)[ ](.*):[ ]([\+\-]\d+)([%％])>/i;
  var noteC1 = /<(?:ELEMENT AMPLIFY)[ ](\d+):[ ]([\+\-]\d+)([%％])>/i;
  var noteC2 = /<(?:ELEMENT AMPLIFY)[ ](.*):[ ]([\+\-]\d+)([%％])>/i;
  var noteC3 = /<(?:ELEMENT MAGNIFY)[ ](\d+):[ ]([\+\-]\d+)([%％])>/i;
  var noteC4 = /<(?:ELEMENT MAGNIFY)[ ](.*):[ ]([\+\-]\d+)([%％])>/i;
  var noteD1 = /<FORCE ELEMENT[ ](\d+)[ ]RATE:[ ](\d+)([%％])>/i;
  var noteD2 = /<FORCE ELEMENT[ ](\d+)[ ]RATE:[ ]-(\d+)([%％])>/i;
  var noteD3 = /<FORCE ELEMENT[ ](.*)[ ]RATE:[ ](\d+)([%％])>/i;
  var noteD4 = /<FORCE ELEMENT[ ](.*)[ ]RATE:[ ]-(\d+)([%％])>/i;
  for (var n = 1; n < group.length; n++) {
    var obj = group[n];
    var notedata = obj.note.split(/[\r\n]+/);

    obj.elementAbsorb = [];
    obj.elementReflect = {};
    obj.elementAmplify = {};
    obj.elementMagnify = {};
    obj.elementNull = false;
    obj.elementForcedRate = {};

    for (var i = 0; i < notedata.length; i++) {
      var line = notedata[i];
      if (line.match(/<(?:ELEMENT ABSORB):[ ]*(\d+(?:\s*,\s*\d+)*)>/i)) {
        var array = JSON.parse('[' + RegExp.$1.match(/\d+/g) + ']');
        obj.elementAbsorb = obj.elementAbsorb.concat(array);
      } else if (line.match(noteA1)) {
        var range = Yanfly.Util.getRange(parseInt(RegExp.$1),
          parseInt(RegExp.$2));
        obj.elementAbsorb = obj.elementAbsorb.concat(range);
      } else if (line.match(/<(?:ELEMENT ABSORB):[ ](.*)>/i)) {
        var text = String(RegExp.$1);
        var array = text.split(',');
        var length = array.length;
        for (var j = 0; j < length; ++j) {
          var name = array[j].toUpperCase().trim();
          if (Yanfly.ElementIdRef[name]) {
            var id = Yanfly.ElementIdRef[name];
            obj.elementAbsorb.push(id);
          }
        }
      } else if (line.match(noteB1)) {
        var elementId = parseInt(RegExp.$1);
        var rate = parseFloat(RegExp.$2 * 0.01);
        obj.elementReflect[elementId] = rate;
      } else if (line.match(noteB2)) {
        var name = String(RegExp.$1).toUpperCase().trim();
        var rate = parseFloat(RegExp.$2 * 0.01);
        if (Yanfly.ElementIdRef[name]) {
          var id = Yanfly.ElementIdRef[name];
          obj.elementReflect[id] = rate;
        }
      } else if (line.match(noteC1)) {
        var elementId = parseInt(RegExp.$1);
        var rate = parseFloat(RegExp.$2 * 0.01);
        obj.elementAmplify[elementId] = rate;
      } else if (line.match(noteC2)) {
        var name = String(RegExp.$1).toUpperCase().trim();
        var rate = parseFloat(RegExp.$2 * 0.01);
        if (Yanfly.ElementIdRef[name]) {
          var id = Yanfly.ElementIdRef[name];
          obj.elementAmplify[id] = rate;
        }
      } else if (line.match(noteC3)) {
        var elementId = parseInt(RegExp.$1);
        var rate = parseFloat(RegExp.$2 * 0.01);
        obj.elementMagnify[elementId] = rate;
      } else if (line.match(noteC4)) {
        var name = String(RegExp.$1).toUpperCase().trim();
        var rate = parseFloat(RegExp.$2 * 0.01);
        if (Yanfly.ElementIdRef[name]) {
          var id = Yanfly.ElementIdRef[name];
          obj.elementMagnify[id] = rate;
        }
      } else if (line.match(/<(?:ELEMENT NULL)>/i)) {
        obj.elementNull = true;
      } else if (line.match(noteD1)) {
        var elementId = parseInt(RegExp.$1);
        var rate = parseFloat(RegExp.$2 * 0.01);
        obj.elementForcedRate[elementId] = rate;
      } else if (line.match(noteD2)) {
        var elementId = parseInt(RegExp.$1);
        var rate = parseFloat(RegExp.$2 * 0.01);
        obj.elementForcedRate[elementId] = rate * -1;
      } else if (line.match(noteD3)) {
        var name = String(RegExp.$1).toUpperCase().trim();
        var rate = parseFloat(RegExp.$2 * 0.01);
        if (Yanfly.ElementIdRef[name]) {
          var id = Yanfly.ElementIdRef[name];
          obj.elementForcedRate[id] = rate;
        }
      } else if (line.match(noteD4)) {
        var name = String(RegExp.$1).toUpperCase().trim();
        var rate = parseFloat(RegExp.$2 * 0.01);
        if (Yanfly.ElementIdRef[name]) {
          var id = Yanfly.ElementIdRef[name];
          obj.elementForcedRate[id] = rate * -1;
        }
      }
    }
  }
};

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

if (Imported.YEP_BattleEngineCore) {

Yanfly.Ele.BattleManager_processActionSequence =
    BattleManager.processActionSequence;
BattleManager.processActionSequence = function(actionName, actionArgs) {
  // ADD ELEMENT: X
  if (actionName === 'ADD ELEMENT') {
    return this.actionAddElement(actionArgs);
  }
  // CLEAR ELEMENT
  if (actionName === 'CLEAR ELEMENT') {
    return this.actionClearElement();
  }
  // FORCE ELEMENT: X
  if (actionName === 'FORCE ELEMENT') {
    return this.actionForceElement(actionArgs);
  }
  // FORCE ELEMENT
  if (actionName === 'NULL ELEMENT') {
    return this.actionNullElement();
  }
  return Yanfly.Ele.BattleManager_processActionSequence.call(this,
    actionName, actionArgs);
};

BattleManager.actionAddElement = function(actionArgs) {
    if (!actionArgs) return;
    var array = [];
    var length = actionArgs.length;
    for (var i = 0; i < length; ++i) {
      var ele = actionArgs[i].toUpperCase().trim();
      if (ele.match(/(\d+)/i)) {
        array.push(parseInt(RegExp.$1));
      } else if (Yanfly.ElementIdRef[ele]) {
        array.push(Yanfly.ElementIdRef[ele]);
      }
    }
    $gameTemp._addedElements = array;
    return true;
};

BattleManager.actionClearElement = function() {
    $gameTemp._addedElements = undefined;
    $gameTemp._forcedElements = undefined;
    return true;
};

BattleManager.actionForceElement = function(actionArgs) {
    if (!actionArgs) return;
    var array = [];
    var length = actionArgs.length;
    for (var i = 0; i < length; ++i) {
      var ele = actionArgs[i].toUpperCase().trim();
      if (ele.match(/(\d+)/i)) {
        array.push(parseInt(RegExp.$1));
      } else if (Yanfly.ElementIdRef[ele]) {
        array.push(Yanfly.ElementIdRef[ele]);
      }
    }
    $gameTemp._forcedElements = array;
    return true;
};

BattleManager.actionNullElement = function() {
    $gameTemp._forcedElements = [];
    return true;
};

}; // Imported.YEP_BattleEngineCore

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

Yanfly.Ele.Game_BtlrBase_elementRate =
    Game_BattlerBase.prototype.elementRate;
Game_BattlerBase.prototype.elementRate = function(elementId) {
  var rate = this.forcedElementRate(elementId);
  if (rate !== undefined) return rate;
  var result = Yanfly.Ele.Game_BtlrBase_elementRate.call(this, elementId);
  if (this.isAbsorbElement(elementId) && result > 0) {
    result = Math.min(result - 2.0, -0.01);
  }
  return result;
};

Game_BattlerBase.prototype.getObjElementReflectRate = function(obj, elementId) {
  if (!obj) return 0;
  if (!obj.elementReflect) return 0;
  return obj.elementReflect[elementId] || 0;
};

Game_BattlerBase.prototype.getObjElementAmplifyRate = function(obj, elementId) {
  if (!obj) return 0;
  if (!obj.elementAmplify) return 0;
  return obj.elementAmplify[elementId] || 0;
};

Game_BattlerBase.prototype.getObjElementMagnifyRate = function(obj, elementId) {
  if (!obj) return 0;
  if (!obj.elementMagnify) return 0;
  return obj.elementMagnify[elementId] || 0;
};

Game_BattlerBase.prototype.getObjElementForcedRate = function(obj, elementId) {
  if (!obj) return undefined;
  if (!obj.elementForcedRate) return undefined;
  return obj.elementForcedRate[elementId] || undefined;
};

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

Game_Battler.prototype.isAbsorbElement = function(elementId) {
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var state = this.states()[i];
    if (!state) continue;
    if (!state.elementAbsorb) continue;
    if (state.elementAbsorb.contains(elementId)) return true;
  }
  return false;
};

Game_Battler.prototype.elementReflectRate = function(elementId) {
  var rate = 0;
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var obj = this.states()[i];
    rate += this.getObjElementReflectRate(obj, elementId);
  }
  return rate;
};

Game_Battler.prototype.elementAmplifyRate = function(elementId) {
  var rate = 0;
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var obj = this.states()[i];
    rate += this.getObjElementAmplifyRate(obj, elementId);
  }
  return rate;
};

Game_Battler.prototype.elementMagnifyRate = function(elementId) {
  var rate = 1;
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var obj = this.states()[i];
    rate += this.getObjElementMagnifyRate(obj, elementId);
  }
  return rate;
};

Game_Battler.prototype.isNullElement = function() {
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var state = this.states()[i];
    if (state && state.elementNull) return true;
  }
  return false;
};

Game_Battler.prototype.forcedElementRate = function(elementId) {
  var length = this.states().length;
  for (var i = 0; i < length; ++i) {
    var state = this.states()[i];
    var rate = this.getObjElementForcedRate(state, elementId);
    if (rate !== undefined) return rate;
  }
  return undefined;
};

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

Game_Actor.prototype.isAbsorbElement = function(elementId) {
  if (this.actor().elementAbsorb.contains(elementId)) return true;
  if (this.currentClass().elementAbsorb.contains(elementId)) return true;
  var length = this.equips().length;
  for (var i = 0; i < length; ++i) {
    var equip = this.equips()[i];
    if (!equip) continue;
    if (!equip.elementAbsorb) continue;
    if (equip.elementAbsorb.contains(elementId)) return true;
  }
  return Game_Battler.prototype.isAbsorbElement.call(this, elementId);
};

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

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

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

Game_Actor.prototype.isNullElement = function() {
  if (this.actor().elementNull) return true;
  if (this.currentClass().elementNull) return true;
  var length = this.equips().length;
  for (var i = 0; i < length; ++i) {
    var equip = this.equips()[i];
    if (equip && equip.elementNull) return true;
  }
  return Game_Battler.prototype.isNullElement.call(this);
};

Game_Actor.prototype.forcedElementRate = function(elementId) {
  var rate = Game_Battler.prototype.forcedElementRate.call(this, elementId);
  if (rate !== undefined) return rate;
  var length = this.equips().length;
  for (var i = 0; i < length; ++i) {
    var equip = this.equips()[i];
    rate = this.getObjElementForcedRate(equip, elementId);
    if (rate !== undefined) return rate;
  }
  rate = this.getObjElementForcedRate(this.currentClass(), elementId);
  if (rate !== undefined) return rate;
  rate = this.getObjElementForcedRate(this.actor(), elementId);
  if (rate !== undefined) return rate;
  return undefined;
};

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

Game_Enemy.prototype.isAbsorbElement = function(elementId) {
  if (this.enemy().elementAbsorb.contains(elementId)) return true;
  return Game_Battler.prototype.isAbsorbElement.call(this, elementId);
};

Game_Enemy.prototype.elementReflectRate = function(elementId) {
  var rate = Game_Battler.prototype.elementReflectRate.call(this, elementId);
  rate += this.getObjElementReflectRate(this.enemy(), elementId);
  return rate;
};

Game_Enemy.prototype.elementAmplifyRate = function(elementId) {
  var rate = Game_Battler.prototype.elementAmplifyRate.call(this, elementId);
  rate += this.getObjElementAmplifyRate(this.enemy(), elementId);
  return rate;
};

Game_Enemy.prototype.elementMagnifyRate = function(elementId) {
  var rate = Game_Battler.prototype.elementMagnifyRate.call(this, elementId);
  rate += this.getObjElementMagnifyRate(this.enemy(), elementId);
  return rate;
};

Game_Enemy.prototype.isNullElement = function() {
  if (this.enemy().elementNull) return true;
  return Game_Battler.prototype.isNullElement.call(this);
};

Game_Enemy.prototype.forcedElementRate = function(elementId) {
  var rate = Game_Battler.prototype.forcedElementRate.call(this, elementId);
  if (rate !== undefined) return rate;
  rate = this.getObjElementForcedRate(this.enemy(), elementId);
  if (rate !== undefined) return rate;
  return undefined;
};

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

Game_Action.prototype.getItemElements = function() {
  if ($gameTemp._forcedElements !== undefined) {
    return $gameTemp._forcedElements.filter(Yanfly.Util.onlyUnique);
  }
  if (this.subject().isNullElement()) return [];
  var elements = [];
  if (this.item().damage.elementId < 0) {
    Yanfly.Util.extend(elements, this.subject().attackElements());
  } else if (this.item().damage.elementId > 0) {
    elements.push(this.item().damage.elementId);
  }
  Yanfly.Util.extend(elements, this.item().multipleElements);
  if ($gameTemp._addedElements !== undefined) {
    Yanfly.Util.extend(elements, $gameTemp._addedElements);
  }
  return elements.filter(Yanfly.Util.onlyUnique);
};

Game_Action.prototype.calcElementRate = function(target) {
  if (!this.item()) return 1;
  var finalRate;
  var elements = this.getItemElements();
  if (elements.length < 1) return 1.00;
  var rule = this.item().elementMultiRule;
  var average = 0;
  while (elements.length > 0) {
    var elementId = elements.shift();
    var eleRate = target.elementRate(elementId);
    eleRate *= Math.max(0, this.subject().elementMagnifyRate(elementId));
    var absorbed = eleRate < 0;

    eleRate += this.subject().elementAmplifyRate(elementId);
    if (rule === 0) { // Lowest Rate
      finalRate = finalRate || eleRate;
      finalRate = Math.min(finalRate, eleRate);
    } else if (rule === 1) { // Additive
      finalRate = finalRate || 1.00;
      eleRate -= 1.00;
      finalRate += eleRate;
    } else if (rule === 2) { // Multiplicative
      finalRate = finalRate || 1.00;
      finalRate *= Math.abs(eleRate);
      if (eleRate < 0) finalRate = Math.abs(finalRate) * -1;
    } else if (rule === 3) { // Highest
      finalRate = finalRate || eleRate;
      finalRate = Math.max(finalRate, eleRate);
    } else if (rule === 4) { // Average
      finalRate = finalRate || 0;
      finalRate += eleRate;
      average += 1;
    } else {
      finalRate = this.calcElementRateRule(target, elementId, finalRate,
        eleRate, rule);
    }
  }
  if (rule === 4) finalRate /= Math.max(1, average);
  if (finalRate === undefined) finalRate = 1.00;
  return finalRate;
};

Game_Action.prototype.calcElementRateRule = function(target, elementId,
finalRate, eleRate, rule) {
  return finalRate;
};

Yanfly.Ele.Game_Action_itemMrf = Game_Action.prototype.itemMrf;
Game_Action.prototype.itemMrf = function(target) {
  var rate = Yanfly.Ele.Game_Action_itemMrf.call(this, target);
  if (this.item().bypassElementReflect) return rate;
  var elements = this.getItemElements();
  while (elements.length > 0) {
    var elementId = elements.shift();
    rate += target.elementReflectRate(elementId);
  }
  return rate;
};

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

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

Yanfly.Util.getRange = function(n, m) {
    var result = [];
    for (var i = n; i <= m; ++i) result.push(i);
    return result;
};

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

Yanfly.Util.onlyUnique = function(value, index, self) {
    return self.indexOf(value) === index;
};

//=============================================================================
// End of File
//=============================================================================