TrinityCore
Loading...
Searching...
No Matches
StatSystem.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Unit.h"
19#include "Creature.h"
20#include "Item.h"
21#include "Pet.h"
22#include "Player.h"
23#include "SharedDefines.h"
24#include "SpellAuras.h"
25#include "SpellAuraEffects.h"
26#include "SpellMgr.h"
27#include "World.h"
28#include <numeric>
29
30inline bool _ModifyUInt32(bool apply, uint32& baseValue, int32& amount)
31{
32 // If amount is negative, change sign and value of apply.
33 if (amount < 0)
34 {
35 apply = !apply;
36 amount = -amount;
37 }
38 if (apply)
39 baseValue += amount;
40 else
41 {
42 // Make sure we do not get uint32 overflow.
43 if (amount > int32(baseValue))
44 amount = baseValue;
45 baseValue -= amount;
46 }
47 return apply;
48}
49
50/*#######################################
51######## ########
52######## UNIT STAT SYSTEM ########
53######## ########
54#######################################*/
55
61
63{
64 float totalMin = 0.f;
65 float totalMax = 0.f;
66
67 float tmpMin, tmpMax;
68 for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
69 {
70 CalculateMinMaxDamage(attType, false, true, tmpMin, tmpMax, i);
71 totalMin += tmpMin;
72 totalMax += tmpMax;
73 }
74
75 switch (attType)
76 {
77 case BASE_ATTACK:
78 default:
81 break;
82 case OFF_ATTACK:
85 break;
86 case RANGED_ATTACK:
89 break;
90 }
91}
92
93/*#######################################
94######## ########
95######## PLAYERS STAT SYSTEM ########
96######## ########
97#######################################*/
98
100{
101 if (stat > STAT_SPIRIT)
102 return false;
103
104 // value = ((base_value * base_pct) + total_value) * total_pct
105 float value = GetTotalStatValue(stat);
106
107 SetStat(stat, int32(value));
108
109 if (stat == STAT_STAMINA || stat == STAT_INTELLECT || stat == STAT_STRENGTH)
110 {
111 Pet* pet = GetPet();
112 if (pet)
113 pet->UpdateStats(stat);
114 }
115
116 switch (stat)
117 {
118 case STAT_STRENGTH:
120 break;
121 case STAT_AGILITY:
122 UpdateArmor();
125 break;
126 case STAT_STAMINA:
128 break;
129 case STAT_INTELLECT:
132 UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
133 break;
134 case STAT_SPIRIT:
135 break;
136 default:
137 break;
138 }
139
140 if (stat == STAT_STRENGTH)
141 {
145 }
146 else if (stat == STAT_AGILITY)
147 {
150 }
151 else
152 {
153 // Need update (exist AP from stat auras)
158 }
159
162
163 // Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat
164 uint32 mask = 0;
166 for (AuraEffectList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i)
167 if (Stats((*i)->GetMiscValueB()) == stat)
168 mask |= (*i)->GetMiscValue();
169 if (mask)
170 {
171 for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
172 if (mask & (1 << rating))
173 ApplyRatingMod(CombatRating(rating), 0, true);
174 }
175 return true;
176}
177
178void Player::ApplySpellPowerBonus(int32 amount, bool apply)
179{
180 apply = _ModifyUInt32(apply, m_baseSpellPower, amount);
181
182 // For speed just update for client
184 for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
186}
187
189{
190 // Magic damage modifiers implemented in Unit::SpellDamageBonusDone
191 // This information for client side use only
192 // Get healing bonus for all schools
194 // Get damage bonus for all schools
196 for (uint16 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
197 {
198 SetInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, std::accumulate(modDamageAuras.begin(), modDamageAuras.end(), 0, [i](int32 negativeMod, AuraEffect const* aurEff)
199 {
200 if (aurEff->GetAmount() < 0 && aurEff->GetMiscValue() & (1 << i))
201 negativeMod += aurEff->GetAmount();
202 return negativeMod;
203 }));
205 }
206}
207
241
243{
245 m_spellPenetrationItemMod += apply ? amount : -amount;
246}
247
249{
250 if (school > SPELL_SCHOOL_NORMAL)
251 {
253 SetResistance(SpellSchools(school), int32(value));
254
255 Pet* pet = GetPet();
256 if (pet)
257 pet->UpdateResistances(school);
258 }
259 else
260 UpdateArmor();
261}
262
264{
265 UnitMods unitMod = UNIT_MOD_ARMOR;
266
267 float value = GetFlatModifierValue(unitMod, BASE_VALUE); // base armor (from items)
268 value *= GetPctModifierValue(unitMod, BASE_PCT); // armor percent from items
269 value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats
270 value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
271
272 //add dynamic flat mods
274 for (AuraEffectList::const_iterator i = mResbyIntellect.begin(); i != mResbyIntellect.end(); ++i)
275 {
276 if ((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)
277 value += CalculatePct(GetStat(Stats((*i)->GetMiscValueB())), (*i)->GetAmount());
278 }
279
280 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
281
282 SetArmor(int32(value));
283
284 Pet* pet = GetPet();
285 if (pet)
286 pet->UpdateArmor();
287}
288
290{
291 float stamina = GetStat(STAT_STAMINA);
292 float baseStam = std::min(20.0f, stamina);
293 float moreStam = stamina - baseStam;
294
295 return baseStam + (moreStam * 10.0f);
296}
297
299{
300 float intellect = GetStat(STAT_INTELLECT);
301
302 float baseInt = std::min(20.0f, intellect);
303 float moreInt = intellect - baseInt;
304
305 return baseInt + (moreInt * 15.0f);
306}
307
309{
310 UnitMods unitMod = UNIT_MOD_HEALTH;
311
312 float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
313 value *= GetPctModifierValue(unitMod, BASE_PCT);
315 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
316
317 SetMaxHealth((uint32)value);
318}
319
321{
323
324 float bonusPower = (power == POWER_MANA && GetCreatePowerValue(power) > 0) ? GetManaBonusFromIntellect() : 0;
325
326 float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
327 value *= GetPctModifierValue(unitMod, BASE_PCT);
328 value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
329 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
330
331 SetMaxPower(power, uint32(std::lroundf(value)));
332}
333
334void Player::ApplyFeralAPBonus(int32 amount, bool apply)
335{
336 _ModifyUInt32(apply, m_baseFeralAP, amount);
338}
339
341{
342 float val2 = 0.0f;
343 float level = float(GetLevel());
344
346
347 if (ranged)
348 {
349 switch (GetClass())
350 {
351 case CLASS_HUNTER:
352 val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f;
353 break;
354 case CLASS_ROGUE:
355 val2 = level + GetStat(STAT_AGILITY) - 10.0f;
356 break;
357 case CLASS_WARRIOR:
358 val2 = level + GetStat(STAT_AGILITY) - 10.0f;
359 break;
360 case CLASS_DRUID:
361 switch (GetShapeshiftForm())
362 {
363 case FORM_CAT:
364 case FORM_BEAR:
365 case FORM_DIREBEAR:
366 val2 = 0.0f; break;
367 default:
368 val2 = GetStat(STAT_AGILITY) - 10.0f; break;
369 }
370 break;
371 default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
372 }
373 }
374 else
375 {
376 switch (GetClass())
377 {
378 case CLASS_WARRIOR:
379 val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f;
380 break;
381 case CLASS_PALADIN:
382 val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f;
383 break;
385 val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f;
386 break;
387 case CLASS_ROGUE:
388 val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f;
389 break;
390 case CLASS_HUNTER:
391 val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f;
392 break;
393 case CLASS_SHAMAN:
394 val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f;
395 break;
396 case CLASS_DRUID:
397 {
398 // Check if Predatory Strikes is skilled
399 float levelBonus = 0.0f;
400 float weaponBonus = 0.0f;
401 if (IsInFeralForm())
402 {
404 levelBonus = CalculatePct(1.0f, levelMod->GetAmount());
405
406 // = 0 if removing the weapon, do not calculate bonus (uses template)
407 if (m_baseFeralAP)
408 {
409 if (Item const* weapon = m_items[EQUIPMENT_SLOT_MAINHAND])
410 {
412 {
413 ItemTemplate const* itemTemplate = weapon->GetTemplate();
414 int32 bonusAP = itemTemplate->GetTotalAPBonus() + m_baseFeralAP;
415 weaponBonus = CalculatePct(static_cast<float>(bonusAP), weaponMod->GetAmount());
416 }
417 }
418 }
419 }
420
421 switch (GetShapeshiftForm())
422 {
423 case FORM_CAT:
424 val2 = GetLevel() * levelBonus + GetStat(STAT_STRENGTH) * 2.0f + GetStat(STAT_AGILITY) - 20.0f + weaponBonus + m_baseFeralAP;
425 break;
426 case FORM_BEAR:
427 case FORM_DIREBEAR:
428 val2 = GetLevel() * levelBonus + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + weaponBonus + m_baseFeralAP;
429 break;
430 case FORM_MOONKIN:
431 val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f + m_baseFeralAP;
432 break;
433 default:
434 val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f;
435 break;
436 }
437 break;
438 }
439 case CLASS_MAGE:
440 val2 = GetStat(STAT_STRENGTH) - 10.0f;
441 break;
442 case CLASS_PRIEST:
443 val2 = GetStat(STAT_STRENGTH) - 10.0f;
444 break;
445 case CLASS_WARLOCK:
446 val2 = GetStat(STAT_STRENGTH) - 10.0f;
447 break;
448 }
449 }
450
451 SetStatFlatModifier(unitMod, BASE_VALUE, val2);
452
453 float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
454 float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE);
455
456 //add dynamic flat mods
457 if (ranged)
458 {
459 if ((GetClassMask() & CLASSMASK_WAND_USERS) == 0)
460 {
462 for (AuraEffect const* aurEff : mRAPbyStat)
463 attPowerMod += CalculatePct(GetStat(Stats(aurEff->GetMiscValue())), aurEff->GetAmount());
464 }
465 }
466 else
467 {
469 for (AuraEffect const* aurEff : mAPbyStat)
470 attPowerMod += CalculatePct(GetStat(Stats(aurEff->GetMiscValue())), aurEff->GetAmount());
471 }
472
473 // applies to both, amount updated in PeriodicTick each 30 seconds
475
476 float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
477
478 if (ranged)
479 {
480 SetRangedAttackPower(int32(base_attPower));
481 if (attPowerMod >= 0)
482 SetRangedAttackPowerModPos(int32(attPowerMod));
483 if (attPowerMod <= 0)
484 SetRangedAttackPowerModNeg(int32(attPowerMod));
485 SetRangedAttackPowerMultiplier(attPowerMultiplier);
486 }
487 else
488 {
489 SetAttackPower(int32(base_attPower));
490 if (attPowerMod >= 0)
491 SetAttackPowerModPos(int32(attPowerMod));
492 if (attPowerMod <= 0)
493 SetAttackPowerModNeg(int32(attPowerMod));
494 SetAttackPowerMultiplier(attPowerMultiplier);
495 }
496
497 Pet* pet = GetPet(); //update pet's AP
498 Guardian* guardian = GetGuardianPet();
499 //automatically update weapon damage after attack power modification
500 if (ranged)
501 {
503 if (pet && pet->IsHunterPet()) // At ranged attack change for hunter pet
505 }
506 else
507 {
509 if (CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
511 if (GetClass() == CLASS_SHAMAN || GetClass() == CLASS_PALADIN) // mental quickness
513
514 if (pet && (pet->IsPetGhoul() || pet->IsRisenAlly())) // At melee attack power change for DK pet
516
517 if (guardian && guardian->IsSpiritWolf()) // At melee attack power change for Shaman feral spirit
518 guardian->UpdateAttackPowerAndDamage();
519 }
520}
521
526
527void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) const
528{
529 // Only proto damage, not affected by any mods
530 if (damageIndex != 0)
531 {
532 minDamage = 0.0f;
533 maxDamage = 0.0f;
534
535 if (!IsInFeralForm() && CanUseAttackType(attType))
536 {
537 minDamage = GetWeaponDamageRange(attType, MINDAMAGE, damageIndex);
538 maxDamage = GetWeaponDamageRange(attType, MAXDAMAGE, damageIndex);
539 }
540 return;
541 }
542
543 UnitMods unitMod;
544
545 switch (attType)
546 {
547 case BASE_ATTACK:
548 default:
549 unitMod = UNIT_MOD_DAMAGE_MAINHAND;
550 break;
551 case OFF_ATTACK:
552 unitMod = UNIT_MOD_DAMAGE_OFFHAND;
553 break;
554 case RANGED_ATTACK:
555 unitMod = UNIT_MOD_DAMAGE_RANGED;
556 break;
557 }
558
559 float const attackPowerMod = std::max(GetAPMultiplier(attType, normalized), 0.25f);
560
561 float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE);
562 baseValue += GetTotalAttackPowerValue(attType) / 14.0f * attackPowerMod;
563
564 float basePct = GetPctModifierValue(unitMod, BASE_PCT);
565 float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
566 float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f;
567
568 float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
569 float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
570
571 // check if player is druid and in cat or bear forms
572 if (IsInFeralForm())
573 {
574 uint8 lvl = GetLevel();
575 if (lvl > 60)
576 lvl = 60;
577
578 weaponMinDamage = lvl * 0.85f * attackPowerMod;
579 weaponMaxDamage = lvl * 1.25f * attackPowerMod;
580 }
581 else if (!CanUseAttackType(attType)) // check if player not in form but still can't use (disarm case)
582 {
583 // cannot use ranged/off attack, set values to 0
584 if (attType != BASE_ATTACK)
585 {
586 minDamage = 0.f;
587 maxDamage = 0.f;
588 return;
589 }
590
591 weaponMinDamage = BASE_MINDAMAGE;
592 weaponMaxDamage = BASE_MAXDAMAGE;
593 }
594 else if (attType == RANGED_ATTACK) // add ammo DPS to ranged primary damage
595 {
596 weaponMinDamage += GetAmmoDPS() * attackPowerMod;
597 weaponMaxDamage += GetAmmoDPS() * attackPowerMod;
598 }
599
600 minDamage = ((weaponMinDamage + baseValue) * basePct + totalValue) * totalPct;
601 maxDamage = ((weaponMaxDamage + baseValue) * basePct + totalValue) * totalPct;
602}
603
610
612{
613 // No block
614 float value = 0.0f;
615 if (CanBlock())
616 {
617 // Base value
618 value = 5.0f;
619 // Modify value from defense skill
620 value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
621 // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
623 // Increase from rating
625
626 if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
627 value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) : value;
628
629 value = value < 0.0f ? 0.0f : value;
630 }
632}
633
635{
636 BaseModGroup modGroup;
637 uint16 index;
638 CombatRating cr;
639
640 switch (attType)
641 {
642 case OFF_ATTACK:
643 modGroup = OFFHAND_CRIT_PERCENTAGE;
645 cr = CR_CRIT_MELEE;
646 break;
647 case RANGED_ATTACK:
648 modGroup = RANGED_CRIT_PERCENTAGE;
650 cr = CR_CRIT_RANGED;
651 break;
652 case BASE_ATTACK:
653 default:
654 modGroup = CRIT_PERCENTAGE;
656 cr = CR_CRIT_MELEE;
657 break;
658 }
659
660 // flat = bonus from crit auras, pct = bonus from agility, combat rating = mods from items
661 float value = GetBaseModValue(modGroup, FLAT_MOD) + GetBaseModValue(modGroup, PCT_MOD) + GetRatingBonusValue(cr);
662
663 // Modify crit from weapon skill and maximized defense skill of same level victim difference
664 value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
665
666 if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
667 value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) : value;
668
669 value = std::max(0.0f, value);
670 SetStatFloatValue(index, value);
671}
672
685
687{
688 0.9560f, // Warrior
689 0.9560f, // Paladin
690 0.9880f, // Hunter
691 0.9880f, // Rogue
692 0.9830f, // Priest
693 0.9560f, // DK
694 0.9880f, // Shaman
695 0.9830f, // Mage
696 0.9830f, // Warlock
697 0.0f, // ??
698 0.9720f // Druid
699};
700
701// helper function
702float CalculateDiminishingReturns(float const (&capArray)[MAX_CLASSES], uint8 playerClass, float nonDiminishValue, float diminishValue)
703{
704 // 1 1 k cx
705 // --- = --- + --- <=> x' = --------
706 // x' c x x + ck
707
708 // where:
709 // k is m_diminishing_k for that class
710 // c is capArray for that class
711 // x is chance before DR (diminishValue)
712 // x' is chance after DR (our result)
713
714 uint32 const classIdx = playerClass - 1;
715
716 float const k = m_diminishing_k[classIdx];
717 float const c = capArray[classIdx];
718
719 float result = c * diminishValue / (diminishValue + c * k);
720 result += nonDiminishValue;
721 return result;
722}
723
724float const miss_cap[MAX_CLASSES] =
725{
726 16.00f, // Warrior //correct
727 16.00f, // Paladin //correct
728 16.00f, // Hunter //?
729 16.00f, // Rogue //?
730 16.00f, // Priest //?
731 16.00f, // DK //correct
732 16.00f, // Shaman //?
733 16.00f, // Mage //?
734 16.00f, // Warlock //?
735 0.0f, // ??
736 16.00f // Druid //?
737};
738
740{
741 float diminishing = 0.0f, nondiminishing = 0.0f;
742 // Modify value from defense skill (only bonus from defense rating diminishes)
743 nondiminishing += (int32(GetSkillValue(SKILL_DEFENSE)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
744 diminishing += (GetRatingBonusValue(CR_DEFENSE_SKILL) * 0.04f);
745
746 // apply diminishing formula to diminishing miss chance
747 return CalculateDiminishingReturns(miss_cap, GetClass(), nondiminishing, diminishing);
748}
749
750float const parry_cap[MAX_CLASSES] =
751{
752 47.003525f, // Warrior
753 47.003525f, // Paladin
754 145.560408f, // Hunter
755 145.560408f, // Rogue
756 0.0f, // Priest
757 47.003525f, // DK
758 145.560408f, // Shaman
759 0.0f, // Mage
760 0.0f, // Warlock
761 0.0f, // ??
762 0.0f // Druid
763};
764
766{
767 // No parry
768 float value = 0.0f;
769 uint32 pclass = GetClass() - 1;
770 if (CanParry() && parry_cap[pclass] > 0.0f)
771 {
772 float nondiminishing = 5.0f;
773 // Parry from rating
774 float diminishing = GetRatingBonusValue(CR_PARRY);
775 // Modify value from defense skill (only bonus from defense rating diminishes)
776 nondiminishing += (int32(GetSkillValue(SKILL_DEFENSE)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
777 diminishing += (GetRatingBonusValue(CR_DEFENSE_SKILL) * 0.04f);
778 // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
780
781 // apply diminishing formula to diminishing parry chance
782 value = CalculateDiminishingReturns(parry_cap, GetClass(), nondiminishing, diminishing);
783
784 if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
785 value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) : value;
786
787 value = value < 0.0f ? 0.0f : value;
788 }
790}
791
792float const dodge_cap[MAX_CLASSES] =
793{
794 88.129021f, // Warrior
795 88.129021f, // Paladin
796 145.560408f, // Hunter
797 145.560408f, // Rogue
798 150.375940f, // Priest
799 88.129021f, // DK
800 145.560408f, // Shaman
801 150.375940f, // Mage
802 150.375940f, // Warlock
803 0.0f, // ??
804 116.890707f // Druid
805};
806
808{
809 float diminishing = 0.0f, nondiminishing = 0.0f;
810 GetDodgeFromAgility(diminishing, nondiminishing);
811 // Modify value from defense skill (only bonus from defense rating diminishes)
812 nondiminishing += (int32(GetSkillValue(SKILL_DEFENSE)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
813 diminishing += (GetRatingBonusValue(CR_DEFENSE_SKILL) * 0.04f);
814 // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
816 // Dodge from rating
817 diminishing += GetRatingBonusValue(CR_DODGE);
818
819 // apply diminishing formula to diminishing dodge chance
820 float value = CalculateDiminishingReturns(dodge_cap, GetClass(), nondiminishing, diminishing);
821
822 if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
823 value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) : value;
824
825 value = value < 0.0f ? 0.0f : value;
827}
828
830{
831 // For normal school set zero crit chance
832 if (school == SPELL_SCHOOL_NORMAL)
833 {
835 return;
836 }
837 // For others recalculate it from:
838 float crit = 0.0f;
839 // Crit from Intellect
841 // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
843 // Increase crit from SPELL_AURA_MOD_CRIT_PCT
845 // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
847 // Increase crit from spell crit ratings
849
850 // Store crit value
852}
853
859
864
869
875
881
883{
884 if (attack == RANGED_ATTACK)
885 return;
886
888
889 Item const* weapon = GetWeaponForAttack(attack, true);
890 expertise += GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE, [weapon](AuraEffect const* aurEff) -> bool
891 {
892 return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon);
893 });
894
895 if (expertise < 0)
896 expertise = 0;
897
898 switch (attack)
899 {
900 case BASE_ATTACK:
902 break;
903 case OFF_ATTACK:
905 break;
906 default:
907 break;
908 }
909}
910
911void Player::ApplyManaRegenBonus(int32 amount, bool apply)
912{
913 _ModifyUInt32(apply, m_baseManaRegen, amount);
915}
916
917void Player::ApplyHealthRegenBonus(int32 amount, bool apply)
918{
919 _ModifyUInt32(apply, m_baseHealthRegen, amount);
920}
921
922static std::pair<float, Optional<Rates>> const powerRegenInfo[MAX_POWERS] =
923{
924 { 0.f, RATE_POWER_MANA }, // POWER_MANA
925 { -12.5f, RATE_POWER_RAGE_LOSS }, // POWER_RAGE, -1.25 rage per second
926 { 0.f, std::nullopt }, // POWER_FOCUS
927 { 10.f, RATE_POWER_ENERGY }, // POWER_ENERGY, +10 energy per second
928 { 0.f, std::nullopt }, // POWER_HAPPINESS
929 { 0.f, std::nullopt }, // POWER_RUNE
930 { -12.5f, RATE_POWER_RUNICPOWER_LOSS } // POWER_RUNIC_POWER, -1.25 runic power per second
931};
932
934{
935 if (power == POWER_HEALTH || power >= MAX_POWERS)
936 return;
937
938 float result_regen = 0.f; // Out-of-combat / without last mana use effect
939 float result_regen_interrupted = 0.f; // In combat / with last mana use effect
940 float modifier = 1.f; // Config rate or any other modifiers
941
944 {
947 return;
948 }
949
950 switch (power)
951 {
952 case POWER_MANA:
953 {
954 float Intellect = GetStat(STAT_INTELLECT);
955 // Mana regen from spirit and intellect
956 float power_regen = std::sqrt(Intellect) * OCTRegenMPPerSpirit();
957 // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
959
960 // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
962
963 // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
965 for (AuraEffectList::const_iterator i = regenAura.begin(); i != regenAura.end(); ++i)
966 power_regen_mp5 += GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 500.0f;
967
968 // Set regen rate in cast state apply only on spirit based regen
970 if (modManaRegenInterrupt > 100)
971 modManaRegenInterrupt = 100;
972
973 result_regen = power_regen_mp5 + power_regen;
974 result_regen_interrupted = power_regen_mp5 + CalculatePct(power_regen, modManaRegenInterrupt);
975
976 if (GetLevel() < 15)
977 modifier *= 2.066f - (GetLevel() * 0.066f);
978 break;
979 }
980 case POWER_RAGE:
981 case POWER_ENERGY:
983 {
984 result_regen = powerRegenInfo[AsUnderlyingType(power)].first;
985 result_regen_interrupted = 0.f;
986
988 result_regen_interrupted += static_cast<float>(GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, AsUnderlyingType(power))) / 5.f;
989
990 if (power != POWER_RUNIC_POWER) // Butchery requires combat
991 result_regen += result_regen_interrupted;
992 break;
993 }
994 default:
995 break;
996 }
997
998 if (powerRegenInfo[AsUnderlyingType(power)].second.has_value())
999 modifier *= sWorld->getRate(powerRegenInfo[AsUnderlyingType(power)].second.value()); // Config rate
1000
1001 result_regen *= modifier;
1002 result_regen_interrupted *= modifier;
1003
1004 // Unit fields contain an offset relative to the base power regeneration.
1005 if (power != POWER_MANA)
1006 result_regen -= powerRegenInfo[AsUnderlyingType(power)].first;
1007
1008 if (power == POWER_ENERGY)
1009 result_regen_interrupted = result_regen;
1010
1013}
1014
1016{
1017 if (power == POWER_HEALTH || power >= MAX_POWERS)
1018 return 0.f;
1019
1020 bool interrupted = HasAuraType(SPELL_AURA_INTERRUPT_REGEN) ||
1021 (power == POWER_MANA && IsUnderLastManaUseEffect()) ||
1022 (power != POWER_MANA && IsInCombat());
1023
1025 if (power != POWER_MANA)
1026 regen += (power == POWER_ENERGY || !interrupted) ? powerRegenInfo[AsUnderlyingType(power)].first : 0.f;
1027
1028 return regen;
1029}
1030
1032{
1033 if (rune >= NUM_RUNE_TYPES)
1034 return;
1035
1036 if (uint32 cooldown = GetRuneTypeBaseCooldown(rune))
1037 SetFloatValue(PLAYER_RUNE_REGEN_1 + uint8(rune), float(1 * IN_MILLISECONDS) / float(cooldown));
1038}
1039
1041{
1042 SetCanModifyStats(false);
1043
1046
1047 SetCanModifyStats(true);
1048
1050}
1051
1053{
1054 SetCanModifyStats(false);
1055
1058
1059 SetCanModifyStats(true);
1060
1062}
1063
1064/*#######################################
1065######## ########
1066######## MOBS STAT SYSTEM ########
1067######## ########
1068#######################################*/
1069
1071{
1072 return true;
1073}
1074
1076{
1080
1081 for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
1083
1085
1086 return true;
1087}
1088
1090{
1091 if (school > SPELL_SCHOOL_NORMAL)
1092 {
1094 SetResistance(SpellSchools(school), int32(value));
1095 }
1096 else
1097 UpdateArmor();
1098}
1099
1101{
1102 float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
1103 SetArmor(int32(value));
1104}
1105
1107{
1109 SetMaxHealth(uint32(value));
1110}
1111
1113{
1115
1116 float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
1117 value *= GetPctModifierValue(unitMod, BASE_PCT);
1118 value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
1119 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
1120
1121 SetMaxPower(power, uint32(std::lroundf(value)));
1122}
1123
1125{
1127
1128 float baseAttackPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
1129 float attackPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE);
1130 float attackPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
1131
1132 if (ranged)
1133 {
1134 SetRangedAttackPower(int32(baseAttackPower));
1135 if (attackPowerMod >= 0)
1136 SetRangedAttackPowerModPos(int32(attackPowerMod));
1137 if (attackPowerMod <= 0)
1138 SetRangedAttackPowerModNeg(int32(attackPowerMod));
1139 SetRangedAttackPowerMultiplier(attackPowerMultiplier);
1140 }
1141 else
1142 {
1143 SetAttackPower(int32(baseAttackPower));
1144 if (attackPowerMod >= 0)
1145 SetAttackPowerModPos(int32(attackPowerMod));
1146 if (attackPowerMod <= 0)
1147 SetAttackPowerModNeg(int32(attackPowerMod));
1148 SetAttackPowerMultiplier(attackPowerMultiplier);
1149 }
1150
1151 // automatically update weapon damage after attack power modification
1152 if (ranged)
1154 else
1155 {
1158 }
1159}
1160
1161void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex /*= 0*/) const
1162{
1163 // creatures only have one damage
1164 if (damageIndex != 0)
1165 {
1166 minDamage = 0.f;
1167 maxDamage = 0.f;
1168 return;
1169 }
1170
1171 float variance = 1.0f;
1172 UnitMods unitMod;
1173 switch (attType)
1174 {
1175 case BASE_ATTACK:
1176 default:
1177 variance = GetCreatureTemplate()->BaseVariance;
1178 unitMod = UNIT_MOD_DAMAGE_MAINHAND;
1179 break;
1180 case OFF_ATTACK:
1181 variance = GetCreatureTemplate()->BaseVariance;
1182 unitMod = UNIT_MOD_DAMAGE_OFFHAND;
1183 break;
1184 case RANGED_ATTACK:
1185 variance = GetCreatureTemplate()->RangeVariance;
1186 unitMod = UNIT_MOD_DAMAGE_RANGED;
1187 break;
1188 }
1189
1190 if (attType == OFF_ATTACK && !haveOffhandWeapon())
1191 {
1192 minDamage = 0.0f;
1193 maxDamage = 0.0f;
1194 return;
1195 }
1196
1197 float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
1198 float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
1199
1200 if (!CanUseAttackType(attType)) // disarm case
1201 {
1202 weaponMinDamage = 0.0f;
1203 weaponMaxDamage = 0.0f;
1204 }
1205
1206 float attackPower = GetTotalAttackPowerValue(attType);
1207 float attackSpeedMulti = GetAPMultiplier(attType, normalized);
1208 float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE) + (attackPower / 14.0f) * variance;
1209 float basePct = GetPctModifierValue(unitMod, BASE_PCT) * attackSpeedMulti;
1210 float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
1211 float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f;
1212 float dmgMultiplier = GetCreatureTemplate()->ModDamage; // = ModDamage * _GetDamageMod(rank);
1213
1214 minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
1215 maxDamage = ((weaponMaxDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
1216}
1217
1218/*#######################################
1219######## ########
1220######## PETS STAT SYSTEM ########
1221######## ########
1222#######################################*/
1223
1224#define ENTRY_IMP 416
1225#define ENTRY_VOIDWALKER 1860
1226#define ENTRY_SUCCUBUS 1863
1227#define ENTRY_FELHUNTER 417
1228#define ENTRY_FELGUARD 17252
1229#define ENTRY_WATER_ELEMENTAL 510
1230#define ENTRY_TREANT 1964
1231#define ENTRY_FIRE_ELEMENTAL 15438
1232#define ENTRY_GHOUL 26125
1233#define ENTRY_BLOODWORM 28017
1234
1236{
1237 if (stat >= MAX_STATS)
1238 return false;
1239
1240 // value = ((base_value * base_pct) + total_value) * total_pct
1241 float value = GetTotalStatValue(stat);
1242 //ApplyStatBuffMod(stat, m_statFromOwner[stat], false);
1243 float ownersBonus = 0.0f;
1244
1245 Unit* owner = GetOwner();
1246 // Handle Death Knight Glyphs and Talents
1247 float mod = 0.75f;
1248 if ((IsPetGhoul() || IsRisenAlly()) && (stat == STAT_STAMINA || stat == STAT_STRENGTH))
1249 {
1250 if (stat == STAT_STAMINA)
1251 mod = 0.3f; // Default Owner's Stamina scale
1252 else
1253 mod = 0.7f; // Default Owner's Strength scale
1254
1255 // Check just if owner has Ravenous Dead since it's effect is not an aura
1257 if (aurEff)
1258 {
1259 SpellInfo const* spellInfo = aurEff->GetSpellInfo(); // Then get the SpellProto and add the dummy effect value
1260 AddPct(mod, spellInfo->GetEffect(EFFECT_1).CalcValue()); // Ravenous Dead edits the original scale
1261 }
1262 // Glyph of the Ghoul
1263 aurEff = owner->GetAuraEffect(58686, 0);
1264 if (aurEff)
1265 mod += CalculatePct(1.0f, aurEff->GetAmount()); // Glyph of the Ghoul adds a flat value to the scale mod
1266 ownersBonus = float(owner->GetStat(stat)) * mod;
1267 value += ownersBonus;
1268 }
1269 else if (stat == STAT_STAMINA)
1270 {
1271 if (owner->GetClass() == CLASS_WARLOCK && IsPet())
1272 {
1273 ownersBonus = CalculatePct(owner->GetStat(STAT_STAMINA), 75);
1274 value += ownersBonus;
1275 }
1276 else
1277 {
1278 mod = 0.45f;
1279 if (IsPet())
1280 {
1281 PetSpellMap::const_iterator itr = (ToPet()->m_spells.find(62758)); // Wild Hunt rank 1
1282 if (itr == ToPet()->m_spells.end())
1283 itr = ToPet()->m_spells.find(62762); // Wild Hunt rank 2
1284
1285 if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
1286 {
1287 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
1288 AddPct(mod, spellInfo->GetEffect(EFFECT_0).CalcValue());
1289 }
1290 }
1291 ownersBonus = float(owner->GetStat(stat)) * mod;
1292 value += ownersBonus;
1293 }
1294 }
1295 //warlock's and mage's pets gain 30% of owner's intellect
1296 else if (stat == STAT_INTELLECT)
1297 {
1298 if (owner->GetClass() == CLASS_WARLOCK || owner->GetClass() == CLASS_MAGE)
1299 {
1300 ownersBonus = CalculatePct(owner->GetStat(stat), 30);
1301 value += ownersBonus;
1302 }
1303 }
1304/*
1305 else if (stat == STAT_STRENGTH)
1306 {
1307 if (IsPetGhoul())
1308 value += float(owner->GetStat(stat)) * 0.3f;
1309 }
1310*/
1311
1312 SetStat(stat, int32(value));
1313 m_statFromOwner[stat] = ownersBonus;
1314 UpdateStatBuffMod(stat);
1315
1316 switch (stat)
1317 {
1319 case STAT_AGILITY: UpdateArmor(); break;
1320 case STAT_STAMINA: UpdateMaxHealth(); break;
1322 case STAT_SPIRIT:
1323 default:
1324 break;
1325 }
1326
1327 return true;
1328}
1329
1331{
1333
1334 for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
1335 UpdateStats(Stats(i));
1336
1337 for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
1339
1341
1342 return true;
1343}
1344
1346{
1347 if (school > SPELL_SCHOOL_NORMAL)
1348 {
1350
1351 // hunter and warlock pets gain 40% of owner's resistance
1352 if (IsPet())
1353 value += float(CalculatePct(m_owner->GetResistance(SpellSchools(school)), 40));
1354
1355 SetResistance(SpellSchools(school), int32(value));
1356 }
1357 else
1358 UpdateArmor();
1359}
1360
1362{
1363 float value = 0.0f;
1364 float bonus_armor = 0.0f;
1365 UnitMods unitMod = UNIT_MOD_ARMOR;
1366
1367 // hunter and warlock pets gain 35% of owner's armor value
1368 if (IsPet())
1369 bonus_armor = float(CalculatePct(m_owner->GetArmor(), 35));
1370
1371 value = GetFlatModifierValue(unitMod, BASE_VALUE);
1372 value *= GetPctModifierValue(unitMod, BASE_PCT);
1373 value += GetStat(STAT_AGILITY) * 2.0f;
1374 value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
1375 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
1376
1377 SetArmor(int32(value));
1378}
1379
1381{
1382 UnitMods unitMod = UNIT_MOD_HEALTH;
1383 float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
1384
1385 float multiplicator;
1386 switch (GetEntry())
1387 {
1388 case ENTRY_IMP: multiplicator = 8.4f; break;
1389 case ENTRY_VOIDWALKER: multiplicator = 11.0f; break;
1390 case ENTRY_SUCCUBUS: multiplicator = 9.1f; break;
1391 case ENTRY_FELHUNTER: multiplicator = 9.5f; break;
1392 case ENTRY_FELGUARD: multiplicator = 11.0f; break;
1393 case ENTRY_BLOODWORM: multiplicator = 1.0f; break;
1394 default: multiplicator = 10.0f; break;
1395 }
1396
1397 float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
1398 value *= GetPctModifierValue(unitMod, BASE_PCT);
1399 value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator;
1400 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
1401
1402 SetMaxHealth((uint32)value);
1403}
1404
1406{
1408
1409 float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
1410 float multiplicator = 15.0f;
1411
1412 switch (GetEntry())
1413 {
1414 case ENTRY_IMP: multiplicator = 4.95f; break;
1415 case ENTRY_VOIDWALKER:
1416 case ENTRY_SUCCUBUS:
1417 case ENTRY_FELHUNTER:
1418 case ENTRY_FELGUARD: multiplicator = 11.5f; break;
1419 default: multiplicator = 15.0f; break;
1420 }
1421
1422 float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
1423 value *= GetPctModifierValue(unitMod, BASE_PCT);
1424 value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator;
1425 value *= GetPctModifierValue(unitMod, TOTAL_PCT);
1426
1427 SetMaxPower(power, uint32(value));
1428}
1429
1431{
1432 if (ranged)
1433 return;
1434
1435 float val = 0.0f;
1436 float bonusAP = 0.0f;
1438
1439 if (GetEntry() == ENTRY_IMP) // imp's attack power
1440 val = GetStat(STAT_STRENGTH) - 10.0f;
1441 else
1442 val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
1443
1444 Unit* owner = GetOwner();
1445 if (owner && owner->GetTypeId() == TYPEID_PLAYER)
1446 {
1447 if (IsHunterPet()) //hunter pets benefit from owner's attack power
1448 {
1449 float mod = 1.0f; //Hunter contribution modifier
1450 if (IsPet())
1451 {
1452 PetSpellMap::const_iterator itr = ToPet()->m_spells.find(62758); //Wild Hunt rank 1
1453 if (itr == ToPet()->m_spells.end())
1454 itr = ToPet()->m_spells.find(62762); //Wild Hunt rank 2
1455
1456 if (itr != ToPet()->m_spells.end()) // If pet has Wild Hunt
1457 {
1458 SpellInfo const* sProto = sSpellMgr->AssertSpellInfo(itr->first); // Then get the SpellProto and add the dummy effect value
1459 mod += CalculatePct(1.0f, sProto->GetEffect(EFFECT_1).CalcValue());
1460 }
1461 }
1462
1463 bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f * mod;
1464 if (AuraEffect* aurEff = owner->GetAuraEffectOfRankedSpell(34453, EFFECT_1, owner->GetGUID())) // Animal Handler
1465 {
1466 AddPct(bonusAP, aurEff->GetAmount());
1467 AddPct(val, aurEff->GetAmount());
1468 }
1470 }
1471 else if (IsPetGhoul() || IsRisenAlly()) //ghouls benefit from deathknight's attack power (may be summon pet or not)
1472 {
1473 bonusAP = owner->GetTotalAttackPowerValue(BASE_ATTACK) * 0.22f;
1475 }
1476 else if (IsSpiritWolf()) //wolf benefit from shaman's attack power
1477 {
1478 float dmg_multiplier = 0.31f;
1479 if (m_owner->GetAuraEffect(63271, 0)) // Glyph of Feral Spirit
1480 dmg_multiplier = 0.61f;
1481 bonusAP = owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier;
1482 SetBonusDamage(int32(owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier));
1483 }
1484 //demons benefit from warlocks shadow or fire damage
1485 else if (IsPet())
1486 {
1489 int32 maximum = (fire > shadow) ? fire : shadow;
1490 if (maximum < 0)
1491 maximum = 0;
1492 SetBonusDamage(int32(maximum * 0.15f));
1493 bonusAP = maximum * 0.57f;
1494 }
1495 //water elementals benefit from mage's frost damage
1496 else if (GetEntry() == ENTRY_WATER_ELEMENTAL)
1497 {
1499 if (frost < 0)
1500 frost = 0;
1501 SetBonusDamage(int32(frost * 0.4f));
1502 }
1503 }
1504
1506
1507 //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
1508 float base_attPower = GetFlatModifierValue(unitMod, BASE_VALUE) * GetPctModifierValue(unitMod, BASE_PCT);
1509 float attPowerMod = GetFlatModifierValue(unitMod, TOTAL_VALUE);
1510 float attPowerMultiplier = GetPctModifierValue(unitMod, TOTAL_PCT) - 1.0f;
1511
1512 SetAttackPower(int32(base_attPower));
1513 SetAttackPowerModPos(int32(attPowerMod));
1514 SetAttackPowerMultiplier(attPowerMultiplier);
1515
1516 //automatically update weapon damage after attack power modification
1518}
1519
1521{
1522 if (attType > BASE_ATTACK)
1523 return;
1524
1525 float bonusDamage = 0.0f;
1527 {
1528 //force of nature
1529 if (GetEntry() == ENTRY_TREANT)
1530 {
1532 if (spellDmg > 0)
1533 bonusDamage = spellDmg * 0.09f;
1534 }
1535 //greater fire elemental
1536 else if (GetEntry() == ENTRY_FIRE_ELEMENTAL)
1537 {
1539 if (spellDmg > 0)
1540 bonusDamage = spellDmg * 0.4f;
1541 }
1542 }
1543
1545
1546 float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
1547
1548 float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed + bonusDamage;
1549 float base_pct = GetPctModifierValue(unitMod, BASE_PCT);
1550 float total_value = GetFlatModifierValue(unitMod, TOTAL_VALUE);
1551 float total_pct = GetPctModifierValue(unitMod, TOTAL_PCT);
1552
1553 float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
1554 float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
1555
1556 float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
1557 float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
1558
1559 // Pet's base damage changes depending on happiness
1560 if (IsHunterPet())
1561 {
1562 switch (ToPet()->GetHappinessState())
1563 {
1564 case HAPPY:
1565 // 125% of normal damage
1566 mindamage = mindamage * 1.25f;
1567 maxdamage = maxdamage * 1.25f;
1568 break;
1569 case CONTENT:
1570 // 100% of normal damage, nothing to modify
1571 break;
1572 case UNHAPPY:
1573 // 75% of normal damage
1574 mindamage = mindamage * 0.75f;
1575 maxdamage = maxdamage * 0.75f;
1576 break;
1577 }
1578 }
1579
1582 for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
1583 {
1584 switch ((*itr)->GetSpellInfo()->Id)
1585 {
1586 case 61682:
1587 case 61683:
1588 AddPct(mindamage, -(*itr)->GetAmount());
1589 AddPct(maxdamage, -(*itr)->GetAmount());
1590 break;
1591 default:
1592 break;
1593 }
1594 }
1595
1598}
1599
1601{
1602 m_bonusSpellDamage = damage;
1603 if (GetOwner()->GetTypeId() == TYPEID_PLAYER)
1605}
@ IN_MILLISECONDS
Definition Common.h:35
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
#define MAX_ITEM_PROTO_DAMAGES
@ TYPEID_PLAYER
Definition ObjectGuid.h:39
@ HAPPY
Definition PetDefines.h:52
@ CONTENT
Definition PetDefines.h:51
@ UNHAPPY
Definition PetDefines.h:50
@ EQUIPMENT_SLOT_MAINHAND
Definition Player.h:567
RuneType
Definition Player.h:287
@ NUM_RUNE_TYPES
Definition Player.h:292
@ EFFECT_1
@ EFFECT_0
SpellSchools
@ SPELL_SCHOOL_SHADOW
@ SPELL_SCHOOL_NORMAL
@ SPELL_SCHOOL_NATURE
@ SPELL_SCHOOL_FROST
@ SPELL_SCHOOL_FIRE
@ SPELL_SCHOOL_HOLY
@ MAX_SPELL_SCHOOL
#define CLASSMASK_WAND_USERS
SpellSchoolMask
@ SPELL_SCHOOL_MASK_NORMAL
@ SPELL_SCHOOL_MASK_ALL
WeaponAttackType
@ OFF_ATTACK
@ BASE_ATTACK
@ RANGED_ATTACK
@ CLASS_HUNTER
@ CLASS_DRUID
@ CLASS_SHAMAN
@ CLASS_PRIEST
@ CLASS_WARRIOR
@ CLASS_WARLOCK
@ CLASS_MAGE
@ CLASS_DEATH_KNIGHT
@ CLASS_PALADIN
@ CLASS_ROGUE
@ SPELLFAMILY_DRUID
@ SPELLFAMILY_DEATHKNIGHT
Powers
@ MAX_POWERS
@ POWER_RAGE
@ POWER_HEALTH
@ POWER_RUNIC_POWER
@ POWER_ENERGY
@ POWER_MANA
#define MAX_CLASSES
Stats
@ STAT_SPIRIT
@ STAT_INTELLECT
@ MAX_STATS
@ STAT_AGILITY
@ STAT_STRENGTH
@ STAT_STAMINA
@ SKILL_DEFENSE
@ SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE
@ SPELL_AURA_MOD_ATTACKSPEED
@ SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
@ SPELL_AURA_MOD_PARRY_PERCENT
@ SPELL_AURA_MOD_RATING_FROM_STAT
@ SPELL_AURA_MOD_SPELL_HIT_CHANCE
@ SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT
@ SPELL_AURA_MOD_EXPERTISE
@ SPELL_AURA_INTERRUPT_REGEN
@ SPELL_AURA_MOD_POWER_REGEN
@ SPELL_AURA_MOD_SPELL_CRIT_CHANCE
@ SPELL_AURA_DUMMY
@ SPELL_AURA_MOD_DODGE_PERCENT
@ SPELL_AURA_MOD_MANA_REGEN_INTERRUPT
@ SPELL_AURA_MOD_CRIT_PCT
@ SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT
@ SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
@ SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT
@ SPELL_AURA_MOD_POWER_REGEN_PERCENT
@ SPELL_AURA_MOD_MANA_REGEN_FROM_STAT
@ SPELL_AURA_MOD_DAMAGE_DONE
@ SPELL_AURA_PREVENT_REGENERATE_POWER
@ SPELL_AURA_MOD_BLOCK_PERCENT
@ FORM_DIREBEAR
@ FORM_MOONKIN
@ FORM_CAT
@ FORM_BEAR
#define sSpellMgr
Definition SpellMgr.h:738
float const miss_cap[MAX_CLASSES]
float const dodge_cap[MAX_CLASSES]
#define ENTRY_FELGUARD
#define ENTRY_FIRE_ELEMENTAL
#define ENTRY_BLOODWORM
static std::pair< float, Optional< Rates > > const powerRegenInfo[MAX_POWERS]
#define ENTRY_VOIDWALKER
#define ENTRY_FELHUNTER
float CalculateDiminishingReturns(float const (&capArray)[MAX_CLASSES], uint8 playerClass, float nonDiminishValue, float diminishValue)
float const parry_cap[MAX_CLASSES]
#define ENTRY_TREANT
bool _ModifyUInt32(bool apply, uint32 &baseValue, int32 &amount)
#define ENTRY_WATER_ELEMENTAL
#define ENTRY_SUCCUBUS
float const m_diminishing_k[MAX_CLASSES]
#define ENTRY_IMP
#define BASE_MAXDAMAGE
Definition UnitDefines.h:26
#define BASE_MINDAMAGE
Definition UnitDefines.h:25
@ BASE_VALUE
Definition Unit.h:138
@ TOTAL_VALUE
Definition Unit.h:139
@ FLAT_MOD
Definition Unit.h:204
@ PCT_MOD
Definition Unit.h:205
@ MINDAMAGE
Definition Unit.h:152
@ MAXDAMAGE
Definition Unit.h:153
UnitMods
Definition Unit.h:157
@ UNIT_MOD_DAMAGE_OFFHAND
Definition Unit.h:181
@ UNIT_MOD_ARMOR
Definition Unit.h:171
@ UNIT_MOD_ATTACK_POWER
Definition Unit.h:178
@ UNIT_MOD_RESISTANCE_START
Definition Unit.h:187
@ UNIT_MOD_HEALTH
Definition Unit.h:163
@ UNIT_MOD_DAMAGE_RANGED
Definition Unit.h:182
@ UNIT_MOD_POWER_START
Definition Unit.h:189
@ UNIT_MOD_DAMAGE_MAINHAND
Definition Unit.h:180
@ UNIT_MOD_ATTACK_POWER_RANGED
Definition Unit.h:179
#define MAX_COMBAT_RATING
Definition Unit.h:349
BaseModGroup
Definition Unit.h:194
@ OFFHAND_CRIT_PERCENTAGE
Definition Unit.h:197
@ CRIT_PERCENTAGE
Definition Unit.h:195
@ RANGED_CRIT_PERCENTAGE
Definition Unit.h:196
CombatRating
Definition Unit.h:321
@ CR_EXPERTISE
Definition Unit.h:345
@ CR_HIT_MELEE
Definition Unit.h:327
@ CR_ARMOR_PENETRATION
Definition Unit.h:346
@ CR_CRIT_MELEE
Definition Unit.h:330
@ CR_CRIT_RANGED
Definition Unit.h:331
@ CR_PARRY
Definition Unit.h:325
@ CR_DODGE
Definition Unit.h:324
@ CR_DEFENSE_SKILL
Definition Unit.h:323
@ CR_BLOCK
Definition Unit.h:326
@ CR_HIT_SPELL
Definition Unit.h:329
@ CR_CRIT_SPELL
Definition Unit.h:332
@ CR_HIT_RANGED
Definition Unit.h:328
@ TOTAL_PCT
Definition Unit.h:146
@ BASE_PCT
Definition Unit.h:145
@ PLAYER_FIELD_MOD_DAMAGE_DONE_POS
@ UNIT_FIELD_MINDAMAGE
@ PLAYER_EXPERTISE
@ PLAYER_RANGED_CRIT_PERCENTAGE
@ PLAYER_CRIT_PERCENTAGE
@ PLAYER_SPELL_CRIT_PERCENTAGE1
@ PLAYER_FIELD_COMBAT_RATING_1
@ PLAYER_PARRY_PERCENTAGE
@ PLAYER_OFFHAND_CRIT_PERCENTAGE
@ PLAYER_SHIELD_BLOCK
@ UNIT_FIELD_MAXDAMAGE
@ PLAYER_FIELD_MOD_TARGET_RESISTANCE
@ PLAYER_BLOCK_PERCENTAGE
@ UNIT_FIELD_MAXOFFHANDDAMAGE
@ UNIT_FIELD_MAXRANGEDDAMAGE
@ PLAYER_DODGE_PERCENTAGE
@ PLAYER_RUNE_REGEN_1
@ PLAYER_OFFHAND_EXPERTISE
@ PLAYER_FIELD_MOD_HEALING_DONE_POS
@ UNIT_FIELD_MINRANGEDDAMAGE
@ PLAYER_PET_SPELL_POWER
@ UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER
@ PLAYER_FIELD_MOD_DAMAGE_DONE_NEG
@ UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER
@ UNIT_FIELD_MINOFFHANDDAMAGE
T AddPct(T &base, U pct)
Definition Util.h:77
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:554
T CalculatePct(T base, U pct)
Definition Util.h:71
SpellInfo const * GetSpellInfo() const
int32 GetAmount() const
bool UpdateAllStats() override
void UpdateResistances(uint32 school) override
uint32 m_spells[MAX_CREATURE_SPELLS]
Definition Creature.h:229
bool UpdateStats(Stats stat) override
void UpdateAttackPowerAndDamage(bool ranged=false) override
void UpdateArmor() override
CreatureTemplate const * GetCreatureTemplate() const
Definition Creature.h:186
void UpdateMaxPower(Powers power) override
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float &minDamage, float &maxDamage, uint8 damageIndex) const override
void UpdateMaxHealth() override
void UpdateResistances(uint32 school) override
void UpdateMaxPower(Powers power) override
void UpdateMaxHealth() override
bool UpdateStats(Stats stat) override
void SetBonusDamage(int32 damage)
int32 m_bonusSpellDamage
float m_statFromOwner[MAX_STATS]
void UpdateDamagePhysical(WeaponAttackType attType) override
void UpdateArmor() override
bool UpdateAllStats() override
void UpdateAttackPowerAndDamage(bool ranged=false) override
Definition Item.h:62
Unit * GetOwner() const
bool IsRisenAlly() const
bool IsSpiritWolf() const
Unit *const m_owner
bool IsPetGhoul() const
int32 GetInt32Value(uint16 index) const
Definition Object.cpp:243
void SetInt32Value(uint16 index, int32 value)
Definition Object.cpp:572
void ApplyModInt32Value(uint16 index, int32 val, bool apply)
Definition Object.cpp:737
TypeID GetTypeId() const
Definition Object.h:93
void ApplyModUInt32Value(uint16 index, int32 val, bool apply)
Definition Object.cpp:728
float GetFloatValue(uint16 index) const
Definition Object.cpp:261
uint32 GetEntry() const
Definition Object.h:81
void SetFloatValue(uint16 index, float value)
Definition Object.cpp:655
void SetStatFloatValue(uint16 index, float value)
Definition Object.cpp:712
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
void SetUInt32Value(uint16 index, uint32 value)
Definition Object.cpp:585
void SetStatInt32Value(uint16 index, int32 value)
Definition Object.cpp:720
Definition Pet.h:40
HappinessState GetHappinessState()
Definition Pet.cpp:722
PetSpellMap m_spells
Definition Pet.h:128
void UpdateParryPercentage()
void UpdateAllSpellCritChances()
void UpdateArmorPenetration(int32 amount)
void UpdateRuneRegen(RuneType rune)
float GetHealthBonusFromStamina()
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float &minDamage, float &maxDamage, uint8 damageIndex) const override
void UpdateDodgePercentage()
float GetPowerRegen(Powers power) const
void UpdateShieldBlockValue()
void UpdateMaxPower(Powers power) override
bool CanParry() const
Definition Player.h:1895
void ApplyRatingMod(CombatRating cr, int32 value, bool apply)
Definition Player.cpp:5260
void UpdateCritPercentage(WeaponAttackType attType)
uint16 GetSkillValue(uint32 skill) const
Definition Player.cpp:5892
void UpdatePowerRegen(Powers power)
float GetRatingBonusValue(CombatRating cr) const
Definition Player.cpp:5199
float GetManaBonusFromIntellect()
bool CanBlock() const
Definition Player.h:1897
void UpdateBlockPercentage()
void ApplySpellPenetrationBonus(int32 amount, bool apply)
void UpdateArmor() override
void RecalculateRating(CombatRating cr)
Definition Player.h:1674
Pet * GetPet() const
Definition Player.cpp:20286
uint32 m_baseFeralAP
Definition Player.h:2414
bool UpdateStats(Stats stat) override
void _RemoveAllItemMods()
Definition Player.cpp:7868
void _ApplyAllItemMods()
Definition Player.cpp:7913
void UpdateSpellHitChances()
void UpdateExpertise(WeaponAttackType attType)
void _ApplyAllStatBonuses()
float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
Definition Player.cpp:5066
float GetSpellCritFromIntellect() const
Definition Player.cpp:5166
void ApplyManaRegenBonus(int32 amount, bool apply)
void GetDodgeFromAgility(float &diminishing, float &nondiminishing) const
Definition Player.cpp:5113
void SetBaseModPctValue(BaseModGroup modGroup, float val)
Definition Player.cpp:4987
float GetAmmoDPS() const
Definition Player.h:1169
void UpdateResistances(uint32 school) override
void UpdateSpellDamageAndHealingBonus()
uint32 m_baseSpellPower
Definition Player.h:2413
void UpdateMaxHealth() override
void UpdateMeleeHitChances()
Item * m_items[PLAYER_SLOTS_COUNT]
Definition Player.h:2383
uint32 m_baseHealthRegen
Definition Player.h:2416
void UpdateRangedHitChances()
uint32 m_baseManaRegen
Definition Player.h:2415
void UpdateAllRatings()
Definition Player.cpp:5379
void ApplySpellPowerBonus(int32 amount, bool apply)
void UpdateAllCritPercentages()
float GetMeleeCritFromAgility() const
Definition Player.cpp:5096
bool UpdateAllStats() override
Item * GetWeaponForAttack(WeaponAttackType attackType, bool useable=false) const
Definition Player.cpp:9607
void _RemoveAllStatBonuses()
int32 m_spellPenetrationItemMod
Definition Player.h:2417
uint32 GetShieldBlockValue() const override
Definition Player.cpp:5090
void ApplyFeralAPBonus(int32 amount, bool apply)
void UpdateDefenseBonusesMod()
void ApplyHealthRegenBonus(int32 amount, bool apply)
float OCTRegenMPPerSpirit() const
Definition Player.cpp:5241
void UpdateAttackPowerAndDamage(bool ranged=false) override
uint32 GetRuneTypeBaseCooldown(RuneType runeType) const
Definition Player.cpp:24342
float GetMissPercentageFromDefense() const
void UpdateSpellCritChance(uint32 school)
int32 CalcValue(WorldObject const *caster=nullptr, int32 const *basePoints=nullptr) const
SpellEffectInfo const & GetEffect(SpellEffIndex index) const
Definition SpellInfo.h:483
Definition Unit.h:769
float GetPctModifierValue(UnitMods unitMod, UnitModifierPctType modifierType) const
Definition Unit.cpp:9095
bool IsUnderLastManaUseEffect() const
Definition Unit.cpp:10851
bool IsHunterPet() const
Definition Unit.h:885
AuraEffectList const & GetAuraEffectsByType(AuraType type) const
Definition Unit.h:1384
void SetStat(Stats stat, int32 val)
Definition Unit.h:904
float GetTotalAuraModValue(UnitMods unitMod) const
Definition Unit.cpp:9242
Pet * ToPet()
Definition Unit.h:1788
float GetTotalStatValue(Stats stat) const
Definition Unit.cpp:9229
bool HasAuraTypeWithMiscvalue(AuraType auraType, int32 miscValue) const
Definition Unit.cpp:4555
uint8 GetClass() const
Definition Unit.h:895
bool CanUseAttackType(uint8 attacktype) const
Definition Unit.cpp:2382
uint32 GetClassMask() const
Definition Unit.h:897
virtual void UpdateAllResistances()
ShapeshiftForm GetShapeshiftForm() const
Definition Unit.h:1490
float GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type, uint8 damageIndex=0) const
Definition Unit.cpp:9330
uint32 GetDefenseSkillValue(Unit const *target=nullptr) const
Definition Unit.cpp:2557
void SetAttackPowerModNeg(int32 attackPowerMod)
Definition Unit.h:1543
bool haveOffhandWeapon() const
Definition Unit.cpp:510
bool IsPet() const
Definition Unit.h:884
bool CanDualWield() const
Definition Unit.h:837
AuraEffect * GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID=ObjectGuid::Empty) const
Definition Unit.cpp:4359
virtual void UpdateResistances(uint32 school)=0
float m_modRangedHitChance
Definition Unit.h:1498
void SetRangedAttackPowerModNeg(int32 attackPowerMod)
Definition Unit.h:1547
void SetMaxPower(Powers power, uint32 val)
Definition Unit.cpp:9462
void SetRangedAttackPowerModPos(int32 attackPowerMod)
Definition Unit.h:1546
int32 GetTotalAuraModifierByMiscMask(AuraType auraType, uint32 misc_mask) const
Definition Unit.cpp:4812
virtual void UpdateDamagePhysical(WeaponAttackType attType)
void _ApplyAllAuraStatMods()
Definition Unit.cpp:4353
void SetRangedAttackPowerMultiplier(float attackPowerMult)
Definition Unit.h:1548
uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const *target=nullptr) const
Definition Unit.cpp:2786
void SetResistance(SpellSchools school, int32 val)
Definition Unit.h:910
float GetTotalAuraMultiplierByMiscValue(AuraType auraType, int32 misc_value) const
Definition Unit.cpp:4862
uint32 GetCreateHealth() const
Definition Unit.h:1450
AuraEffect * GetAuraEffectOfRankedSpell(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID=ObjectGuid::Empty) const
Definition Unit.cpp:4373
bool HasAuraType(AuraType auraType) const
Definition Unit.cpp:4542
uint32 GetArmor() const
Definition Unit.h:905
float m_modMeleeHitChance
Definition Unit.h:1497
void _RemoveAllAuraStatMods()
Definition Unit.cpp:4347
uint32 GetAttackTime(WeaponAttackType att) const
Definition Unit.cpp:8471
int32 GetTotalAuraModifier(AuraType auraType) const
Definition Unit.cpp:4792
float GetAPMultiplier(WeaponAttackType attType, bool normalized) const
Definition Unit.cpp:10811
void SetAttackPower(int32 attackPower)
Definition Unit.h:1541
int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) const
Definition Unit.cpp:7606
float GetCreateStat(Stats stat) const
Definition Unit.h:1456
void SetArmor(int32 val)
Definition Unit.h:906
bool IsInFeralForm() const
Definition Unit.cpp:8969
void SetCanModifyStats(bool modifyStats)
Definition Unit.h:1532
float GetFlatModifierValue(UnitMods unitMod, UnitModifierFlatType modifierType) const
Definition Unit.cpp:9084
float GetStat(Stats stat) const
Definition Unit.h:903
virtual void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float &minDamage, float &maxDamage, uint8 damageIndex) const =0
int32 GetTotalAuraModifierByMiscValue(AuraType auraType, int32 misc_value) const
Definition Unit.cpp:4852
bool HasAuraTypeWithValue(AuraType auraType, int32 value) const
Definition Unit.cpp:4573
void SetMaxHealth(uint32 val)
Definition Unit.cpp:9393
std::list< AuraEffect * > AuraEffectList
Definition Unit.h:787
void SetRangedAttackPower(int32 attackPower)
Definition Unit.h:1545
int32 GetResistance(SpellSchools school) const
Definition Unit.h:908
float m_modSpellHitChance
Definition Unit.h:1499
Guardian * GetGuardianPet() const
Definition Unit.cpp:5894
void SetAttackPowerMultiplier(float attackPowerMult)
Definition Unit.h:1544
int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const
Definition Unit.cpp:6987
void SetStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float val)
Definition Unit.cpp:9066
uint32 GetCreatePowerValue(Powers power) const
Definition Unit.cpp:9487
void SetAttackPowerModPos(int32 attackPowerMod)
Definition Unit.h:1542
uint8 GetLevel() const
Definition Unit.h:889
bool IsInCombat() const
Definition Unit.h:1144
float GetTotalAttackPowerValue(WeaponAttackType attType) const
Definition Unit.cpp:9312
void UpdateStatBuffMod(Stats stat)
Definition Unit.cpp:4969
uint32 GetMaxSkillValueForLevel(Unit const *target=nullptr) const
Definition Unit.h:1019
#define sWorld
Definition World.h:900
@ RATE_POWER_RAGE_LOSS
Definition World.h:413
@ RATE_POWER_RUNICPOWER_LOSS
Definition World.h:415
@ RATE_POWER_ENERGY
Definition World.h:417
@ RATE_POWER_MANA
Definition World.h:411
@ CONFIG_STATS_LIMITS_DODGE
Definition World.h:196
@ CONFIG_STATS_LIMITS_PARRY
Definition World.h:197
@ CONFIG_STATS_LIMITS_CRIT
Definition World.h:199
@ CONFIG_STATS_LIMITS_BLOCK
Definition World.h:198
@ CONFIG_STATS_LIMITS_ENABLE
Definition World.h:159
int32 GetTotalAPBonus() const