diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 9c79a94aee..d56f7141b0 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -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])); 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 diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 0f31d335c6..6129c37932 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -194,8 +194,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma 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 UpdateArmor() override; void UpdateMaxHealth() override; diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 9a5233a9b1..68e2bf6d3c 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -380,7 +380,7 @@ bool Minion::IsWarlockMinion() const Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject) , 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; if (properties && SummonTitle(properties->Title) == SummonTitle::Pet) { diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index 0b70eb0846..8385dd150c 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -119,8 +119,6 @@ class TC_GAME_API Guardian : public Minion bool InitStatsForLevel(uint8 level); void InitSummon() override; - bool UpdateStats(StatType stat) override; - bool UpdateAllStats() override; void UpdateResistances(uint32 school) override; void UpdateArmor() override; void UpdateMaxHealth() override; @@ -129,11 +127,11 @@ class TC_GAME_API Guardian : public Minion void UpdateDamagePhysical(WeaponAttackType attType) override; 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); protected: int32 m_bonusSpellDamage; - float m_statFromOwner[MAX_STATS]; + float m_statFromOwner[AsUnderlyingType(StatType::Max)]; }; class TC_GAME_API Puppet : public Minion diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 37cf7f2587..49604b82e0 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -28,6 +28,7 @@ #include "SpellHistory.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" +#include "Stats.h" #include "Unit.h" #include "Util.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 - UpdateAllStats(); SetFullHealth(); // Set full health and mana after pet scaling auras has been applied if (IsHunterPet()) @@ -773,25 +773,27 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) if (pInfo->armor > 0) SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - for (uint8 stat = 0; stat < MAX_STATS; ++stat) - SetCreateStat(StatType(stat), float(pInfo->stats[stat])); + for (StatType stat : AllStats) + GetStats().SetBaseStatValue(stat, pInfo->stats[AsUnderlyingType(stat)]); } else // not exist in DB, use some default fake data { // 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); - uint32 basehp = stats->GenerateHealth(cinfo); + uint32 basehp = baseStats->GenerateHealth(cinfo); uint32 health = uint32(basehp * healthmod); SetCreateHealth(health); - SetCreateMana(stats->BaseMana); - SetCreateStat(STAT_STRENGTH, 22); - SetCreateStat(STAT_AGILITY, 22); - SetCreateStat(STAT_STAMINA, 25); - SetCreateStat(STAT_INTELLECT, 28); - SetCreateStat(STAT_SPIRIT, 27); + SetCreateMana(baseStats->BaseMana); + + Stats& stats = GetStats(); + stats.SetBaseStatValue(StatType::Strength, 22); + stats.SetBaseStatValue(StatType::Agility, 22); + 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, 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); 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 AddAura(58877, this); if (!HasAura(61783)) // Feral Pet Scaling @@ -998,8 +999,6 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) } } - UpdateAllStats(); - SetFullHealth(); SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); return true; @@ -1876,7 +1875,7 @@ void Pet::CastPetAura(PetAura const* aura) args.TriggerFlags = TRIGGERED_FULL_MASK; 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); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7c8b5a277f..f1b97dee40 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1231,10 +1231,7 @@ void Player::Update(uint32 p_time) if (m_petScalingSynchTimer.Passed()) { if (pet) - { pet->UpdatePetScalingAuras(); - pet->UpdateAllStats(); - } m_petScalingSynchTimer.Reset(1000); } @@ -2232,8 +2229,8 @@ void Player::GiveLevel(uint8 level) packet.PowerDelta[4] = 0; packet.PowerDelta[5] = 0; - for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) - packet.StatDelta[i] = int32(info.stats[i]) - GetCreateStat(StatType(i)); + for (StatType stat : AllStats) + packet.StatDelta[AsUnderlyingType(stat)] = int32(info.stats[AsUnderlyingType(stat)]) - GetStats().GetBaseStatValue(stat); SendDirectMessage(packet.Write()); @@ -2248,9 +2245,9 @@ void Player::GiveLevel(uint8 level) UpdateSkillsForLevel(); - // save base values (bonuses already included in stored stats - for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetCreateStat(StatType(i), info.stats[i]); + // save base values (bonuses already included in stored stats) + for (StatType stat : AllStats) + GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]); SetCreateHealth(basehp); SetCreateMana(basemana); @@ -2259,7 +2256,8 @@ void Player::GiveLevel(uint8 level) InitTaxiNodesForLevel(); InitGlyphsForLevel(); - UpdateAllStats(); + UpdateAllRatings(); + UpdateAllResistances(); _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); // save base values (bonuses already included in stored stats - for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetCreateStat(StatType(i), info.stats[i]); - - for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) - SetStat(StatType(i), info.stats[i]); + for (StatType stat : AllStats) + GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]); SetCreateHealth(basehp); //set create powers SetCreateMana(basemana); - SetArmor(int32(m_createStats[STAT_AGILITY]*2)); - - InitStatBuffMods(); - //reset rating fields values for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index) SetUInt32Value(index, 0); @@ -2451,8 +2442,6 @@ void Player::InitStatsForLevel(bool reapplyMods) // Dodge percentage 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_RESISTANCEBUFFMODSNEGATIVE + AsUnderlyingType(SPELL_SCHOOL_NORMAL), 0.0f); // set other resistance to original value (0) @@ -4875,8 +4864,8 @@ float Player::GetMeleeCritFromAgility() const if (critBase == nullptr || critRatio == nullptr) return 0.0f; - float crit = critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio; - return crit*100.0f; + float crit = critBase->base + GetStat(StatType::Agility) * critRatio->ratio; + return crit * 100.0f; } 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) return; - float baseAgility = GetCreateStat(STAT_AGILITY) * GetPctModifierValue(UnitMods(UNIT_MOD_STAT_START + AsUnderlyingType(STAT_AGILITY)), BASE_PCT); - float bonusAgility = GetStat(STAT_AGILITY) - baseAgility; + int32 baseAgility = GetStats().GetBaseStatValue(StatType::Agility); + int32 bonusAgility = GetStat(StatType::Agility) - baseAgility; // calculate diminishing (green in char screen) and non-diminishing (white) contribution diminishing = 100.0f * bonusAgility * dodgeRatio->ratio * critToDodge[playerClass -1 ]; @@ -4955,7 +4944,7 @@ float Player::GetSpellCritFromIntellect() const if (critBase == nullptr || critRatio == nullptr) 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; } @@ -7464,24 +7453,34 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply HandleStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply); break; case ITEM_MOD_AGILITY: // modify agility - HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply); - UpdateStatBuffMod(STAT_AGILITY); + if (apply) + GetStats().AddBaseStatModifier(StatType::Agility, val); + else + GetStats().RemoveBaseStatModifier(StatType::Agility, val); break; case ITEM_MOD_STRENGTH: //modify strength - HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply); - UpdateStatBuffMod(STAT_STRENGTH); + if (apply) + GetStats().AddBaseStatModifier(StatType::Strength, val); + else + GetStats().RemoveBaseStatModifier(StatType::Strength, val); break; case ITEM_MOD_INTELLECT: //modify intellect - HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply); - UpdateStatBuffMod(STAT_INTELLECT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Intellect, val); + else + GetStats().RemoveBaseStatModifier(StatType::Intellect, val); break; case ITEM_MOD_SPIRIT: //modify spirit - HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply); - UpdateStatBuffMod(STAT_SPIRIT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Spirit, val); + else + GetStats().RemoveBaseStatModifier(StatType::Spirit, val); break; case ITEM_MOD_STAMINA: //modify stamina - HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply); - UpdateStatBuffMod(STAT_STAMINA); + if (apply) + GetStats().AddBaseStatModifier(StatType::Stamina, val); + else + GetStats().RemoveBaseStatModifier(StatType::Stamina, val); break; case ITEM_MOD_DEFENSE_SKILL_RATING: 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); break; case ITEM_MOD_AGILITY: - HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, -removeValue, apply); - UpdateStatBuffMod(STAT_AGILITY); + if (apply) + GetStats().RemoveBaseStatModifier(StatType::Agility, removeValue); + else + GetStats().AddBaseStatModifier(StatType::Agility, removeValue); break; case ITEM_MOD_STRENGTH: - HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, -removeValue, apply); - UpdateStatBuffMod(STAT_STRENGTH); + if (apply) + GetStats().RemoveBaseStatModifier(StatType::Strength, removeValue); + else + GetStats().AddBaseStatModifier(StatType::Strength, removeValue); break; case ITEM_MOD_INTELLECT: - HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, -removeValue, apply); - UpdateStatBuffMod(STAT_INTELLECT); + if (apply) + GetStats().RemoveBaseStatModifier(StatType::Intellect, removeValue); + else + GetStats().AddBaseStatModifier(StatType::Intellect, removeValue); break; case ITEM_MOD_SPIRIT: - HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, -removeValue, apply); - UpdateStatBuffMod(STAT_SPIRIT); + if (apply) + GetStats().RemoveBaseStatModifier(StatType::Spirit, removeValue); + else + GetStats().AddBaseStatModifier(StatType::Spirit, removeValue); break; case ITEM_MOD_STAMINA: - HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, -removeValue, apply); - UpdateStatBuffMod(STAT_STAMINA); + if (apply) + GetStats().RemoveBaseStatModifier(StatType::Stamina, removeValue); + else + GetStats().AddBaseStatModifier(StatType::Stamina, removeValue); break; case ITEM_MOD_DEFENSE_SKILL_RATING: 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); break; case ITEM_MOD_AGILITY: - HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, addValue, apply); - UpdateStatBuffMod(STAT_AGILITY); + if (apply) + GetStats().AddBaseStatModifier(StatType::Agility, addValue); + else + GetStats().RemoveBaseStatModifier(StatType::Agility, addValue); break; case ITEM_MOD_STRENGTH: - HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, addValue, apply); - UpdateStatBuffMod(STAT_STRENGTH); + if (apply) + GetStats().AddBaseStatModifier(StatType::Strength, addValue); + else + GetStats().RemoveBaseStatModifier(StatType::Strength, addValue); break; case ITEM_MOD_INTELLECT: - HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, addValue, apply); - UpdateStatBuffMod(STAT_INTELLECT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Intellect, addValue); + else + GetStats().RemoveBaseStatModifier(StatType::Intellect, addValue); break; case ITEM_MOD_SPIRIT: - HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, addValue, apply); - UpdateStatBuffMod(STAT_SPIRIT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Spirit, addValue); + else + GetStats().RemoveBaseStatModifier(StatType::Spirit, addValue); break; case ITEM_MOD_STAMINA: - HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, addValue, apply); - UpdateStatBuffMod(STAT_STAMINA); + if (apply) + GetStats().AddBaseStatModifier(StatType::Spirit, addValue); + else + GetStats().RemoveBaseStatModifier(StatType::Spirit, addValue); break; case ITEM_MOD_DEFENSE_SKILL_RATING: ApplyRatingMod(CR_DEFENSE_SKILL, int32(addValue), apply); @@ -13042,28 +13061,38 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool break; case ITEM_MOD_AGILITY: TC_LOG_DEBUG("entities.player.items", "+ %u AGILITY", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_AGILITY); + if (apply) + GetStats().AddBaseStatModifier(StatType::Agility, enchant_amount); + else + GetStats().RemoveBaseStatModifier(StatType::Agility, enchant_amount); break; case ITEM_MOD_STRENGTH: TC_LOG_DEBUG("entities.player.items", "+ %u STRENGTH", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_STRENGTH); + if (apply) + GetStats().AddBaseStatModifier(StatType::Strength, enchant_amount); + else + GetStats().RemoveBaseStatModifier(StatType::Strength, enchant_amount); break; case ITEM_MOD_INTELLECT: TC_LOG_DEBUG("entities.player.items", "+ %u INTELLECT", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_INTELLECT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Intellect, enchant_amount); + else + GetStats().RemoveBaseStatModifier(StatType::Intellect, enchant_amount); break; case ITEM_MOD_SPIRIT: TC_LOG_DEBUG("entities.player.items", "+ %u SPIRIT", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_SPIRIT); + if (apply) + GetStats().AddBaseStatModifier(StatType::Spirit, enchant_amount); + else + GetStats().RemoveBaseStatModifier(StatType::Spirit, enchant_amount); break; case ITEM_MOD_STAMINA: TC_LOG_DEBUG("entities.player.items", "+ %u STAMINA", enchant_amount); - HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply); - UpdateStatBuffMod(STAT_STAMINA); + if (apply) + GetStats().AddBaseStatModifier(StatType::Stamina, enchant_amount); + else + GetStats().RemoveBaseStatModifier(StatType::Stamina, enchant_amount); break; case ITEM_MOD_DEFENSE_SKILL_RATING: 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 SetCanModifyStats(true); - UpdateAllStats(); + UpdateAllRatings(); + UpdateAllResistances(); // restore remembered power/health values (but not more max values) SetHealth(savedHealth); @@ -20002,8 +20032,8 @@ void Player::_SaveStats(CharacterDatabaseTransaction& trans) const for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) stmt->setUInt32(index++, GetMaxPower(PowerType(i))); - for (uint8 i = 0; i < MAX_STATS; ++i) - stmt->setUInt32(index++, GetStat(StatType(i))); + for (StatType stat : AllStats) + stmt->setUInt32(index++, GetStat(stat)); for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) stmt->setUInt32(index++, GetResistance(SpellSchools(i))); @@ -20028,9 +20058,9 @@ void Player::outDebugValues() const 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", "AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY), GetStat(STAT_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", "STAMINA is: \t\t%f", GetStat(STAT_STAMINA)); + 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%i\t\tSPIRIT is: \t\t%i", GetStat(StatType::Intellect), GetStat(StatType::Spirit)); + 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", "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)); @@ -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 - pet->UpdateAllStats(); pet->SetFullHealth(); pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA)); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 8df6d1bd52..bdbde05eb0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1742,8 +1742,6 @@ class TC_GAME_API Player : public Unit, public GridObject float GetHealthBonusFromStamina(); float GetManaBonusFromIntellect(); - bool UpdateStats(StatType stat) override; - bool UpdateAllStats() override; void ApplySpellPenetrationBonus(int32 amount, bool apply); void UpdateResistances(uint32 school) override; void UpdateArmor() override; diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h index 425aaeae7e..63256b8e42 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -52,8 +52,6 @@ class TC_GAME_API Totem : public Minion void SetTotemDuration(uint32 duration) { m_duration = duration; } TotemType GetTotemType() const { return m_type; } - bool UpdateStats(StatType /*stat*/) override { return true; } - bool UpdateAllStats() override { return true; } void UpdateResistances(uint32 /*school*/) override { } void UpdateArmor() override { } void UpdateMaxHealth() override { } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 72c28331c5..c745d02bc3 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -150,9 +150,9 @@ void Unit::UpdatePowerRegeneration(PowerType powerType) baseRegen += powerRegenMod; // SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5 - float spiritRegen = GetStat(STAT_SPIRIT) * DBCManager::GetGtOCTRegenMPPerSpirit(getClass(), getLevel()); - if (GetStat(STAT_INTELLECT) > 0.0f) - spiritRegen *= std::sqrt(GetStat(STAT_INTELLECT)); + float spiritRegen = GetStat(StatType::Spirit) * DBCManager::GetGtOCTRegenMPPerSpirit(getClass(), getLevel()); + if (GetStat(StatType::Intellect) > 0.0f) + spiritRegen *= std::sqrt(GetStat(StatType::Intellect)); // SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus 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) { if (HasAuraType(SPELL_AURA_OVERRIDE_SPELL_POWER_BY_AP_PCT)) @@ -334,37 +276,6 @@ void Player::UpdateSpellHealingPercentTaken() 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) { 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)) ratio = hpBase->ratio; - float stamina = GetStat(STAT_STAMINA); + float stamina = GetStat(StatType::Stamina); float baseStam = std::min(20.0f, stamina); float moreStam = stamina - baseStam; @@ -422,7 +333,7 @@ float Player::GetHealthBonusFromStamina() float Player::GetManaBonusFromIntellect() { // 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 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; 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 { - float strengthValue = std::max((GetStat(STAT_STRENGTH) - 10.0f) * entry->AttackPowerPerStrength, 0.0f); - float agilityValue = std::max((GetStat(STAT_AGILITY) - 10.0f) * entry->AttackPowerPerAgility, 0.0f); + float strengthValue = std::max((GetStat(StatType::Strength) - 10.0f) * entry->AttackPowerPerStrength, 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 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; } @@ -917,8 +828,6 @@ void Player::_ApplyAllStatBonuses() _ApplyAllItemMods(); SetCanModifyStats(true); - - UpdateAllStats(); } void Player::_RemoveAllStatBonuses() @@ -929,8 +838,6 @@ void Player::_RemoveAllStatBonuses() _RemoveAllAuraStatMods(); 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) { 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) { if (school > SPELL_SCHOOL_NORMAL) @@ -1177,7 +1013,7 @@ void Guardian::UpdateArmor() void Guardian::UpdateMaxHealth() { 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 value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreateHealth(); @@ -1195,7 +1031,7 @@ void Guardian::UpdateMaxPower(PowerType 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 value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetCreatePowerValue(power); @@ -1212,7 +1048,7 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged) return; float ap_per_strength = 2.0f; - float val = GetStat(STAT_STRENGTH) - 20.0f; + float val = GetStat(StatType::Strength) - 20.0f; val *= ap_per_strength; diff --git a/src/server/game/Entities/Unit/Stats.cpp b/src/server/game/Entities/Unit/Stats.cpp index 0a8e3c2bec..bfa9ad5f46 100644 --- a/src/server/game/Entities/Unit/Stats.cpp +++ b/src/server/game/Entities/Unit/Stats.cpp @@ -16,4 +16,315 @@ */ #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(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(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(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(aurEff->GetMiscValue()); + if (auraStatType == StatType::AllStats || auraStatType == statType) + return true; + + if (auraStatType == StatType::PrimaryStat && statType == GetPrimaryStat()) + return true; + + return false; + }); + + return modifier; +} diff --git a/src/server/game/Entities/Unit/Stats.h b/src/server/game/Entities/Unit/Stats.h index 0dc1dcff27..ad0d8e5a0c 100644 --- a/src/server/game/Entities/Unit/Stats.h +++ b/src/server/game/Entities/Unit/Stats.h @@ -19,10 +19,34 @@ #define _Stats_h__ #include "Define.h" +#include "Util.h" +#include 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 AllStats = +{ + StatType::Strength, + StatType::Agility, + StatType::Stamina, + StatType::Intellect, + StatType::Spirit +}; + +class TC_GAME_API Stats { public: Stats() = delete; @@ -31,6 +55,31 @@ public: ~Stats(); 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 _baseStats; + std::array _baseStatModifiers; + std::array _basePctMultipliers; + std::array _totalModifiers; + std::array _totalPctMultipliers; + + void updateStat(StatType statType); + float getAuraMultiplierForStatType(AuraType auraType, StatType statType) const; + int32 getAuraModifierForStatType(AuraType auraType, StatType statType) const; + }; #endif // _Stats_h__ diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ea05241317..70b129f5b7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -79,6 +79,7 @@ #include "SpellInfo.h" #include "SpellMgr.h" #include "SpellPackets.h" +#include "Stats.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -306,7 +307,8 @@ Unit::Unit(bool isWorldObject) : m_charmer(nullptr), m_charmed(nullptr), i_motionMaster(std::make_unique(this)), m_vehicle(nullptr), 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(this)) + i_AI(nullptr), m_aiLocked(false), m_spellHistory(std::make_unique(this)), + _stats(std::make_unique(this)) { m_objectType |= TYPEMASK_UNIT; m_objectTypeId = TYPEID_UNIT; @@ -348,8 +350,6 @@ Unit::Unit(bool isWorldObject) : m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE; } - m_createStats.fill(0.0f); - m_attacking = nullptr; m_modMeleeHitChance = 0.0f; m_modRangedHitChance = 0.0f; @@ -4807,82 +4807,6 @@ void Unit::UpdateResistanceBuffModsMod(SpellSchools school) 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(UNIT_MOD_STAT_START + AsUnderlyingType(stat)); - - // includes value from items and enchantments - float baseModValue = GetFlatModifierValue(unitMod, BASE_VALUE); - if (IsGuardian()) - baseModValue = static_cast(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) { 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 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 if (withSpellPowerPctMod) @@ -7315,7 +7239,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask, bool withSpell // Check if we are ever using mana - PaperDollFrame.lua 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 if (withSpellPowerPctMod) @@ -8972,13 +8896,6 @@ void Unit::UpdateUnitMod(UnitMods 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: UpdateArmor(); break; @@ -9101,19 +9018,6 @@ void Unit::UpdateAllDamagePctDoneMods() 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 { if (unitMod >= UNIT_MOD_END) @@ -9150,25 +9054,6 @@ SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const 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 { switch (unitMod) @@ -10735,7 +10620,6 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) // this enables pet details window (Shift+P) pet->InitPetCreateSpells(); //pet->InitLevelupSpellsForLevel(); - pet->UpdateAllStats(); pet->SetFullHealth(); pet->CastSpell(pet, 99289, true); // Energize pet->SetReactState(REACT_ASSIST); @@ -14383,3 +14267,16 @@ void Unit::SetGameClientMovingMe(GameClient* 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)); +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 34b8dfa689..17bcf22e98 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -18,20 +18,22 @@ #ifndef __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 #include #include #include #include +#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 uint8 MAX_SPELL_CHARM = 4; @@ -87,6 +89,7 @@ class Spell; class SpellCastTargets; class SpellHistory; class SpellInfo; +class Stats; class Totem; class Transport; class TransportBase; @@ -161,11 +164,6 @@ enum WeaponDamageRange 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_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, @@ -192,8 +190,6 @@ enum UnitMods UNIT_MOD_DAMAGE_RANGED, UNIT_MOD_END, // 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_END = UNIT_MOD_RESISTANCE_ARCANE + 1, 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); } 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); } 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; 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); } uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); } void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); } uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); } 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); } 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 UpdateAllDamagePctDoneMods(); - float GetTotalStatValue(StatType stat) const; float GetTotalAuraModValue(UnitMods unitMod) const; SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const; - StatType GetStatByAuraGroup(UnitMods unitMod) const; PowerType GetPowerTypeByAuraGroup(UnitMods unitMod) const; bool CanModifyStats() const { return m_canModifyStats; } 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 UpdateAllResistances(); virtual void UpdateArmor() = 0; @@ -1665,8 +1649,6 @@ class TC_GAME_API Unit : public WorldObject void _UpdateAutoRepeatSpell(); - std::array m_createStats; - AttackerSet m_attackers; Unit* m_attacking; @@ -1819,6 +1801,12 @@ class TC_GAME_API Unit : public WorldObject std::unordered_map m_pendingMovementChanges; /* Player Movement fields END*/ + + private: + std::unique_ptr _stats; + public: + Stats& GetStats() const; + int32 GetStat(StatType statType) const; }; namespace Trinity diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 9333513e9f..00d0ceb42a 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -55,6 +55,7 @@ #include "SpellAuras.h" #include "SpellMgr.h" #include "SpellScript.h" +#include "Stats.h" #include "StringConvert.h" #include "TemporarySummon.h" #include "TerrainMgr.h" @@ -3282,10 +3283,9 @@ void ObjectMgr::LoadPetLevelInfo() pLevelInfo->mana = fields[3].GetUInt16(); pLevelInfo->armor = fields[9].GetUInt32(); - for (int i = 0; i < MAX_STATS; i++) - { - pLevelInfo->stats[i] = fields[i+4].GetUInt16(); - } + + for (StatType stat : AllStats) + pLevelInfo->stats[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 4].GetUInt16(); ++count; } @@ -3705,7 +3705,7 @@ void ObjectMgr::LoadPlayerInfo() { struct RaceStats { - std::array StatModifier = { }; + std::array StatModifier = { }; }; std::array raceStatModifiers = { }; @@ -3732,8 +3732,8 @@ void ObjectMgr::LoadPlayerInfo() continue; } - for (uint8 i = 0; i < MAX_STATS; ++i) - raceStatModifiers[current_race].StatModifier[i] = fields[i + 1].GetInt16(); + for (StatType stat : AllStats) + raceStatModifiers[current_race].StatModifier[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 1].GetInt16(); } while (raceStatsResult->NextRow()); @@ -3781,8 +3781,8 @@ void ObjectMgr::LoadPlayerInfo() playerInfo->levelInfo = std::make_unique(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1]; - for (uint8 i = 0; i < MAX_STATS; i++) - levelInfo.stats[i] = fields[i + 2].GetUInt16() + raceStatModifiers[race].StatModifier[i]; + for (StatType stat : AllStats) + 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) *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) { switch (_class) { case CLASS_WARRIOR: - info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 9 && !(lvl%2) ? 1: 0); break; case CLASS_PALADIN: - info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0); - info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 3 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 6 && (lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 7 ? 1: 0); break; case CLASS_HUNTER: - info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); - info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0); - info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 4 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 8 && (lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); break; case CLASS_ROGUE: - info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); - info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0); - info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 5 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 8 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0)); break; case CLASS_PRIEST: - info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0); - info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 5 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 3 ? 1: 0); break; case CLASS_SHAMAN: - info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); - info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0); - info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0); - info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0); - info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 4 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 7 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 5 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 4 ? 1: 0); break; case CLASS_MAGE: - info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0); - info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0)); - info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 5 ? 1: 0); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); break; case CLASS_WARLOCK: - info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); - info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0); - info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); - info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 9 && !(lvl%2) ? 1: 0); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0)); break; case CLASS_DRUID: - info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0)); - info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0)); - info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0)); - info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0)); - info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Strength)] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Stamina)] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Agility)] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Intellect)] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0)); + info->stats[AsUnderlyingType(StatType::Spirit)] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0)); } } } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 208a85534a..56c40c3449 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -672,7 +672,7 @@ typedef std::vector PlayerCreateInfoItems; struct PlayerLevelInfo { - uint16 stats[MAX_STATS] = { }; + uint16 stats[5] = { }; }; typedef std::vector PlayerCreateInfoSpells; @@ -714,7 +714,7 @@ struct PlayerInfo struct PetLevelInfo { - uint16 stats[MAX_STATS] = { }; + uint16 stats[5] = { }; uint16 health = 0; uint16 mana = 0; uint16 armor = 0; diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 0369e70ecb..2000b59cd3 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -405,7 +405,7 @@ namespace WorldPackets int32 Level = 0; int32 HealthDelta = 0; std::array PowerDelta = { }; - std::array StatDelta = { }; + std::array StatDelta = { }; }; class BindPointUpdate final : public ServerPacket diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 0ebc2181fc..deb920ac2f 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -16,6 +16,11 @@ */ #include "SpellAuraEffects.h" + +#include + +#include + #include "Battlefield.h" #include "BattlefieldMgr.h" #include "Battleground.h" @@ -41,14 +46,13 @@ #include "SpellHistory.h" #include "SpellMgr.h" #include "SpellScript.h" +#include "Stats.h" #include "ThreatManager.h" #include "Unit.h" #include "Util.h" #include "Vehicle.h" #include "Weather.h" #include "WorldPacket.h" -#include -#include 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))) return; - if (GetMiscValue() < -2 || GetMiscValue() > 4) + StatType stat = static_cast(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(); - int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_STAT, true, GetMiscValue()); - 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)); - } - } + target->GetStats().UpdateTotalStatModifier(stat); } void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode, bool apply) const { if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; + StatType stat = static_cast(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(); - - 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); - } - } - } + target->GetStats().UpdateBaseStatMultiplier(stat); } 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))) return; - if (GetMiscValue() < -1 || GetMiscValue() > 4) + StatType stat = static_cast(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; } Unit* target = aurApp->GetTarget(); - - // 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(CalculatePct(target->GetMaxHealth(), healthPct), (zeroHealth ? 0 : 1))); + target->GetStats().UpdateTotalStatMultiplier(stat); } void AuraEffect::HandleAuraModResistenceOfStatPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 8bac941329..d82bb8d918 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -4388,7 +4388,7 @@ class spell_gen_vengeance : public AuraScript 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); int32 bp = std::min(damageBonus, healthCap); 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(healthCap, CalculatePct(damageLastTwoSeconds, 33)); if (damageLastTwoSeconds) target->CastSpell(target, SPELL_VENGEANCE_TRIGGERED, CastSpellExtraArgs(true).AddSpellBP0(damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT1, damageLastTwoSeconds).AddSpellMod(SPELLVALUE_BASE_POINT2, damageLastTwoSeconds)); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index a0e9408a06..1c53e2acd4 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -4213,8 +4213,8 @@ public: PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - float str = caster->GetStat(STAT_STRENGTH); - float agi = caster->GetStat(STAT_AGILITY); + float str = caster->GetStat(StatType::Strength); + float agi = caster->GetStat(StatType::Agility); switch (aurEff->GetId()) { @@ -4359,10 +4359,10 @@ public: PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - float str = caster->GetStat(STAT_STRENGTH); - float agi = caster->GetStat(STAT_AGILITY); - float intl = caster->GetStat(STAT_INTELLECT); - float spi = caster->GetStat(STAT_SPIRIT); + int32 str = caster->GetStat(StatType::Strength); + int32 agi = caster->GetStat(StatType::Agility); + int32 intl = caster->GetStat(StatType::Intellect); + int32 spi = caster->GetStat(StatType::Spirit); float stat = 0.0f; uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; diff --git a/src/server/scripts/Spells/spell_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp index 2e84bad6b0..7bc8f90e54 100644 --- a/src/server/scripts/Spells/spell_pet.cpp +++ b/src/server/scripts/Spells/spell_pet.cpp @@ -100,7 +100,7 @@ class spell_warl_pet_scaling_01 : public AuraScript { 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; switch (pet->GetEntry()) @@ -180,7 +180,7 @@ class spell_warl_pet_scaling_02 : public AuraScript { if (Player* owner = pet->GetOwner()) { - float intellect = owner->GetStat(STAT_INTELLECT); + float intellect = owner->GetStat(StatType::Intellect); float manaBonus = 0.0f; 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)) 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)) 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()) { - float stamina = CalculatePct(owner->GetStat(STAT_STAMINA), 30); + float stamina = CalculatePct(owner->GetStat(StatType::Stamina), 30); float staminaBonus = stamina * 7.5f; float maxHealthBonus = owner->CountPctFromMaxHealth(50); @@ -1063,7 +1063,7 @@ class spell_mage_water_elemental_scaling_02 : public AuraScript { 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 maxManaBonus = owner->CountPctFromMaxPower(POWER_MANA, 50); diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index be282f0533..055ecc2ba9 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -847,7 +847,7 @@ class spell_sha_mana_tide_totem : public AuraScript return; if (Unit* summoner = caster->ToTempSummon()->GetSummoner()) - amount = CalculatePct(summoner->GetStat(STAT_SPIRIT), amount); + amount = CalculatePct(summoner->GetStat(StatType::Spirit), amount); else amount = 0; } diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 4899be6c0c..a5d6a287ec 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -227,17 +227,6 @@ enum MoneyConstants 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 { POWER_MANA = 0,