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

Revision 37, 32.7 kB (checked in by yumileroy, 17 years ago)

[svn] * svn:eol-style native set on all files that need it

Original author: Neo2003
Date: 2008-10-11 14:16:25-05:00

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