diff options
Diffstat (limited to 'src/server/game/StatSystem.cpp')
-rw-r--r-- | src/server/game/StatSystem.cpp | 1263 |
1 files changed, 1263 insertions, 0 deletions
diff --git a/src/server/game/StatSystem.cpp b/src/server/game/StatSystem.cpp new file mode 100644 index 00000000000..7bc3877afc0 --- /dev/null +++ b/src/server/game/StatSystem.cpp @@ -0,0 +1,1263 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Unit.h" +#include "Player.h" +#include "Pet.h" +#include "Creature.h" +#include "SharedDefines.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" + +/*####################################### +######## ######## +######## PLAYERS STAT SYSTEM ######## +######## ######## +#######################################*/ + +bool Player::UpdateStats(Stats stat) +{ + if (stat > STAT_SPIRIT) + return false; + + // 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_STRENGTH: + UpdateShieldBlockValue(); + break; + case STAT_AGILITY: + UpdateArmor(); + UpdateAllCritPercentages(); + UpdateDodgePercentage(); + break; + case STAT_STAMINA: UpdateMaxHealth(); break; + case STAT_INTELLECT: + UpdateMaxPower(POWER_MANA); + UpdateAllSpellCritChances(); + UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently + break; + + case STAT_SPIRIT: + break; + + default: + break; + } + + if (stat == STAT_STRENGTH) + { + UpdateAttackPowerAndDamage(false); + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(true); + } + else if (stat == STAT_AGILITY) + { + UpdateAttackPowerAndDamage(false); + UpdateAttackPowerAndDamage(true); + } + else + { + // Need update (exist AP from stat auras) + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(false); + if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, stat)) + UpdateAttackPowerAndDamage(true); + } + + UpdateSpellDamageAndHealingBonus(); + UpdateManaRegen(); + + // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat + uint32 mask = 0; + AuraEffectList const& modRatingFromStat = GetAuraEffectsByType(SPELL_AURA_MOD_RATING_FROM_STAT); + for (AuraEffectList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i) + if (Stats((*i)->GetMiscValueB()) == stat) + mask |= (*i)->GetMiscValue(); + if (mask) + { + for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) + if (mask & (1 << rating)) + ApplyRatingMod(CombatRating(rating), 0, true); + } + return true; +} + +void Player::ApplySpellPowerBonus(int32 amount, bool apply) +{ + m_baseSpellPower+=apply?amount:-amount; + + // For speed just update for client + ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply); + for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, amount, apply);; +} + +void Player::UpdateSpellDamageAndHealingBonus() +{ + // Magic damage modifiers implemented in Unit::SpellDamageBonus + // This information for client side use only + // Get healing bonus for all schools + SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL)); + // Get damage bonus for all schools + for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i))); +} + +bool Player::UpdateAllStats() +{ + for (int 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 (int i = POWER_MANA; i < MAX_POWERS; ++i) + UpdateMaxPower(Powers(i)); + + UpdateAllRatings(); + UpdateAllCritPercentages(); + UpdateAllSpellCritChances(); + UpdateDefenseBonusesMod(); + UpdateShieldBlockValue(); + UpdateSpellDamageAndHealingBonus(); + UpdateManaRegen(); + UpdateExpertise(BASE_ATTACK); + UpdateExpertise(OFF_ATTACK); + RecalculateRating(CR_ARMOR_PENETRATION); + for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) + UpdateResistances(i); + + return true; +} + +void Player::UpdateResistances(uint32 school) +{ + if (school > SPELL_SCHOOL_NORMAL) + { + float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); + SetResistance(SpellSchools(school), int32(value)); + + Pet *pet = GetPet(); + if (pet) + pet->UpdateResistances(school); + } + else + UpdateArmor(); +} + +void Player::UpdateArmor() +{ + float value = 0.0f; + UnitMods unitMod = UNIT_MOD_ARMOR; + + value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items) + value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items + value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats + value += GetModifierValue(unitMod, TOTAL_VALUE); + + //add dynamic flat mods + AuraEffectList const& mResbyIntellect = GetAuraEffectsByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT); + for (AuraEffectList::const_iterator i = mResbyIntellect.begin(); i != mResbyIntellect.end(); ++i) + { + if ((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) + value += int32(GetStat(Stats((*i)->GetMiscValueB())) * (*i)->GetAmount() / 100.0f); + } + + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetArmor(int32(value)); + + Pet *pet = GetPet(); + if (pet) + pet->UpdateArmor(); + + UpdateAttackPowerAndDamage(); // armor dependent auras update for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR +} + +float Player::GetHealthBonusFromStamina() +{ + float stamina = GetStat(STAT_STAMINA); + + float baseStam = stamina < 20 ? stamina : 20; + float moreStam = stamina - baseStam; + + return baseStam + (moreStam*10.0f); +} + +float Player::GetManaBonusFromIntellect() +{ + float intellect = GetStat(STAT_INTELLECT); + + float baseInt = intellect < 20 ? intellect : 20; + float moreInt = intellect - baseInt; + + return baseInt + (moreInt*15.0f); +} + +void Player::UpdateMaxHealth() +{ + UnitMods unitMod = UNIT_MOD_HEALTH; + + float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); + value *= GetModifierValue(unitMod, BASE_PCT); + value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina(); + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetMaxHealth((uint32)value); +} + +void Player::UpdateMaxPower(Powers power) +{ + UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power); + + float bonusPower = (power == POWER_MANA && GetCreatePowers(power) > 0) ? GetManaBonusFromIntellect() : 0; + + float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power); + value *= GetModifierValue(unitMod, BASE_PCT); + value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower; + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetMaxPower(power, uint32(value)); +} + +void Player::ApplyFeralAPBonus(int32 amount, bool apply) +{ + m_baseFeralAP+= apply ? amount:-amount; + UpdateAttackPowerAndDamage(); +} + +void Player::UpdateAttackPowerAndDamage(bool ranged) +{ + float val2 = 0.0f; + float level = float(getLevel()); + + UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; + + uint16 index = UNIT_FIELD_ATTACK_POWER; + uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS; + uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER; + + if (ranged) + { + index = UNIT_FIELD_RANGED_ATTACK_POWER; + index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS; + index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; + + switch(getClass()) + { + case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; break; + case CLASS_ROGUE: val2 = level + GetStat(STAT_AGILITY) - 10.0f; break; + case CLASS_WARRIOR:val2 = level + GetStat(STAT_AGILITY) - 10.0f; break; + case CLASS_DRUID: + switch(m_form) + { + case FORM_CAT: + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = 0.0f; break; + default: + val2 = GetStat(STAT_AGILITY) - 10.0f; break; + } + break; + default: val2 = GetStat(STAT_AGILITY) - 10.0f; break; + } + } + else + { + switch(getClass()) + { + case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_DEATH_KNIGHT: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; + case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; + case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break; + case CLASS_DRUID: + { + //Check if Predatory Strikes is skilled + float mLevelMult = 0.0; + switch(m_form) + { + case FORM_CAT: + case FORM_BEAR: + case FORM_DIREBEAR: + case FORM_MOONKIN: + { + Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_DUMMY); + for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + { + // Predatory Strikes (effect 0) + if ((*itr)->GetEffIndex() == 0 && (*itr)->GetSpellProto()->SpellIconID == 1563) + { + mLevelMult = (*itr)->GetAmount() / 100.0f; + break; + } + } + break; + } + default: break; + } + + switch(m_form) + { + case FORM_CAT: + val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f + m_baseFeralAP; break; + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break; + case FORM_MOONKIN: + val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break; + default: + val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break; + } + break; + } + case CLASS_MAGE: val2 = GetStat(STAT_STRENGTH) - 10.0f; break; + case CLASS_PRIEST: val2 = GetStat(STAT_STRENGTH) - 10.0f; break; + case CLASS_WARLOCK: val2 = GetStat(STAT_STRENGTH) - 10.0f; break; + } + } + + SetModifierValue(unitMod, BASE_VALUE, val2); + + float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); + + //add dynamic flat mods + if (ranged) + { + if ((getClassMask() & CLASSMASK_WAND_USERS) == 0) + { + AuraEffectList const& mRAPbyStat = GetAuraEffectsByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT); + for (AuraEffectList::const_iterator i = mRAPbyStat.begin(); i != mRAPbyStat.end(); ++i) + attPowerMod += int32(GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 100.0f); + } + } + else + { + AuraEffectList const& mAPbyStat = GetAuraEffectsByType(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT); + for (AuraEffectList::const_iterator i = mAPbyStat.begin(); i != mAPbyStat.end(); ++i) + attPowerMod += int32(GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 100.0f); + + AuraEffectList const& mAPbyArmor = GetAuraEffectsByType(SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR); + for (AuraEffectList::const_iterator iter = mAPbyArmor.begin(); iter != mAPbyArmor.end(); ++iter) + // always: ((*i)->GetModifier()->m_miscvalue == 1 == SPELL_SCHOOL_MASK_NORMAL) + attPowerMod += int32(GetArmor() / (*iter)->GetAmount()); + } + + float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + + SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field + SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field + + Pet *pet = GetPet(); //update pet's AP + //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 (CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon + UpdateDamagePhysical(OFF_ATTACK); + if (getClass() == CLASS_SHAMAN || getClass() == CLASS_PALADIN) // mental quickness + UpdateSpellDamageAndHealingBonus(); + + if (pet && pet->IsPetGhoul()) // At ranged attack change for hunter pet + pet->UpdateAttackPowerAndDamage(); + } +} + +void Player::UpdateShieldBlockValue() +{ + SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue()); +} + +void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& min_damage, float& max_damage) +{ + UnitMods unitMod; + UnitMods attPower; + + switch(attType) + { + case BASE_ATTACK: + default: + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + attPower = UNIT_MOD_ATTACK_POWER; + break; + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; + attPower = UNIT_MOD_ATTACK_POWER; + break; + case RANGED_ATTACK: + unitMod = UNIT_MOD_DAMAGE_RANGED; + attPower = UNIT_MOD_ATTACK_POWER_RANGED; + break; + } + + float att_speed = GetAPMultiplier(attType,normalized); + + float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed; + float base_pct = GetModifierValue(unitMod, BASE_PCT); + float total_value = GetModifierValue(unitMod, TOTAL_VALUE); + float total_pct = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f; + + float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE); + float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE); + + if (IsInFeralForm()) //check if player is druid and in cat or bear forms + { + uint8 lvl = getLevel(); + if (lvl > 60) + lvl = 60; + + weapon_mindamage = lvl*0.85f*att_speed; + weapon_maxdamage = lvl*1.25f*att_speed; + } + 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) + { + min_damage = 0; + max_damage = 0; + return; + } + weapon_mindamage = BASE_MINDAMAGE; + weapon_maxdamage = BASE_MAXDAMAGE; + } + else if (attType == RANGED_ATTACK) //add ammo DPS to ranged damage + { + weapon_mindamage += GetAmmoDPS() * att_speed; + weapon_maxdamage += GetAmmoDPS() * att_speed; + } + + min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct; + max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct; +} + +void Player::UpdateDamagePhysical(WeaponAttackType attType) +{ + float mindamage; + float maxdamage; + + CalculateMinMaxDamage(attType, false, true, mindamage, maxdamage); + + switch (attType) + { + case BASE_ATTACK: + default: + SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage); + break; + case OFF_ATTACK: + SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage); + break; + case RANGED_ATTACK: + SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage); + break; + } +} + +void Player::UpdateDefenseBonusesMod() +{ + UpdateBlockPercentage(); + UpdateParryPercentage(); + UpdateDodgePercentage(); +} + +void Player::UpdateBlockPercentage() +{ + // No block + float value = 0.0f; + if (CanBlock()) + { + // Base value + value = 5.0f; + // Modify value from defense skill + value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f; + // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura + value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); + // Increase from rating + value += GetRatingBonusValue(CR_BLOCK); + value = value < 0.0f ? 0.0f : value; + } + SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value); +} + +void Player::UpdateCritPercentage(WeaponAttackType attType) +{ + BaseModGroup modGroup; + uint16 index; + CombatRating cr; + + switch(attType) + { + case OFF_ATTACK: + modGroup = OFFHAND_CRIT_PERCENTAGE; + index = PLAYER_OFFHAND_CRIT_PERCENTAGE; + cr = CR_CRIT_MELEE; + break; + case RANGED_ATTACK: + modGroup = RANGED_CRIT_PERCENTAGE; + index = PLAYER_RANGED_CRIT_PERCENTAGE; + cr = CR_CRIT_RANGED; + break; + case BASE_ATTACK: + default: + modGroup = CRIT_PERCENTAGE; + index = PLAYER_CRIT_PERCENTAGE; + cr = CR_CRIT_MELEE; + break; + } + + float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr); + // Modify crit from weapon skill and maximized defense skill of same level victim difference + value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f; + value = value < 0.0f ? 0.0f : value; + SetStatFloatValue(index, value); +} + +void Player::UpdateAllCritPercentages() +{ + float value = GetMeleeCritFromAgility(); + + SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value); + SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value); + SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value); + + UpdateCritPercentage(BASE_ATTACK); + UpdateCritPercentage(OFF_ATTACK); + UpdateCritPercentage(RANGED_ATTACK); +} + +void Player::UpdateParryPercentage() +{ + // No parry + float value = 0.0f; + if (CanParry()) + { + // Base parry + value = 5.0f; + // Modify value from defense skill + value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f; + // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura + value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + // Parry from rating + value += GetRatingBonusValue(CR_PARRY); + value = value < 0.0f ? 0.0f : value; + } + SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value); +} + +void Player::UpdateDodgePercentage() +{ + // Dodge from agility + float value = GetDodgeFromAgility(); + // Modify value from defense skill + value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f; + // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura + value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); + // Dodge from rating + value += GetRatingBonusValue(CR_DODGE); + value = value < 0.0f ? 0.0f : value; + SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value); +} + +void Player::UpdateSpellCritChance(uint32 school) +{ + // For normal school set zero crit chance + if (school == SPELL_SCHOOL_NORMAL) + { + SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f); + return; + } + // For others recalculate it from: + float crit = 0.0f; + // Crit from Intellect + crit += GetSpellCritFromIntellect(); + // 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 by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL + crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school); + // Increase crit from spell crit ratings + crit += GetRatingBonusValue(CR_CRIT_SPELL); + + // Store crit value + SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit); +} + +void Player::UpdateArmorPenetration(int32 amount) +{ + // Store Rating Value + SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_ARMOR_PENETRATION, amount); +} + +void Player::UpdateMeleeHitChances() +{ + m_modMeleeHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + m_modMeleeHitChance+= GetRatingBonusValue(CR_HIT_MELEE); +} + +void Player::UpdateRangedHitChances() +{ + m_modRangedHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE); + m_modRangedHitChance+= GetRatingBonusValue(CR_HIT_RANGED); +} + +void Player::UpdateSpellHitChances() +{ + m_modSpellHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); + m_modSpellHitChance+= GetRatingBonusValue(CR_HIT_SPELL); +} + +void Player::UpdateAllSpellCritChances() +{ + for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) + UpdateSpellCritChance(i); +} + +void Player::UpdateExpertise(WeaponAttackType attack) +{ + if (attack == RANGED_ATTACK) + return; + + int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE)); + + Item *weapon = GetWeaponForAttack(attack); + + AuraEffectList const& expAuras = GetAuraEffectsByType(SPELL_AURA_MOD_EXPERTISE); + for (AuraEffectList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr) + { + // item neutral spell + if ((*itr)->GetSpellProto()->EquippedItemClass == -1) + expertise += (*itr)->GetAmount(); + // item dependent spell + else if (weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto())) + expertise += (*itr)->GetAmount(); + } + + if (expertise < 0) + expertise = 0; + + switch(attack) + { + case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break; + case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break; + default: break; + } +} + +void Player::ApplyManaRegenBonus(int32 amount, bool apply) +{ + m_baseManaRegen+= apply ? amount : -amount; + UpdateManaRegen(); +} + +void Player::ApplyHealthRegenBonus(int32 amount, bool apply) +{ + m_baseHealthRegen+= apply ? amount : -amount; +} + +void Player::UpdateManaRegen() +{ + float Intellect = GetStat(STAT_INTELLECT); + // Mana regen from spirit and intellect + float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit(); + // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen + power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA); + + // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura + float power_regen_mp5 = (GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) + m_baseManaRegen) / 5.0f; + + // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura + AuraEffectList const& regenAura = GetAuraEffectsByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT); + for (AuraEffectList::const_iterator i = regenAura.begin(); i != regenAura.end(); ++i) + { + power_regen_mp5 += GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 500.0f; + } + + // Set regen rate in cast state apply only on spirit based regen + int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); + if (modManaRegenInterrupt > 100) + modManaRegenInterrupt = 100; + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f); + + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, power_regen_mp5 + power_regen); +} + +void Player::_ApplyAllStatBonuses() +{ + SetCanModifyStats(false); + + _ApplyAllAuraStatMods(); + _ApplyAllItemMods(); + + SetCanModifyStats(true); + + UpdateAllStats(); +} + +void Player::_RemoveAllStatBonuses() +{ + SetCanModifyStats(false); + + _RemoveAllItemMods(); + _RemoveAllAuraStatMods(); + + SetCanModifyStats(true); + + UpdateAllStats(); +} + +/*####################################### +######## ######## +######## MOBS STAT SYSTEM ######## +######## ######## +#######################################*/ + +bool Creature::UpdateStats(Stats /*stat*/) +{ + return true; +} + +bool Creature::UpdateAllStats() +{ + UpdateMaxHealth(); + UpdateAttackPowerAndDamage(); + UpdateAttackPowerAndDamage(true); + + for (int i = POWER_MANA; i < MAX_POWERS; ++i) + UpdateMaxPower(Powers(i)); + + for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) + UpdateResistances(i); + + return true; +} + +void Creature::UpdateResistances(uint32 school) +{ + if (school > SPELL_SCHOOL_NORMAL) + { + float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); + SetResistance(SpellSchools(school), int32(value)); + } + else + UpdateArmor(); +} + +void Creature::UpdateArmor() +{ + float value = GetTotalAuraModValue(UNIT_MOD_ARMOR); + SetArmor(int32(value)); +} + +void Creature::UpdateMaxHealth() +{ + float value = GetTotalAuraModValue(UNIT_MOD_HEALTH); + SetMaxHealth((uint32)value); +} + +void Creature::UpdateMaxPower(Powers power) +{ + UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power); + + float value = GetTotalAuraModValue(unitMod); + SetMaxPower(power, uint32(value)); +} + +void Creature::UpdateAttackPowerAndDamage(bool ranged) +{ + UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; + + uint16 index = UNIT_FIELD_ATTACK_POWER; + uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS; + uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER; + + if (ranged) + { + index = UNIT_FIELD_RANGED_ATTACK_POWER; + index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS; + index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; + } + + float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); + float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + + SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field + SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field + + //automatically update weapon damage after attack power modification + if (ranged) + UpdateDamagePhysical(RANGED_ATTACK); + else + { + UpdateDamagePhysical(BASE_ATTACK); + UpdateDamagePhysical(OFF_ATTACK); + } +} + +void Creature::UpdateDamagePhysical(WeaponAttackType attType) +{ + 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 att_speed = float(GetAttackTime(attType))/1000.0f; + + float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE); + float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE); + + /* difference in AP between current attack power and base value from DB */ + float att_pwr_change = GetTotalAttackPowerValue(attType) - GetCreatureInfo()->attackpower; + float base_value = GetModifierValue(unitMod, BASE_VALUE) + (att_pwr_change * GetAPMultiplier(attType, false) / 14.0f); + float base_pct = GetModifierValue(unitMod, BASE_PCT); + float total_value = GetModifierValue(unitMod, TOTAL_VALUE); + float total_pct = GetModifierValue(unitMod, TOTAL_PCT); + float dmg_multiplier = GetCreatureInfo()->dmg_multiplier; + + if (!CanUseAttackType(attType)) + { + weapon_mindamage = 0; + weapon_maxdamage = 0; + } + + float mindamage = ((base_value + weapon_mindamage) * dmg_multiplier * base_pct + total_value) * total_pct; + float maxdamage = ((base_value + weapon_maxdamage) * dmg_multiplier * base_pct + total_value) * total_pct; + + switch(attType) + { + case BASE_ATTACK: + default: + SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage); + break; + case OFF_ATTACK: + SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage); + break; + case RANGED_ATTACK: + SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage); + SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage); + break; + } +} + +/*####################################### +######## ######## +######## 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 + +bool Guardian::UpdateStats(Stats stat) +{ + if (stat >= MAX_STATS) + return false; + + // value = ((base_value * base_pct) + total_value) * total_pct + float value = GetTotalStatValue(stat); + + Unit *owner = GetOwner(); + // Handle Death Knight Glyphs and Talents + float mod = 0.75f; + if (IsPetGhoul() && (stat == STAT_STAMINA || stat == STAT_STRENGTH)) + { + switch (stat) + { + case STAT_STAMINA: mod = 0.3f; break; // Default Owner's Stamina scale + case STAT_STRENGTH: mod = 0.7f; break; // Default Owner's Strength scale + default: break; + } + // Ravenous Dead + AuraEffect const *aurEff; + // Check just if owner has Ravenous Dead since it's effect is not an aura + aurEff = owner->GetAuraEffect(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, SPELLFAMILY_DEATHKNIGHT, 3010, 0); + if (aurEff) + { + SpellEntry const* sProto = aurEff->GetSpellProto(); // Then get the SpellProto and add the dummy effect value + mod += mod * (sProto->EffectBasePoints[1] / 100.0f); // Ravenous Dead edits the original scale + } + // Glyph of the Ghoul + aurEff = owner->GetAuraEffect(58686, 0); + if (aurEff) + mod += (aurEff->GetAmount() / 100.0f); // Glyph of the Ghoul adds a flat value to the scale mod + value += float(owner->GetStat(stat)) * mod; + } + else if (stat == STAT_STAMINA) + { + if (owner->getClass() == CLASS_WARLOCK && isPet()) + value += float(owner->GetStat(STAT_STAMINA)) * 0.75f; + else + { + mod = 0.3f; + if (this->ToCreature()->isPet()) + { + PetSpellMap::const_iterator itr = (((Pet*)this)->m_spells.find(62758)); // Wild Hunt rank 1 + if (itr == ((Pet*)this)->m_spells.end()) + { + itr = ((Pet*)this)->m_spells.find(62762); // Wild Hunt rank 2 + } + if (itr != ((Pet*)this)->m_spells.end()) // If pet has Wild Hunt + { + + SpellEntry const* sProto = sSpellStore.LookupEntry(itr->first); // Then get the SpellProto and add the dummy effect value + mod += mod * (sProto->EffectBasePoints[0] / 100.0f); + } + } + value += float(owner->GetStat(stat)) * mod; + } + } + //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) + value += float(owner->GetStat(stat)) * 0.3f; + } +/* + else if (stat == STAT_STRENGTH) + { + if (IsPetGhoul()) + value += float(owner->GetStat(stat)) * 0.3f; + } +*/ + + SetStat(stat, int32(value)); + + switch (stat) + { + case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break; + case STAT_AGILITY: UpdateArmor(); break; + case STAT_STAMINA: UpdateMaxHealth(); break; + case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break; + case STAT_SPIRIT: + default: + break; + } + + return true; +} + +bool Guardian::UpdateAllStats() +{ + for (int i = STAT_STRENGTH; i < MAX_STATS; ++i) + UpdateStats(Stats(i)); + + for (int i = POWER_MANA; i < MAX_POWERS; ++i) + UpdateMaxPower(Powers(i)); + + for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i) + UpdateResistances(i); + + return true; +} + +void Guardian::UpdateResistances(uint32 school) +{ + if (school > SPELL_SCHOOL_NORMAL) + { + float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); + + // hunter and warlock pets gain 40% of owner's resistance + if (isPet()) + value += float(m_owner->GetResistance(SpellSchools(school))) * 0.4f; + + SetResistance(SpellSchools(school), int32(value)); + } + else + UpdateArmor(); +} + +void Guardian::UpdateArmor() +{ + float value = 0.0f; + float bonus_armor = 0.0f; + UnitMods unitMod = UNIT_MOD_ARMOR; + + // hunter and warlock pets gain 35% of owner's armor value + if (isPet()) + bonus_armor = 0.35f * float(m_owner->GetArmor()); + + value = GetModifierValue(unitMod, BASE_VALUE); + value *= GetModifierValue(unitMod, BASE_PCT); + value += GetStat(STAT_AGILITY) * 2.0f; + value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor; + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetArmor(int32(value)); +} + +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_GHOUL: multiplicator = 5.4f; break; + default: multiplicator = 10.0f; break; + } + + float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); + value *= GetModifierValue(unitMod, BASE_PCT); + value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator; + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetMaxHealth((uint32)value); +} + +void Guardian::UpdateMaxPower(Powers power) +{ + UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power); + + float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f; + float multiplicator = 15.0f; + + switch(GetEntry()) + { + case ENTRY_IMP: multiplicator = 4.95f; break; + case ENTRY_VOIDWALKER: + case ENTRY_SUCCUBUS: + case ENTRY_FELHUNTER: + case ENTRY_FELGUARD: multiplicator = 11.5f; break; + default: multiplicator = 15.0f; break; + } + + float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power); + value *= GetModifierValue(unitMod, BASE_PCT); + value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; + value *= GetModifierValue(unitMod, TOTAL_PCT); + + SetMaxPower(power, uint32(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; + + Unit* owner = GetOwner(); + if (owner && owner->GetTypeId() == TYPEID_PLAYER) + { + if (isHunterPet()) //hunter pets benefit from owner's attack power + { + float mod = 1.0f; //Hunter contribution modifier + if (this->ToCreature()->isPet()) + { + PetSpellMap::const_iterator itr = ((Pet*)this)->m_spells.find(62758); //Wild Hunt rank 1 + if (itr == ((Pet*)this)->m_spells.end()) + { + itr = ((Pet*)this)->m_spells.find(62762); //Wild Hunt rank 2 + } + if (itr != ((Pet*)this)->m_spells.end()) // If pet has Wild Hunt + { + + SpellEntry const* sProto = sSpellStore.LookupEntry(itr->first); // Then get the SpellProto and add the dummy effect value + mod += (sProto->EffectBasePoints[1] / 100.0f); + } + } + 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)); + } + //demons benefit from warlocks shadow or fire damage + else if (isPet()) + { + int32 fire = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FIRE); + int32 shadow = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + 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 = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FROST); + if (frost < 0) + frost = 0; + SetBonusDamage(int32(frost * 0.4f)); + } + } + + SetModifierValue(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 = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT); + float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE); + float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f; + + //UNIT_FIELD_(RANGED)_ATTACK_POWER field + SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower); + //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field + SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)attPowerMod); + //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field + SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 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 (m_owner->GetTypeId() == TYPEID_PLAYER) + { + //force of nature + if (GetEntry() == ENTRY_TREANT) + { + int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_NATURE)) - m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_NATURE); + if (spellDmg > 0) + bonusDamage = spellDmg * 0.09f; + } + //greater fire elemental + else if (GetEntry() == ENTRY_FIRE_ELEMENTAL) + { + int32 spellDmg = int32(m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE)) - m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FIRE); + if (spellDmg > 0) + bonusDamage = spellDmg * 0.4f; + } + } + + UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND; + + float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f; + + float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed + bonusDamage; + float base_pct = GetModifierValue(unitMod, BASE_PCT); + float total_value = GetModifierValue(unitMod, TOTAL_VALUE); + float total_pct = GetModifierValue(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; + + // Pet's base damage changes depending on happiness + if (isHunterPet() && attType == BASE_ATTACK) + { + switch(((Pet*)this)->GetHappinessState()) + { + case HAPPY: + // 125% of normal damage + mindamage = mindamage * 1.25; + maxdamage = maxdamage * 1.25; + break; + case CONTENT: + // 100% of normal damage, nothing to modify + break; + case UNHAPPY: + // 75% of normal damage + mindamage = mindamage * 0.75; + maxdamage = maxdamage * 0.75; + break; + } + } + + Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_MOD_ATTACKSPEED); + for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + { + switch ((*itr)->GetSpellProto()->Id) + { + case 61682: + case 61683: + mindamage = mindamage * (100.0f-float((*itr)->GetAmount()))/100.0f; + maxdamage = maxdamage * (100.0f-float((*itr)->GetAmount()))/100.0f; + break; + default: + break; + } + } + + SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage); + SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage); +} |