Core/Units: moved base stats handling into a own class

This commit is contained in:
Ovahlord committed 2026-06-10 12:20:35 +02:00
1 parent c1c7dc5ef9
commit af40c9404b
22 files changed
+628 -600

No files matched your search

@@ -631,7 +631,15 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
SetStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE])); SetStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE]));
SetCanModifyStats(true); SetCanModifyStats(true);
UpdateAllStats();
UpdateMaxHealth();
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(PowerType(i));
UpdateAllResistances();
} }
// checked and error show at loading templates // checked and error show at loading templates
@@ -194,8 +194,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr, bool updateLevel = true); bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr, bool updateLevel = true);
bool UpdateStats(StatType stat) override;
bool UpdateAllStats() override;
void UpdateResistances(uint32 school) override; void UpdateResistances(uint32 school) override;
void UpdateArmor() override; void UpdateArmor() override;
void UpdateMaxHealth() override; void UpdateMaxHealth() override;
@@ -380,7 +380,7 @@ bool Minion::IsWarlockMinion() const
Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject) Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject)
, m_bonusSpellDamage(0) , m_bonusSpellDamage(0)
{ {
memset(m_statFromOwner, 0, sizeof(float)*MAX_STATS); memset(m_statFromOwner, 0, sizeof(float)*AsUnderlyingType(StatType::Max));
m_unitTypeMask |= UNIT_MASK_GUARDIAN; m_unitTypeMask |= UNIT_MASK_GUARDIAN;
if (properties && SummonTitle(properties->Title) == SummonTitle::Pet) if (properties && SummonTitle(properties->Title) == SummonTitle::Pet)
{ {
@@ -119,8 +119,6 @@ class TC_GAME_API Guardian : public Minion
bool InitStatsForLevel(uint8 level); bool InitStatsForLevel(uint8 level);
void InitSummon() override; void InitSummon() override;
bool UpdateStats(StatType stat) override;
bool UpdateAllStats() override;
void UpdateResistances(uint32 school) override; void UpdateResistances(uint32 school) override;
void UpdateArmor() override; void UpdateArmor() override;
void UpdateMaxHealth() override; void UpdateMaxHealth() override;
@@ -129,11 +127,11 @@ class TC_GAME_API Guardian : public Minion
void UpdateDamagePhysical(WeaponAttackType attType) override; void UpdateDamagePhysical(WeaponAttackType attType) override;
int32 GetBonusDamage() const { return m_bonusSpellDamage; } int32 GetBonusDamage() const { return m_bonusSpellDamage; }
float GetBonusStatFromOwner(StatType stat) const { return m_statFromOwner[stat]; } float GetBonusStatFromOwner(StatType stat) const { return m_statFromOwner[AsUnderlyingType(stat)]; }
void SetBonusDamage(int32 damage); void SetBonusDamage(int32 damage);
protected: protected:
int32 m_bonusSpellDamage; int32 m_bonusSpellDamage;
float m_statFromOwner[MAX_STATS]; float m_statFromOwner[AsUnderlyingType(StatType::Max)];
}; };
class TC_GAME_API Puppet : public Minion class TC_GAME_API Puppet : public Minion
+14 -15
View File
@@ -28,6 +28,7 @@
#include "SpellHistory.h" #include "SpellHistory.h"
#include "SpellAuras.h" #include "SpellAuras.h"
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include "Stats.h"
#include "Unit.h" #include "Unit.h"
#include "Util.h" #include "Util.h"
#include "Group.h" #include "Group.h"
@@ -296,7 +297,6 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur
} }
CleanupActionBar(); // remove unknown spells from action bar after load CleanupActionBar(); // remove unknown spells from action bar after load
UpdateAllStats();
SetFullHealth(); // Set full health and mana after pet scaling auras has been applied SetFullHealth(); // Set full health and mana after pet scaling auras has been applied
if (IsHunterPet()) if (IsHunterPet())
@@ -773,25 +773,27 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
if (pInfo->armor > 0) if (pInfo->armor > 0)
SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
for (uint8 stat = 0; stat < MAX_STATS; ++stat) for (StatType stat : AllStats)
SetCreateStat(StatType(stat), float(pInfo->stats[stat])); GetStats().SetBaseStatValue(stat, pInfo->stats[AsUnderlyingType(stat)]);
} }
else // not exist in DB, use some default fake data else // not exist in DB, use some default fake data
{ {
// remove elite bonuses included in DB values // remove elite bonuses included in DB values
// remove elite bonuses included in DB values // remove elite bonuses included in DB values
CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class); CreatureBaseStats const* baseStats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class);
float healthmod = _GetHealthMod(cinfo->rank); float healthmod = _GetHealthMod(cinfo->rank);
uint32 basehp = stats->GenerateHealth(cinfo); uint32 basehp = baseStats->GenerateHealth(cinfo);
uint32 health = uint32(basehp * healthmod); uint32 health = uint32(basehp * healthmod);
SetCreateHealth(health); SetCreateHealth(health);
SetCreateMana(stats->BaseMana); SetCreateMana(baseStats->BaseMana);
SetCreateStat(STAT_STRENGTH, 22);
SetCreateStat(STAT_AGILITY, 22); Stats& stats = GetStats();
SetCreateStat(STAT_STAMINA, 25); stats.SetBaseStatValue(StatType::Strength, 22);
SetCreateStat(STAT_INTELLECT, 28); stats.SetBaseStatValue(StatType::Agility, 22);
SetCreateStat(STAT_SPIRIT, 27); stats.SetBaseStatValue(StatType::Stamina, 25);
stats.SetBaseStatValue(StatType::Intellect, 28);
stats.SetBaseStatValue(StatType::Spirit, 27);
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)));
@@ -897,7 +899,6 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, m_owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier / 14); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, m_owner->GetTotalAttackPowerValue(BASE_ATTACK) * dmg_multiplier / 14);
SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(GetOwner()->GetArmor()) * 0.35f); // Bonus Armor (35% of player armor) SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(GetOwner()->GetArmor()) * 0.35f); // Bonus Armor (35% of player armor)
SetStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(GetOwner()->GetStat(STAT_STAMINA)) * 0.3f); // Bonus Stamina (30% of player stamina)
if (!HasAura(58877)) // Spirit Hunt if (!HasAura(58877)) // Spirit Hunt
AddAura(58877, this); AddAura(58877, this);
if (!HasAura(61783)) // Feral Pet Scaling if (!HasAura(61783)) // Feral Pet Scaling
@@ -998,8 +999,6 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
} }
} }
UpdateAllStats();
SetFullHealth(); SetFullHealth();
SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
return true; return true;
@@ -1876,7 +1875,7 @@ void Pet::CastPetAura(PetAura const* aura)
args.TriggerFlags = TRIGGERED_FULL_MASK; args.TriggerFlags = TRIGGERED_FULL_MASK;
if (auraId == 35696) // Demonic Knowledge if (auraId == 35696) // Demonic Knowledge
args.AddSpellMod(SPELLVALUE_BASE_POINT0, CalculatePct(aura->GetDamage(), GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT))); args.AddSpellMod(SPELLVALUE_BASE_POINT0, CalculatePct(aura->GetDamage(), GetStat(StatType::Stamina) + GetStat(StatType::Intellect)));
CastSpell(this, auraId, args); CastSpell(this, auraId, args);
} }
+101 -72
View File
@@ -1231,10 +1231,7 @@ void Player::Update(uint32 p_time)
if (m_petScalingSynchTimer.Passed()) if (m_petScalingSynchTimer.Passed())
{ {
if (pet) if (pet)
{
pet->UpdatePetScalingAuras(); pet->UpdatePetScalingAuras();
pet->UpdateAllStats();
}
m_petScalingSynchTimer.Reset(1000); m_petScalingSynchTimer.Reset(1000);
} }
@@ -2232,8 +2229,8 @@ void Player::GiveLevel(uint8 level)
packet.PowerDelta[4] = 0; packet.PowerDelta[4] = 0;
packet.PowerDelta[5] = 0; packet.PowerDelta[5] = 0;
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) for (StatType stat : AllStats)
packet.StatDelta[i] = int32(info.stats[i]) - GetCreateStat(StatType(i)); packet.StatDelta[AsUnderlyingType(stat)] = int32(info.stats[AsUnderlyingType(stat)]) - GetStats().GetBaseStatValue(stat);
SendDirectMessage(packet.Write()); SendDirectMessage(packet.Write());
@@ -2248,9 +2245,9 @@ void Player::GiveLevel(uint8 level)
UpdateSkillsForLevel(); UpdateSkillsForLevel();
// save base values (bonuses already included in stored stats // save base values (bonuses already included in stored stats)
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) for (StatType stat : AllStats)
SetCreateStat(StatType(i), info.stats[i]); GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]);
SetCreateHealth(basehp); SetCreateHealth(basehp);
SetCreateMana(basemana); SetCreateMana(basemana);
@@ -2259,7 +2256,8 @@ void Player::GiveLevel(uint8 level)
InitTaxiNodesForLevel(); InitTaxiNodesForLevel();
InitGlyphsForLevel(); InitGlyphsForLevel();
UpdateAllStats(); UpdateAllRatings();
UpdateAllResistances();
_ApplyAllLevelScaleItemMods(true); // Moved to above SetFullHealth so player will have full health from Heirlooms _ApplyAllLevelScaleItemMods(true); // Moved to above SetFullHealth so player will have full health from Heirlooms
@@ -2378,21 +2376,14 @@ void Player::InitStatsForLevel(bool reapplyMods)
SetObjectScale(1.0f); SetObjectScale(1.0f);
// save base values (bonuses already included in stored stats // save base values (bonuses already included in stored stats
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) for (StatType stat : AllStats)
SetCreateStat(StatType(i), info.stats[i]); GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]);
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
SetStat(StatType(i), info.stats[i]);
SetCreateHealth(basehp); SetCreateHealth(basehp);
//set create powers //set create powers
SetCreateMana(basemana); SetCreateMana(basemana);
SetArmor(int32(m_createStats[STAT_AGILITY]*2));
InitStatBuffMods();
//reset rating fields values //reset rating fields values
for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index) for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
SetUInt32Value(index, 0); SetUInt32Value(index, 0);
@@ -2451,8 +2442,6 @@ void Player::InitStatsForLevel(bool reapplyMods)
// Dodge percentage // Dodge percentage
SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f); SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
// set armor (resistance 0) to original value (create_agility*2)
SetArmor(int32(m_createStats[STAT_AGILITY]*2));
SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f); SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f);
SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f); SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f);
// set other resistance to original value (0) // set other resistance to original value (0)
@@ -4875,8 +4864,8 @@ float Player::GetMeleeCritFromAgility() const
if (critBase == nullptr || critRatio == nullptr) if (critBase == nullptr || critRatio == nullptr)
return 0.0f; return 0.0f;
float crit = critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio; float crit = critBase->base + GetStat(StatType::Agility) * critRatio->ratio;
return crit*100.0f; return crit * 100.0f;
} }
void Player::GetDodgeFromAgility(float &diminishing, float &nondiminishing) const void Player::GetDodgeFromAgility(float &diminishing, float &nondiminishing) const
@@ -4934,8 +4923,8 @@ void Player::GetDodgeFromAgility(float &diminishing, float &nondiminishing) cons
if (!dodgeRatio || playerClass > MAX_CLASSES) if (!dodgeRatio || playerClass > MAX_CLASSES)
return; return;
float baseAgility = GetCreateStat(STAT_AGILITY) * GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + AsUnderlyingType(STAT_AGILITY)), BASE_PCT); int32 baseAgility = GetStats().GetBaseStatValue(StatType::Agility);
float bonusAgility = GetStat(STAT_AGILITY) - baseAgility; int32 bonusAgility = GetStat(StatType::Agility) - baseAgility;
// calculate diminishing (green in char screen) and non-diminishing (white) contribution // calculate diminishing (green in char screen) and non-diminishing (white) contribution
diminishing = 100.0f * bonusAgility * dodgeRatio->ratio * critToDodge[playerClass -1 ]; diminishing = 100.0f * bonusAgility * dodgeRatio->ratio * critToDodge[playerClass -1 ];
@@ -4955,7 +4944,7 @@ float Player::GetSpellCritFromIntellect() const
if (critBase == nullptr || critRatio == nullptr) if (critBase == nullptr || critRatio == nullptr)
return 0.0f; return 0.0f;
float crit = critBase->base + GetStat(STAT_INTELLECT) * critRatio->ratio; float crit = critBase->base + GetStat(StatType::Intellect) * critRatio->ratio;
return crit * 100.0f; return crit * 100.0f;
} }
@@ -7464,24 +7453,34 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply); HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
break; break;
case ITEM_MOD_AGILITY: // modify agility case ITEM_MOD_AGILITY: // modify agility
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply); if (apply)
UpdateStatBuffMod(STAT_AGILITY); GetStats().AddBaseStatModifier(StatType::Agility, val);
else
GetStats().RemoveBaseStatModifier(StatType::Agility, val);
break; break;
case ITEM_MOD_STRENGTH: //modify strength case ITEM_MOD_STRENGTH: //modify strength
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply); if (apply)
UpdateStatBuffMod(STAT_STRENGTH); GetStats().AddBaseStatModifier(StatType::Strength, val);
else
GetStats().RemoveBaseStatModifier(StatType::Strength, val);
break; break;
case ITEM_MOD_INTELLECT: //modify intellect case ITEM_MOD_INTELLECT: //modify intellect
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply); if (apply)
UpdateStatBuffMod(STAT_INTELLECT); GetStats().AddBaseStatModifier(StatType::Intellect, val);
else
GetStats().RemoveBaseStatModifier(StatType::Intellect, val);
break; break;
case ITEM_MOD_SPIRIT: //modify spirit case ITEM_MOD_SPIRIT: //modify spirit
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply); if (apply)
UpdateStatBuffMod(STAT_SPIRIT); GetStats().AddBaseStatModifier(StatType::Spirit, val);
else
GetStats().RemoveBaseStatModifier(StatType::Spirit, val);
break; break;
case ITEM_MOD_STAMINA: //modify stamina case ITEM_MOD_STAMINA: //modify stamina
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); if (apply)
UpdateStatBuffMod(STAT_STAMINA); GetStats().AddBaseStatModifier(StatType::Stamina, val);
else
GetStats().RemoveBaseStatModifier(StatType::Stamina, val);
break; break;
case ITEM_MOD_DEFENSE_SKILL_RATING: case ITEM_MOD_DEFENSE_SKILL_RATING:
ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply); ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
@@ -12646,24 +12645,34 @@ void Player::ApplyReforgeEnchantment(Item* item, bool apply)
HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, -removeValue, apply); HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, -removeValue, apply);
break; break;
case ITEM_MOD_AGILITY: case ITEM_MOD_AGILITY:
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, -removeValue, apply); if (apply)
UpdateStatBuffMod(STAT_AGILITY); GetStats().RemoveBaseStatModifier(StatType::Agility, removeValue);
else
GetStats().AddBaseStatModifier(StatType::Agility, removeValue);
break; break;
case ITEM_MOD_STRENGTH: case ITEM_MOD_STRENGTH:
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, -removeValue, apply); if (apply)
UpdateStatBuffMod(STAT_STRENGTH); GetStats().RemoveBaseStatModifier(StatType::Strength, removeValue);
else
GetStats().AddBaseStatModifier(StatType::Strength, removeValue);
break; break;
case ITEM_MOD_INTELLECT: case ITEM_MOD_INTELLECT:
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, -removeValue, apply); if (apply)
UpdateStatBuffMod(STAT_INTELLECT); GetStats().RemoveBaseStatModifier(StatType::Intellect, removeValue);
else
GetStats().AddBaseStatModifier(StatType::Intellect, removeValue);
break; break;
case ITEM_MOD_SPIRIT: case ITEM_MOD_SPIRIT:
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, -removeValue, apply); if (apply)
UpdateStatBuffMod(STAT_SPIRIT); GetStats().RemoveBaseStatModifier(StatType::Spirit, removeValue);
else
GetStats().AddBaseStatModifier(StatType::Spirit, removeValue);
break; break;
case ITEM_MOD_STAMINA: case ITEM_MOD_STAMINA:
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, -removeValue, apply); if (apply)
UpdateStatBuffMod(STAT_STAMINA); GetStats().RemoveBaseStatModifier(StatType::Stamina, removeValue);
else
GetStats().AddBaseStatModifier(StatType::Stamina, removeValue);
break; break;
case ITEM_MOD_DEFENSE_SKILL_RATING: case ITEM_MOD_DEFENSE_SKILL_RATING:
ApplyRatingMod(CR_DEFENSE_SKILL, -int32(removeValue), apply); ApplyRatingMod(CR_DEFENSE_SKILL, -int32(removeValue), apply);
@@ -12762,24 +12771,34 @@ void Player::ApplyReforgeEnchantment(Item* item, bool apply)
HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, addValue, apply); HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, addValue, apply);
break; break;
case ITEM_MOD_AGILITY: case ITEM_MOD_AGILITY:
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, addValue, apply); if (apply)
UpdateStatBuffMod(STAT_AGILITY); GetStats().AddBaseStatModifier(StatType::Agility, addValue);
else
GetStats().RemoveBaseStatModifier(StatType::Agility, addValue);
break; break;
case ITEM_MOD_STRENGTH: case ITEM_MOD_STRENGTH:
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, addValue, apply); if (apply)
UpdateStatBuffMod(STAT_STRENGTH); GetStats().AddBaseStatModifier(StatType::Strength, addValue);
else
GetStats().RemoveBaseStatModifier(StatType::Strength, addValue);
break; break;
case ITEM_MOD_INTELLECT: case ITEM_MOD_INTELLECT:
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, addValue, apply); if (apply)
UpdateStatBuffMod(STAT_INTELLECT); GetStats().AddBaseStatModifier(StatType::Intellect, addValue);
else
GetStats().RemoveBaseStatModifier(StatType::Intellect, addValue);
break; break;
case ITEM_MOD_SPIRIT: case ITEM_MOD_SPIRIT:
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, addValue, apply); if (apply)
UpdateStatBuffMod(STAT_SPIRIT); GetStats().AddBaseStatModifier(StatType::Spirit, addValue);
else
GetStats().RemoveBaseStatModifier(StatType::Spirit, addValue);
break; break;
case ITEM_MOD_STAMINA: case ITEM_MOD_STAMINA:
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, addValue, apply); if (apply)
UpdateStatBuffMod(STAT_STAMINA); GetStats().AddBaseStatModifier(StatType::Spirit, addValue);
else
GetStats().RemoveBaseStatModifier(StatType::Spirit, addValue);
break; break;
case ITEM_MOD_DEFENSE_SKILL_RATING: case ITEM_MOD_DEFENSE_SKILL_RATING:
ApplyRatingMod(CR_DEFENSE_SKILL, int32(addValue), apply); ApplyRatingMod(CR_DEFENSE_SKILL, int32(addValue), apply);
@@ -13042,28 +13061,38 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
break; break;
case ITEM_MOD_AGILITY: case ITEM_MOD_AGILITY:
TC_LOG_DEBUG("entities.player.items", "+ %u AGILITY", enchant_amount); TC_LOG_DEBUG("entities.player.items", "+ %u AGILITY", enchant_amount);
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); if (apply)
UpdateStatBuffMod(STAT_AGILITY); GetStats().AddBaseStatModifier(StatType::Agility, enchant_amount);
else
GetStats().RemoveBaseStatModifier(StatType::Agility, enchant_amount);
break; break;
case ITEM_MOD_STRENGTH: case ITEM_MOD_STRENGTH:
TC_LOG_DEBUG("entities.player.items", "+ %u STRENGTH", enchant_amount); TC_LOG_DEBUG("entities.player.items", "+ %u STRENGTH", enchant_amount);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); if (apply)
UpdateStatBuffMod(STAT_STRENGTH); GetStats().AddBaseStatModifier(StatType::Strength, enchant_amount);
else
GetStats().RemoveBaseStatModifier(StatType::Strength, enchant_amount);
break; break;
case ITEM_MOD_INTELLECT: case ITEM_MOD_INTELLECT:
TC_LOG_DEBUG("entities.player.items", "+ %u INTELLECT", enchant_amount); TC_LOG_DEBUG("entities.player.items", "+ %u INTELLECT", enchant_amount);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); if (apply)
UpdateStatBuffMod(STAT_INTELLECT); GetStats().AddBaseStatModifier(StatType::Intellect, enchant_amount);
else
GetStats().RemoveBaseStatModifier(StatType::Intellect, enchant_amount);
break; break;
case ITEM_MOD_SPIRIT: case ITEM_MOD_SPIRIT:
TC_LOG_DEBUG("entities.player.items", "+ %u SPIRIT", enchant_amount); TC_LOG_DEBUG("entities.player.items", "+ %u SPIRIT", enchant_amount);
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); if (apply)
UpdateStatBuffMod(STAT_SPIRIT); GetStats().AddBaseStatModifier(StatType::Spirit, enchant_amount);
else
GetStats().RemoveBaseStatModifier(StatType::Spirit, enchant_amount);
break; break;
case ITEM_MOD_STAMINA: case ITEM_MOD_STAMINA:
TC_LOG_DEBUG("entities.player.items", "+ %u STAMINA", enchant_amount); TC_LOG_DEBUG("entities.player.items", "+ %u STAMINA", enchant_amount);
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); if (apply)
UpdateStatBuffMod(STAT_STAMINA); GetStats().AddBaseStatModifier(StatType::Stamina, enchant_amount);
else
GetStats().RemoveBaseStatModifier(StatType::Stamina, enchant_amount);
break; break;
case ITEM_MOD_DEFENSE_SKILL_RATING: case ITEM_MOD_DEFENSE_SKILL_RATING:
ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply); ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
@@ -17157,7 +17186,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
//apply all stat bonuses from items and auras //apply all stat bonuses from items and auras
SetCanModifyStats(true); SetCanModifyStats(true);
UpdateAllStats(); UpdateAllRatings();
UpdateAllResistances();
// restore remembered power/health values (but not more max values) // restore remembered power/health values (but not more max values)
SetHealth(savedHealth); SetHealth(savedHealth);
@@ -20002,8 +20032,8 @@ void Player::_SaveStats(CharacterDatabaseTransaction& trans) const
for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
stmt->setUInt32(index++, GetMaxPower(PowerType(i))); stmt->setUInt32(index++, GetMaxPower(PowerType(i)));
for (uint8 i = 0; i < MAX_STATS; ++i) for (StatType stat : AllStats)
stmt->setUInt32(index++, GetStat(StatType(i))); stmt->setUInt32(index++, GetStat(stat));
for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
stmt->setUInt32(index++, GetResistance(SpellSchools(i))); stmt->setUInt32(index++, GetResistance(SpellSchools(i)));
@@ -20028,9 +20058,9 @@ void Player::outDebugValues() const
return; return;
TC_LOG_DEBUG("entities.unit", "HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA)); TC_LOG_DEBUG("entities.unit", "HP is: \t\t\t%u\t\tMP is: \t\t\t%u", GetMaxHealth(), GetMaxPower(POWER_MANA));
TC_LOG_DEBUG("entities.unit", "AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH)); TC_LOG_DEBUG("entities.unit", "AGILITY is: \t\t%i\t\tSTRENGTH is: \t\t%i", GetStat(StatType::Agility), GetStat(StatType::Strength));
TC_LOG_DEBUG("entities.unit", "INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f", GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT)); TC_LOG_DEBUG("entities.unit", "INTELLECT is: \t\t%i\t\tSPIRIT is: \t\t%i", GetStat(StatType::Intellect), GetStat(StatType::Spirit));
TC_LOG_DEBUG("entities.unit", "STAMINA is: \t\t%f", GetStat(STAT_STAMINA)); TC_LOG_DEBUG("entities.unit", "STAMINA is: \t\t%i", GetStat(StatType::Stamina));
TC_LOG_DEBUG("entities.unit", "Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE)); TC_LOG_DEBUG("entities.unit", "Armor is: \t\t%u\t\tBlock is: \t\t%f", GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
TC_LOG_DEBUG("entities.unit", "HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE)); TC_LOG_DEBUG("entities.unit", "HolyRes is: \t\t%u\t\tFireRes is: \t\t%u", GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
TC_LOG_DEBUG("entities.unit", "NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST)); TC_LOG_DEBUG("entities.unit", "NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u", GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
@@ -27446,7 +27476,6 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
} }
// Update all stats after we have applied pet scaling auras to make sure we have all fields initialized properly // Update all stats after we have applied pet scaling auras to make sure we have all fields initialized properly
pet->UpdateAllStats();
pet->SetFullHealth(); pet->SetFullHealth();
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA)); pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
-2
View File
@@ -1742,8 +1742,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
float GetHealthBonusFromStamina(); float GetHealthBonusFromStamina();
float GetManaBonusFromIntellect(); float GetManaBonusFromIntellect();
bool UpdateStats(StatType stat) override;
bool UpdateAllStats() override;
void ApplySpellPenetrationBonus(int32 amount, bool apply); void ApplySpellPenetrationBonus(int32 amount, bool apply);
void UpdateResistances(uint32 school) override; void UpdateResistances(uint32 school) override;
void UpdateArmor() override; void UpdateArmor() override;
-2
View File
@@ -52,8 +52,6 @@ class TC_GAME_API Totem : public Minion
void SetTotemDuration(uint32 duration) { m_duration = duration; } void SetTotemDuration(uint32 duration) { m_duration = duration; }
TotemType GetTotemType() const { return m_type; } TotemType GetTotemType() const { return m_type; }
bool UpdateStats(StatType /*stat*/) override { return true; }
bool UpdateAllStats() override { return true; }
void UpdateResistances(uint32 /*school*/) override { } void UpdateResistances(uint32 /*school*/) override { }
void UpdateArmor() override { } void UpdateArmor() override { }
void UpdateMaxHealth() override { } void UpdateMaxHealth() override { }
+12 -176
View File
@@ -150,9 +150,9 @@ void Unit::UpdatePowerRegeneration(PowerType powerType)
baseRegen += powerRegenMod; baseRegen += powerRegenMod;
// SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5 // SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5
float spiritRegen = GetStat(STAT_SPIRIT) * DBCManager::GetGtOCTRegenMPPerSpirit(getClass(), getLevel()); float spiritRegen = GetStat(StatType::Spirit) * DBCManager::GetGtOCTRegenMPPerSpirit(getClass(), getLevel());
if (GetStat(STAT_INTELLECT) > 0.0f) if (GetStat(StatType::Intellect) > 0.0f)
spiritRegen *= std::sqrt(GetStat(STAT_INTELLECT)); spiritRegen *= std::sqrt(GetStat(StatType::Intellect));
// SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus // SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus
baseRegen *= powerRegenModPct; baseRegen *= powerRegenModPct;
@@ -224,64 +224,6 @@ void Unit::UpdatePowerRegeneration(PowerType powerType)
######## ######## ######## ########
#######################################*/ #######################################*/
bool Player::UpdateStats(StatType stat)
{
if (stat > STAT_SPIRIT)
return false;
// value = ((base_value * base_pct) + total_value) * total_pct
float value = GetTotalStatValue(stat);
SetStat(stat, int32(value));
switch (stat)
{
case STAT_AGILITY:
UpdateArmor();
UpdateAllCritPercentages();
UpdateDodgePercentage();
break;
case STAT_STAMINA:
UpdateMaxHealth();
break;
case STAT_INTELLECT:
UpdateMaxPower(POWER_MANA);
UpdateAllSpellCritChances();
UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
break;
case STAT_SPIRIT:
break;
default:
break;
}
if (stat == STAT_STRENGTH)
UpdateAttackPowerAndDamage(false);
else if (stat == STAT_AGILITY)
{
UpdateAttackPowerAndDamage(false);
UpdateAttackPowerAndDamage(true);
}
UpdateSpellDamageAndHealingBonus();
UpdatePowerRegeneration(POWER_MANA);
// Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat
uint32 mask = 0;
AuraEffectList const& modRatingFromStat = GetAuraEffectsByType(SPELL_AURA_MOD_RATING_FROM_STAT);
for (AuraEffectList::const_iterator i = modRatingFromStat.begin(); i != modRatingFromStat.end(); ++i)
if (StatType((*i)->GetMiscValueB()) == stat)
mask |= (*i)->GetMiscValue();
if (mask)
{
for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
if (mask & (1 << rating))
ApplyRatingMod(CombatRating(rating), 0, true);
}
return true;
}
void Player::ApplySpellPowerBonus(int32 amount, bool apply) void Player::ApplySpellPowerBonus(int32 amount, bool apply)
{ {
if (HasAuraType(SPELL_AURA_OVERRIDE_SPELL_POWER_BY_AP_PCT)) if (HasAuraType(SPELL_AURA_OVERRIDE_SPELL_POWER_BY_AP_PCT))
@@ -334,37 +276,6 @@ void Player::UpdateSpellHealingPercentTaken()
SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, TakenTotalMod); SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, TakenTotalMod);
} }
bool Player::UpdateAllStats()
{
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
float value = GetTotalStatValue(StatType(i));
SetStat(StatType(i), int32(value));
}
UpdateArmor();
// calls UpdateAttackPowerAndDamage() in UpdateArmor for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
UpdateAttackPowerAndDamage(true);
UpdateMaxHealth();
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(PowerType(i));
UpdateAllRatings();
UpdateAllCritPercentages();
UpdateAllSpellCritChances();
UpdateBlockPercentage();
UpdateParryPercentage();
UpdateDodgePercentage();
UpdateSpellDamageAndHealingBonus();
UpdatePowerRegeneration(POWER_MANA);
UpdateExpertise(BASE_ATTACK);
UpdateExpertise(OFF_ATTACK);
UpdateAllResistances();
return true;
}
void Player::ApplySpellPenetrationBonus(int32 amount, bool apply) void Player::ApplySpellPenetrationBonus(int32 amount, bool apply)
{ {
ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -amount, apply); ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -amount, apply);
@@ -412,7 +323,7 @@ float Player::GetHealthBonusFromStamina()
if (gtOCTHpPerStaminaEntry const* hpBase = sGtOCTHpPerStaminaStore.LookupEntry((getClass() - 1) * GT_MAX_LEVEL + getLevel() - 1)) if (gtOCTHpPerStaminaEntry const* hpBase = sGtOCTHpPerStaminaStore.LookupEntry((getClass() - 1) * GT_MAX_LEVEL + getLevel() - 1))
ratio = hpBase->ratio; ratio = hpBase->ratio;
float stamina = GetStat(STAT_STAMINA); float stamina = GetStat(StatType::Stamina);
float baseStam = std::min(20.0f, stamina); float baseStam = std::min(20.0f, stamina);
float moreStam = stamina - baseStam; float moreStam = stamina - baseStam;
@@ -422,7 +333,7 @@ float Player::GetHealthBonusFromStamina()
float Player::GetManaBonusFromIntellect() float Player::GetManaBonusFromIntellect()
{ {
// Taken from PaperDollFrame.lua - 4.3.4.15595 // Taken from PaperDollFrame.lua - 4.3.4.15595
float intellect = GetStat(STAT_INTELLECT); float intellect = GetStat(StatType::Intellect);
float baseInt = std::min(20.0f, intellect); float baseInt = std::min(20.0f, intellect);
float moreInt = intellect - baseInt; float moreInt = intellect - baseInt;
@@ -479,15 +390,15 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
uint16 index_mult = ranged ? UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER : UNIT_FIELD_ATTACK_POWER_MULTIPLIER; uint16 index_mult = ranged ? UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER : UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
if (ranged) if (ranged)
val2 = (level + std::max(GetStat(STAT_AGILITY) - 10.0f, 0.0f)) * entry->RangedAttackPowerPerAgility; val2 = (level + std::max(GetStat(StatType::Agility) - 10.0f, 0.0f)) * entry->RangedAttackPowerPerAgility;
else else
{ {
float strengthValue = std::max((GetStat(STAT_STRENGTH) - 10.0f) * entry->AttackPowerPerStrength, 0.0f); float strengthValue = std::max((GetStat(StatType::Strength) - 10.0f) * entry->AttackPowerPerStrength, 0.0f);
float agilityValue = std::max((GetStat(STAT_AGILITY) - 10.0f) * entry->AttackPowerPerAgility, 0.0f); float agilityValue = std::max((GetStat(StatType::Agility) - 10.0f) * entry->AttackPowerPerAgility, 0.0f);
// Druids in Bear and Cat form get two points attack power per agility point // Druids in Bear and Cat form get two points attack power per agility point
if (GetShapeshiftForm() == FORM_BEAR || GetShapeshiftForm() == FORM_CAT) if (GetShapeshiftForm() == FORM_BEAR || GetShapeshiftForm() == FORM_CAT)
agilityValue = std::max((GetStat(STAT_AGILITY) - 10.0f) * 2, 0.0f); agilityValue = std::max((GetStat(StatType::Agility) - 10.0f) * 2, 0.0f);
val2 = strengthValue + agilityValue; val2 = strengthValue + agilityValue;
} }
@@ -917,8 +828,6 @@ void Player::_ApplyAllStatBonuses()
_ApplyAllItemMods(); _ApplyAllItemMods();
SetCanModifyStats(true); SetCanModifyStats(true);
UpdateAllStats();
} }
void Player::_RemoveAllStatBonuses() void Player::_RemoveAllStatBonuses()
@@ -929,8 +838,6 @@ void Player::_RemoveAllStatBonuses()
_RemoveAllAuraStatMods(); _RemoveAllAuraStatMods();
SetCanModifyStats(true); SetCanModifyStats(true);
UpdateAllStats();
} }
/*####################################### /*#######################################
@@ -939,25 +846,6 @@ void Player::_RemoveAllStatBonuses()
######## ######## ######## ########
#######################################*/ #######################################*/
bool Creature::UpdateStats(StatType /*stat*/)
{
return true;
}
bool Creature::UpdateAllStats()
{
UpdateMaxHealth();
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(PowerType(i));
UpdateAllResistances();
return true;
}
void Creature::UpdateResistances(uint32 school) void Creature::UpdateResistances(uint32 school)
{ {
if (school > SPELL_SCHOOL_NORMAL) if (school > SPELL_SCHOOL_NORMAL)
@@ -1096,58 +984,6 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
######## ######## ######## ########
#######################################*/ #######################################*/
bool Guardian::UpdateStats(StatType stat)
{
if (stat >= MAX_STATS)
return false;
// value = ((base_value * base_pct) + total_value) * total_pct
float value = GetTotalStatValue(stat);
//ApplyStatBuffMod(stat, m_statFromOwner[stat], false);
float ownersBonus = 0.0f;
SetStat(stat, int32(value));
m_statFromOwner[stat] = ownersBonus;
UpdateStatBuffMod(stat);
switch (stat)
{
case STAT_STRENGTH:
UpdateAttackPowerAndDamage();
break;
case STAT_AGILITY:
UpdateArmor();
break;
case STAT_STAMINA:
UpdateMaxHealth();
break;
case STAT_INTELLECT:
UpdateMaxPower(POWER_MANA);
break;
case STAT_SPIRIT:
break;
default:
break;
}
return true;
}
bool Guardian::UpdateAllStats()
{
UpdateMaxHealth();
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
UpdateStats(StatType(i));
for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(PowerType(i));
UpdateAllResistances();
return true;
}
void Guardian::UpdateResistances(uint32 school) void Guardian::UpdateResistances(uint32 school)
{ {
if (school > SPELL_SCHOOL_NORMAL) if (school > SPELL_SCHOOL_NORMAL)
@@ -1177,7 +1013,7 @@ void Guardian::UpdateArmor()
void Guardian::UpdateMaxHealth() void Guardian::UpdateMaxHealth()
{ {
UnitMods unitMod = UNIT_MOD_HEALTH; UnitMods unitMod = UNIT_MOD_HEALTH;
float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA); float stamina = GetStat(StatType::Stamina) - GetStats().GetBaseStatValue(StatType::Stamina);
float multiplicator = 10.0f; float multiplicator = 10.0f;
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
@@ -1195,7 +1031,7 @@ void Guardian::UpdateMaxPower(PowerType power)
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power)); UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(power));
float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f; float addValue = (power == POWER_MANA) ? GetStat(StatType::Intellect) - GetStats().GetBaseStatValue(StatType::Intellect) : 0.0f;
float multiplicator = 15.0f; float multiplicator = 15.0f;
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power); float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power);
@@ -1212,7 +1048,7 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged)
return; return;
float ap_per_strength = 2.0f; float ap_per_strength = 2.0f;
float val = GetStat(STAT_STRENGTH) - 20.0f; float val = GetStat(StatType::Strength) - 20.0f;
val *= ap_per_strength; val *= ap_per_strength;
+311
View File
@@ -16,4 +16,315 @@
*/ */
#include "Stats.h" #include "Stats.h"
#include "Object.h"
#include "Player.h"
#include "SpellAuraEffects.h"
#include "Unit.h"
Stats::~Stats() = default;
Stats::Stats(Unit* owner) : _owner(owner), _baseStats({ }), _baseStatModifiers({ }), _basePctMultipliers({ }),
_totalModifiers({ }), _totalPctMultipliers({ })
{
}
/// Returns the unmodified base value of the specified stat.
/// This is the raw stat value that depends on the unit level.
/// @param statType The stat which base value will be returned
/// @return returns the base value fo the specified stat
int32 Stats::GetBaseStatValue(StatType statType) const
{
return _baseStats[AsUnderlyingType(statType)];
}
/// Sets the unmodified base value of the specified stat and
/// updates all bonus calculations afterward. This method is
/// meant to be used for initializing level based raw stats
/// @param statType The stat which base value will be changed
/// @param value The new base value that will be set
void Stats::SetBaseStatValue(StatType statType, int32 value)
{
_baseStats[AsUnderlyingType(statType)] = value;
updateStat(statType);
}
/// Adds a modifier on specified stat value. This method is meant
/// to be used for applying item and enchantment based bonus effects
/// @param statType The stat which modifier value will be changed
/// @param value The amount of the modifier that will be applied
void Stats::AddBaseStatModifier(StatType statType, int32 value)
{
_baseStatModifiers[AsUnderlyingType(statType)] += value;
updateStat(statType);
}
/// Removes a modifier on specified stat value. This method is meant
/// to be used for removing item and enchantment based bonus effects
/// @param statType The stat which modifier value will be changed
/// @param value The amount of the modifier that will be removed
void Stats::RemoveBaseStatModifier(StatType statType, int32 value)
{
_baseStatModifiers[AsUnderlyingType(statType)] -= value;
updateStat(statType);
}
/// Updates the stored base percentage multipliers applied by SPELL_AURA_MOD_PERCENT_STAT.
/// This method also applies stacking rules
/// @param statType The stat which multiplier value will be updated
void Stats::UpdateBaseStatMultiplier(StatType statType)
{
if (statType == StatType::AllStats)
{
for (StatType stat : AllStats)
{
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_PERCENT_STAT, stat);
_basePctMultipliers[AsUnderlyingType(stat)] = multiplier;
updateStat(stat);
}
}
else if (statType == StatType::PrimaryStat)
{
StatType primaryStat = GetPrimaryStat();
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_PERCENT_STAT, primaryStat);
_basePctMultipliers[AsUnderlyingType(primaryStat)] = multiplier;
updateStat(primaryStat);
}
else
{
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_PERCENT_STAT, statType);
_basePctMultipliers[AsUnderlyingType(statType)] = multiplier;
updateStat(statType);
}
}
/// Updates the stored base modifiers applied by SPELL_AURA_MOD_STAT.
/// This method also applies stacking rules
/// @param statType The stat which modifier value will be updated
void Stats::UpdateTotalStatModifier(StatType statType)
{
if (statType == StatType::AllStats)
{
for (StatType stat : AllStats)
{
int32 modifier = getAuraModifierForStatType(SPELL_AURA_MOD_STAT, stat);
_totalModifiers[AsUnderlyingType(stat)] = modifier;
updateStat(stat);
}
}
else if (statType == StatType::PrimaryStat)
{
StatType primaryStat = GetPrimaryStat();
int32 modifier = getAuraModifierForStatType(SPELL_AURA_MOD_STAT, primaryStat);
_totalModifiers[AsUnderlyingType(primaryStat)] = modifier;
updateStat(primaryStat);
}
else
{
int32 modifier = getAuraModifierForStatType(SPELL_AURA_MOD_STAT, statType);
_totalModifiers[AsUnderlyingType(statType)] = modifier;
updateStat(statType);
}
}
/// Updates the stored total percentage multipliers applied by SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE.
/// This method also applies stacking rules
/// @param statType The stat which multiplier value will be updated
void Stats::UpdateTotalStatMultiplier(StatType statType)
{
if (statType == StatType::AllStats)
{
for (StatType stat : AllStats)
{
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, stat);
_totalPctMultipliers[AsUnderlyingType(stat)] = multiplier;
updateStat(stat);
}
}
else if (statType == StatType::PrimaryStat)
{
StatType primaryStat = GetPrimaryStat();
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, primaryStat);
_totalPctMultipliers[AsUnderlyingType(primaryStat)] = multiplier;
updateStat(primaryStat);
}
else
{
float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, statType);
_totalPctMultipliers[AsUnderlyingType(statType)] = multiplier;
updateStat(statType);
}
}
/// Returns the unit's primary stat type based on its class and specialization
/// @return The primarily used stat of the unit
StatType Stats::GetPrimaryStat() const
{
switch (_owner->getClass())
{
case CLASS_WARRIOR:
case CLASS_DEATH_KNIGHT:
{
if (Player* player = Object::ToPlayer(_owner))
{
uint32 talentTree = player->GetPrimaryTalentTree(player->GetActiveSpec());
if (talentTree == TALENT_TREE_WARRIOR_PROTECTION || talentTree == TALENT_TREE_DEATH_KNIGHT_BLOOD)
return StatType::Stamina;
}
return StatType::Strength;
}
case CLASS_HUNTER:
case CLASS_ROGUE:
return StatType::Agility;
case CLASS_MAGE:
case CLASS_WARLOCK:
case CLASS_PRIEST:
return StatType::Intellect;
case CLASS_DRUID:
if (Player* player = Object::ToPlayer(_owner))
{
if (player->GetPrimaryTalentTree(player->GetActiveSpec()) == TALENT_TREE_DRUID_FERAL_COMBAT)
{
if (player->GetShapeshiftForm() == FORM_BEAR)
return StatType::Stamina;
return StatType::Agility;
}
}
return StatType::Intellect;
case CLASS_SHAMAN:
if (Player* player = Object::ToPlayer(_owner))
if (player->GetPrimaryTalentTree(player->GetActiveSpec()) == TALENT_TREE_SHAMAN_ENHANCEMENT)
return StatType::Agility;
return StatType::Intellect;
case CLASS_PALADIN:
{
if (Player* player = Object::ToPlayer(_owner))
{
uint32 talentTree = player->GetPrimaryTalentTree(player->GetActiveSpec());
if (talentTree == TALENT_TREE_PALADIN_PROTECTION)
return StatType::Stamina;
if (talentTree == TALENT_TREE_PALADIN_HOLY)
return StatType::Intellect;
}
return StatType::Strength;
}
default:
return StatType::Strength;
}
}
void Stats::updateStat(StatType statType)
{
int32 negStatValue = 0;
int32 posStatValue = 0;
// ==== Calculate the base flat value (Level stats and item/enchantment mods)
int32 baseStatValue = _baseStats[AsUnderlyingType(statType)];
int32 baseModifier = _baseStatModifiers[AsUnderlyingType(statType)];
if (baseModifier > 0)
posStatValue += baseModifier;
else
negStatValue += baseModifier;
int32 flatStatValue = baseStatValue + baseModifier;
// ==== Calculate the total base value (Base pct multiplier auras)
double basePctMultiplier = _basePctMultipliers[AsUnderlyingType(statType)];
int32 baseTotalValue = static_cast<int32>(flatStatValue * basePctMultiplier);
if (flatStatValue < baseTotalValue)
posStatValue += baseTotalValue - flatStatValue;
else
negStatValue += flatStatValue - baseTotalValue;
// ==== Calculate the total value (Total value buff auras)
int32 totalModifier = _totalModifiers[AsUnderlyingType(statType)];
if (totalModifier > 0)
posStatValue += totalModifier;
else
negStatValue += totalModifier;
int32 totalValue = baseTotalValue + baseModifier;
// ==== Calculate the total value (Total pct multipliers)
double totalPctMultiplier = _totalPctMultipliers[AsUnderlyingType(statType)];
int32 statValue = static_cast<int32>(totalValue * totalPctMultiplier);
if (totalValue < statValue)
posStatValue += statValue - totalValue;
else
negStatValue += statValue - totalValue;
// ==== Apply the stats
_owner->SetInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(statType), statValue);
_owner->SetInt32Value(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(statType), posStatValue);
_owner->SetInt32Value(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(statType), negStatValue);
switch (statType)
{
case StatType::Strength:
_owner->UpdateAttackPowerAndDamage();
break;
case StatType::Agility:
_owner->UpdateAttackPowerAndDamage(false);
_owner->UpdateAttackPowerAndDamage(true);
_owner->UpdateArmor();
if (Player* player = Object::ToPlayer(_owner))
{
player->UpdateAllCritPercentages();
player->UpdateDodgePercentage();
}
break;
case StatType::Stamina:
_owner->UpdateMaxHealth();
break;
case StatType::Intellect:
_owner->UpdateMaxPower(POWER_MANA);
_owner->UpdatePowerRegeneration(POWER_MANA);
if (Player* player = Object::ToPlayer(_owner))
{
player->UpdateAllSpellCritChances();
player->UpdateSpellDamageAndHealingBonus();
}
break;
case StatType::Spirit:
_owner->UpdatePowerRegeneration(POWER_MANA);
break;
default:
break;
}
}
float Stats::getAuraMultiplierForStatType(AuraType auraType, StatType statType) const
{
float multiplier = _owner->GetTotalAuraMultiplier(auraType, [&](AuraEffect const* aurEff)
{
StatType auraStatType = static_cast<StatType>(aurEff->GetMiscValue());
if (auraStatType == StatType::AllStats || auraStatType == statType)
return true;
if (auraStatType == StatType::PrimaryStat && statType == GetPrimaryStat())
return true;
return false;
});
return multiplier;
}
int32 Stats::getAuraModifierForStatType(AuraType auraType, StatType statType) const
{
int32 modifier = _owner->GetTotalAuraModifier(auraType, [&](AuraEffect const* aurEff)
{
StatType auraStatType = static_cast<StatType>(aurEff->GetMiscValue());
if (auraStatType == StatType::AllStats || auraStatType == statType)
return true;
if (auraStatType == StatType::PrimaryStat && statType == GetPrimaryStat())
return true;
return false;
});
return modifier;
}
+50 -1
View File
@@ -19,10 +19,34 @@
#define _Stats_h__ #define _Stats_h__
#include "Define.h" #include "Define.h"
#include "Util.h"
#include <array>
class Unit; class Unit;
enum AuraType : uint32;
class TC_GAME_API //Stats enum class StatType : int8
{
PrimaryStat = -2,
AllStats = -1,
Strength = 0,
Agility = 1,
Stamina = 2,
Intellect = 3,
Spirit = 4,
Max
};
static constexpr std::array<StatType, AsUnderlyingType(StatType::Max)> AllStats =
{
StatType::Strength,
StatType::Agility,
StatType::Stamina,
StatType::Intellect,
StatType::Spirit
};
class TC_GAME_API Stats
{ {
public: public:
Stats() = delete; Stats() = delete;
@@ -31,6 +55,31 @@ public:
~Stats(); ~Stats();
explicit Stats(Unit* owner); explicit Stats(Unit* owner);
int32 GetBaseStatValue(StatType statType) const;
void SetBaseStatValue(StatType statType, int32 value);
void AddBaseStatModifier(StatType statType, int32 value);
void RemoveBaseStatModifier(StatType statType, int32 value);
void UpdateBaseStatMultiplier(StatType statType);
void UpdateTotalStatModifier(StatType statType);
void UpdateTotalStatMultiplier(StatType statType);
StatType GetPrimaryStat() const;
private:
Unit* _owner;
std::array<int32, AsUnderlyingType(StatType::Max)> _baseStats;
std::array<int32, AsUnderlyingType(StatType::Max)> _baseStatModifiers;
std::array<double, AsUnderlyingType(StatType::Max)> _basePctMultipliers;
std::array<int32, AsUnderlyingType(StatType::Max)> _totalModifiers;
std::array<double, AsUnderlyingType(StatType::Max)> _totalPctMultipliers;
void updateStat(StatType statType);
float getAuraMultiplierForStatType(AuraType auraType, StatType statType) const;
int32 getAuraModifierForStatType(AuraType auraType, StatType statType) const;
}; };
#endif // _Stats_h__ #endif // _Stats_h__
+18 -121
View File
@@ -79,6 +79,7 @@
#include "SpellInfo.h" #include "SpellInfo.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "SpellPackets.h" #include "SpellPackets.h"
#include "Stats.h"
#include "TemporarySummon.h" #include "TemporarySummon.h"
#include "Totem.h" #include "Totem.h"
#include "Transport.h" #include "Transport.h"
@@ -306,7 +307,8 @@ Unit::Unit(bool isWorldObject) :
m_charmer(nullptr), m_charmed(nullptr), m_charmer(nullptr), m_charmed(nullptr),
i_motionMaster(std::make_unique<MotionMaster>(this)), m_vehicle(nullptr), i_motionMaster(std::make_unique<MotionMaster>(this)), m_vehicle(nullptr),
m_unitTypeMask(UNIT_MASK_NONE), m_isEngaged(false), m_combatManager(this), m_threatManager(this), m_unitTypeMask(UNIT_MASK_NONE), m_isEngaged(false), m_combatManager(this), m_threatManager(this),
i_AI(nullptr), m_aiLocked(false), m_spellHistory(std::make_unique<SpellHistory>(this)) i_AI(nullptr), m_aiLocked(false), m_spellHistory(std::make_unique<SpellHistory>(this)),
_stats(std::make_unique<Stats>(this))
{ {
m_objectType |= TYPEMASK_UNIT; m_objectType |= TYPEMASK_UNIT;
m_objectTypeId = TYPEID_UNIT; m_objectTypeId = TYPEID_UNIT;
@@ -348,8 +350,6 @@ Unit::Unit(bool isWorldObject) :
m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE; m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
} }
m_createStats.fill(0.0f);
m_attacking = nullptr; m_attacking = nullptr;
m_modMeleeHitChance = 0.0f; m_modMeleeHitChance = 0.0f;
m_modRangedHitChance = 0.0f; m_modRangedHitChance = 0.0f;
@@ -4807,82 +4807,6 @@ void Unit::UpdateResistanceBuffModsMod(SpellSchools school)
SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(school), modNeg); SetFloatValue(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(school), modNeg);
} }
void Unit::InitStatBuffMods()
{
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
SetFloatValue(UNIT_FIELD_POSSTAT0+i, 0);
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
SetFloatValue(UNIT_FIELD_NEGSTAT0+i, 0);
}
void Unit::UpdateStatBuffMod(StatType stat)
{
float modPos = 0.0f;
float modNeg = 0.0f;
float currentValue = 0.0f;
float previousValue = 0.0f;
UnitMods const unitMod = static_cast<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
// includes value from items and enchantments
float baseModValue = GetFlatModifierValue(unitMod, BASE_VALUE);
if (IsGuardian())
baseModValue = static_cast<Guardian*>(this)->GetBonusStatFromOwner(stat);
if (baseModValue >= 0.0f)
modPos = baseModValue;
else
modNeg = baseModValue;
previousValue = baseModValue;
// SPELL_AURA_MOD_PERCENT_STAT affects the base_value as well as the create stat
previousValue += GetCreateStat(stat);
float multiplier = GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [stat](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat)
return true;
return false;
});
currentValue = previousValue * multiplier;
if (G3D::fuzzyGe(currentValue, previousValue))
modPos += currentValue - previousValue;
else
modNeg -= previousValue - currentValue;
// total_value offsets
previousValue += GetTotalAuraModifier(SPELL_AURA_MOD_STAT, [&](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() < 0 || aurEff->GetMiscValue() == stat)
{
if (aurEff->GetAmount() > 0)
modPos += aurEff->GetAmount();
else
modNeg += aurEff->GetAmount();
return true;
}
return false;
});
// total_pct multiplier
multiplier = GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [stat](AuraEffect const* aurEff) -> bool
{
if (!aurEff->GetMiscValueB() || aurEff->GetMiscValueB() & (1 << stat))
return true;
return false;
});
currentValue = previousValue * multiplier;
if (G3D::fuzzyGe(currentValue, previousValue))
modPos += currentValue - previousValue;
else
modNeg -= previousValue - currentValue;
SetFloatValue(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(stat), modPos);
SetFloatValue(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(stat), modNeg);
}
void Unit::_RegisterDynObject(DynamicObject* dynObj) void Unit::_RegisterDynObject(DynamicObject* dynObj)
{ {
m_dynObj.push_back(dynObj); m_dynObj.push_back(dynObj);
@@ -6727,7 +6651,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask, bool withSpellP
// Check if we are ever using mana - PaperDollFrame.lua // Check if we are ever using mana - PaperDollFrame.lua
if (GetPowerIndex(POWER_MANA) != MAX_POWERS) if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
DoneAdvertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)) - 10); // spellpower from intellect DoneAdvertisedBenefit += std::max(0, int32(GetStat(StatType::Intellect)) - 10); // spellpower from intellect
// Spell Power bonus from SPELL_AURA_MOD_SPELL_POWER_PCT // Spell Power bonus from SPELL_AURA_MOD_SPELL_POWER_PCT
if (withSpellPowerPctMod) if (withSpellPowerPctMod)
@@ -7315,7 +7239,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask, bool withSpell
// Check if we are ever using mana - PaperDollFrame.lua // Check if we are ever using mana - PaperDollFrame.lua
if (GetPowerIndex(POWER_MANA) != MAX_POWERS) if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
advertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)) - 10); // spellpower from intellect advertisedBenefit += std::max(0, int32(GetStat(StatType::Intellect)) - 10); // spellpower from intellect
// Spell Power bonus from SPELL_AURA_MOD_SPELL_POWER_PCT // Spell Power bonus from SPELL_AURA_MOD_SPELL_POWER_PCT
if (withSpellPowerPctMod) if (withSpellPowerPctMod)
@@ -8972,13 +8896,6 @@ void Unit::UpdateUnitMod(UnitMods unitMod)
switch (unitMod) switch (unitMod)
{ {
case UNIT_MOD_STAT_STRENGTH:
case UNIT_MOD_STAT_AGILITY:
case UNIT_MOD_STAT_STAMINA:
case UNIT_MOD_STAT_INTELLECT:
case UNIT_MOD_STAT_SPIRIT:
UpdateStats(GetStatByAuraGroup(unitMod));
break;
case UNIT_MOD_ARMOR: case UNIT_MOD_ARMOR:
UpdateArmor(); UpdateArmor();
break; break;
@@ -9101,19 +9018,6 @@ void Unit::UpdateAllDamagePctDoneMods()
UpdateDamagePctDoneMods(WeaponAttackType(i)); UpdateDamagePctDoneMods(WeaponAttackType(i));
} }
float Unit::GetTotalStatValue(StatType stat) const
{
UnitMods unitMod = UnitMods(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
// value = ((base_value * base_pct) + total_value) * total_pct
float value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateStat(stat);
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
return value;
}
float Unit::GetTotalAuraModValue(UnitMods unitMod) const float Unit::GetTotalAuraModValue(UnitMods unitMod) const
{ {
if (unitMod >= UNIT_MOD_END) if (unitMod >= UNIT_MOD_END)
@@ -9150,25 +9054,6 @@ SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
return school; return school;
} }
StatType Unit::GetStatByAuraGroup(UnitMods unitMod) const
{
StatType stat = STAT_STRENGTH;
switch (unitMod)
{
case UNIT_MOD_STAT_STRENGTH: stat = STAT_STRENGTH; break;
case UNIT_MOD_STAT_AGILITY: stat = STAT_AGILITY; break;
case UNIT_MOD_STAT_STAMINA: stat = STAT_STAMINA; break;
case UNIT_MOD_STAT_INTELLECT: stat = STAT_INTELLECT; break;
case UNIT_MOD_STAT_SPIRIT: stat = STAT_SPIRIT; break;
default:
break;
}
return stat;
}
PowerType Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const PowerType Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
{ {
switch (unitMod) switch (unitMod)
@@ -10735,7 +10620,6 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
// this enables pet details window (Shift+P) // this enables pet details window (Shift+P)
pet->InitPetCreateSpells(); pet->InitPetCreateSpells();
//pet->InitLevelupSpellsForLevel(); //pet->InitLevelupSpellsForLevel();
pet->UpdateAllStats();
pet->SetFullHealth(); pet->SetFullHealth();
pet->CastSpell(pet, 99289, true); // Energize pet->CastSpell(pet, 99289, true); // Energize
pet->SetReactState(REACT_ASSIST); pet->SetReactState(REACT_ASSIST);
@@ -14383,3 +14267,16 @@ void Unit::SetGameClientMovingMe(GameClient* gameClientMovingMe)
{ {
_gameClientMovingMe = gameClientMovingMe; _gameClientMovingMe = gameClientMovingMe;
} }
Stats& Unit::GetStats() const
{
return *_stats;
}
int32 Unit::GetStat(StatType statType) const
{
if (statType < StatType::Strength)
return 0;
return GetInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(statType));
}
+17 -29
View File
@@ -18,20 +18,22 @@
#ifndef __UNIT_H #ifndef __UNIT_H
#define __UNIT_H #define __UNIT_H
#include "Object.h"
#include "CombatManager.h"
#include "SpellAuraDefines.h"
#include "SpellDefines.h"
#include "ThreatManager.h"
#include "Timer.h"
#include "UnitDefines.h"
#include "Util.h"
#include <array> #include <array>
#include <forward_list> #include <forward_list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <stack> #include <stack>
#include "CombatManager.h"
#include "Object.h"
#include "SpellAuraDefines.h"
#include "SpellDefines.h"
#include "Stats.h"
#include "ThreatManager.h"
#include "Timer.h"
#include "UnitDefines.h"
#include "Util.h"
constexpr uint32 WORLD_TRIGGER = 12999; constexpr uint32 WORLD_TRIGGER = 12999;
constexpr uint8 MAX_SPELL_CHARM = 4; constexpr uint8 MAX_SPELL_CHARM = 4;
@@ -87,6 +89,7 @@ class Spell;
class SpellCastTargets; class SpellCastTargets;
class SpellHistory; class SpellHistory;
class SpellInfo; class SpellInfo;
class Stats;
class Totem; class Totem;
class Transport; class Transport;
class TransportBase; class TransportBase;
@@ -161,11 +164,6 @@ enum WeaponDamageRange
enum UnitMods enum UnitMods
{ {
UNIT_MOD_STAT_STRENGTH, // UNIT_MOD_STAT_STRENGTH..UNIT_MOD_STAT_SPIRIT must be in existed order, it's accessed by index values of Stats enum.
UNIT_MOD_STAT_AGILITY,
UNIT_MOD_STAT_STAMINA,
UNIT_MOD_STAT_INTELLECT,
UNIT_MOD_STAT_SPIRIT,
UNIT_MOD_HEALTH, UNIT_MOD_HEALTH,
UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_RUNIC_POWER must be in existed order, it's accessed by index values of Powers enum. UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_RUNIC_POWER must be in existed order, it's accessed by index values of Powers enum.
UNIT_MOD_RAGE, UNIT_MOD_RAGE,
@@ -192,8 +190,6 @@ enum UnitMods
UNIT_MOD_DAMAGE_RANGED, UNIT_MOD_DAMAGE_RANGED,
UNIT_MOD_END, UNIT_MOD_END,
// synonyms // synonyms
UNIT_MOD_STAT_START = UNIT_MOD_STAT_STRENGTH,
UNIT_MOD_STAT_END = UNIT_MOD_STAT_SPIRIT + 1,
UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR, UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR,
UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1, UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1,
UNIT_MOD_POWER_START = UNIT_MOD_MANA, UNIT_MOD_POWER_START = UNIT_MOD_MANA,
@@ -774,8 +770,6 @@ class TC_GAME_API Unit : public WorldObject
uint32 getClassMask() const { return 1 << (getClass()-1); } uint32 getClassMask() const { return 1 << (getClass()-1); }
uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER); } uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER); }
float GetStat(StatType stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(stat))); }
void SetStat(StatType stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(stat), val); }
uint32 GetArmor() const { return GetResistance(SPELL_SCHOOL_NORMAL); } uint32 GetArmor() const { return GetResistance(SPELL_SCHOOL_NORMAL); }
void SetArmor(int32 val) { SetResistance(SPELL_SCHOOL_NORMAL, val); } void SetArmor(int32 val) { SetResistance(SPELL_SCHOOL_NORMAL, val); }
@@ -1280,17 +1274,11 @@ class TC_GAME_API Unit : public WorldObject
int32 GetMaxNegativeAuraModifierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const; int32 GetMaxNegativeAuraModifierByAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const;
void UpdateResistanceBuffModsMod(SpellSchools school); void UpdateResistanceBuffModsMod(SpellSchools school);
void InitStatBuffMods();
void UpdateStatBuffMod(StatType stat);
void SetCreateStat(StatType stat, float val) { m_createStats[stat] = val; }
void SetCreateHealth(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_HEALTH, val); } void SetCreateHealth(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_HEALTH, val); }
uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); } uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); }
void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); } void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); }
uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); } uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); }
int32 GetCreatePowerValue(PowerType power) const; int32 GetCreatePowerValue(PowerType power) const;
float GetPosStat(StatType stat) const { return GetFloatValue(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(stat)); }
float GetNegStat(StatType stat) const { return GetFloatValue(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(stat)); }
float GetCreateStat(StatType stat) const { return m_createStats[stat]; }
ObjectGuid GetChannelObjectGuid() const { return GetGuidValue(UNIT_FIELD_CHANNEL_OBJECT); } ObjectGuid GetChannelObjectGuid() const { return GetGuidValue(UNIT_FIELD_CHANNEL_OBJECT); }
void SetChannelObjectGuid(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, guid); } void SetChannelObjectGuid(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, guid); }
@@ -1360,15 +1348,11 @@ class TC_GAME_API Unit : public WorldObject
void UpdateDamagePctDoneMods(WeaponAttackType attackType); void UpdateDamagePctDoneMods(WeaponAttackType attackType);
void UpdateAllDamagePctDoneMods(); void UpdateAllDamagePctDoneMods();
float GetTotalStatValue(StatType stat) const;
float GetTotalAuraModValue(UnitMods unitMod) const; float GetTotalAuraModValue(UnitMods unitMod) const;
SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const; SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
StatType GetStatByAuraGroup(UnitMods unitMod) const;
PowerType GetPowerTypeByAuraGroup(UnitMods unitMod) const; PowerType GetPowerTypeByAuraGroup(UnitMods unitMod) const;
bool CanModifyStats() const { return m_canModifyStats; } bool CanModifyStats() const { return m_canModifyStats; }
void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; } void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
virtual bool UpdateStats(StatType stat) = 0;
virtual bool UpdateAllStats() = 0;
virtual void UpdateResistances(uint32 school) = 0; virtual void UpdateResistances(uint32 school) = 0;
virtual void UpdateAllResistances(); virtual void UpdateAllResistances();
virtual void UpdateArmor() = 0; virtual void UpdateArmor() = 0;
@@ -1665,8 +1649,6 @@ class TC_GAME_API Unit : public WorldObject
void _UpdateAutoRepeatSpell(); void _UpdateAutoRepeatSpell();
std::array<float, MAX_STATS> m_createStats;
AttackerSet m_attackers; AttackerSet m_attackers;
Unit* m_attacking; Unit* m_attacking;
@@ -1819,6 +1801,12 @@ class TC_GAME_API Unit : public WorldObject
std::unordered_map<MovementChangeType, PlayerMovementPendingChange> m_pendingMovementChanges; std::unordered_map<MovementChangeType, PlayerMovementPendingChange> m_pendingMovementChanges;
/* Player Movement fields END*/ /* Player Movement fields END*/
private:
std::unique_ptr<Stats> _stats;
public:
Stats& GetStats() const;
int32 GetStat(StatType statType) const;
}; };
namespace Trinity namespace Trinity
+55 -55
View File
@@ -55,6 +55,7 @@
#include "SpellAuras.h" #include "SpellAuras.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "SpellScript.h" #include "SpellScript.h"
#include "Stats.h"
#include "StringConvert.h" #include "StringConvert.h"
#include "TemporarySummon.h" #include "TemporarySummon.h"
#include "TerrainMgr.h" #include "TerrainMgr.h"
@@ -3282,10 +3283,9 @@ void ObjectMgr::LoadPetLevelInfo()
pLevelInfo->mana = fields[3].GetUInt16(); pLevelInfo->mana = fields[3].GetUInt16();
pLevelInfo->armor = fields[9].GetUInt32(); pLevelInfo->armor = fields[9].GetUInt32();
for (int i = 0; i < MAX_STATS; i++)
{ for (StatType stat : AllStats)
pLevelInfo->stats[i] = fields[i+4].GetUInt16(); pLevelInfo->stats[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 4].GetUInt16();
}
++count; ++count;
} }
@@ -3705,7 +3705,7 @@ void ObjectMgr::LoadPlayerInfo()
{ {
struct RaceStats struct RaceStats
{ {
std::array<int16, MAX_STATS> StatModifier = { }; std::array<int16, AsUnderlyingType(StatType::Max)> StatModifier = { };
}; };
std::array<RaceStats, MAX_RACES> raceStatModifiers = { }; std::array<RaceStats, MAX_RACES> raceStatModifiers = { };
@@ -3732,8 +3732,8 @@ void ObjectMgr::LoadPlayerInfo()
continue; continue;
} }
for (uint8 i = 0; i < MAX_STATS; ++i) for (StatType stat : AllStats)
raceStatModifiers[current_race].StatModifier[i] = fields[i + 1].GetInt16(); raceStatModifiers[current_race].StatModifier[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 1].GetInt16();
} while (raceStatsResult->NextRow()); } while (raceStatsResult->NextRow());
@@ -3781,8 +3781,8 @@ void ObjectMgr::LoadPlayerInfo()
playerInfo->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); playerInfo->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1]; PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1];
for (uint8 i = 0; i < MAX_STATS; i++) for (StatType stat : AllStats)
levelInfo.stats[i] = fields[i + 2].GetUInt16() + raceStatModifiers[race].StatModifier[i]; levelInfo.stats[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 2].GetUInt16() + raceStatModifiers[race].StatModifier[AsUnderlyingType(stat)];
} }
} }
@@ -3940,73 +3940,73 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play
// base data (last known level) // base data (last known level)
*info = ASSERT_NOTNULL(Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(_class) }))->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1]; *info = ASSERT_NOTNULL(Trinity::Containers::MapGetValuePtr(_playerInfo, { Races(race), Classes(_class) }))->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
// if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32 // if conversion from uint32 to uint8 causes unexpected behavior, change lvl to uint32
for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl) for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
{ {
switch (_class) switch (_class)
{ {
case CLASS_WARRIOR: case CLASS_WARRIOR:
info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 9 && !(lvl%2) ? 1: 0);
break; break;
case CLASS_PALADIN: case CLASS_PALADIN:
info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 3 ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 6 && (lvl%2) ? 1: 0);
info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 7 ? 1: 0);
break; break;
case CLASS_HUNTER: case CLASS_HUNTER:
info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 4 ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0);
info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 8 && (lvl%2) ? 1: 0);
info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
break; break;
case CLASS_ROGUE: case CLASS_ROGUE:
info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 5 ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0);
info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 8 && !(lvl%2) ? 1: 0);
info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
break; break;
case CLASS_PRIEST: case CLASS_PRIEST:
info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 5 ? 1: 0);
info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 3 ? 1: 0);
break; break;
case CLASS_SHAMAN: case CLASS_SHAMAN:
info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0);
info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 7 && !(lvl%2) ? 1: 0);
info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 5 ? 1: 0);
info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 4 ? 1: 0);
break; break;
case CLASS_MAGE: case CLASS_MAGE:
info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 5 ? 1: 0);
info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
break; break;
case CLASS_WARLOCK: case CLASS_WARLOCK:
info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 9 && !(lvl%2) ? 1: 0);
info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
break; break;
case CLASS_DRUID: case CLASS_DRUID:
info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0)); info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0)); info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
} }
} }
} }
+2 -2
View File
@@ -672,7 +672,7 @@ typedef std::vector<PlayerCreateInfoItem> PlayerCreateInfoItems;
struct PlayerLevelInfo struct PlayerLevelInfo
{ {
uint16 stats[MAX_STATS] = { }; uint16 stats[5] = { };
}; };
typedef std::vector<uint32> PlayerCreateInfoSpells; typedef std::vector<uint32> PlayerCreateInfoSpells;
@@ -714,7 +714,7 @@ struct PlayerInfo
struct PetLevelInfo struct PetLevelInfo
{ {
uint16 stats[MAX_STATS] = { }; uint16 stats[5] = { };
uint16 health = 0; uint16 health = 0;
uint16 mana = 0; uint16 mana = 0;
uint16 armor = 0; uint16 armor = 0;
+1 -1
View File
@@ -405,7 +405,7 @@ namespace WorldPackets
int32 Level = 0; int32 Level = 0;
int32 HealthDelta = 0; int32 HealthDelta = 0;
std::array<int32, 6> PowerDelta = { }; std::array<int32, 6> PowerDelta = { };
std::array<int32, MAX_STATS> StatDelta = { }; std::array<int32, 5> StatDelta = { };
}; };
class BindPointUpdate final : public ServerPacket class BindPointUpdate final : public ServerPacket
@@ -16,6 +16,11 @@
*/ */
#include "SpellAuraEffects.h" #include "SpellAuraEffects.h"
#include <G3D/g3dmath.h>
#include <numeric>
#include "Battlefield.h" #include "Battlefield.h"
#include "BattlefieldMgr.h" #include "BattlefieldMgr.h"
#include "Battleground.h" #include "Battleground.h"
@@ -41,14 +46,13 @@
#include "SpellHistory.h" #include "SpellHistory.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "SpellScript.h" #include "SpellScript.h"
#include "Stats.h"
#include "ThreatManager.h" #include "ThreatManager.h"
#include "Unit.h" #include "Unit.h"
#include "Util.h" #include "Util.h"
#include "Vehicle.h" #include "Vehicle.h"
#include "Weather.h" #include "Weather.h"
#include "WorldPacket.h" #include "WorldPacket.h"
#include <G3D/g3dmath.h>
#include <numeric>
class Aura; class Aura;
// //
@@ -3567,71 +3571,30 @@ void AuraEffect::HandleAuraModStat(AuraApplication const* aurApp, uint8 mode, bo
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return; return;
if (GetMiscValue() < -2 || GetMiscValue() > 4) StatType stat = static_cast<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{ {
TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue()); TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return; return;
} }
Unit* target = aurApp->GetTarget(); Unit* target = aurApp->GetTarget();
int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_STAT, true, GetMiscValue()); target->GetStats().UpdateTotalStatModifier(stat);
if (abs(spellGroupVal) >= abs(GetAmount()))
return;
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
// -1 or -2 is all stats (misc < -2 checked in function beginning)
if (GetMiscValue() < 0 || GetMiscValue() == i)
{
if (spellGroupVal)
{
target->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(spellGroupVal), !apply);
if (target->GetTypeId() == TYPEID_PLAYER || target->IsPet())
target->UpdateStatBuffMod(StatType(i));
}
target->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetAmount()), apply);
if (target->GetTypeId() == TYPEID_PLAYER || target->IsPet())
target->UpdateStatBuffMod(StatType(i));
}
}
} }
void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode, bool apply) const void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode, bool apply) const
{ {
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return; return;
StatType stat = static_cast<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{
TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return;
}
Unit* target = aurApp->GetTarget(); Unit* target = aurApp->GetTarget();
target->GetStats().UpdateBaseStatMultiplier(stat);
if (GetMiscValue() < -1 || GetMiscValue() > 4)
{
TC_LOG_ERROR("spells", "WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
return;
}
// only players have base stats
if (target->GetTypeId() != TYPEID_PLAYER)
return;
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
if (GetMiscValue() == i || GetMiscValue() == -1)
{
if (apply)
target->ApplyStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(GetAmount()));
else
{
float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [i](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() == i || aurEff->GetMiscValue() == -1)
return true;
return false;
});
target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, amount);
}
}
}
} }
void AuraEffect::HandleModSpellDamagePercentFromStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const void AuraEffect::HandleModSpellDamagePercentFromStat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -3713,48 +3676,15 @@ void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return; return;
if (GetMiscValue() < -1 || GetMiscValue() > 4) StatType stat = static_cast<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{ {
TC_LOG_ERROR("spells", "WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid"); TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return; return;
} }
Unit* target = aurApp->GetTarget(); Unit* target = aurApp->GetTarget();
target->GetStats().UpdateTotalStatMultiplier(stat);
// save current health state
float healthPct = target->GetHealthPct();
bool zeroHealth = !target->IsAlive();
// players in corpse state may mean two different states:
/// 1. player just died but did not release (in this case health == 0)
/// 2. player is corpse running (ie ghost) (in this case health == 1)
if (target->getDeathState() == CORPSE)
zeroHealth = (target->GetHealth() == 0);
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
if ((GetMiscValueB() & 1 << i) || !GetMiscValueB()) // affect the same stats
{
float amount = target->GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [i](AuraEffect const* aurEff) -> bool
{
if ((aurEff->GetMiscValueB() & 1 << i) || aurEff->GetMiscValueB() == -1)
return true;
return false;
});
if (target->GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT) == amount)
continue;
target->SetStatPctModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, amount);
if (target->GetTypeId() == TYPEID_PLAYER || target->IsPet())
target->UpdateStatBuffMod(StatType(i));
}
}
// recalculate current HP/MP after applying aura modifications (only for spells with SPELL_ATTR0_IS_ABILITY 0x00000010 flag)
// this check is total bullshit i think
if (((GetMiscValueB() & 1 << STAT_STAMINA) || GetMiscValueB() == - 1) && m_spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY))
target->SetHealth(std::max<uint32>(CalculatePct(target->GetMaxHealth(), healthPct), (zeroHealth ? 0 : 1)));
} }
void AuraEffect::HandleAuraModResistenceOfStatPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const void AuraEffect::HandleAuraModResistenceOfStatPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
+2 -2
View File
@@ -4388,7 +4388,7 @@ class spell_gen_vengeance : public AuraScript
if (!caster->GetAura(SPELL_VENGEANCE_TRIGGERED, caster->GetGUID())) if (!caster->GetAura(SPELL_VENGEANCE_TRIGGERED, caster->GetGUID()))
{ {
uint32 healthCap = CalculatePct(caster->GetCreateHealth(), 10) + caster->GetStat(STAT_STAMINA); uint32 healthCap = CalculatePct(caster->GetCreateHealth(), 10) + caster->GetStat(StatType::Stamina);
uint32 damageBonus = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), 33); uint32 damageBonus = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), 33);
int32 bp = std::min<int32>(damageBonus, healthCap); int32 bp = std::min<int32>(damageBonus, healthCap);
caster->CastSpell(caster, SPELL_VENGEANCE_TRIGGERED, CastSpellExtraArgs(true).AddSpellBP0(bp).AddSpellMod(SPELLVALUE_BASE_POINT1, bp).AddSpellMod(SPELLVALUE_BASE_POINT2, bp)); caster->CastSpell(caster, SPELL_VENGEANCE_TRIGGERED, CastSpellExtraArgs(true).AddSpellBP0(bp).AddSpellMod(SPELLVALUE_BASE_POINT1, bp).AddSpellMod(SPELLVALUE_BASE_POINT2, bp));
@@ -4448,7 +4448,7 @@ class spell_gen_vengeance_triggered : public AuraScript
} }
} }
uint32 healthCap = CalculatePct(target->GetCreateHealth(), 10) + target->GetStat(STAT_STAMINA); uint32 healthCap = CalculatePct(target->GetCreateHealth(), 10) + target->GetStat(StatType::Stamina);
damageLastTwoSeconds = std::min<int32>(healthCap, CalculatePct(damageLastTwoSeconds, 33)); damageLastTwoSeconds = std::min<int32>(healthCap, CalculatePct(damageLastTwoSeconds, 33));
if (damageLastTwoSeconds) if (damageLastTwoSeconds)
target->CastSpell(target, SPELL_VENGEANCE_TRIGGERED, CastSpellExtraArgs(true).AddSpellBP0(damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT1, damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT2, damageLastTwoSeconds)); target->CastSpell(target, SPELL_VENGEANCE_TRIGGERED, CastSpellExtraArgs(true).AddSpellBP0(damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT1, damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT2, damageLastTwoSeconds));
+6 -6
View File
@@ -4213,8 +4213,8 @@ public:
PreventDefaultAction(); PreventDefaultAction();
Unit* caster = eventInfo.GetActor(); Unit* caster = eventInfo.GetActor();
float str = caster->GetStat(STAT_STRENGTH); float str = caster->GetStat(StatType::Strength);
float agi = caster->GetStat(STAT_AGILITY); float agi = caster->GetStat(StatType::Agility);
switch (aurEff->GetId()) switch (aurEff->GetId())
{ {
@@ -4359,10 +4359,10 @@ public:
PreventDefaultAction(); PreventDefaultAction();
Unit* caster = eventInfo.GetActor(); Unit* caster = eventInfo.GetActor();
float str = caster->GetStat(STAT_STRENGTH); int32 str = caster->GetStat(StatType::Strength);
float agi = caster->GetStat(STAT_AGILITY); int32 agi = caster->GetStat(StatType::Agility);
float intl = caster->GetStat(STAT_INTELLECT); int32 intl = caster->GetStat(StatType::Intellect);
float spi = caster->GetStat(STAT_SPIRIT); int32 spi = caster->GetStat(StatType::Spirit);
float stat = 0.0f; float stat = 0.0f;
uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGTH;
+6 -6
View File
@@ -100,7 +100,7 @@ class spell_warl_pet_scaling_01 : public AuraScript
{ {
if (Player* owner = pet->GetOwner()) if (Player* owner = pet->GetOwner())
{ {
float stamina = owner->GetStat(STAT_STAMINA) * 0.6496f; float stamina = owner->GetStat(StatType::Stamina) * 0.6496f;
float staminaBonus = 0.0f; float staminaBonus = 0.0f;
switch (pet->GetEntry()) switch (pet->GetEntry())
@@ -180,7 +180,7 @@ class spell_warl_pet_scaling_02 : public AuraScript
{ {
if (Player* owner = pet->GetOwner()) if (Player* owner = pet->GetOwner())
{ {
float intellect = owner->GetStat(STAT_INTELLECT); float intellect = owner->GetStat(StatType::Intellect);
float manaBonus = 0.0f; float manaBonus = 0.0f;
switch (pet->GetEntry()) switch (pet->GetEntry())
{ {
@@ -795,7 +795,7 @@ class spell_dk_pet_scaling_01 : public AuraScript
if (AuraEffect const* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, DEATH_KNIGHT_ICON_ID_GLYPH_OF_RAISE_DEAD, EFFECT_0)) if (AuraEffect const* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, DEATH_KNIGHT_ICON_ID_GLYPH_OF_RAISE_DEAD, EFFECT_0))
percentage += aurEff->GetAmount(); percentage += aurEff->GetAmount();
amount = int32(CalculatePct(owner->GetStat(STAT_STAMINA), percentage)); amount = int32(CalculatePct(owner->GetStat(StatType::Stamina), percentage));
} }
} }
@@ -810,7 +810,7 @@ class spell_dk_pet_scaling_01 : public AuraScript
if (AuraEffect const* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, DEATH_KNIGHT_ICON_ID_GLYPH_OF_RAISE_DEAD, EFFECT_0)) if (AuraEffect const* aurEff = owner->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, DEATH_KNIGHT_ICON_ID_GLYPH_OF_RAISE_DEAD, EFFECT_0))
percentage += aurEff->GetAmount(); percentage += aurEff->GetAmount();
amount = int32(CalculatePct(owner->GetStat(STAT_STAMINA), percentage)); amount = int32(CalculatePct(owner->GetStat(StatType::Stamina), percentage));
} }
} }
@@ -1030,7 +1030,7 @@ class spell_mage_water_elemental_scaling_01 : public AuraScript
{ {
if (Player* owner = pet->GetOwner()) if (Player* owner = pet->GetOwner())
{ {
float stamina = CalculatePct(owner->GetStat(STAT_STAMINA), 30); float stamina = CalculatePct(owner->GetStat(StatType::Stamina), 30);
float staminaBonus = stamina * 7.5f; float staminaBonus = stamina * 7.5f;
float maxHealthBonus = owner->CountPctFromMaxHealth(50); float maxHealthBonus = owner->CountPctFromMaxHealth(50);
@@ -1063,7 +1063,7 @@ class spell_mage_water_elemental_scaling_02 : public AuraScript
{ {
if (Player* owner = pet->GetOwner()) if (Player* owner = pet->GetOwner())
{ {
float intellect = CalculatePct(owner->GetStat(STAT_INTELLECT), 30); float intellect = CalculatePct(owner->GetStat(StatType::Intellect), 30);
float intellectBonus = intellect * 5.0f; float intellectBonus = intellect * 5.0f;
float maxManaBonus = owner->CountPctFromMaxPower(POWER_MANA, 50); float maxManaBonus = owner->CountPctFromMaxPower(POWER_MANA, 50);
+1 -1
View File
@@ -847,7 +847,7 @@ class spell_sha_mana_tide_totem : public AuraScript
return; return;
if (Unit* summoner = caster->ToTempSummon()->GetSummoner()) if (Unit* summoner = caster->ToTempSummon()->GetSummoner())
amount = CalculatePct(summoner->GetStat(STAT_SPIRIT), amount); amount = CalculatePct(summoner->GetStat(StatType::Spirit), amount);
else else
amount = 0; amount = 0;
} }
-11
View File
@@ -227,17 +227,6 @@ enum MoneyConstants
GOLD = SILVER*100 GOLD = SILVER*100
}; };
enum StatType
{
STAT_STRENGTH = 0,
STAT_AGILITY = 1,
STAT_STAMINA = 2,
STAT_INTELLECT = 3,
STAT_SPIRIT = 4
};
#define MAX_STATS 5
enum PowerType enum PowerType
{ {
POWER_MANA = 0, POWER_MANA = 0,