/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Unit.h"
#include "DB2Stores.h"
#include "Item.h"
#include "Player.h"
#include "Pet.h"
#include "GameTables.h"
#include "ObjectMgr.h"
#include "SharedDefines.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "World.h"
#include
#include
inline bool _ModifyUInt32(bool apply, uint32& baseValue, int32& amount)
{
// If amount is negative, change sign and value of apply.
if (amount < 0)
{
apply = !apply;
amount = -amount;
}
if (apply)
baseValue += amount;
else
{
// Make sure we do not get uint32 overflow.
if (amount > int32(baseValue))
amount = baseValue;
baseValue -= amount;
}
return apply;
}
/*#######################################
######## ########
######## UNIT STAT SYSTEM ########
######## ########
#######################################*/
void Unit::UpdateAllResistances()
{
for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
UpdateResistances(i);
}
void Unit::UpdateDamagePhysical(WeaponAttackType attType)
{
float minDamage = 0.0f;
float maxDamage = 0.0f;
CalculateMinMaxDamage(attType, false, true, minDamage, maxDamage);
switch (attType)
{
case BASE_ATTACK:
default:
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MinDamage), minDamage);
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MaxDamage), maxDamage);
break;
case OFF_ATTACK:
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MinOffHandDamage), minDamage);
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MaxOffHandDamage), maxDamage);
break;
case RANGED_ATTACK:
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MinRangedDamage), minDamage);
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MaxRangedDamage), maxDamage);
break;
}
}
int32 Unit::GetCreatePowerValue(Powers power) const
{
if (power == POWER_MANA)
return GetCreateMana();
if (PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power))
return powerType->MaxBasePower;
return 0;
}
/*#######################################
######## ########
######## PLAYERS STAT SYSTEM ########
######## ########
#######################################*/
bool Player::UpdateStats(Stats stat)
{
// value = ((base_value * base_pct) + total_value) * total_pct
float value = GetTotalStatValue(stat);
SetStat(stat, int32(value));
if (stat == STAT_STAMINA || stat == STAT_INTELLECT || stat == STAT_STRENGTH)
{
Pet* pet = GetPet();
if (pet)
pet->UpdateStats(stat);
}
switch (stat)
{
case STAT_AGILITY:
UpdateAllCritPercentages();
UpdateDodgePercentage();
break;
case STAT_STAMINA:
UpdateMaxHealth();
break;
case STAT_INTELLECT:
UpdateSpellCritChance();
break;
default:
break;
}
if (stat == STAT_STRENGTH)
UpdateAttackPowerAndDamage(false);
else if (stat == STAT_AGILITY)
{
UpdateAttackPowerAndDamage(false);
UpdateAttackPowerAndDamage(true);
}
UpdateArmor();
UpdateSpellDamageAndHealingBonus();
UpdateManaRegen();
return true;
}
void Player::ApplySpellPowerBonus(int32 amount, bool apply)
{
if (HasAuraType(SPELL_AURA_OVERRIDE_SPELL_POWER_BY_AP_PCT))
return;
apply = _ModifyUInt32(apply, m_baseSpellPower, amount);
// For speed just update for client
ApplyModUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModHealingDonePos), amount, apply);
for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
ApplyModDamageDonePos(SpellSchools(i), amount, apply);
if (HasAuraType(SPELL_AURA_OVERRIDE_ATTACK_POWER_BY_SP_PCT))
{
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
}
}
void Player::UpdateSpellDamageAndHealingBonus()
{
// Magic damage modifiers implemented in Unit::SpellDamageBonusDone
// This information for client side use only
// Get healing bonus for all schools
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModHealingDonePos), SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL));
// Get damage bonus for all schools
Unit::AuraEffectList const& modDamageAuras = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE);
for (uint16 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
{
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModDamageDoneNeg, i),
std::accumulate(modDamageAuras.begin(), modDamageAuras.end(), 0, [i](int32 negativeMod, AuraEffect const* aurEff)
{
if (aurEff->GetAmount() < 0 && aurEff->GetMiscValue() & (1 << i))
negativeMod += aurEff->GetAmount();
return negativeMod;
}));
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModDamageDonePos, i),
SpellBaseDamageBonusDone(SpellSchoolMask(1 << i)) - m_activePlayerData->ModDamageDoneNeg[i]);
}
if (HasAuraType(SPELL_AURA_OVERRIDE_ATTACK_POWER_BY_SP_PCT))
{
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
}
}
bool Player::UpdateAllStats()
{
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
float value = GetTotalStatValue(Stats(i));
SetStat(Stats(i), int32(value));
}
UpdateArmor();
// calls UpdateAttackPowerAndDamage() in UpdateArmor for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
UpdateAttackPowerAndDamage(true);
UpdateMaxHealth();
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(Powers(i));
UpdateAllRatings();
UpdateAllCritPercentages();
UpdateSpellCritChance();
UpdateBlockPercentage();
UpdateParryPercentage();
UpdateDodgePercentage();
UpdateSpellDamageAndHealingBonus();
UpdateManaRegen();
UpdateExpertise(BASE_ATTACK);
UpdateExpertise(OFF_ATTACK);
RecalculateRating(CR_ARMOR_PENETRATION);
UpdateAllResistances();
return true;
}
void Player::ApplySpellPenetrationBonus(int32 amount, bool apply)
{
ApplyModTargetResistance(-amount, apply);
m_spellPenetrationItemMod += apply ? amount : -amount;
}
void Player::UpdateResistances(uint32 school)
{
if (school > SPELL_SCHOOL_NORMAL)
{
Unit::UpdateResistances(school);
Pet* pet = GetPet();
if (pet)
pet->UpdateResistances(school);
}
else
UpdateArmor();
}
void Player::UpdateArmor()
{
UnitMods unitMod = UNIT_MOD_ARMOR;
float value = GetFlatModifierValue(unitMod, BASE_VALUE); // base armor
value *= GetPctModifierValue(unitMod, BASE_PCT); // armor percent
// SPELL_AURA_MOD_ARMOR_PCT_FROM_STAT counts as base armor
GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PCT_FROM_STAT, [this, &value](AuraEffect const* aurEff) {
int32 miscValue = aurEff->GetMiscValue();
Stats stat = (miscValue != -2) ? Stats(miscValue) : GetPrimaryStat();
value += CalculatePct(float(GetStat(stat)), aurEff->GetAmount());
return true;
});
float baseValue = value;
value += GetFlatModifierValue(unitMod, TOTAL_VALUE); // bonus armor from auras and items
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
value *= GetTotalAuraMultiplier(SPELL_AURA_MOD_BONUS_ARMOR_PCT);
SetArmor(int32(value), int32(value - baseValue));
Pet* pet = GetPet();
if (pet)
pet->UpdateArmor();
UpdateAttackPowerAndDamage(); // armor dependent auras update for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
}
float Player::GetHealthBonusFromStamina() const
{
// Taken from PaperDollFrame.lua - 6.0.3.19085
float ratio = 10.0f;
if (GtHpPerStaEntry const* hpBase = sHpPerStaGameTable.GetRow(GetLevel()))
ratio = hpBase->Health;
float stamina = GetStat(STAT_STAMINA);
return stamina * ratio;
}
Stats Player::GetPrimaryStat() const
{
uint8 primaryStatPriority = [&]() -> uint8
{
if (ChrSpecializationEntry const* specialization = GetPrimarySpecializationEntry())
return specialization->PrimaryStatPriority;
return sChrClassesStore.AssertEntry(GetClass())->PrimaryStatPriority;
}();
if (primaryStatPriority >= 4)
return STAT_STRENGTH;
if (primaryStatPriority >= 2)
return STAT_AGILITY;
return STAT_INTELLECT;
}
void Player::UpdateMaxHealth()
{
UnitMods unitMod = UNIT_MOD_HEALTH;
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetMaxHealth((uint32)value);
}
uint32 Player::GetPowerIndex(Powers power) const
{
return sDB2Manager.GetPowerIndexByClass(power, GetClass());
}
void Player::UpdateMaxPower(Powers power)
{
uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
return;
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power));
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetMaxPower(power, (int32)std::lroundf(value));
}
void Player::UpdateAttackPowerAndDamage(bool ranged)
{
float val2 = 0.0f;
float level = float(GetLevel());
ChrClassesEntry const* entry = sChrClassesStore.AssertEntry(GetClass());
UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
if (!HasAuraType(SPELL_AURA_OVERRIDE_ATTACK_POWER_BY_SP_PCT))
{
if (!ranged)
{
float strengthValue = std::max(GetStat(STAT_STRENGTH) * entry->AttackPowerPerStrength, 0.0f);
float agilityValue = std::max(GetStat(STAT_AGILITY) * entry->AttackPowerPerAgility, 0.0f);
SpellShapeshiftFormEntry const* form = sSpellShapeshiftFormStore.LookupEntry(GetShapeshiftForm());
// Directly taken from client, SHAPESHIFT_FLAG_AP_FROM_STRENGTH ?
if (form && form->Flags & 0x20)
agilityValue += std::max(GetStat(STAT_AGILITY) * entry->AttackPowerPerStrength, 0.0f);
val2 = strengthValue + agilityValue;
}
else
val2 = (level + std::max(GetStat(STAT_AGILITY), 0.0f)) * entry->RangedAttackPowerPerAgility;
}
else
{
int32 minSpellPower = m_activePlayerData->ModHealingDonePos;
for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
minSpellPower = std::min(minSpellPower, m_activePlayerData->ModDamageDonePos[i]);
val2 = CalculatePct(float(minSpellPower), *m_activePlayerData->OverrideAPBySpellPowerPercent);
}
SetStatFlatModifier(unitMod, BASE_VALUE, val2);
float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE);
float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
if (ranged)
{
SetRangedAttackPower(int32(base_attPower));
SetRangedAttackPowerModPos(int32(attPowerMod));
SetRangedAttackPowerMultiplier(attPowerMultiplier);
}
else
{
SetAttackPower(int32(base_attPower));
SetAttackPowerModPos(int32(attPowerMod));
SetAttackPowerMultiplier(attPowerMultiplier);
}
Pet* pet = GetPet(); //update pet's AP
Guardian* guardian = GetGuardianPet();
//automatically update weapon damage after attack power modification
if (ranged)
{
UpdateDamagePhysical(RANGED_ATTACK);
if (pet && pet->IsHunterPet()) // At ranged attack change for hunter pet
pet->UpdateAttackPowerAndDamage();
}
else
{
UpdateDamagePhysical(BASE_ATTACK);
if (Item* offhand = GetWeaponForAttack(OFF_ATTACK, true))
if (CanDualWield() || offhand->GetTemplate()->HasFlag(ITEM_FLAG3_ALWAYS_ALLOW_DUAL_WIELD))
UpdateDamagePhysical(OFF_ATTACK);
if (HasAuraType(SPELL_AURA_OVERRIDE_SPELL_POWER_BY_AP_PCT))
UpdateSpellDamageAndHealingBonus();
if (pet && pet->IsPetGhoul()) // At melee attack power change for DK pet
pet->UpdateAttackPowerAndDamage();
if (guardian && guardian->IsSpiritWolf()) // At melee attack power change for Shaman feral spirit
guardian->UpdateAttackPowerAndDamage();
}
}
void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) const
{
UnitMods unitMod;
switch (attType)
{
case BASE_ATTACK:
default:
unitMod = UNIT_MOD_DAMAGE_MAINHAND;
break;
case OFF_ATTACK:
unitMod = UNIT_MOD_DAMAGE_OFFHAND;
break;
case RANGED_ATTACK:
unitMod = UNIT_MOD_DAMAGE_RANGED;
break;
}
float attackPowerMod = std::max(GetAPMultiplier(attType, normalized), 0.25f);
float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType, false) / 3.5f * attackPowerMod;
float basePct = GetPctModifierValue(unitMod, BASE_PCT);
float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f;
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
float versaDmgMod = 1.0f;
AddPct(versaDmgMod, GetRatingBonusValue(CR_VERSATILITY_DAMAGE_DONE) + float(GetTotalAuraModifier(SPELL_AURA_MOD_VERSATILITY)));
SpellShapeshiftFormEntry const* shapeshift = sSpellShapeshiftFormStore.LookupEntry(GetShapeshiftForm());
if (shapeshift && shapeshift->CombatRoundTime)
{
weaponMinDamage = weaponMinDamage * shapeshift->CombatRoundTime / 1000.0f / attackPowerMod;
weaponMaxDamage = weaponMaxDamage * shapeshift->CombatRoundTime / 1000.0f / attackPowerMod;
}
else if (!CanUseAttackType(attType)) // check if player not in form but still can't use (disarm case)
{
// cannot use ranged/off attack, set values to 0
if (attType != BASE_ATTACK)
{
minDamage = 0;
maxDamage = 0;
return;
}
weaponMinDamage = BASE_MINDAMAGE;
weaponMaxDamage = BASE_MAXDAMAGE;
}
minDamage = ((weaponMinDamage + baseValue) * basePct + totalValue) * totalPct * versaDmgMod;
maxDamage = ((weaponMaxDamage + baseValue) * basePct + totalValue) * totalPct * versaDmgMod;
}
void Player::UpdateBlockPercentage()
{
// No block
float value = 0.0f;
if (CanBlock())
{
// Base value
value = 5.0f;
// Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
// Increase from rating
value += GetRatingBonusValue(CR_BLOCK);
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) : value;
}
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::BlockPercentage), value);
}
void Player::UpdateCritPercentage(WeaponAttackType attType)
{
auto applyCritLimit = [](float value)
{
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) : value;
return value;
};
switch (attType)
{
case OFF_ATTACK:
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::OffhandCritPercentage),
applyCritLimit(GetBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD) + GetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD) + GetRatingBonusValue(CR_CRIT_MELEE)));
break;
case RANGED_ATTACK:
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::RangedCritPercentage),
applyCritLimit(GetBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD) + GetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD) + GetRatingBonusValue(CR_CRIT_RANGED)));
break;
case BASE_ATTACK:
default:
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CritPercentage),
applyCritLimit(GetBaseModValue(CRIT_PERCENTAGE, FLAT_MOD) + GetBaseModValue(CRIT_PERCENTAGE, PCT_MOD) + GetRatingBonusValue(CR_CRIT_MELEE)));
break;
}
}
void Player::UpdateAllCritPercentages()
{
float value = 5.0f;
SetBaseModPctValue(CRIT_PERCENTAGE, value);
SetBaseModPctValue(OFFHAND_CRIT_PERCENTAGE, value);
SetBaseModPctValue(RANGED_CRIT_PERCENTAGE, value);
UpdateCritPercentage(BASE_ATTACK);
UpdateCritPercentage(OFF_ATTACK);
UpdateCritPercentage(RANGED_ATTACK);
}
void Player::UpdateMastery()
{
if (!CanUseMastery())
{
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Mastery), 0.0f);
return;
}
float value = GetTotalAuraModifier(SPELL_AURA_MASTERY);
value += GetRatingBonusValue(CR_MASTERY);
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Mastery), value);
ChrSpecializationEntry const* chrSpec = GetPrimarySpecializationEntry();
if (!chrSpec)
return;
for (auto const& [_, aura] : GetOwnedAuras())
if (aura->GetCasterGUID() == GetGUID() && aura->GetSpellInfo()->HasAttribute(SPELL_ATTR8_MASTERY_AFFECTS_POINTS))
for (AuraEffect* auraEff : aura->GetAuraEffects())
if (G3D::fuzzyNe(auraEff->GetSpellEffectInfo().BonusCoefficient, 0.0f))
auraEff->RecalculateAmount(this);
}
void Player::UpdateVersatilityDamageDone()
{
// No proof that CR_VERSATILITY_DAMAGE_DONE is allways = ActivePlayerData::Versatility
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Versatility), m_activePlayerData->CombatRatings[CR_VERSATILITY_DAMAGE_DONE]);
if (GetClass() == CLASS_HUNTER)
UpdateDamagePhysical(RANGED_ATTACK);
else
UpdateDamagePhysical(BASE_ATTACK);
}
void Player::UpdateHealingDonePercentMod()
{
float value = 1.0f;
AddPct(value, GetRatingBonusValue(CR_VERSATILITY_HEALING_DONE) + GetTotalAuraModifier(SPELL_AURA_MOD_VERSATILITY));
for (AuraEffect const* auraEffect : GetAuraEffectsByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT))
AddPct(value, auraEffect->GetAmount());
for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ModHealingDonePercent, i), value);
}
float const m_diminishing_k[MAX_CLASSES] =
{
0.9560f, // Warrior
0.9560f, // Paladin
0.9880f, // Hunter
0.9880f, // Rogue
0.9830f, // Priest
0.9560f, // DK
0.9880f, // Shaman
0.9830f, // Mage
0.9830f, // Warlock
0.9830f, // Monk
0.9720f, // Druid
0.9830f, // Demon Hunter
0.9880f, // Evoker
1.0f, // Adventurer
};
// helper function
float CalculateDiminishingReturns(float const (&capArray)[MAX_CLASSES], uint8 playerClass, float nonDiminishValue, float diminishValue)
{
// 1 1 k cx
// --- = --- + --- <=> x' = --------
// x' c x x + ck
// where:
// k is m_diminishing_k for that class
// c is capArray for that class
// x is chance before DR (diminishValue)
// x' is chance after DR (our result)
uint32 const classIdx = playerClass - 1;
float const k = m_diminishing_k[classIdx];
float const c = capArray[classIdx];
float result = c * diminishValue / (diminishValue + c * k);
result += nonDiminishValue;
return result;
}
float const parry_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
0.0f, // Priest
65.631440f, // DK
145.560408f, // Shaman
0.0f, // Mage
0.0f, // Warlock
90.6425f, // Monk
0.0f, // Druid
65.631440f, // Demon Hunter
0.0f, // Evoker
0.0f, // Adventurer
};
void Player::UpdateParryPercentage()
{
// No parry
float value = 0.0f;
uint32 pclass = GetClass() - 1;
if (CanParry() && parry_cap[pclass] > 0.0f)
{
float nondiminishing = 5.0f;
// Parry from rating
float diminishing = GetRatingBonusValue(CR_PARRY);
// Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
// apply diminishing formula to diminishing parry chance
value = CalculateDiminishingReturns(parry_cap, GetClass(), nondiminishing, diminishing);
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) : value;
}
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ParryPercentage), value);
}
float const dodge_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
150.375940f, // Priest
65.631440f, // DK
145.560408f, // Shaman
150.375940f, // Mage
150.375940f, // Warlock
145.560408f, // Monk
116.890707f, // Druid
145.560408f, // Demon Hunter
145.560408f, // Evoker
0.0f, // Adventurer
};
void Player::UpdateDodgePercentage()
{
float diminishing = 0.0f, nondiminishing = 0.0f;
GetDodgeFromAgility(diminishing, nondiminishing);
// Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
// Dodge from rating
diminishing += GetRatingBonusValue(CR_DODGE);
// apply diminishing formula to diminishing dodge chance
float value = CalculateDiminishingReturns(dodge_cap, GetClass(), nondiminishing, diminishing);
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) : value;
SetUpdateFieldStatValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::DodgePercentage), value);
}
void Player::UpdateSpellCritChance()
{
float crit = 5.0f;
// Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
// Increase crit from SPELL_AURA_MOD_CRIT_PCT
crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT);
// Increase crit from spell crit ratings
crit += GetRatingBonusValue(CR_CRIT_SPELL);
// Store crit value
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::SpellCritPercentage), crit);
}
void Player::UpdateCorruption()
{
float effectiveCorruption = GetRatingBonusValue(CR_CORRUPTION) - GetRatingBonusValue(CR_CORRUPTION_RESISTANCE);
for (CorruptionEffectsEntry const* corruptionEffect : sCorruptionEffectsStore)
{
if ((CorruptionEffectsFlag(corruptionEffect->Flags) & CorruptionEffectsFlag::Disabled) != CorruptionEffectsFlag::None)
continue;
if (effectiveCorruption < corruptionEffect->MinCorruption)
{
RemoveAura(corruptionEffect->Aura);
continue;
}
if (!ConditionMgr::IsPlayerMeetingCondition(this, corruptionEffect->PlayerConditionID))
{
RemoveAura(corruptionEffect->Aura);
continue;
}
CastSpell(this, corruptionEffect->Aura, true);
}
}
void Player::UpdateArmorPenetration(int32 amount)
{
// Store Rating Value
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CombatRatings, CR_ARMOR_PENETRATION), amount);
}
void Player::UpdateMeleeHitChances()
{
m_modMeleeHitChance = 7.5f + GetRatingBonusValue(CR_HIT_MELEE);
}
void Player::UpdateRangedHitChances()
{
m_modRangedHitChance = 7.5f + GetRatingBonusValue(CR_HIT_RANGED);
}
void Player::UpdateSpellHitChances()
{
m_modSpellHitChance = 15.0f + (float)GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
m_modSpellHitChance += GetRatingBonusValue(CR_HIT_SPELL);
}
void Player::UpdateExpertise(WeaponAttackType attack)
{
if (attack == RANGED_ATTACK)
return;
int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
Item const* weapon = GetWeaponForAttack(attack, true);
expertise += GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE, [weapon](AuraEffect const* aurEff) -> bool
{
return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon);
});
if (expertise < 0)
expertise = 0;
switch (attack)
{
case BASE_ATTACK:
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::MainhandExpertise), expertise);
break;
case OFF_ATTACK:
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::OffhandExpertise), expertise);
break;
default:
break;
}
}
void Player::ApplyManaRegenBonus(int32 amount, bool apply)
{
_ModifyUInt32(apply, m_baseManaRegen, amount);
UpdateManaRegen();
}
void Player::ApplyHealthRegenBonus(int32 amount, bool apply)
{
_ModifyUInt32(apply, m_baseHealthRegen, amount);
}
void Player::UpdateManaRegen()
{
uint32 manaIndex = GetPowerIndex(POWER_MANA);
if (manaIndex == MAX_POWERS)
return;
// Get base of Mana Pool in sBaseMPGameTable
uint32 basemana = 0;
sObjectMgr->GetPlayerClassLevelInfo(GetClass(), GetLevel(), basemana);
float base_regen = basemana / 100.f;
base_regen += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA);
// Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT
base_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
// Apply PCT bonus from SPELL_AURA_MOD_MANA_REGEN_PCT
base_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_MANA_REGEN_PCT, POWER_MANA);
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, manaIndex), base_regen);
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, manaIndex), base_regen);
}
void Player::UpdateAllRunesRegen()
{
if (GetClass() != CLASS_DEATH_KNIGHT)
return;
uint32 runeIndex = GetPowerIndex(POWER_RUNES);
if (runeIndex == MAX_POWERS)
return;
PowerTypeEntry const* runeEntry = sDB2Manager.GetPowerTypeEntry(POWER_RUNES);
uint32 cooldown = GetRuneBaseCooldown();
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, runeIndex), float(1 * IN_MILLISECONDS) / float(cooldown) - runeEntry->RegenPeace);
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, runeIndex), float(1 * IN_MILLISECONDS) / float(cooldown) - runeEntry->RegenCombat);
}
void Player::_ApplyAllStatBonuses()
{
SetCanModifyStats(false);
_ApplyAllAuraStatMods();
_ApplyAllItemMods();
ApplyAllAzeriteItemMods(true);
SetCanModifyStats(true);
UpdateAllStats();
}
void Player::_RemoveAllStatBonuses()
{
SetCanModifyStats(false);
ApplyAllAzeriteItemMods(false);
_RemoveAllItemMods();
_RemoveAllAuraStatMods();
SetCanModifyStats(true);
UpdateAllStats();
}
/*#######################################
######## ########
######## MOBS STAT SYSTEM ########
######## ########
#######################################*/
int32 Creature::GetCreatePowerValue(Powers power) const
{
if (PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power))
if (!powerType->GetFlags().HasFlag(PowerTypeFlags::IsUsedByNPCs))
return 0;
return Unit::GetCreatePowerValue(power);
}
bool Creature::UpdateStats(Stats /*stat*/)
{
return true;
}
bool Creature::UpdateAllStats()
{
UpdateMaxHealth();
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(Powers(i));
UpdateAllResistances();
return true;
}
void Creature::UpdateArmor()
{
float baseValue = GetFlatModifierValue(UNIT_MOD_ARMOR, BASE_VALUE);
float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
SetArmor(int32(baseValue), int32(value - baseValue));
}
void Creature::UpdateMaxHealth()
{
float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
SetMaxHealth(uint32(value));
}
uint32 Creature::GetPowerIndex(Powers power) const
{
if (power == GetPowerType())
return 0;
switch (power)
{
case POWER_COMBO_POINTS:
return 2;
case POWER_ALTERNATE_POWER:
return 1;
case POWER_ALTERNATE_QUEST:
return 3;
case POWER_ALTERNATE_ENCOUNTER:
return 4;
case POWER_ALTERNATE_MOUNT:
return 5;
default:
break;
}
return MAX_POWERS;
}
void Creature::UpdateMaxPower(Powers power)
{
if (GetPowerIndex(power) == MAX_POWERS)
return;
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power));
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetMaxPower(power, (int32)std::lroundf(value));
}
void Creature::UpdateAttackPowerAndDamage(bool ranged)
{
UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
float baseAttackPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
float attackPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
if (ranged)
{
SetRangedAttackPower(int32(baseAttackPower));
SetRangedAttackPowerMultiplier(attackPowerMultiplier);
}
else
{
SetAttackPower(int32(baseAttackPower));
SetAttackPowerMultiplier(attackPowerMultiplier);
}
// automatically update weapon damage after attack power modification
if (ranged)
UpdateDamagePhysical(RANGED_ATTACK);
else
{
UpdateDamagePhysical(BASE_ATTACK);
UpdateDamagePhysical(OFF_ATTACK);
}
}
void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) const
{
float variance = 1.0f;
UnitMods unitMod;
switch (attType)
{
case BASE_ATTACK:
default:
variance = GetCreatureTemplate()->BaseVariance;
unitMod = UNIT_MOD_DAMAGE_MAINHAND;
break;
case OFF_ATTACK:
variance = GetCreatureTemplate()->BaseVariance;
unitMod = UNIT_MOD_DAMAGE_OFFHAND;
break;
case RANGED_ATTACK:
variance = GetCreatureTemplate()->RangeVariance;
unitMod = UNIT_MOD_DAMAGE_RANGED;
break;
}
if (attType == OFF_ATTACK && !haveOffhandWeapon())
{
minDamage = 0.0f;
maxDamage = 0.0f;
return;
}
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
if (!CanUseAttackType(attType)) // disarm case
{
weaponMinDamage = 0.0f;
weaponMaxDamage = 0.0f;
}
float attackPower = GetTotalAttackPowerValue(attType, false);
float attackSpeedMulti = GetAPMultiplier(attType, normalized);
float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE) + (attackPower / 3.5f) * variance;
float basePct = GetPctModifierValue(unitMod, BASE_PCT) * attackSpeedMulti;
float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f;
float dmgMultiplier = GetCreatureDifficulty()->DamageModifier; // = DamageModifier * GetDamageMod(rank);
minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
maxDamage = ((weaponMaxDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
}
/*#######################################
######## ########
######## PETS STAT SYSTEM ########
######## ########
#######################################*/
#define ENTRY_IMP 416
#define ENTRY_VOIDWALKER 1860
#define ENTRY_SUCCUBUS 1863
#define ENTRY_FELHUNTER 417
#define ENTRY_FELGUARD 17252
#define ENTRY_WATER_ELEMENTAL 510
#define ENTRY_TREANT 1964
#define ENTRY_FIRE_ELEMENTAL 15438
#define ENTRY_GHOUL 26125
#define ENTRY_BLOODWORM 28017
bool Guardian::UpdateStats(Stats stat)
{
// value = ((base_value * base_pct) + total_value) * total_pct
float value = GetTotalStatValue(stat);
UpdateStatBuffMod(stat);
float ownersBonus = 0.0f;
Unit* owner = GetOwner();
// Handle Death Knight Glyphs and Talents
float mod = 0.75f;
if (IsPetGhoul() && (stat == STAT_STAMINA || stat == STAT_STRENGTH))
{
if (stat == STAT_STAMINA)
mod = 0.3f; // Default Owner's Stamina scale
else
mod = 0.7f; // Default Owner's Strength scale
ownersBonus = float(owner->GetStat(stat)) * mod;
value += ownersBonus;
}
else if (stat == STAT_STAMINA)
{
ownersBonus = CalculatePct(owner->GetStat(STAT_STAMINA), 30);
value += ownersBonus;
}
//warlock's and mage's pets gain 30% of owner's intellect
else if (stat == STAT_INTELLECT)
{
if (owner->GetClass() == CLASS_WARLOCK || owner->GetClass() == CLASS_MAGE)
{
ownersBonus = CalculatePct(owner->GetStat(stat), 30);
value += ownersBonus;
}
}
/*
else if (stat == STAT_STRENGTH)
{
if (IsPetGhoul())
value += float(owner->GetStat(stat)) * 0.3f;
}
*/
SetStat(stat, int32(value));
m_statFromOwner[stat] = ownersBonus;
UpdateStatBuffMod(stat);
switch (stat)
{
case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
case STAT_AGILITY: UpdateArmor(); break;
case STAT_STAMINA: UpdateMaxHealth(); break;
case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
default:
break;
}
return true;
}
bool Guardian::UpdateAllStats()
{
UpdateMaxHealth();
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
UpdateStats(Stats(i));
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(Powers(i));
UpdateAllResistances();
return true;
}
void Guardian::UpdateResistances(uint32 school)
{
if (school > SPELL_SCHOOL_NORMAL)
{
float baseValue = GetFlatModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + school), BASE_VALUE);
float bonusValue = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)) - baseValue;
// hunter and warlock pets gain 40% of owner's resistance
if (IsPet())
{
baseValue += float(CalculatePct(m_owner->GetResistance(SpellSchools(school)), 40));
bonusValue += float(CalculatePct(m_owner->GetBonusResistanceMod(SpellSchools(school)), 40));
}
SetResistance(SpellSchools(school), int32(baseValue));
SetBonusResistanceMod(SpellSchools(school), int32(bonusValue));
}
else
UpdateArmor();
}
void Guardian::UpdateArmor()
{
float baseValue = 0.0f;
float value = 0.0f;
float bonus_armor = 0.0f;
UnitMods unitMod = UNIT_MOD_ARMOR;
// hunter pets gain 35% of owner's armor value, warlock pets gain 100% of owner's armor
if (IsHunterPet())
bonus_armor = float(CalculatePct(m_owner->GetArmor(), 70));
else if (IsPet())
bonus_armor = m_owner->GetArmor();
value = GetFlatModifierValue(unitMod, BASE_VALUE);
baseValue = value;
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetArmor(int32(baseValue), int32(value - baseValue));
}
void Guardian::UpdateMaxHealth()
{
UnitMods unitMod = UNIT_MOD_HEALTH;
float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
float multiplicator;
switch (GetEntry())
{
case ENTRY_IMP: multiplicator = 8.4f; break;
case ENTRY_VOIDWALKER: multiplicator = 11.0f; break;
case ENTRY_SUCCUBUS: multiplicator = 9.1f; break;
case ENTRY_FELHUNTER: multiplicator = 9.5f; break;
case ENTRY_FELGUARD: multiplicator = 11.0f; break;
case ENTRY_BLOODWORM: multiplicator = 1.0f; break;
default: multiplicator = 10.0f; break;
}
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator;
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetMaxHealth((uint32)value);
}
void Guardian::UpdateMaxPower(Powers power)
{
if (GetPowerIndex(power) == MAX_POWERS)
return;
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power));
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
SetMaxPower(power, int32(value));
}
void Guardian::UpdateAttackPowerAndDamage(bool ranged)
{
if (ranged)
return;
float val = 0.0f;
float bonusAP = 0.0f;
UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
if (GetEntry() == ENTRY_IMP) // imp's attack power
val = GetStat(STAT_STRENGTH) - 10.0f;
else
val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
Player* owner = GetOwner() ? GetOwner()->ToPlayer() : nullptr;
if (owner)
{
if (IsHunterPet()) //hunter pets benefit from owner's attack power
{
float mod = 1.0f; //Hunter contribution modifier
bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f * mod;
SetBonusDamage(int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1287f * mod));
}
else if (IsPetGhoul()) //ghouls benefit from deathknight's attack power (may be summon pet or not)
{
bonusAP = owner->GetTotalAttackPowerValue(BASE_ATTACK) * 0.22f;
SetBonusDamage(int32(owner->GetTotalAttackPowerValue(BASE_ATTACK) * 0.1287f));
}
else if (IsSpiritWolf()) //wolf benefit from shaman's attack power
{
float dmg_multiplier = 0.31f;
bonusAP = owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier;
SetBonusDamage(int32(owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier));
}
//demons benefit from warlocks shadow or fire damage
else if (IsPet())
{
int32 fire = owner->m_activePlayerData->ModDamageDonePos[SPELL_SCHOOL_FIRE] - owner->m_activePlayerData->ModDamageDoneNeg[SPELL_SCHOOL_FIRE];
int32 shadow = owner->m_activePlayerData->ModDamageDonePos[SPELL_SCHOOL_SHADOW] - owner->m_activePlayerData->ModDamageDoneNeg[SPELL_SCHOOL_SHADOW];
int32 maximum = (fire > shadow) ? fire : shadow;
if (maximum < 0)
maximum = 0;
SetBonusDamage(int32(maximum * 0.15f));
bonusAP = maximum * 0.57f;
}
//water elementals benefit from mage's frost damage
else if (GetEntry() == ENTRY_WATER_ELEMENTAL)
{
int32 frost = owner->m_activePlayerData->ModDamageDonePos[SPELL_SCHOOL_FROST] - owner->m_activePlayerData->ModDamageDoneNeg[SPELL_SCHOOL_FROST];
if (frost < 0)
frost = 0;
SetBonusDamage(int32(frost * 0.4f));
}
}
SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
//in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
SetAttackPower(int32(base_attPower));
SetAttackPowerMultiplier(attPowerMultiplier);
//automatically update weapon damage after attack power modification
UpdateDamagePhysical(BASE_ATTACK);
}
void Guardian::UpdateDamagePhysical(WeaponAttackType attType)
{
if (attType > BASE_ATTACK)
return;
float bonusDamage = 0.0f;
if (Player* playerOwner = m_owner->ToPlayer())
{
//force of nature
if (GetEntry() == ENTRY_TREANT)
{
int32 spellDmg = playerOwner->m_activePlayerData->ModDamageDonePos[SPELL_SCHOOL_NATURE] - playerOwner->m_activePlayerData->ModDamageDoneNeg[SPELL_SCHOOL_NATURE];
if (spellDmg > 0)
bonusDamage = spellDmg * 0.09f;
}
//greater fire elemental
else if (GetEntry() == ENTRY_FIRE_ELEMENTAL)
{
int32 spellDmg = playerOwner->m_activePlayerData->ModDamageDonePos[SPELL_SCHOOL_FIRE] - playerOwner->m_activePlayerData->ModDamageDoneNeg[SPELL_SCHOOL_FIRE];
if (spellDmg > 0)
bonusDamage = spellDmg * 0.4f;
}
}
UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
float att_speed = float(GetBaseAttackTime(BASE_ATTACK))/1000.0f;
float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType, false) / 3.5f * att_speed + bonusDamage;
float base_pct = GetPctModifierValue(unitMod, BASE_PCT);
float total_value = GetFlatModifierValue(unitMod, TOTAL_VALUE);
float total_pct = GetPctModifierValue(unitMod, TOTAL_PCT);
float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MinDamage), mindamage);
SetUpdateFieldStatValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::MaxDamage), maxdamage);
}
void Guardian::SetBonusDamage(int32 damage)
{
m_bonusSpellDamage = damage;
if (Player* playerOwner = GetOwner()->ToPlayer())
playerOwner->SetPetSpellPower(damage);
}