[7986] MaNGOS 0.13 release.
[getmangos.git] / src / game / StatSystem.cpp
blobb22ccd03fd245b6bd110d60bd93ec35ee6cdb741
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 (effect 0)
306 if ((*itr)->GetEffIndex()==0 && (*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 // Set regen rate in cast state apply only on spirit based regen
675 int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
676 if (modManaRegenInterrupt > 100)
677 modManaRegenInterrupt = 100;
678 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
680 SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, power_regen_mp5 + power_regen);
683 void Player::_ApplyAllStatBonuses()
685 SetCanModifyStats(false);
687 _ApplyAllAuraMods();
688 _ApplyAllItemMods();
690 SetCanModifyStats(true);
692 UpdateAllStats();
695 void Player::_RemoveAllStatBonuses()
697 SetCanModifyStats(false);
699 _RemoveAllItemMods();
700 _RemoveAllAuraMods();
702 SetCanModifyStats(true);
704 UpdateAllStats();
707 /*#######################################
708 ######## ########
709 ######## MOBS STAT SYSTEM ########
710 ######## ########
711 #######################################*/
713 bool Creature::UpdateStats(Stats /*stat*/)
715 return true;
718 bool Creature::UpdateAllStats()
720 UpdateMaxHealth();
721 UpdateAttackPowerAndDamage();
723 for(int i = POWER_MANA; i < MAX_POWERS; ++i)
724 UpdateMaxPower(Powers(i));
726 for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
727 UpdateResistances(i);
729 return true;
732 void Creature::UpdateResistances(uint32 school)
734 if(school > SPELL_SCHOOL_NORMAL)
736 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
737 SetResistance(SpellSchools(school), int32(value));
739 else
740 UpdateArmor();
743 void Creature::UpdateArmor()
745 float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
746 SetArmor(int32(value));
749 void Creature::UpdateMaxHealth()
751 float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
752 SetMaxHealth((uint32)value);
755 void Creature::UpdateMaxPower(Powers power)
757 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
759 float value = GetTotalAuraModValue(unitMod);
760 SetMaxPower(power, uint32(value));
763 void Creature::UpdateAttackPowerAndDamage(bool ranged)
765 UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
767 uint16 index = UNIT_FIELD_ATTACK_POWER;
768 uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
769 uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
771 if(ranged)
773 index = UNIT_FIELD_RANGED_ATTACK_POWER;
774 index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
775 index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
778 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
779 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
780 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
782 SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
783 SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
784 SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
786 if(ranged)
787 return;
788 //automatically update weapon damage after attack power modification
789 UpdateDamagePhysical(BASE_ATTACK);
792 void Creature::UpdateDamagePhysical(WeaponAttackType attType)
794 if(attType > BASE_ATTACK)
795 return;
797 UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
799 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType);
800 float base_pct = GetModifierValue(unitMod, BASE_PCT);
801 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
802 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
803 float dmg_multiplier = GetCreatureInfo()->dmg_multiplier;
805 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
806 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
808 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct * dmg_multiplier;
809 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct * dmg_multiplier;
811 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
812 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
815 /*#######################################
816 ######## ########
817 ######## PETS STAT SYSTEM ########
818 ######## ########
819 #######################################*/
821 bool Pet::UpdateStats(Stats stat)
823 if(stat > STAT_SPIRIT)
824 return false;
826 // value = ((base_value * base_pct) + total_value) * total_pct
827 float value = GetTotalStatValue(stat);
829 Unit *owner = GetOwner();
830 if ( stat == STAT_STAMINA )
832 if(owner)
833 value += float(owner->GetStat(stat)) * 0.3f;
835 //warlock's and mage's pets gain 30% of owner's intellect
836 else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
838 if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
839 value += float(owner->GetStat(stat)) * 0.3f;
842 SetStat(stat, int32(value));
844 switch(stat)
846 case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
847 case STAT_AGILITY: UpdateArmor(); break;
848 case STAT_STAMINA: UpdateMaxHealth(); break;
849 case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
850 case STAT_SPIRIT:
851 default:
852 break;
855 return true;
858 bool Pet::UpdateAllStats()
860 for (int i = STAT_STRENGTH; i < MAX_STATS; ++i)
861 UpdateStats(Stats(i));
863 for(int i = POWER_MANA; i < MAX_POWERS; ++i)
864 UpdateMaxPower(Powers(i));
866 for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
867 UpdateResistances(i);
869 return true;
872 void Pet::UpdateResistances(uint32 school)
874 if(school > SPELL_SCHOOL_NORMAL)
876 float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
878 Unit *owner = GetOwner();
879 // hunter and warlock pets gain 40% of owner's resistance
880 if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
881 value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
883 SetResistance(SpellSchools(school), int32(value));
885 else
886 UpdateArmor();
889 void Pet::UpdateArmor()
891 float value = 0.0f;
892 float bonus_armor = 0.0f;
893 UnitMods unitMod = UNIT_MOD_ARMOR;
895 Unit *owner = GetOwner();
896 // hunter and warlock pets gain 35% of owner's armor value
897 if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
898 bonus_armor = 0.35f * float(owner->GetArmor());
900 value = GetModifierValue(unitMod, BASE_VALUE);
901 value *= GetModifierValue(unitMod, BASE_PCT);
902 value += GetStat(STAT_AGILITY) * 2.0f;
903 value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
904 value *= GetModifierValue(unitMod, TOTAL_PCT);
906 SetArmor(int32(value));
909 void Pet::UpdateMaxHealth()
911 UnitMods unitMod = UNIT_MOD_HEALTH;
912 float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
914 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
915 value *= GetModifierValue(unitMod, BASE_PCT);
916 value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
917 value *= GetModifierValue(unitMod, TOTAL_PCT);
919 SetMaxHealth((uint32)value);
922 void Pet::UpdateMaxPower(Powers power)
924 UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
926 float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
928 float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
929 value *= GetModifierValue(unitMod, BASE_PCT);
930 value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
931 value *= GetModifierValue(unitMod, TOTAL_PCT);
933 SetMaxPower(power, uint32(value));
936 void Pet::UpdateAttackPowerAndDamage(bool ranged)
938 if(ranged)
939 return;
941 float val = 0.0f;
942 float bonusAP = 0.0f;
943 UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
945 if(GetEntry() == 416) // imp's attack power
946 val = GetStat(STAT_STRENGTH) - 10.0f;
947 else
948 val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
950 Unit* owner = GetOwner();
951 if( owner && owner->GetTypeId()==TYPEID_PLAYER)
953 if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power
955 bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
956 SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.1287f));
958 //demons benefit from warlocks shadow or fire damage
959 else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
961 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);
962 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);
963 int32 maximum = (fire > shadow) ? fire : shadow;
964 if(maximum < 0)
965 maximum = 0;
966 SetBonusDamage( int32(maximum * 0.15f));
967 bonusAP = maximum * 0.57f;
969 //water elementals benefit from mage's frost damage
970 else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
972 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);
973 if(frost < 0)
974 frost = 0;
975 SetBonusDamage( int32(frost * 0.4f));
979 SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
981 //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
982 float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
983 float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
984 float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
986 //UNIT_FIELD_(RANGED)_ATTACK_POWER field
987 SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower);
988 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
989 SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)attPowerMod);
990 //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
991 SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
993 //automatically update weapon damage after attack power modification
994 UpdateDamagePhysical(BASE_ATTACK);
997 void Pet::UpdateDamagePhysical(WeaponAttackType attType)
999 if(attType > BASE_ATTACK)
1000 return;
1002 UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
1004 float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
1006 float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
1007 float base_pct = GetModifierValue(unitMod, BASE_PCT);
1008 float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
1009 float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
1011 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
1012 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
1014 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
1015 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
1017 // Pet's base damage changes depending on happiness
1018 if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
1020 switch(GetHappinessState())
1022 case HAPPY:
1023 // 125% of normal damage
1024 mindamage = mindamage * 1.25;
1025 maxdamage = maxdamage * 1.25;
1026 break;
1027 case CONTENT:
1028 // 100% of normal damage, nothing to modify
1029 break;
1030 case UNHAPPY:
1031 // 75% of normal damage
1032 mindamage = mindamage * 0.75;
1033 maxdamage = maxdamage * 0.75;
1034 break;
1038 SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
1039 SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);