From 0f6a99bd85068dadd775f01fc88f8b50e4c0a48f Mon Sep 17 00:00:00 2001 From: Ovahlord <18347559+ovahlord@users.noreply.github.com> Date: Fri, 12 Jun 2026 11:55:12 +0200 Subject: [PATCH] Core/Units: moved stats modifiers and multipliers into a struct for future ports of the remaining secondary stats --- src/server/game/Entities/Pet/Pet.cpp | 2 +- src/server/game/Entities/Player/Player.cpp | 8 +- src/server/game/Entities/Unit/Stats.cpp | 122 +++++++++--------- src/server/game/Entities/Unit/Stats.h | 35 +++-- src/server/game/Globals/ObjectMgr.cpp | 6 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 6 +- 6 files changed, 96 insertions(+), 83 deletions(-) diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 49604b82e0..0af08d4dcb 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -773,7 +773,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) if (pInfo->armor > 0) SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) GetStats().SetBaseStatValue(stat, pInfo->stats[AsUnderlyingType(stat)]); } else // not exist in DB, use some default fake data diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8856e86998..600ec27f9f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2230,7 +2230,7 @@ void Player::GiveLevel(uint8 level) packet.PowerDelta[4] = 0; packet.PowerDelta[5] = 0; - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) packet.StatDelta[AsUnderlyingType(stat)] = int32(info.stats[AsUnderlyingType(stat)]) - GetStats().GetBaseStatValue(stat); SendDirectMessage(packet.Write()); @@ -2250,7 +2250,7 @@ void Player::GiveLevel(uint8 level) SetCreateMana(basemana); // save base values (bonuses already included in stored stats) - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]); InitTalentForLevel(); @@ -2380,7 +2380,7 @@ void Player::InitStatsForLevel(bool reapplyMods) SetCreateMana(basemana); // save base values (bonuses already included in stored stats - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) GetStats().SetBaseStatValue(stat, info.stats[AsUnderlyingType(stat)]); //reset rating fields values @@ -20030,7 +20030,7 @@ void Player::_SaveStats(CharacterDatabaseTransaction& trans) const for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) stmt->setUInt32(index++, GetMaxPower(PowerType(i))); - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) stmt->setUInt32(index++, GetStat(stat)); for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) diff --git a/src/server/game/Entities/Unit/Stats.cpp b/src/server/game/Entities/Unit/Stats.cpp index e3b93a19e9..5ec7744072 100644 --- a/src/server/game/Entities/Unit/Stats.cpp +++ b/src/server/game/Entities/Unit/Stats.cpp @@ -23,11 +23,8 @@ Stats::~Stats() = default; -Stats::Stats(Unit* owner) : _owner(owner), _baseStats({ }), _baseStatModifiers({ }), _basePctMultipliers({ }), - _totalModifiers({ }), _totalPctMultipliers({ }) +Stats::Stats(Unit* owner) : _owner(owner), _primaryStatData({ }) { - _basePctMultipliers.fill(1.0); - _totalPctMultipliers.fill(1.0); } /// Returns the unmodified base value of the specified stat. @@ -36,7 +33,7 @@ Stats::Stats(Unit* owner) : _owner(owner), _baseStats({ }), _baseStatModifiers({ /// @return returns the base value fo the specified stat int32 Stats::GetBaseStatValue(StatType statType) const { - return _baseStats[AsUnderlyingType(statType)]; + return _primaryStatData[AsUnderlyingType(statType)].BaseStat; } /// Sets the unmodified base value of the specified stat and @@ -46,7 +43,7 @@ int32 Stats::GetBaseStatValue(StatType statType) const /// @param value The new base value that will be set void Stats::SetBaseStatValue(StatType statType, int32 value) { - _baseStats[AsUnderlyingType(statType)] = value; + _primaryStatData[AsUnderlyingType(statType)].BaseStat= value; updateStat(statType); } @@ -56,7 +53,7 @@ void Stats::SetBaseStatValue(StatType statType, int32 value) /// @param value The amount of the modifier that will be applied void Stats::AddBaseStatModifier(StatType statType, int32 value) { - _baseStatModifiers[AsUnderlyingType(statType)] += value; + _primaryStatData[AsUnderlyingType(statType)].BaseStatModifier += value; updateStat(statType); } @@ -66,7 +63,7 @@ void Stats::AddBaseStatModifier(StatType statType, int32 value) /// @param value The amount of the modifier that will be removed void Stats::RemoveBaseStatModifier(StatType statType, int32 value) { - _baseStatModifiers[AsUnderlyingType(statType)] -= value; + _primaryStatData[AsUnderlyingType(statType)].BaseStatModifier -= value; updateStat(statType); } @@ -75,19 +72,19 @@ void Stats::RemoveBaseStatModifier(StatType statType, int32 value) /// @param statType The stat which multiplier value will be updated void Stats::UpdateBaseStatMultiplier(StatType statType) { - if (statType == StatType::AllStats || statType == StatType::AllStats2) + if (statType == StatType::AllPrimaryStats || statType == StatType::AllPrimaryStats2) { - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) { float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_PERCENT_STAT, stat); - _basePctMultipliers[AsUnderlyingType(stat)] = multiplier; + _primaryStatData[AsUnderlyingType(stat)].BasePctMultiplier = multiplier; updateStat(stat); } } else { float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_PERCENT_STAT, statType); - _basePctMultipliers[AsUnderlyingType(statType)] = multiplier; + _primaryStatData[AsUnderlyingType(statType)].BasePctMultiplier = multiplier; updateStat(statType); } } @@ -97,19 +94,19 @@ void Stats::UpdateBaseStatMultiplier(StatType statType) /// @param statType The stat which modifier value will be updated void Stats::UpdateTotalStatModifier(StatType statType) { - if (statType == StatType::AllStats || statType == StatType::AllStats2) + if (statType == StatType::AllPrimaryStats || statType == StatType::AllPrimaryStats2) { - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) { int32 modifier = getAuraModifierForStatType(SPELL_AURA_MOD_STAT, stat); - _totalModifiers[AsUnderlyingType(stat)] = modifier; + _primaryStatData[AsUnderlyingType(stat)].TotalModifier = modifier; updateStat(stat); } } else { int32 modifier = getAuraModifierForStatType(SPELL_AURA_MOD_STAT, statType); - _totalModifiers[AsUnderlyingType(statType)] = modifier; + _primaryStatData[AsUnderlyingType(statType)].TotalModifier = modifier; updateStat(statType); } } @@ -119,19 +116,19 @@ void Stats::UpdateTotalStatModifier(StatType statType) /// @param statType The stat which multiplier value will be updated void Stats::UpdateTotalStatMultiplier(StatType statType) { - if (statType == StatType::AllStats || statType == StatType::AllStats2) + if (statType == StatType::AllPrimaryStats || statType == StatType::AllPrimaryStats2) { - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) { float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, stat); - _totalPctMultipliers[AsUnderlyingType(stat)] = multiplier; + _primaryStatData[AsUnderlyingType(stat)].TotalPctMultiplier = multiplier; updateStat(stat); } } else { float multiplier = getAuraMultiplierForStatType(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, statType); - _totalPctMultipliers[AsUnderlyingType(statType)] = multiplier; + _primaryStatData[AsUnderlyingType(statType)].TotalPctMultiplier = multiplier; updateStat(statType); } } @@ -200,41 +197,9 @@ void Stats::updateStat(StatType statType) { int32 negStatValue = 0; int32 posStatValue = 0; + int32 statValue = 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(std::round(flatStatValue * basePctMultiplier)); - if (flatStatValue < baseTotalValue) - posStatValue += baseTotalValue - flatStatValue; - else - negStatValue += baseTotalValue - flatStatValue; - - // ==== Calculate the total value (Total value buff auras) - int32 totalModifier = _totalModifiers[AsUnderlyingType(statType)]; - if (totalModifier > 0) - posStatValue += totalModifier; - else - negStatValue += totalModifier; - - int32 totalValue = baseTotalValue + totalModifier; - - // ==== Calculate the total value (Total pct multipliers) - double totalPctMultiplier = _totalPctMultipliers[AsUnderlyingType(statType)]; - int32 statValue = static_cast(std::round(totalValue * totalPctMultiplier)); - if (totalValue < statValue) - posStatValue += statValue - totalValue; - else - negStatValue += statValue - totalValue; + calculateStatValues(_primaryStatData[AsUnderlyingType(statType)], statValue, posStatValue, negStatValue); // ==== Apply the stats _owner->SetInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(statType), statValue); @@ -286,22 +251,63 @@ void Stats::updateStat(StatType statType) combatRatingMask |= aurEff->GetMiscValue(); } - Player* player = Object::ToPlayer(_owner); if (combatRatingMask) { + Player* player = Object::ToPlayer(_owner); for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating) if ((combatRatingMask & (1 << rating)) != 0) - player->ApplyRatingMod(CombatRating(rating), 0, true); + player->ApplyRatingMod(static_cast(rating), 0, true); } } } +void Stats::calculateStatValues(StatData const& statData, int32& statValue, int32& posStatValue, int32& negStatValue) +{ + negStatValue = 0; + posStatValue = 0; + + // ==== Calculate the base flat value (Level stats and item/enchantment mods) + int32 baseStatValue = statData.BaseStat; + int32 baseModifier = statData.BaseStatModifier; + if (baseModifier > 0) + posStatValue += baseModifier; + else + negStatValue += baseModifier; + + int32 flatStatValue = baseStatValue + baseModifier; + + // ==== Calculate the total base value (Base pct multiplier auras) + double basePctMultiplier = statData.BasePctMultiplier; + int32 baseTotalValue = static_cast(std::round(flatStatValue * basePctMultiplier)); + if (flatStatValue < baseTotalValue) + posStatValue += baseTotalValue - flatStatValue; + else + negStatValue += baseTotalValue - flatStatValue; + + // ==== Calculate the total value (Total value buff auras) + int32 totalModifier = statData.TotalModifier; + if (totalModifier > 0) + posStatValue += totalModifier; + else + negStatValue += totalModifier; + + int32 totalValue = baseTotalValue + totalModifier; + + // ==== Calculate the total value (Total pct multipliers) + double totalPctMultiplier = statData.TotalPctMultiplier; + statValue = static_cast(std::round(totalValue * totalPctMultiplier)); + if (totalValue < statValue) + posStatValue += statValue - totalValue; + else + negStatValue += statValue - totalValue; +} + 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::AllStats2 || auraStatType == statType) + if (auraStatType == StatType::AllPrimaryStats || auraStatType == StatType::AllPrimaryStats2 || auraStatType == statType) return true; return false; @@ -315,7 +321,7 @@ int32 Stats::getAuraModifierForStatType(AuraType auraType, StatType statType) co int32 modifier = _owner->GetTotalAuraModifier(auraType, [&](AuraEffect const* aurEff) { StatType auraStatType = static_cast(aurEff->GetMiscValue()); - if (auraStatType == StatType::AllStats || auraStatType == StatType::AllStats2 || auraStatType == statType) + if (auraStatType == StatType::AllPrimaryStats || auraStatType == StatType::AllPrimaryStats2 || auraStatType == statType) return true; return false; diff --git a/src/server/game/Entities/Unit/Stats.h b/src/server/game/Entities/Unit/Stats.h index 690486f615..1506b47320 100644 --- a/src/server/game/Entities/Unit/Stats.h +++ b/src/server/game/Entities/Unit/Stats.h @@ -27,17 +27,18 @@ enum AuraType : uint32; enum class StatType : int8 { - AllStats2 = -2, // There is only one spell using this and it also states that it increases all stats - AllStats = -1, - Strength = 0, - Agility = 1, - Stamina = 2, - Intellect = 3, - Spirit = 4, - Max + // Primary Stats + AllPrimaryStats2 = -2, // There is only one spell using this and it also states that it increases all stats + AllPrimaryStats = -1, + Strength = 0, + Agility = 1, + Stamina = 2, + Intellect = 3, + Spirit = 4, + Max, }; -static constexpr std::array AllStats = +static constexpr std::array AllPrimaryStats = { StatType::Strength, StatType::Agility, @@ -46,6 +47,15 @@ static constexpr std::array AllStats StatType::Spirit }; +struct StatData +{ + int32 BaseStat = 0; + int32 BaseStatModifier = 0; + int32 TotalModifier = 0; + double BasePctMultiplier = 1.0; + double TotalPctMultiplier = 1.0; +}; + class TC_GAME_API Stats { public: @@ -70,13 +80,10 @@ public: private: Unit* _owner; - std::array _baseStats; - std::array _baseStatModifiers; - std::array _basePctMultipliers; - std::array _totalModifiers; - std::array _totalPctMultipliers; + std::array _primaryStatData; void updateStat(StatType statType); + void calculateStatValues(StatData const& statData, int32& statValue, int32& posStatValue, int32& negStatValue); float getAuraMultiplierForStatType(AuraType auraType, StatType statType) const; int32 getAuraModifierForStatType(AuraType auraType, StatType statType) const; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 00d0ceb42a..ee149043be 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3284,7 +3284,7 @@ void ObjectMgr::LoadPetLevelInfo() pLevelInfo->armor = fields[9].GetUInt32(); - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) pLevelInfo->stats[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 4].GetUInt16(); ++count; @@ -3732,7 +3732,7 @@ void ObjectMgr::LoadPlayerInfo() continue; } - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) raceStatModifiers[current_race].StatModifier[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 1].GetInt16(); } while (raceStatsResult->NextRow()); @@ -3781,7 +3781,7 @@ void ObjectMgr::LoadPlayerInfo() playerInfo->levelInfo = std::make_unique(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); PlayerLevelInfo& levelInfo = playerInfo->levelInfo[current_level - 1]; - for (StatType stat : AllStats) + for (StatType stat : AllPrimaryStats) levelInfo.stats[AsUnderlyingType(stat)] = fields[AsUnderlyingType(stat) + 2].GetUInt16() + raceStatModifiers[race].StatModifier[AsUnderlyingType(stat)]; } } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 8aefc1dc2a..7df395e593 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -3570,7 +3570,7 @@ void AuraEffect::HandleAuraModStat(AuraApplication const* aurApp, uint8 mode, bo return; StatType stat = static_cast(GetMiscValue()); - if (stat < StatType::AllStats2 || stat >= StatType::Max) + if (stat < StatType::AllPrimaryStats2 || 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; @@ -3586,7 +3586,7 @@ void AuraEffect::HandleModPercentStat(AuraApplication const* aurApp, uint8 mode, return; StatType stat = static_cast(GetMiscValue()); - if (stat < StatType::AllStats2 || stat >= StatType::Max) + if (stat < StatType::AllPrimaryStats2 || stat >= StatType::Max) { TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_PERCENT_STAT ", GetId(), GetEffIndex(), GetMiscValue()); return; @@ -3676,7 +3676,7 @@ void AuraEffect::HandleModTotalPercentStat(AuraApplication const* aurApp, uint8 return; StatType stat = static_cast(GetMiscValue()); - if (stat < StatType::AllStats2 || stat >= StatType::Max) + if (stat < StatType::AllPrimaryStats2 || stat >= StatType::Max) { TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE ", GetId(), GetEffIndex(), GetMiscValue()); return;