[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / src / game / StatSystem.cpp
blob9d40f59dcf61cb0a42d8285497fa971cc50cdb66
1 /*
2 * Copyright (C) 2005-2009 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 "Unit.h"
20 #include "Player.h"
21 #include "Pet.h"
22 #include "Creature.h"
23 #include "SharedDefines.h"
24 #include "SpellAuras.h"
26 /*#######################################
27 ######## ########
28 ######## PLAYERS STAT SYSTEM ########
29 ######## ########
30 #######################################*/
32 bool Player::UpdateStats(Stats stat)
34 if(stat > STAT_SPIRIT)
35 return false;
37 // value = ((base_value * base_pct) + total_value) * total_pct
38 float value = GetTotalStatValue(stat);
40 SetStat(stat, int32(value));
42 if(stat == STAT_STAMINA || stat == STAT_INTELLECT)
44 Pet *pet = GetPet();
45 if(pet)
46 pet->UpdateStats(stat);
49 switch(stat)
51 case STAT_STRENGTH:
52 UpdateShieldBlockValue();
53 break;
54 case STAT_AGILITY:
55 UpdateArmor();
56 UpdateAllCritPercentages();
57 UpdateDodgePercentage();
58 break;
59 case STAT_STAMINA: UpdateMaxHealth(); break;
60 case STAT_INTELLECT:
61 UpdateMaxPower(POWER_MANA);
62 UpdateAllSpellCritChances();
63 UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
64 break;
66 case STAT_SPIRIT:
67 break;
69 default:
70 break;
72 // Need update (exist AP from stat auras)
73 UpdateAttackPowerAndDamage();
74 UpdateAttackPowerAndDamage(true);
76 UpdateSpellDamageAndHealingBonus();
77 UpdateManaRegen();
79 // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat
80 uint32 mask = 0;
81 AuraList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT);
82 for(AuraList::const_iterator i = modRatingFromStat.begin();i != modRatingFromStat.end(); ++i)
83 if (Stats((*i)->GetMiscBValue()) == stat)
84 mask |= (*i)->GetMiscValue();
85 if (mask)
87 for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
88 if (mask & (1 << rating))
89 ApplyRatingMod(CombatRating(rating), 0, true);
91 return true;
94 void Player::ApplySpellDamageBonus(int32 amount, bool apply)
96 m_baseSpellDamage+=apply?amount:-amount;
97 // For speed just update for client
98 ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply);
101 void Player::ApplySpellHealingBonus(int32 amount, bool apply)
103 m_baseSpellHealing+=apply?amount:-amount;
104 // For speed just update for client
105 for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
106 ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, amount, apply);;
109 void Player::UpdateSpellDamageAndHealingBonus()
111 // Magic damage modifiers implemented in Unit::SpellDamageBonus
112 // This information for client side use only
113 // Get healing bonus for all schools
114 SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL));
115 // Get damage bonus for all schools
116 for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
117 SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i)));
120 bool Player::UpdateAllStats()
122 for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
124 float value = GetTotalStatValue(Stats(i));
125 SetStat(Stats(i), (int32)value);
128 UpdateAttackPowerAndDamage();
129 UpdateAttackPowerAndDamage(true);
130 UpdateArmor();
131 UpdateMaxHealth();
133 for(int i = POWER_MANA; i < MAX_POWERS; i++)
134 UpdateMaxPower(Powers(i));
136 UpdateAllCritPercentages();
137 UpdateAllSpellCritChances();
138 UpdateDefenseBonusesMod();
139 UpdateShieldBlockValue();
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 += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats
173 value += GetModifierValue(unitMod, TOTAL_VALUE);
175 //add dynamic flat mods
176 AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
177 for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
179 Modifier* mod = (*i)->GetModifier();
180 if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
181 value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
184 value *= GetModifierValue(unitMod, TOTAL_PCT);
186 SetArmor(int32(value));
188 Pet *pet = GetPet();
189 if(pet)
190 pet->UpdateArmor();
193 float Player::GetHealthBonusFromStamina()
195 float stamina = GetStat(STAT_STAMINA);
197 float baseStam = stamina < 20 ? stamina : 20;
198 float moreStam = stamina - baseStam;
200 return baseStam + (moreStam*10.0f);
203 float Player::GetManaBonusFromIntellect()
205 float intellect = GetStat(STAT_INTELLECT);
207 float baseInt = intellect < 20 ? intellect : 20;
208 float moreInt = intellect - baseInt;
210 return baseInt + (moreInt*15.0f);
213 void Player::UpdateMaxHealth()
215 UnitMods unitMod = UNIT_MOD_HEALTH;
217 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
218 value *= GetModifierValue(unitMod, BASE_PCT);
219 value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
220 value *= GetModifierValue(unitMod, TOTAL_PCT);
222 SetMaxHealth((uint32)value);
225 void Player::UpdateMaxPower(Powers power)
227 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
229 float bonusPower = (power == POWER_MANA) ? GetManaBonusFromIntellect() : 0;
231 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
232 value *= GetModifierValue(unitMod, BASE_PCT);
233 value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
234 value *= GetModifierValue(unitMod, TOTAL_PCT);
236 SetMaxPower(power, uint32(value));
239 void Player::ApplyFeralAPBonus(int32 amount, bool apply)
241 m_baseFeralAP+= apply ? amount:-amount;
242 UpdateAttackPowerAndDamage();
245 void Player::UpdateAttackPowerAndDamage(bool ranged )
247 float val2 = 0.0f;
248 float level = float(getLevel());
250 UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
252 uint16 index = UNIT_FIELD_ATTACK_POWER;
253 uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
254 uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
256 if(ranged)
258 index = UNIT_FIELD_RANGED_ATTACK_POWER;
259 index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
260 index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
262 switch(getClass())
264 case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; break;
265 case CLASS_ROGUE: val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
266 case CLASS_WARRIOR:val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
267 case CLASS_DRUID:
268 switch(m_form)
270 case FORM_CAT:
271 case FORM_BEAR:
272 case FORM_DIREBEAR:
273 val2 = 0.0f; break;
274 default:
275 val2 = GetStat(STAT_AGILITY) - 10.0f; break;
277 break;
278 default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
281 else
283 switch(getClass())
285 case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
286 case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
287 case CLASS_DEATH_KNIGHT: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
288 case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
289 case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
290 case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
291 case CLASS_DRUID:
293 //Check if Predatory Strikes is skilled
294 float mLevelMult = 0.0;
295 switch(m_form)
297 case FORM_CAT:
298 case FORM_BEAR:
299 case FORM_DIREBEAR:
300 case FORM_MOONKIN:
302 Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
303 for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
305 // Predatory Strikes
306 if ((*itr)->GetSpellProto()->SpellIconID == 1563)
308 mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f;
309 break;
312 break;
316 switch(m_form)
318 case FORM_CAT:
319 val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f + m_baseFeralAP; break;
320 case FORM_BEAR:
321 case FORM_DIREBEAR:
322 val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break;
323 case FORM_MOONKIN:
324 val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f + m_baseFeralAP; break;
325 default:
326 val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
328 break;
330 case CLASS_MAGE: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
331 case CLASS_PRIEST: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
332 case CLASS_WARLOCK: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
336 SetModifierValue(unitMod, BASE_VALUE, val2);
338 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
339 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
341 //add dynamic flat mods
342 if( ranged )
344 if ((getClassMask() & CLASSMASK_WAND_USERS)==0)
346 AuraList const& mRAPbyStat = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
347 for(AuraList::const_iterator i = mRAPbyStat.begin();i != mRAPbyStat.end(); ++i)
348 attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
351 else
353 AuraList const& mAPbyStat = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT);
354 for(AuraList::const_iterator i = mAPbyStat.begin();i != mAPbyStat.end(); ++i)
355 attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
358 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
360 SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
361 SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
362 SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
364 //automatically update weapon damage after attack power modification
365 if(ranged)
367 UpdateDamagePhysical(RANGED_ATTACK);
369 Pet *pet = GetPet(); //update pet's AP
370 if(pet)
371 pet->UpdateAttackPowerAndDamage();
373 else
375 UpdateDamagePhysical(BASE_ATTACK);
376 if(CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
377 UpdateDamagePhysical(OFF_ATTACK);
381 void Player::UpdateShieldBlockValue()
383 SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
386 void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
388 UnitMods unitMod;
389 UnitMods attPower;
391 switch(attType)
393 case BASE_ATTACK:
394 default:
395 unitMod = UNIT_MOD_DAMAGE_MAINHAND;
396 attPower = UNIT_MOD_ATTACK_POWER;
397 break;
398 case OFF_ATTACK:
399 unitMod = UNIT_MOD_DAMAGE_OFFHAND;
400 attPower = UNIT_MOD_ATTACK_POWER;
401 break;
402 case RANGED_ATTACK:
403 unitMod = UNIT_MOD_DAMAGE_RANGED;
404 attPower = UNIT_MOD_ATTACK_POWER_RANGED;
405 break;
408 float att_speed = GetAPMultiplier(attType,normalized);
410 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
411 float base_pct = GetModifierValue(unitMod, BASE_PCT);
412 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
413 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
415 float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
416 float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
418 if (IsInFeralForm()) //check if player is druid and in cat or bear forms
420 uint32 lvl = getLevel();
421 if ( lvl > 60 ) lvl = 60;
423 weapon_mindamage = lvl*0.85*att_speed;
424 weapon_maxdamage = lvl*1.25*att_speed;
426 else if(!IsUseEquipedWeapon(attType==BASE_ATTACK)) //check if player not in form but still can't use weapon (broken/etc)
428 weapon_mindamage = BASE_MINDAMAGE;
429 weapon_maxdamage = BASE_MAXDAMAGE;
431 else if(attType == RANGED_ATTACK) //add ammo DPS to ranged damage
433 weapon_mindamage += GetAmmoDPS() * att_speed;
434 weapon_maxdamage += GetAmmoDPS() * att_speed;
437 min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
438 max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
441 void Player::UpdateDamagePhysical(WeaponAttackType attType)
443 float mindamage;
444 float maxdamage;
446 CalculateMinMaxDamage(attType,false,mindamage,maxdamage);
448 switch(attType)
450 case BASE_ATTACK:
451 default:
452 SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
453 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
454 break;
455 case OFF_ATTACK:
456 SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
457 SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
458 break;
459 case RANGED_ATTACK:
460 SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
461 SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
462 break;
466 void Player::UpdateDefenseBonusesMod()
468 UpdateBlockPercentage();
469 UpdateParryPercentage();
470 UpdateDodgePercentage();
473 void Player::UpdateBlockPercentage()
475 // No block
476 float value = 0.0f;
477 if(CanBlock())
479 // Base value
480 value = 5.0f;
481 // Modify value from defense skill
482 value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
483 // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
484 value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
485 // Increase from rating
486 value += GetRatingBonusValue(CR_BLOCK);
487 value = value < 0.0f ? 0.0f : value;
489 SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
492 void Player::UpdateCritPercentage(WeaponAttackType attType)
494 BaseModGroup modGroup;
495 uint16 index;
496 CombatRating cr;
498 switch(attType)
500 case OFF_ATTACK:
501 modGroup = OFFHAND_CRIT_PERCENTAGE;
502 index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
503 cr = CR_CRIT_MELEE;
504 break;
505 case RANGED_ATTACK:
506 modGroup = RANGED_CRIT_PERCENTAGE;
507 index = PLAYER_RANGED_CRIT_PERCENTAGE;
508 cr = CR_CRIT_RANGED;
509 break;
510 case BASE_ATTACK:
511 default:
512 modGroup = CRIT_PERCENTAGE;
513 index = PLAYER_CRIT_PERCENTAGE;
514 cr = CR_CRIT_MELEE;
515 break;
518 float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
519 // Modify crit from weapon skill and maximized defense skill of same level victim difference
520 value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
521 value = value < 0.0f ? 0.0f : value;
522 SetStatFloatValue(index, value);
525 void Player::UpdateAllCritPercentages()
527 float value = GetMeleeCritFromAgility();
529 SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
530 SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
531 SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
533 UpdateCritPercentage(BASE_ATTACK);
534 UpdateCritPercentage(OFF_ATTACK);
535 UpdateCritPercentage(RANGED_ATTACK);
538 void Player::UpdateParryPercentage()
540 // No parry
541 float value = 0.0f;
542 if (CanParry())
544 // Base parry
545 value = 5.0f;
546 // Modify value from defense skill
547 value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
548 // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
549 value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
550 // Parry from rating
551 value += GetRatingBonusValue(CR_PARRY);
552 value = value < 0.0f ? 0.0f : value;
554 SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
557 void Player::UpdateDodgePercentage()
559 // Dodge from agility
560 float value = GetDodgeFromAgility();
561 // Modify value from defense skill
562 value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
563 // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
564 value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
565 // Dodge from rating
566 value += GetRatingBonusValue(CR_DODGE);
567 value = value < 0.0f ? 0.0f : value;
568 SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
571 void Player::UpdateSpellCritChance(uint32 school)
573 // For normal school set zero crit chance
574 if(school == SPELL_SCHOOL_NORMAL)
576 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
577 return;
579 // For others recalculate it from:
580 float crit = 0.0f;
581 // Crit from Intellect
582 crit += GetSpellCritFromIntellect();
583 // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
584 crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
585 // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
586 crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
587 // Increase crit from spell crit ratings
588 crit += GetRatingBonusValue(CR_CRIT_SPELL);
590 // Store crit value
591 SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
594 void Player::UpdateMeleeHitChances()
596 m_modMeleeHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
597 m_modMeleeHitChance+= GetRatingBonusValue(CR_HIT_MELEE);
600 void Player::UpdateRangedHitChances()
602 m_modRangedHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
603 m_modRangedHitChance+= GetRatingBonusValue(CR_HIT_RANGED);
606 void Player::UpdateSpellHitChances()
608 m_modSpellHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
609 m_modSpellHitChance+= GetRatingBonusValue(CR_HIT_SPELL);
612 void Player::UpdateAllSpellCritChances()
614 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
615 UpdateSpellCritChance(i);
618 void Player::UpdateExpertise(WeaponAttackType attack)
620 if(attack==RANGED_ATTACK)
621 return;
623 int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
625 Item *weapon = GetWeaponForAttack(attack);
627 AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
628 for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
630 // item neutral spell
631 if((*itr)->GetSpellProto()->EquippedItemClass == -1)
632 expertise += (*itr)->GetModifier()->m_amount;
633 // item dependent spell
634 else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
635 expertise += (*itr)->GetModifier()->m_amount;
638 if(expertise < 0)
639 expertise = 0;
641 switch(attack)
643 case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
644 case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
645 default: break;
649 void Player::ApplyManaRegenBonus(int32 amount, bool apply)
651 m_baseManaRegen+= apply ? amount : -amount;
652 UpdateManaRegen();
655 void Player::UpdateManaRegen()
657 float Intellect = GetStat(STAT_INTELLECT);
658 // Mana regen from spirit and intellect
659 float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit();
660 // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
661 power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
663 // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
664 float power_regen_mp5 = (GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) + m_baseManaRegen) / 5.0f;
666 // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
667 AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
668 for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
670 Modifier* mod = (*i)->GetModifier();
671 power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
674 // Bonus from some dummy auras
675 AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY);
676 for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
677 if((*i)->GetId() == 34074) // Aspect of the Viper
679 power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f;
680 // Add regen bonus from level in this dummy
681 power_regen_mp5 += getLevel() * 35 / 100;
684 // Set regen rate in cast state apply only on spirit based regen
685 int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
686 if (modManaRegenInterrupt > 100)
687 modManaRegenInterrupt = 100;
688 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
690 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, power_regen_mp5 + power_regen);
693 void Player::_ApplyAllStatBonuses()
695 SetCanModifyStats(false);
697 _ApplyAllAuraMods();
698 _ApplyAllItemMods();
700 SetCanModifyStats(true);
702 UpdateAllStats();
705 void Player::_RemoveAllStatBonuses()
707 SetCanModifyStats(false);
709 _RemoveAllItemMods();
710 _RemoveAllAuraMods();
712 SetCanModifyStats(true);
714 UpdateAllStats();
717 /*#######################################
718 ######## ########
719 ######## MOBS STAT SYSTEM ########
720 ######## ########
721 #######################################*/
723 bool Creature::UpdateStats(Stats /*stat*/)
725 return true;
728 bool Creature::UpdateAllStats()
730 UpdateMaxHealth();
731 UpdateAttackPowerAndDamage();
733 for(int i = POWER_MANA; i < MAX_POWERS; ++i)
734 UpdateMaxPower(Powers(i));
736 for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
737 UpdateResistances(i);
739 return true;
742 void Creature::UpdateResistances(uint32 school)
744 if(school > SPELL_SCHOOL_NORMAL)
746 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
747 SetResistance(SpellSchools(school), int32(value));
749 else
750 UpdateArmor();
753 void Creature::UpdateArmor()
755 float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
756 SetArmor(int32(value));
759 void Creature::UpdateMaxHealth()
761 float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
762 SetMaxHealth((uint32)value);
765 void Creature::UpdateMaxPower(Powers power)
767 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
769 float value = GetTotalAuraModValue(unitMod);
770 SetMaxPower(power, uint32(value));
773 void Creature::UpdateAttackPowerAndDamage(bool ranged)
775 UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
777 uint16 index = UNIT_FIELD_ATTACK_POWER;
778 uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
779 uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
781 if(ranged)
783 index = UNIT_FIELD_RANGED_ATTACK_POWER;
784 index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
785 index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
788 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
789 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
790 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
792 SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
793 SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
794 SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
796 if(ranged)
797 return;
798 //automatically update weapon damage after attack power modification
799 UpdateDamagePhysical(BASE_ATTACK);
802 void Creature::UpdateDamagePhysical(WeaponAttackType attType)
804 if(attType > BASE_ATTACK)
805 return;
807 UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
809 float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
811 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
812 float base_pct = GetModifierValue(unitMod, BASE_PCT);
813 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
814 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
816 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
817 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
819 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct ;
820 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct ;
822 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
823 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
826 /*#######################################
827 ######## ########
828 ######## PETS STAT SYSTEM ########
829 ######## ########
830 #######################################*/
832 bool Pet::UpdateStats(Stats stat)
834 if(stat > STAT_SPIRIT)
835 return false;
837 // value = ((base_value * base_pct) + total_value) * total_pct
838 float value = GetTotalStatValue(stat);
840 Unit *owner = GetOwner();
841 if ( stat == STAT_STAMINA )
843 if(owner)
844 value += float(owner->GetStat(stat)) * 0.3f;
846 //warlock's and mage's pets gain 30% of owner's intellect
847 else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
849 if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
850 value += float(owner->GetStat(stat)) * 0.3f;
853 SetStat(stat, int32(value));
855 switch(stat)
857 case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
858 case STAT_AGILITY: UpdateArmor(); break;
859 case STAT_STAMINA: UpdateMaxHealth(); break;
860 case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
861 case STAT_SPIRIT:
862 default:
863 break;
866 return true;
869 bool Pet::UpdateAllStats()
871 for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
872 UpdateStats(Stats(i));
874 for(int i = POWER_MANA; i < MAX_POWERS; i++)
875 UpdateMaxPower(Powers(i));
877 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
878 UpdateResistances(i);
880 return true;
883 void Pet::UpdateResistances(uint32 school)
885 if(school > SPELL_SCHOOL_NORMAL)
887 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
889 Unit *owner = GetOwner();
890 // hunter and warlock pets gain 40% of owner's resistance
891 if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
892 value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
894 SetResistance(SpellSchools(school), int32(value));
896 else
897 UpdateArmor();
900 void Pet::UpdateArmor()
902 float value = 0.0f;
903 float bonus_armor = 0.0f;
904 UnitMods unitMod = UNIT_MOD_ARMOR;
906 Unit *owner = GetOwner();
907 // hunter and warlock pets gain 35% of owner's armor value
908 if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
909 bonus_armor = 0.35f * float(owner->GetArmor());
911 value = GetModifierValue(unitMod, BASE_VALUE);
912 value *= GetModifierValue(unitMod, BASE_PCT);
913 value += GetStat(STAT_AGILITY) * 2.0f;
914 value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
915 value *= GetModifierValue(unitMod, TOTAL_PCT);
917 SetArmor(int32(value));
920 void Pet::UpdateMaxHealth()
922 UnitMods unitMod = UNIT_MOD_HEALTH;
923 float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
925 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
926 value *= GetModifierValue(unitMod, BASE_PCT);
927 value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
928 value *= GetModifierValue(unitMod, TOTAL_PCT);
930 SetMaxHealth((uint32)value);
933 void Pet::UpdateMaxPower(Powers power)
935 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
937 float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
939 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
940 value *= GetModifierValue(unitMod, BASE_PCT);
941 value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
942 value *= GetModifierValue(unitMod, TOTAL_PCT);
944 SetMaxPower(power, uint32(value));
947 void Pet::UpdateAttackPowerAndDamage(bool ranged)
949 if(ranged)
950 return;
952 float val = 0.0f;
953 float bonusAP = 0.0f;
954 UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
956 if(GetEntry() == 416) // imp's attack power
957 val = GetStat(STAT_STRENGTH) - 10.0f;
958 else
959 val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
961 Unit* owner = GetOwner();
962 if( owner && owner->GetTypeId()==TYPEID_PLAYER)
964 if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power
966 bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
967 SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1287f));
969 //demons benefit from warlocks shadow or fire damage
970 else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
972 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);
973 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);
974 int32 maximum = (fire > shadow) ? fire : shadow;
975 if(maximum < 0)
976 maximum = 0;
977 SetBonusDamage( int32(maximum * 0.15f));
978 bonusAP = maximum * 0.57f;
980 //water elementals benefit from mage's frost damage
981 else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
983 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);
984 if(frost < 0)
985 frost = 0;
986 SetBonusDamage( int32(frost * 0.4f));
990 SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
992 //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
993 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
994 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
995 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
997 //UNIT_FIELD_(RANGED)_ATTACK_POWER field
998 SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower);
999 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
1000 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)attPowerMod);
1001 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
1002 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
1004 //automatically update weapon damage after attack power modification
1005 UpdateDamagePhysical(BASE_ATTACK);
1008 void Pet::UpdateDamagePhysical(WeaponAttackType attType)
1010 if(attType > BASE_ATTACK)
1011 return;
1013 UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
1015 float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
1017 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
1018 float base_pct = GetModifierValue(unitMod, BASE_PCT);
1019 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
1020 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
1022 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
1023 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
1025 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
1026 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
1028 // Pet's base damage changes depending on happiness
1029 if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
1031 switch(GetHappinessState())
1033 case HAPPY:
1034 // 125% of normal damage
1035 mindamage = mindamage * 1.25;
1036 maxdamage = maxdamage * 1.25;
1037 break;
1038 case CONTENT:
1039 // 100% of normal damage, nothing to modify
1040 break;
1041 case UNHAPPY:
1042 // 75% of normal damage
1043 mindamage = mindamage * 0.75;
1044 maxdamage = maxdamage * 0.75;
1045 break;
1049 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
1050 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);