Updated Copyright year to 2013
[getmangos.git] / src / game / StatSystem.cpp
blob52b94b7053c5fc7e0e5c95b4c4377e6a43c53cdf
1 /*
2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "DBCStores.h"
20 #include "Unit.h"
21 #include "Player.h"
22 #include "Pet.h"
23 #include "Creature.h"
24 #include "SharedDefines.h"
25 #include "SpellAuras.h"
26 #include "SpellMgr.h"
28 /*#######################################
29 ######## ########
30 ######## PLAYERS STAT SYSTEM ########
31 ######## ########
32 #######################################*/
34 bool Player::UpdateStats(Stats stat)
36 if (stat > STAT_SPIRIT)
37 return false;
39 // value = ((base_value * base_pct) + total_value) * total_pct
40 float value = GetTotalStatValue(stat);
42 SetStat(stat, int32(value));
44 if (stat == STAT_STAMINA || stat == STAT_INTELLECT)
46 Pet* pet = GetPet();
47 if (pet)
48 pet->UpdateStats(stat);
51 switch (stat)
53 case STAT_STRENGTH:
54 break;
55 case STAT_AGILITY:
56 UpdateArmor();
57 UpdateAllCritPercentages();
58 UpdateDodgePercentage();
59 break;
60 case STAT_STAMINA: UpdateMaxHealth(); break;
61 case STAT_INTELLECT:
62 UpdateMaxPower(POWER_MANA);
63 UpdateAllSpellCritChances();
64 UpdateArmor(); // SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
65 break;
67 case STAT_SPIRIT:
68 break;
70 default:
71 break;
73 // Need update (exist AP from stat auras)
74 UpdateAttackPowerAndDamage();
75 UpdateAttackPowerAndDamage(true);
77 UpdateSpellDamageAndHealingBonus();
78 UpdateManaRegen();
80 // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat
81 uint32 mask = 0;
82 AuraList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT);
83 for (AuraList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i)
84 if (Stats((*i)->GetMiscBValue()) == stat)
85 mask |= (*i)->GetMiscValue();
86 if (mask)
88 for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
89 if (mask & (1 << rating))
90 ApplyRatingMod(CombatRating(rating), 0, true);
92 return true;
95 void Player::ApplySpellPowerBonus(int32 amount, bool apply)
97 m_baseSpellPower += apply ? amount : -amount;
99 // For speed just update for client
100 ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply);
101 for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
102 ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply);;
105 void Player::UpdateSpellDamageAndHealingBonus()
107 // Magic damage modifiers implemented in Unit::SpellDamageBonusDone
108 // This information for client side use only
109 // Get healing bonus for all schools
110 SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonusDone(SPELL_SCHOOL_MASK_ALL));
111 // Get damage bonus for all schools
112 for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
113 SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, SpellBaseDamageBonusDone(SpellSchoolMask(1 << i)));
116 bool Player::UpdateAllStats()
118 for (int i = STAT_STRENGTH; i < MAX_STATS; ++i)
120 float value = GetTotalStatValue(Stats(i));
121 SetStat(Stats(i), (int32)value);
124 UpdateArmor();
125 // calls UpdateAttackPowerAndDamage() in UpdateArmor for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
126 UpdateAttackPowerAndDamage(true);
127 UpdateMaxHealth();
129 for (uint32 i = POWER_MANA; i < MAX_POWERS; ++i)
130 UpdateMaxPower(Powers(i));
132 UpdateAllRatings();
133 UpdateAllCritPercentages();
134 UpdateAllSpellCritChances();
135 UpdateBlockPercentage();
136 UpdateParryPercentage();
137 UpdateShieldBlockDamageValue();
138 UpdateDodgePercentage();
139 UpdateArmorPenetration();
140 UpdateSpellDamageAndHealingBonus();
141 UpdateManaRegen();
142 UpdateExpertise(BASE_ATTACK);
143 UpdateExpertise(OFF_ATTACK);
144 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
145 UpdateResistances(i);
147 return true;
150 void Player::UpdateResistances(uint32 school)
152 if (school > SPELL_SCHOOL_NORMAL)
154 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
155 SetResistance(SpellSchools(school), int32(value));
157 Pet* pet = GetPet();
158 if (pet)
159 pet->UpdateResistances(school);
161 else
162 UpdateArmor();
165 void Player::UpdateArmor()
167 float value = 0.0f;
168 UnitMods unitMod = UNIT_MOD_ARMOR;
170 value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items)
171 value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items
172 value += GetModifierValue(unitMod, TOTAL_VALUE);
174 // add dynamic flat mods
175 AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
176 for (AuraList::const_iterator i = mResbyIntellect.begin(); i != mResbyIntellect.end(); ++i)
178 Modifier* mod = (*i)->GetModifier();
179 if (mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
180 value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
183 value *= GetModifierValue(unitMod, TOTAL_PCT);
185 SetArmor(int32(value));
187 Pet* pet = GetPet();
188 if (pet)
189 pet->UpdateArmor();
191 UpdateAttackPowerAndDamage(); // armor dependent auras update for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
194 float Player::GetHealthBonusFromStamina()
196 GtOCTHpPerStaminaEntry const* hpBase = sGtOCTHpPerStaminaStore.LookupEntry((getClass() - 1) * GT_MAX_LEVEL + getLevel() - 1);
198 float stamina = GetStat(STAT_STAMINA);
200 float baseStam = stamina < 20 ? stamina : 20;
201 float moreStam = stamina - baseStam;
202 if (moreStam < 0.0f)
203 moreStam = 0.0f;
205 return baseStam + moreStam * hpBase->ratio;
208 float Player::GetManaBonusFromIntellect()
210 float intellect = GetStat(STAT_INTELLECT);
212 float baseInt = intellect < 20 ? intellect : 20;
213 float moreInt = intellect - baseInt;
215 return baseInt + (moreInt * 15.0f);
218 void Player::UpdateMaxHealth()
220 UnitMods unitMod = UNIT_MOD_HEALTH;
222 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
223 value *= GetModifierValue(unitMod, BASE_PCT);
224 value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
225 value *= GetModifierValue(unitMod, TOTAL_PCT);
227 SetMaxHealth((uint32)value);
230 void Player::UpdateMaxPower(Powers power)
232 MANGOS_ASSERT(power < MAX_POWERS);
234 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
236 uint32 create_power = GetCreateMaxPowers(power);
238 // ignore classes without mana
239 float bonusPower = (power == POWER_MANA && create_power > 0) ? GetManaBonusFromIntellect() : 0;
241 float value = GetModifierValue(unitMod, BASE_VALUE) + create_power;
242 value *= GetModifierValue(unitMod, BASE_PCT);
243 value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
244 value *= GetModifierValue(unitMod, TOTAL_PCT);
246 SetMaxPower(power, uint32(value));
249 void Player::UpdateAttackPowerAndDamage(bool ranged)
251 ChrClassesEntry const * chrEntry = sChrClassesStore.LookupEntry(getClass());
252 MANGOS_ASSERT(chrEntry);
254 float val2 = 0.0f;
255 float level = float(getLevel());
257 UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
259 uint16 index = UNIT_FIELD_ATTACK_POWER;
260 uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MOD_POS;
261 uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
263 if (ranged)
265 index = UNIT_FIELD_RANGED_ATTACK_POWER;
266 index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS;
267 index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
269 float rapPerAgi = std::max(GetStat(STAT_AGILITY) - 10.0f, 0.0f) * chrEntry->rapPerAgi;
271 switch (getClass())
273 case CLASS_HUNTER: val2 = level * 2.0f + rapPerAgi; break;
274 case CLASS_ROGUE: val2 = level + rapPerAgi; break;
275 case CLASS_WARRIOR: val2 = level + rapPerAgi; break;
276 default: break;
279 else
281 float apPerAgi = std::max(GetStat(STAT_AGILITY) - 10.0f, 0.0f) * chrEntry->apPerAgi;
282 float apPerStr = std::max(GetStat(STAT_STRENGTH) - 10.0f, 0.0f) * chrEntry->apPerStr;
283 float levelmod;
284 switch (getClass())
286 case CLASS_WARRIOR:
287 case CLASS_PALADIN:
288 case CLASS_DEATH_KNIGHT:
289 case CLASS_DRUID:
290 levelmod = 3.0f;
291 break;
292 default:
293 levelmod = 2.0f;
294 break;
297 val2 = level * levelmod + apPerAgi + apPerStr;
299 // extracted from client
300 if (getClass() == CLASS_DRUID && GetShapeshiftForm())
302 if (SpellShapeshiftFormEntry const * entry = sSpellShapeshiftFormStore.LookupEntry(uint32(GetShapeshiftForm())))
303 if (entry->flags1 & 0x20)
304 val2 += std::max(GetStat(STAT_AGILITY) - 10.0f, 0.0f) * chrEntry->apPerStr;
308 SetModifierValue(unitMod, BASE_VALUE, val2);
310 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
311 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
313 // add dynamic flat mods
314 if (ranged)
316 if ((getClassMask() & CLASSMASK_WAND_USERS) == 0)
318 AuraList const& mRAPbyStat = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
319 for (AuraList::const_iterator i = mRAPbyStat.begin(); i != mRAPbyStat.end(); ++i)
320 attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
323 else
325 AuraList const& mAPbyStat = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT);
326 for (AuraList::const_iterator i = mAPbyStat.begin(); i != mAPbyStat.end(); ++i)
327 attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
329 AuraList const& mAPbyArmor = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR);
330 for (AuraList::const_iterator iter = mAPbyArmor.begin(); iter != mAPbyArmor.end(); ++iter)
331 // always: ((*i)->GetModifier()->m_miscvalue == 1 == SPELL_SCHOOL_MASK_NORMAL)
332 attPowerMod += int32(GetArmor() / (*iter)->GetModifier()->m_amount);
335 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
337 SetInt32Value(index, (uint32)base_attPower); // UNIT_FIELD_(RANGED)_ATTACK_POWER field
338 SetInt32Value(index_mod, (uint32)attPowerMod); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
339 SetFloatValue(index_mult, attPowerMultiplier); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
341 // automatically update weapon damage after attack power modification
342 if (ranged)
344 UpdateDamagePhysical(RANGED_ATTACK);
346 Pet* pet = GetPet(); // update pet's AP
347 if (pet)
348 pet->UpdateAttackPowerAndDamage();
350 else
352 UpdateDamagePhysical(BASE_ATTACK);
353 if (CanDualWield() && haveOffhandWeapon()) // allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
354 UpdateDamagePhysical(OFF_ATTACK);
358 void Player::UpdateShieldBlockDamageValue()
360 SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockDamageValue());
363 void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
365 UnitMods unitMod;
366 UnitMods attPower;
368 switch (attType)
370 case BASE_ATTACK:
371 default:
372 unitMod = UNIT_MOD_DAMAGE_MAINHAND;
373 attPower = UNIT_MOD_ATTACK_POWER;
374 break;
375 case OFF_ATTACK:
376 unitMod = UNIT_MOD_DAMAGE_OFFHAND;
377 attPower = UNIT_MOD_ATTACK_POWER;
378 break;
379 case RANGED_ATTACK:
380 unitMod = UNIT_MOD_DAMAGE_RANGED;
381 attPower = UNIT_MOD_ATTACK_POWER_RANGED;
382 break;
385 float att_speed = GetAPMultiplier(attType, normalized);
387 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed;
388 float base_pct = GetModifierValue(unitMod, BASE_PCT);
389 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
390 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
392 float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
393 float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
395 if (IsInFeralForm()) // check if player is druid and in cat or bear forms, non main hand attacks not allowed for this mode so not check attack type
397 float weaponSpeed = GetAttackTime(attType) / 1000.0f;
399 switch (GetShapeshiftForm())
401 case FORM_CAT:
402 weapon_mindamage = weapon_mindamage / weaponSpeed;
403 weapon_maxdamage = weapon_maxdamage / weaponSpeed;
404 break;
405 case FORM_BEAR:
406 weapon_mindamage = weapon_mindamage / weaponSpeed + weapon_mindamage / 2.5f;
407 weapon_maxdamage = weapon_maxdamage / weaponSpeed + weapon_maxdamage / 2.5f;
408 break;
411 else if (!CanUseEquippedWeapon(attType)) // check if player not in form but still can't use weapon (broken/etc)
413 weapon_mindamage = BASE_MINDAMAGE;
414 weapon_maxdamage = BASE_MAXDAMAGE;
416 else if (attType == RANGED_ATTACK) // add ammo DPS to ranged damage
418 weapon_mindamage += GetAmmoDPS() * att_speed;
419 weapon_maxdamage += GetAmmoDPS() * att_speed;
422 min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
423 max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
426 void Player::UpdateDamagePhysical(WeaponAttackType attType)
428 float mindamage;
429 float maxdamage;
431 CalculateMinMaxDamage(attType, false, mindamage, maxdamage);
433 switch (attType)
435 case BASE_ATTACK:
436 default:
437 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
438 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
439 break;
440 case OFF_ATTACK:
441 SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, mindamage);
442 SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, maxdamage);
443 break;
444 case RANGED_ATTACK:
445 SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, mindamage);
446 SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, maxdamage);
447 break;
451 void Player::UpdateBlockPercentage()
453 // No block
454 float value = 0.0f;
455 if (CanBlock())
457 // Base value
458 value = 5.0f;
459 // Increase from SPELL_AURA_MOD_BLOCK_CHANCE_PERCENT aura
460 value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_CHANCE_PERCENT);
461 // Increase from rating (exists only on auras)
462 value += GetRatingBonusValue(CR_BLOCK);
463 value = value < 0.0f ? 0.0f : value;
465 SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
468 void Player::UpdateCritPercentage(WeaponAttackType attType)
470 BaseModGroup modGroup;
471 uint16 index;
472 CombatRating cr;
474 switch (attType)
476 case OFF_ATTACK:
477 modGroup = OFFHAND_CRIT_PERCENTAGE;
478 index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
479 cr = CR_CRIT_MELEE;
480 break;
481 case RANGED_ATTACK:
482 modGroup = RANGED_CRIT_PERCENTAGE;
483 index = PLAYER_RANGED_CRIT_PERCENTAGE;
484 cr = CR_CRIT_RANGED;
485 break;
486 case BASE_ATTACK:
487 default:
488 modGroup = CRIT_PERCENTAGE;
489 index = PLAYER_CRIT_PERCENTAGE;
490 cr = CR_CRIT_MELEE;
491 break;
494 float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
495 // Modify crit from weapon skill and maximized defense skill of same level victim difference
496 value += (int32(GetMaxSkillValueForLevel()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
497 value = value < 0.0f ? 0.0f : value;
498 SetStatFloatValue(index, value);
501 void Player::UpdateAllCritPercentages()
503 float value = GetMeleeCritFromAgility();
505 SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
506 SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
507 SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
509 UpdateCritPercentage(BASE_ATTACK);
510 UpdateCritPercentage(OFF_ATTACK);
511 UpdateCritPercentage(RANGED_ATTACK);
514 const float Player::m_diminishing_k[MAX_CLASSES] =
516 0.9560f, // Warrior
517 0.9560f, // Paladin
518 0.9880f, // Hunter
519 0.9880f, // Rogue
520 0.9830f, // Priest
521 0.9560f, // DK
522 0.9880f, // Shaman
523 0.9830f, // Mage
524 0.9830f, // Warlock
525 0.0f, // ??
526 0.9720f // Druid
529 void Player::UpdateParryPercentage()
531 const float parry_cap[MAX_CLASSES] =
533 65.631440f, // Warrior
534 65.631440f, // Paladin
535 145.560408f, // Hunter
536 145.560408f, // Rogue
537 0.0f, // Priest
538 65.631440f, // DK
539 145.560408f, // Shaman
540 0.0f, // Mage
541 0.0f, // Warlock
542 0.0f, // ??
543 0.0f // Druid
546 // No parry
547 float value = 0.0f;
548 uint32 pclass = getClass() - 1;
549 if (CanParry() && parry_cap[pclass] > 0.0f)
551 // Base parry
552 float nondiminishing = 5.0f;
553 float diminishing = 0.0f;
554 GetParryFromStrength(diminishing, nondiminishing);
555 // Parry from rating
556 diminishing += GetRatingBonusValue(CR_PARRY);
557 // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
558 nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
559 // apply diminishing formula to diminishing parry chance
560 value = nondiminishing + diminishing * parry_cap[pclass] /
561 (diminishing + parry_cap[pclass] * m_diminishing_k[pclass]);
562 value = value < 0.0f ? 0.0f : value;
564 SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
567 void Player::UpdateDodgePercentage()
569 const float dodge_cap[MAX_CLASSES] =
571 65.631440f, // Warrior
572 65.631440f, // Paladin
573 145.560408f, // Hunter
574 145.560408f, // Rogue
575 150.375940f, // Priest
576 65.631440f, // DK
577 145.560408f, // Shaman
578 150.375940f, // Mage
579 150.375940f, // Warlock
580 0.0f, // ??
581 116.890707f // Druid
584 float diminishing = 0.0f;
585 float nondiminishing = 0.0f;
586 // Dodge from agility
587 GetDodgeFromAgility(diminishing, nondiminishing);
588 // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
589 nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
590 // Dodge from rating
591 diminishing += GetRatingBonusValue(CR_DODGE);
592 // apply diminishing formula to diminishing dodge chance
593 uint32 pclass = getClass() - 1;
594 float value = nondiminishing + (diminishing * dodge_cap[pclass] /
595 (diminishing + dodge_cap[pclass] * m_diminishing_k[pclass]));
596 value = value < 0.0f ? 0.0f : value;
597 SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
600 void Player::UpdateSpellCritChance(uint32 school)
602 // For normal school set zero crit chance
603 if (school == SPELL_SCHOOL_NORMAL)
605 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
606 return;
608 // For others recalculate it from:
609 float crit = 0.0f;
610 // Crit from Intellect
611 crit += GetSpellCritFromIntellect();
612 // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
613 crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
614 // Increase crit from SPELL_AURA_MOD_ALL_CRIT_CHANCE
615 crit += GetTotalAuraModifier(SPELL_AURA_MOD_ALL_CRIT_CHANCE);
616 // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
617 crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1 << school);
618 // Increase crit from spell crit ratings
619 crit += GetRatingBonusValue(CR_CRIT_SPELL);
621 // Store crit value
622 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
625 void Player::UpdateMeleeHitChances()
627 m_modMeleeHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
628 m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE);
631 void Player::UpdateRangedHitChances()
633 m_modRangedHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
634 m_modRangedHitChance += GetRatingBonusValue(CR_HIT_RANGED);
637 void Player::UpdateSpellHitChances()
639 m_modSpellHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
640 m_modSpellHitChance += GetRatingBonusValue(CR_HIT_SPELL);
643 void Player::UpdateAllSpellCritChances()
645 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
646 UpdateSpellCritChance(i);
649 void Player::UpdateExpertise(WeaponAttackType attack)
651 if (attack == RANGED_ATTACK)
652 return;
654 int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
656 Item* weapon = GetWeaponForAttack(attack);
658 AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
659 for (AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
661 // item neutral spell
662 if((*itr)->GetSpellProto()->GetEquippedItemClass() == -1)
663 expertise += (*itr)->GetModifier()->m_amount;
664 // item dependent spell
665 else if (weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
666 expertise += (*itr)->GetModifier()->m_amount;
669 if (expertise < 0)
670 expertise = 0;
672 switch (attack)
674 case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
675 case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
676 default: break;
680 void Player::UpdateArmorPenetration()
682 m_armorPenetrationPct = GetRatingBonusValue(CR_ARMOR_PENETRATION);
684 AuraList const& armorAuras = GetAurasByType(SPELL_AURA_MOD_TARGET_ARMOR_PCT);
685 for (AuraList::const_iterator itr = armorAuras.begin(); itr != armorAuras.end(); ++itr)
687 // affects all weapons
688 if((*itr)->GetSpellProto()->GetEquippedItemClass() == -1)
690 m_armorPenetrationPct += (*itr)->GetModifier()->m_amount;
691 continue;
694 // dependent on weapon class
695 for (uint8 i = 0; i < MAX_ATTACK; ++i)
697 Item* weapon = GetWeaponForAttack(WeaponAttackType(i));
698 if (weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
700 m_armorPenetrationPct += (*itr)->GetModifier()->m_amount;
701 break;
707 void Player::ApplyManaRegenBonus(int32 amount, bool apply)
709 m_baseManaRegen += apply ? amount : -amount;
710 UpdateManaRegen();
713 void Player::ApplyHealthRegenBonus(int32 amount, bool apply)
715 m_baseHealthRegen += apply ? amount : -amount;
718 void Player::UpdateManaRegen()
720 float base_regen = GetCreateMana() * 0.01f;
722 // Mana regen from spirit and intellect
723 float spirit_regen = sqrt(GetStat(STAT_INTELLECT)) * OCTRegenMPPerSpirit();
724 // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
725 spirit_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
727 // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
728 float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f;
730 // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
731 AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
732 for (AuraList::const_iterator i = regenAura.begin(); i != regenAura.end(); ++i)
734 Modifier* mod = (*i)->GetModifier();
735 power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
738 // Set regen rate in cast state apply only on spirit based regen
739 int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
740 if (modManaRegenInterrupt > 100)
741 modManaRegenInterrupt = 100;
743 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, base_regen + power_regen_mp5 + spirit_regen * modManaRegenInterrupt / 100.0f);
744 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, base_regen + 0.001f + power_regen_mp5 + spirit_regen);
747 void Player::UpdateMasteryAuras()
749 if (!HasAuraType(SPELL_AURA_MASTERY))
751 SetFloatValue(PLAYER_MASTERY, 0.0f);
752 return;
755 float masteryValue = GetTotalAuraModifier(SPELL_AURA_MASTERY) + GetRatingBonusValue(CR_MASTERY);
756 SetFloatValue(PLAYER_MASTERY, masteryValue);
758 std::vector<uint32> const* masterySpells = GetTalentTreeMasterySpells(m_talentsPrimaryTree[m_activeSpec]);
759 if (!masterySpells)
760 return;
762 for (uint32 i = 0; i < masterySpells->size(); ++i)
764 SpellAuraHolder* holder = GetSpellAuraHolder(masterySpells->at(i));
765 if (!holder)
766 continue;
768 SpellEntry const* spellEntry = holder->GetSpellProto();
770 // calculate mastery scaling coef
771 int32 masteryCoef = GetMasteryCoefficient(spellEntry);
772 if (!masteryCoef)
773 continue;
775 // update aura modifiers
776 for (uint32 j = 0; j < MAX_EFFECT_INDEX; ++j)
778 Aura* aura = holder->GetAuraByEffectIndex(SpellEffectIndex(j));
779 if (!aura)
780 continue;
782 if (spellEntry->CalculateSimpleValue(SpellEffectIndex(j)))
783 continue;
785 aura->ApplyModifier(false, false);
786 aura->GetModifier()->m_amount = int32(masteryValue * masteryCoef / 100.0f);
787 aura->ApplyModifier(true, false);
792 void Player::_ApplyAllStatBonuses()
794 SetCanModifyStats(false);
796 _ApplyAllAuraMods();
797 _ApplyAllItemMods();
799 SetCanModifyStats(true);
801 UpdateAllStats();
804 void Player::_RemoveAllStatBonuses()
806 SetCanModifyStats(false);
808 _RemoveAllItemMods();
809 _RemoveAllAuraMods();
811 SetCanModifyStats(true);
813 UpdateAllStats();
816 /*#######################################
817 ######## ########
818 ######## MOBS STAT SYSTEM ########
819 ######## ########
820 #######################################*/
822 bool Creature::UpdateStats(Stats /*stat*/)
824 return true;
827 bool Creature::UpdateAllStats()
829 UpdateMaxHealth();
830 UpdateAttackPowerAndDamage();
832 for (uint32 i = POWER_MANA; i < MAX_POWERS; ++i)
833 UpdateMaxPower(Powers(i));
835 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
836 UpdateResistances(i);
838 return true;
841 void Creature::UpdateResistances(uint32 school)
843 if (school > SPELL_SCHOOL_NORMAL)
845 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
846 SetResistance(SpellSchools(school), int32(value));
848 else
849 UpdateArmor();
852 void Creature::UpdateArmor()
854 float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
855 SetArmor(int32(value));
858 void Creature::UpdateMaxHealth()
860 float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
861 SetMaxHealth((uint32)value);
864 void Creature::UpdateMaxPower(Powers power)
866 MANGOS_ASSERT(power < MAX_POWERS);
868 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
870 float value = GetTotalAuraModValue(unitMod);
871 SetMaxPower(power, uint32(value));
874 void Creature::UpdateAttackPowerAndDamage(bool ranged)
876 UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
878 uint16 index = UNIT_FIELD_ATTACK_POWER;
879 uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MOD_POS;
880 uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
882 if (ranged)
884 index = UNIT_FIELD_RANGED_ATTACK_POWER;
885 index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS;
886 index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
889 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
890 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
891 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
893 SetInt32Value(index, (uint32)base_attPower); // UNIT_FIELD_(RANGED)_ATTACK_POWER field
894 SetInt32Value(index_mod, (uint32)attPowerMod); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
895 SetFloatValue(index_mult, attPowerMultiplier); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
897 if (ranged)
898 return;
900 // automatically update weapon damage after attack power modification
901 UpdateDamagePhysical(BASE_ATTACK);
902 UpdateDamagePhysical(OFF_ATTACK);
905 void Creature::UpdateDamagePhysical(WeaponAttackType attType)
907 if (attType > OFF_ATTACK)
908 return;
910 UnitMods unitMod = (attType == BASE_ATTACK ? UNIT_MOD_DAMAGE_MAINHAND : UNIT_MOD_DAMAGE_OFFHAND);
912 /* difference in AP between current attack power and base value from DB */
913 float att_pwr_change = GetTotalAttackPowerValue(attType) - GetCreatureInfo()->attackpower;
914 float base_value = GetModifierValue(unitMod, BASE_VALUE) + (att_pwr_change * GetAPMultiplier(attType, false) / 14.0f);
915 float base_pct = GetModifierValue(unitMod, BASE_PCT);
916 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
917 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
918 float dmg_multiplier = GetCreatureInfo()->dmg_multiplier;
920 float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
921 float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
923 float mindamage = ((base_value + weapon_mindamage) * dmg_multiplier * base_pct + total_value) * total_pct;
924 float maxdamage = ((base_value + weapon_maxdamage) * dmg_multiplier * base_pct + total_value) * total_pct;
926 SetStatFloatValue(attType == BASE_ATTACK ? UNIT_FIELD_MINDAMAGE : UNIT_FIELD_MINOFFHANDDAMAGE, mindamage);
927 SetStatFloatValue(attType == BASE_ATTACK ? UNIT_FIELD_MAXDAMAGE : UNIT_FIELD_MAXOFFHANDDAMAGE, maxdamage);
930 /*#######################################
931 ######## ########
932 ######## PETS STAT SYSTEM ########
933 ######## ########
934 #######################################*/
936 bool Pet::UpdateStats(Stats stat)
938 if (stat > STAT_SPIRIT)
939 return false;
941 // value = ((base_value * base_pct) + total_value) * total_pct
942 float value = GetTotalStatValue(stat);
944 Unit* owner = GetOwner();
945 if (stat == STAT_STAMINA)
947 if (owner)
948 value += float(owner->GetStat(stat)) * 0.3f;
950 // warlock's and mage's pets gain 30% of owner's intellect
951 else if (stat == STAT_INTELLECT && getPetType() == SUMMON_PET)
953 if (owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE))
954 value += float(owner->GetStat(stat)) * 0.3f;
957 SetStat(stat, int32(value));
959 switch (stat)
961 case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
962 case STAT_AGILITY: UpdateArmor(); break;
963 case STAT_STAMINA: UpdateMaxHealth(); break;
964 case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
965 case STAT_SPIRIT:
966 default:
967 break;
970 return true;
973 bool Pet::UpdateAllStats()
975 for (int i = STAT_STRENGTH; i < MAX_STATS; ++i)
976 UpdateStats(Stats(i));
978 for (uint32 i = POWER_MANA; i < MAX_POWERS; ++i)
979 UpdateMaxPower(Powers(i));
981 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
982 UpdateResistances(i);
984 return true;
987 void Pet::UpdateResistances(uint32 school)
989 if (school > SPELL_SCHOOL_NORMAL)
991 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
993 Unit* owner = GetOwner();
994 // hunter and warlock pets gain 40% of owner's resistance
995 if (owner && (getPetType() == HUNTER_PET || (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)))
996 value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
998 SetResistance(SpellSchools(school), int32(value));
1000 else
1001 UpdateArmor();
1004 void Pet::UpdateArmor()
1006 float value = 0.0f;
1007 float bonus_armor = 0.0f;
1008 UnitMods unitMod = UNIT_MOD_ARMOR;
1010 Unit* owner = GetOwner();
1011 // hunter and warlock pets gain 35% of owner's armor value
1012 if (owner && (getPetType() == HUNTER_PET || (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)))
1013 bonus_armor = 0.35f * float(owner->GetArmor());
1015 value = GetModifierValue(unitMod, BASE_VALUE);
1016 value *= GetModifierValue(unitMod, BASE_PCT);
1017 value += GetStat(STAT_AGILITY) * 2.0f;
1018 value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
1019 value *= GetModifierValue(unitMod, TOTAL_PCT);
1021 SetArmor(int32(value));
1024 void Pet::UpdateMaxHealth()
1026 UnitMods unitMod = UNIT_MOD_HEALTH;
1027 float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
1029 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
1030 value *= GetModifierValue(unitMod, BASE_PCT);
1031 value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
1032 value *= GetModifierValue(unitMod, TOTAL_PCT);
1034 SetMaxHealth((uint32)value);
1037 void Pet::UpdateMaxPower(Powers power)
1039 MANGOS_ASSERT(power < MAX_POWERS);
1041 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
1043 float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
1045 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateMaxPowers(power);
1046 value *= GetModifierValue(unitMod, BASE_PCT);
1047 value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
1048 value *= GetModifierValue(unitMod, TOTAL_PCT);
1050 SetMaxPower(power, uint32(value));
1053 void Pet::UpdateAttackPowerAndDamage(bool ranged)
1055 if (ranged)
1056 return;
1058 float val = 0.0f;
1059 float bonusAP = 0.0f;
1060 UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
1062 if (GetEntry() == 416) // imp's attack power
1063 val = GetStat(STAT_STRENGTH) - 10.0f;
1064 else
1065 val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
1067 Unit* owner = GetOwner();
1068 if (owner && owner->GetTypeId() == TYPEID_PLAYER)
1070 if (getPetType() == HUNTER_PET) // hunter pets benefit from owner's attack power
1072 bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
1073 SetBonusDamage(int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1287f));
1075 // demons benefit from warlocks shadow or fire damage
1076 else if (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
1078 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);
1079 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);
1080 int32 maximum = (fire > shadow) ? fire : shadow;
1081 if (maximum < 0)
1082 maximum = 0;
1083 SetBonusDamage(int32(maximum * 0.15f));
1084 bonusAP = maximum * 0.57f;
1086 // water elementals benefit from mage's frost damage
1087 else if (getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
1089 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);
1090 if (frost < 0)
1091 frost = 0;
1092 SetBonusDamage(int32(frost * 0.4f));
1096 SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
1098 // in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
1099 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
1100 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
1101 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
1103 // UNIT_FIELD_(RANGED)_ATTACK_POWER field
1104 SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower);
1105 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
1106 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS, (int32)attPowerMod);
1107 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
1108 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
1110 // automatically update weapon damage after attack power modification
1111 UpdateDamagePhysical(BASE_ATTACK);
1114 void Pet::UpdateDamagePhysical(WeaponAttackType attType)
1116 if (attType > BASE_ATTACK)
1117 return;
1119 UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
1121 float att_speed = float(GetAttackTime(BASE_ATTACK)) / 1000.0f;
1123 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed;
1124 float base_pct = GetModifierValue(unitMod, BASE_PCT);
1125 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
1126 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
1128 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
1129 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
1131 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
1132 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
1134 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
1135 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);