root/trunk/src/game/StatSystem.cpp @ 157

Revision 136, 33.6 kB (checked in by yumileroy, 17 years ago)

[svn] Provide creature dual wield support.
Update glancing damage formula.
Do not daze creatures when other creatures attack from the back (need to find a better way).
Fix the damage calculation of +damage aura.

Original author: megamage
Date: 2008-10-29 20:00:21-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "Unit.h"
22#include "Player.h"
23#include "Pet.h"
24#include "Creature.h"
25#include "SharedDefines.h"
26#include "SpellAuras.h"
27
28/*#######################################
29########                         ########
30########   PLAYERS STAT SYSTEM   ########
31########                         ########
32#######################################*/
33
34bool Player::UpdateStats(Stats stat)
35{
36    if(stat > STAT_SPIRIT)
37        return false;
38
39    // value = ((base_value * base_pct) + total_value) * total_pct
40    float value  = GetTotalStatValue(stat);
41
42    SetStat(stat, int32(value));
43
44    if(stat == STAT_STAMINA || stat == STAT_INTELLECT)
45    {
46        Pet *pet = GetPet();
47        if(pet)
48            pet->UpdateStats(stat);
49    }
50
51    switch(stat)
52    {
53        case STAT_STRENGTH:
54            UpdateAttackPowerAndDamage();
55            UpdateShieldBlockValue();
56            break;
57        case STAT_AGILITY:
58            UpdateArmor();
59            UpdateAttackPowerAndDamage(true);
60            if(getClass() == CLASS_ROGUE || getClass() == CLASS_HUNTER || getClass() == CLASS_DRUID && m_form==FORM_CAT)
61                UpdateAttackPowerAndDamage();
62
63            UpdateAllCritPercentages();
64            UpdateDodgePercentage();
65            break;
66
67        case STAT_STAMINA:   UpdateMaxHealth(); break;
68        case STAT_INTELLECT:
69            UpdateMaxPower(POWER_MANA);
70            UpdateAllSpellCritChances();
71            UpdateAttackPowerAndDamage(true);               //SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, only intelect currently
72            UpdateArmor();                                  //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
73            break;
74
75        case STAT_SPIRIT:
76            break;
77
78        default:
79            break;
80    }
81    UpdateSpellDamageAndHealingBonus();
82    UpdateManaRegen();
83    return true;
84}
85
86void Player::UpdateSpellDamageAndHealingBonus()
87{
88    // Magic damage modifiers implemented in Unit::SpellDamageBonus
89    // This information for client side use only
90    // Get healing bonus for all schools
91    SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL));
92    // Get damage bonus for all schools
93    for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
94        SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i)));
95}
96
97bool Player::UpdateAllStats()
98{
99    for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
100    {
101        float value = GetTotalStatValue(Stats(i));
102        SetStat(Stats(i), (int32)value);
103    }
104
105    UpdateAttackPowerAndDamage();
106    UpdateAttackPowerAndDamage(true);
107    UpdateArmor();
108    UpdateMaxHealth();
109
110    for(int i = POWER_MANA; i < MAX_POWERS; i++)
111        UpdateMaxPower(Powers(i));
112
113    UpdateAllCritPercentages();
114    UpdateAllSpellCritChances();
115    UpdateDefenseBonusesMod();
116    UpdateShieldBlockValue();
117    UpdateSpellDamageAndHealingBonus();
118    UpdateManaRegen();
119    UpdateExpertise(BASE_ATTACK);
120    UpdateExpertise(OFF_ATTACK);
121    for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
122        UpdateResistances(i);
123
124    return true;
125}
126
127void Player::UpdateResistances(uint32 school)
128{
129    if(school > SPELL_SCHOOL_NORMAL)
130    {
131        float value  = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
132        SetResistance(SpellSchools(school), int32(value));
133
134        Pet *pet = GetPet();
135        if(pet)
136            pet->UpdateResistances(school);
137    }
138    else
139        UpdateArmor();
140}
141
142void Player::UpdateArmor()
143{
144    float value = 0.0f;
145    UnitMods unitMod = UNIT_MOD_ARMOR;
146
147    value  = GetModifierValue(unitMod, BASE_VALUE);         // base armor (from items)
148    value *= GetModifierValue(unitMod, BASE_PCT);           // armor percent from items
149    value += GetStat(STAT_AGILITY) * 2.0f;                  // armor bonus from stats
150    value += GetModifierValue(unitMod, TOTAL_VALUE);
151
152    //add dynamic flat mods
153    AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
154    for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
155    {
156        Modifier* mod = (*i)->GetModifier();
157        if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
158            value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
159    }
160
161    value *= GetModifierValue(unitMod, TOTAL_PCT);
162
163    SetArmor(int32(value));
164
165    Pet *pet = GetPet();
166    if(pet)
167        pet->UpdateArmor();
168}
169
170float Player::GetHealthBonusFromStamina()
171{
172    float stamina = GetStat(STAT_STAMINA);
173
174    float baseStam = stamina < 20 ? stamina : 20;
175    float moreStam = stamina - baseStam;
176
177    return baseStam + (moreStam*10.0f);
178}
179
180float Player::GetManaBonusFromIntellect()
181{
182    float intellect = GetStat(STAT_INTELLECT);
183
184    float baseInt = intellect < 20 ? intellect : 20;
185    float moreInt = intellect - baseInt;
186
187    return baseInt + (moreInt*15.0f);
188}
189
190void Player::UpdateMaxHealth()
191{
192    UnitMods unitMod = UNIT_MOD_HEALTH;
193
194    float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
195    value *= GetModifierValue(unitMod, BASE_PCT);
196    value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
197    value *= GetModifierValue(unitMod, TOTAL_PCT);
198
199    SetMaxHealth((uint32)value);
200}
201
202void Player::UpdateMaxPower(Powers power)
203{
204    UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
205
206    float bonusPower = (power == POWER_MANA) ? GetManaBonusFromIntellect() : 0;
207
208    float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
209    value *= GetModifierValue(unitMod, BASE_PCT);
210    value += GetModifierValue(unitMod, TOTAL_VALUE) +  bonusPower;
211    value *= GetModifierValue(unitMod, TOTAL_PCT);
212
213    SetMaxPower(power, uint32(value));
214}
215
216void Player::UpdateAttackPowerAndDamage(bool ranged )
217{
218    float val2 = 0.0f;
219    float level = float(getLevel());
220
221    UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
222
223    uint16 index = UNIT_FIELD_ATTACK_POWER;
224    uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
225    uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
226
227    if(ranged)
228    {
229        index = UNIT_FIELD_RANGED_ATTACK_POWER;
230        index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
231        index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
232
233        switch(getClass())
234        {
235            case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f;    break;
236            case CLASS_ROGUE:  val2 = level        + GetStat(STAT_AGILITY) - 10.0f;    break;
237            case CLASS_WARRIOR:val2 = level        + GetStat(STAT_AGILITY) - 10.0f;    break;
238            case CLASS_DRUID:
239                switch(m_form)
240                {
241                    case FORM_CAT:
242                    case FORM_BEAR:
243                    case FORM_DIREBEAR:
244                        val2 = 0.0f; break;
245                    default:
246                        val2 = GetStat(STAT_AGILITY) - 10.0f; break;
247                }
248                break;
249            default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
250        }
251    }
252    else
253    {
254        switch(getClass())
255        {
256            case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f                    - 20.0f; break;
257            case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f                    - 20.0f; break;
258            case CLASS_ROGUE:   val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
259            case CLASS_HUNTER:  val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
260            case CLASS_SHAMAN:  val2 = level*2.0f + GetStat(STAT_STRENGTH)*2.0f                    - 20.0f; break;
261            case CLASS_DRUID:
262            {
263                //Check if Predatory Strikes is skilled
264                float mLevelMult = 0.0;
265                switch(m_form)
266                {
267                    case FORM_CAT:
268                    case FORM_BEAR:
269                    case FORM_DIREBEAR:
270                    case FORM_MOONKIN:
271                    {
272                        Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
273                        for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
274                        {
275                            // Predatory Strikes
276                            if ((*itr)->GetSpellProto()->SpellIconID == 1563)
277                            {
278                                mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f;
279                                break;
280                            }
281                        }
282                        break;
283                    }
284                }
285
286                switch(m_form)
287                {
288                    case FORM_CAT:
289                        val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f; break;
290                    case FORM_BEAR:
291                    case FORM_DIREBEAR:
292                        val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
293                    case FORM_MOONKIN:
294                        val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
295                    default:
296                        val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
297                }
298                break;
299            }
300            case CLASS_MAGE:    val2 =              GetStat(STAT_STRENGTH)                         - 10.0f; break;
301            case CLASS_PRIEST:  val2 =              GetStat(STAT_STRENGTH)                         - 10.0f; break;
302            case CLASS_WARLOCK: val2 =              GetStat(STAT_STRENGTH)                         - 10.0f; break;
303        }
304    }
305
306    SetModifierValue(unitMod, BASE_VALUE, val2);
307
308    float base_attPower  = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
309    float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
310
311    //add dynamic flat mods
312    if( ranged && (getClassMask() & CLASSMASK_WAND_USERS)==0)
313    {
314        AuraList const& mRAPbyIntellect = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
315        for(AuraList::const_iterator i = mRAPbyIntellect.begin();i != mRAPbyIntellect.end(); ++i)
316            attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
317    }
318
319    float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
320
321    SetUInt32Value(index, (uint32)base_attPower);           //UNIT_FIELD_(RANGED)_ATTACK_POWER field
322    SetUInt32Value(index_mod, (uint32)attPowerMod);         //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
323    SetFloatValue(index_mult, attPowerMultiplier);          //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
324
325    //automatically update weapon damage after attack power modification
326    if(ranged)
327    {
328        UpdateDamagePhysical(RANGED_ATTACK);
329
330        Pet *pet = GetPet();                                //update pet's AP
331        if(pet)
332            pet->UpdateAttackPowerAndDamage();
333    }
334    else
335    {
336        UpdateDamagePhysical(BASE_ATTACK);
337        if(CanDualWield() && haveOffhandWeapon())           //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
338            UpdateDamagePhysical(OFF_ATTACK);
339    }
340}
341
342void Player::UpdateShieldBlockValue()
343{
344    SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
345}
346
347void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
348{
349    UnitMods unitMod;
350    UnitMods attPower;
351
352    switch(attType)
353    {
354        case BASE_ATTACK:
355        default:
356            unitMod = UNIT_MOD_DAMAGE_MAINHAND;
357            attPower = UNIT_MOD_ATTACK_POWER;
358            break;
359        case OFF_ATTACK:
360            unitMod = UNIT_MOD_DAMAGE_OFFHAND;
361            attPower = UNIT_MOD_ATTACK_POWER;
362            break;
363        case RANGED_ATTACK:
364            unitMod = UNIT_MOD_DAMAGE_RANGED;
365            attPower = UNIT_MOD_ATTACK_POWER_RANGED;
366            break;
367    }
368
369    float att_speed = GetAPMultiplier(attType,normalized);
370
371    float base_value  = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
372    float base_pct    = GetModifierValue(unitMod, BASE_PCT);
373    float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
374    float total_pct   = GetModifierValue(unitMod, TOTAL_PCT);
375
376    float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
377    float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
378
379    if (IsInFeralForm())                                    //check if player is druid and in cat or bear forms
380    {
381        uint32 lvl = getLevel();
382        if ( lvl > 60 ) lvl = 60;
383
384        weapon_mindamage = lvl*0.85*att_speed;
385        weapon_maxdamage = lvl*1.25*att_speed;
386    }
387    else if(!IsUseEquipedWeapon(attType==BASE_ATTACK))      //check if player not in form but still can't use weapon (broken/etc)
388    {
389        weapon_mindamage = BASE_MINDAMAGE;
390        weapon_maxdamage = BASE_MAXDAMAGE;
391    }
392    else if(attType == RANGED_ATTACK)                       //add ammo DPS to ranged damage
393    {
394        weapon_mindamage += GetAmmoDPS() * att_speed;
395        weapon_maxdamage += GetAmmoDPS() * att_speed;
396    }
397
398    min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
399    max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
400}
401
402void Player::UpdateDamagePhysical(WeaponAttackType attType)
403{
404    float mindamage;
405    float maxdamage;
406
407    CalculateMinMaxDamage(attType,false,mindamage,maxdamage);
408
409    switch(attType)
410    {
411        case BASE_ATTACK:
412        default:
413            SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
414            SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
415            break;
416        case OFF_ATTACK:
417            SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
418            SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
419            break;
420        case RANGED_ATTACK:
421            SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
422            SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
423            break;
424    }
425}
426
427void Player::UpdateDefenseBonusesMod()
428{
429    UpdateBlockPercentage();
430    UpdateParryPercentage();
431    UpdateDodgePercentage();
432}
433
434void Player::UpdateBlockPercentage()
435{
436    // No block
437    float value = 0.0f;
438    if(CanBlock())
439    {
440        // Base value
441        value = 5.0f;
442        // Modify value from defense skill
443        value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
444        // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
445        value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
446        // Increase from rating
447        value += GetRatingBonusValue(CR_BLOCK);
448        value = value < 0.0f ? 0.0f : value;
449    }
450    SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
451}
452
453void Player::UpdateCritPercentage(WeaponAttackType attType)
454{
455    BaseModGroup modGroup;
456    uint16 index;
457    CombatRating cr;
458
459    switch(attType)
460    {
461        case OFF_ATTACK:
462            modGroup = OFFHAND_CRIT_PERCENTAGE;
463            index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
464            cr = CR_CRIT_MELEE;
465            break;
466        case RANGED_ATTACK:
467            modGroup = RANGED_CRIT_PERCENTAGE;
468            index = PLAYER_RANGED_CRIT_PERCENTAGE;
469            cr = CR_CRIT_RANGED;
470            break;
471        case BASE_ATTACK:
472        default:
473            modGroup = CRIT_PERCENTAGE;
474            index = PLAYER_CRIT_PERCENTAGE;
475            cr = CR_CRIT_MELEE;
476            break;
477    }
478
479    float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
480    // Modify crit from weapon skill and maximized defense skill of same level victim difference
481    value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
482    value = value < 0.0f ? 0.0f : value;
483    SetStatFloatValue(index, value);
484}
485
486void Player::UpdateAllCritPercentages()
487{
488    float value = GetMeleeCritFromAgility();
489
490    SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
491    SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
492    SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
493
494    UpdateCritPercentage(BASE_ATTACK);
495    UpdateCritPercentage(OFF_ATTACK);
496    UpdateCritPercentage(RANGED_ATTACK);
497}
498
499void Player::UpdateParryPercentage()
500{
501    // No parry
502    float value = 0.0f;
503    if (CanParry())
504    {
505        // Base parry
506        value  = 5.0f;
507        // Modify value from defense skill
508        value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
509        // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
510        value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
511        // Parry from rating
512        value += GetRatingBonusValue(CR_PARRY);
513        value = value < 0.0f ? 0.0f : value;
514    }
515    SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
516}
517
518void Player::UpdateDodgePercentage()
519{
520    // Dodge from agility
521    float value = GetDodgeFromAgility();
522    // Modify value from defense skill
523    value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
524    // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
525    value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
526    // Dodge from rating
527    value += GetRatingBonusValue(CR_DODGE);
528    value = value < 0.0f ? 0.0f : value;
529    SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
530}
531
532void Player::UpdateSpellCritChance(uint32 school)
533{
534    // For normal school set zero crit chance
535    if(school == SPELL_SCHOOL_NORMAL)
536    {
537        SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
538        return;
539    }
540    // For others recalculate it from:
541    float crit = 0.0f;
542    // Crit from Intellect
543    crit += GetSpellCritFromIntellect();
544    // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
545    crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
546    // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
547    crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
548    // Increase crit from spell crit ratings
549    crit += GetRatingBonusValue(CR_CRIT_SPELL);
550
551    // Store crit value
552    SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
553}
554
555void Player::UpdateAllSpellCritChances()
556{
557    for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
558        UpdateSpellCritChance(i);
559}
560
561void Player::UpdateExpertise(WeaponAttackType attack)
562{
563    if(attack==RANGED_ATTACK)
564        return;
565
566    int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
567
568    Item *weapon = GetWeaponForAttack(attack);
569
570    AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
571    for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
572    {
573        // item neutral spell
574        if((*itr)->GetSpellProto()->EquippedItemClass == -1)
575            expertise += (*itr)->GetModifier()->m_amount;
576        // item dependent spell
577        else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
578            expertise += (*itr)->GetModifier()->m_amount;
579    }
580
581    if(expertise < 0)
582        expertise = 0;
583
584    switch(attack)
585    {
586        case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise);         break;
587        case OFF_ATTACK:  SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
588        default: break;
589    }
590}
591
592void Player::UpdateManaRegen()
593{
594    float Intellect = GetStat(STAT_INTELLECT);
595    // Mana regen from spirit and intellect
596    float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit();
597    // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
598    power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
599
600    // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
601    float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f;
602
603    // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
604    AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
605    for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
606    {
607        Modifier* mod = (*i)->GetModifier();
608        power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
609    }
610
611    // Bonus from some dummy auras
612    AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY);
613    for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
614        if((*i)->GetId() == 34074)                          // Aspect of the Viper
615        {
616            power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f;
617            // Add regen bonus from level in this dummy
618            power_regen_mp5 += getLevel() * 35 / 100;
619        }
620
621    // Set regen rate in cast state apply only on spirit based regen
622    int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
623    if (modManaRegenInterrupt > 100)
624        modManaRegenInterrupt = 100;
625    SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
626
627    SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN, power_regen_mp5 + power_regen);
628}
629
630void Player::_ApplyAllStatBonuses()
631{
632    SetCanModifyStats(false);
633
634    _ApplyAllAuraMods();
635    _ApplyAllItemMods();
636
637    SetCanModifyStats(true);
638
639    UpdateAllStats();
640}
641
642void Player::_RemoveAllStatBonuses()
643{
644    SetCanModifyStats(false);
645
646    _RemoveAllItemMods();
647    _RemoveAllAuraMods();
648
649    SetCanModifyStats(true);
650
651    UpdateAllStats();
652}
653
654/*#######################################
655########                         ########
656########    MOBS STAT SYSTEM     ########
657########                         ########
658#######################################*/
659
660bool Creature::UpdateStats(Stats /*stat*/)
661{
662    return true;
663}
664
665bool Creature::UpdateAllStats()
666{
667    UpdateMaxHealth();
668    UpdateAttackPowerAndDamage();
669    UpdateAttackPowerAndDamage(true);
670
671    for(int i = POWER_MANA; i < MAX_POWERS; ++i)
672        UpdateMaxPower(Powers(i));
673
674    for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
675        UpdateResistances(i);
676
677    return true;
678}
679
680void Creature::UpdateResistances(uint32 school)
681{
682    if(school > SPELL_SCHOOL_NORMAL)
683    {
684        float value  = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
685        SetResistance(SpellSchools(school), int32(value));
686    }
687    else
688        UpdateArmor();
689}
690
691void Creature::UpdateArmor()
692{
693    float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
694    SetArmor(int32(value));
695}
696
697void Creature::UpdateMaxHealth()
698{
699    float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
700    SetMaxHealth((uint32)value);
701}
702
703void Creature::UpdateMaxPower(Powers power)
704{
705    UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
706
707    float value  = GetTotalAuraModValue(unitMod);
708    SetMaxPower(power, uint32(value));
709}
710
711void Creature::UpdateAttackPowerAndDamage(bool ranged)
712{
713    //automatically update weapon damage after attack power modification
714    if(ranged)
715        UpdateDamagePhysical(RANGED_ATTACK);
716    else
717    {
718        UpdateDamagePhysical(BASE_ATTACK);
719        UpdateDamagePhysical(OFF_ATTACK);
720    }
721}
722
723void Creature::UpdateDamagePhysical(WeaponAttackType attType)
724{
725    UnitMods unitMod;
726    switch(attType)
727    {
728        case BASE_ATTACK:
729        default:
730            unitMod = UNIT_MOD_DAMAGE_MAINHAND;
731            break;
732        case OFF_ATTACK:
733            unitMod = UNIT_MOD_DAMAGE_OFFHAND;
734            break;
735        case RANGED_ATTACK:
736            unitMod = UNIT_MOD_DAMAGE_RANGED;
737            break;
738    }
739
740    float att_speed = float(GetAttackTime(attType))/1000.0f;
741
742    float base_value  = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
743    float base_pct    = GetModifierValue(unitMod, BASE_PCT);
744    float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
745    float total_pct   = GetModifierValue(unitMod, TOTAL_PCT);
746
747    float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
748    float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
749
750    float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct ;
751    float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct ;
752
753    switch(attType)
754    {
755        case BASE_ATTACK:
756        default:
757            SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
758            SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
759            break;
760        case OFF_ATTACK:
761            SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
762            SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
763            break;
764        case RANGED_ATTACK:
765            SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
766            SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
767            break;
768    }
769}
770
771/*#######################################
772########                         ########
773########    PETS STAT SYSTEM     ########
774########                         ########
775#######################################*/
776
777bool Pet::UpdateStats(Stats stat)
778{
779    if(stat > STAT_SPIRIT)
780        return false;
781
782    // value = ((base_value * base_pct) + total_value) * total_pct
783    float value  = GetTotalStatValue(stat);
784
785    Unit *owner = GetOwner();
786    if ( stat == STAT_STAMINA )
787    {
788        if(owner)
789            value += float(owner->GetStat(stat)) * 0.3f;
790    }
791                                                            //warlock's and mage's pets gain 30% of owner's intellect
792    else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
793    {
794        if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
795            value += float(owner->GetStat(stat)) * 0.3f;
796    }
797
798    SetStat(stat, int32(value));
799
800    switch(stat)
801    {
802        case STAT_STRENGTH:         UpdateAttackPowerAndDamage();        break;
803        case STAT_AGILITY:          UpdateArmor();                       break;
804        case STAT_STAMINA:          UpdateMaxHealth();                   break;
805        case STAT_INTELLECT:        UpdateMaxPower(POWER_MANA);          break;
806        case STAT_SPIRIT:
807        default:
808            break;
809    }
810
811    return true;
812}
813
814bool Pet::UpdateAllStats()
815{
816    for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
817        UpdateStats(Stats(i));
818
819    for(int i = POWER_MANA; i < MAX_POWERS; i++)
820        UpdateMaxPower(Powers(i));
821
822    for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
823        UpdateResistances(i);
824
825    return true;
826}
827
828void Pet::UpdateResistances(uint32 school)
829{
830    if(school > SPELL_SCHOOL_NORMAL)
831    {
832        float value  = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
833
834        Unit *owner = GetOwner();
835        // hunter and warlock pets gain 40% of owner's resistance
836        if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
837            value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
838
839        SetResistance(SpellSchools(school), int32(value));
840    }
841    else
842        UpdateArmor();
843}
844
845void Pet::UpdateArmor()
846{
847    float value = 0.0f;
848    float bonus_armor = 0.0f;
849    UnitMods unitMod = UNIT_MOD_ARMOR;
850
851    Unit *owner = GetOwner();
852    // hunter and warlock pets gain 35% of owner's armor value
853    if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
854        bonus_armor = 0.35f * float(owner->GetArmor());
855
856    value  = GetModifierValue(unitMod, BASE_VALUE);
857    value *= GetModifierValue(unitMod, BASE_PCT);
858    value += GetStat(STAT_AGILITY) * 2.0f;
859    value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
860    value *= GetModifierValue(unitMod, TOTAL_PCT);
861
862    SetArmor(int32(value));
863}
864
865void Pet::UpdateMaxHealth()
866{
867    UnitMods unitMod = UNIT_MOD_HEALTH;
868    float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
869
870    float value   = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
871    value  *= GetModifierValue(unitMod, BASE_PCT);
872    value  += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
873    value  *= GetModifierValue(unitMod, TOTAL_PCT);
874
875    SetMaxHealth((uint32)value);
876}
877
878void Pet::UpdateMaxPower(Powers power)
879{
880    UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
881    float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
882
883    float value  = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
884    value *= GetModifierValue(unitMod, BASE_PCT);
885    value += GetModifierValue(unitMod, TOTAL_VALUE) +  addValue * 15.0f;
886    value *= GetModifierValue(unitMod, TOTAL_PCT);
887
888    SetMaxPower(power, uint32(value));
889}
890
891void Pet::UpdateAttackPowerAndDamage(bool ranged)
892{
893    if(ranged)
894        return;
895
896    float val = 0.0f;
897    float bonusAP = 0.0f;
898    UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
899
900    if(GetEntry() == 416)                                   // imp's attack power
901        val = GetStat(STAT_STRENGTH) - 10.0f;
902    else
903        val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
904
905    Unit* owner = GetOwner();
906    if( owner && owner->GetTypeId()==TYPEID_PLAYER)
907    {
908        if(getPetType() == HUNTER_PET)                      //hunter pets benefit from owner's attack power
909        {
910            bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
911            SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.125f));
912        }
913        //demons benefit from warlocks shadow or fire damage
914        else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
915        {
916            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);
917            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);
918            int32 maximum  = (fire > shadow) ? fire : shadow;
919            if(maximum < 0)
920                maximum = 0;
921            SetBonusDamage( int32(maximum * 0.15f));
922            bonusAP = maximum * 0.57f;
923        }
924        //water elementals benefit from mage's frost damage
925        else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
926        {
927            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);
928            if(frost < 0)
929                frost = 0;
930            SetBonusDamage( int32(frost * 0.4f));
931        }
932    }
933
934    SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
935
936    //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
937    float base_attPower  = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
938    float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
939    float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
940
941    //UNIT_FIELD_(RANGED)_ATTACK_POWER field
942    SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower);
943    //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
944    SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod);
945    //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
946    SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
947
948    //automatically update weapon damage after attack power modification
949    UpdateDamagePhysical(BASE_ATTACK);
950}
951
952void Pet::UpdateDamagePhysical(WeaponAttackType attType)
953{
954    if(attType > BASE_ATTACK)
955        return;
956
957    UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
958
959    float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
960
961    float base_value  = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
962    float base_pct    = GetModifierValue(unitMod, BASE_PCT);
963    float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
964    float total_pct   = GetModifierValue(unitMod, TOTAL_PCT);
965
966    float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
967    float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
968
969    float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
970    float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
971
972    //  Pet's base damage changes depending on happiness
973    if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
974    {
975        switch(GetHappinessState())
976        {
977            case HAPPY:
978                // 125% of normal damage
979                mindamage = mindamage * 1.25;
980                maxdamage = maxdamage * 1.25;
981                break;
982            case CONTENT:
983                // 100% of normal damage, nothing to modify
984                break;
985            case UNHAPPY:
986                // 75% of normal damage
987                mindamage = mindamage * 0.75;
988                maxdamage = maxdamage * 0.75;
989                break;
990        }
991    }
992
993    SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
994    SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
995}
Note: See TracBrowser for help on using the browser.