Core/Units: Fix stats from items being incorrectly affected by SPELL_AURA_MOD_PERCENT_STAT (#31936)

This commit is contained in:
luis
2026-09-17 15:51:31 -03:00
parent 2cf2860cec
commit 7baf8f83bb
5 changed files with 80 additions and 84 deletions
+16 -16
View File
@@ -2445,7 +2445,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
//set create powers
SetCreateMana(basemana);
SetArmor(int32(m_createStats[STAT_AGILITY] * 2), 0);
SetArmor(int32(GetCreateStat(STAT_AGILITY) * 2), 0);
InitStatBuffMods();
@@ -2504,7 +2504,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::DodgePercentage), 0.0f);
// set armor (resistance 0) to original value (create_agility*2)
SetArmor(int32(m_createStats[STAT_AGILITY] * 2), 0);
SetArmor(int32(GetCreateStat(STAT_AGILITY) * 2), 0);
SetBonusResistanceMod(SPELL_SCHOOL_NORMAL, 0);
// set other resistance to original value (0)
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
@@ -8077,23 +8077,23 @@ void Player::_ApplyItemBonuses(Item* item, 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);
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_AGILITY);
break;
case ITEM_MOD_STRENGTH: //modify strength
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_STRENGTH);
break;
case ITEM_MOD_INTELLECT: //modify intellect
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_INTELLECT);
break;
case ITEM_MOD_SPIRIT: //modify spirit
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_SPIRIT);
break;
case ITEM_MOD_STAMINA: //modify stamina
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_STAMINA);
break;
case ITEM_MOD_DEFENSE_SKILL_RATING:
@@ -8257,28 +8257,28 @@ void Player::_ApplyItemBonuses(Item* item, uint8 slot, bool apply)
ApplyRatingMod(CR_STURDINESS, int32(val), apply);
break;
case ITEM_MOD_AGI_STR_INT:
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_AGILITY);
UpdateStatBuffMod(STAT_STRENGTH);
UpdateStatBuffMod(STAT_INTELLECT);
break;
case ITEM_MOD_AGI_STR:
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_AGILITY);
UpdateStatBuffMod(STAT_STRENGTH);
break;
case ITEM_MOD_AGI_INT:
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_AGILITY);
UpdateStatBuffMod(STAT_INTELLECT);
break;
case ITEM_MOD_STR_INT:
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(val), apply);
HandleStatFlatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(val), apply);
UpdateStatBuffMod(STAT_STRENGTH);
UpdateStatBuffMod(STAT_INTELLECT);
break;
+3 -1
View File
@@ -106,10 +106,11 @@ int32 Unit::GetCreatePowerValue(Powers power) const
bool Player::UpdateStats(Stats stat)
{
// value = ((base_value * base_pct) + total_value) * total_pct
// value = (base_value * base_pct) * total_pct + total_value * total_pct
float value = GetTotalStatValue(stat);
SetStat(stat, int32(value));
UpdateStatBuffMod(stat);
if (stat == STAT_STAMINA || stat == STAT_INTELLECT || stat == STAT_STRENGTH)
{
@@ -201,6 +202,7 @@ bool Player::UpdateAllStats()
{
float value = GetTotalStatValue(Stats(i));
SetStat(Stats(i), int32(value));
UpdateStatBuffMod(Stats(i));
}
UpdateArmor();
+50 -43
View File
@@ -348,7 +348,6 @@ Unit::Unit(bool isWorldObject) :
for (uint8 i = 0; i < UNIT_MOD_END; ++i)
{
m_auraFlatModifiersGroup[i][BASE_VALUE] = 0.0f;
m_auraFlatModifiersGroup[i][BASE_PCT_EXCLUDE_CREATE] = 100.0f;
m_auraFlatModifiersGroup[i][TOTAL_VALUE] = 0.0f;
m_auraPctModifiersGroup[i][BASE_PCT] = 1.0f;
m_auraPctModifiersGroup[i][TOTAL_PCT] = 1.0f;
@@ -369,7 +368,6 @@ Unit::Unit(bool isWorldObject) :
m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
}
m_createStats = { };
m_floatStatPosBuff = { };
m_floatStatNegBuff = { };
@@ -5341,30 +5339,18 @@ void Unit::UpdateStatBuffMod(Stats stat)
{
float modPos = 0.0f;
float modNeg = 0.0f;
float factor = 0.0f;
UnitMods const unitMod = static_cast<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
// includes value from items and enchantments
float modValue = GetFlatModifierValue(unitMod, BASE_VALUE);
if (modValue > 0.f)
modPos += modValue;
else
modNeg += modValue;
if (IsGuardian())
{
modValue = static_cast<Guardian*>(this)->GetBonusStatFromOwner(stat);
float modValue = static_cast<Guardian*>(this)->GetBonusStatFromOwner(stat);
if (modValue > 0.f)
modPos += modValue;
else
modNeg += modValue;
}
// SPELL_AURA_MOD_STAT_BONUS_PCT only affects BASE_VALUE
modPos = CalculatePct(modPos, std::max(GetFlatModifierValue(unitMod, BASE_PCT_EXCLUDE_CREATE), -100.0f));
modNeg = CalculatePct(modNeg, std::max(GetFlatModifierValue(unitMod, BASE_PCT_EXCLUDE_CREATE), -100.0f));
modPos += GetTotalAuraModifier(SPELL_AURA_MOD_STAT, [stat](AuraEffect const* aurEff) -> bool
{
if ((aurEff->GetMiscValue() < 0 || aurEff->GetMiscValue() == stat) && aurEff->GetAmount() > 0)
@@ -5379,25 +5365,35 @@ void Unit::UpdateStatBuffMod(Stats stat)
return false;
});
factor = GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [stat](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat)
return true;
return false;
});
float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE);
baseValue *= GetPctModifierValue(unitMod, BASE_PCT);
baseValue *= GetPctModifierValue(unitMod, TOTAL_PCT);
float baseModFromPct = baseValue - GetFlatModifierValue(unitMod, BASE_VALUE);
factor *= GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [stat](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat)
return true;
return false;
});
float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
AddPct(totalValue, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_STAT_BONUS_PCT, stat));
float totalModFromPct = totalValue * GetPctModifierValue(unitMod, TOTAL_PCT) - totalValue;
float modsFromPct = baseModFromPct + totalModFromPct;
modPos *= factor;
modNeg *= factor;
// recalculate stat bonuses not applied by auras
float nonAuraMod = totalValue;
nonAuraMod -= modPos; // remove positive auras
nonAuraMod -= modNeg; // remove negative auras
m_floatStatPosBuff[stat] = modPos;
m_floatStatNegBuff[stat] = modNeg;
// add item stat bonuses to positive
if (nonAuraMod > 0.f)
modPos += nonAuraMod;
else
modNeg += nonAuraMod;
// add pct mods
if (modsFromPct > 0.f)
modPos += modsFromPct;
else
modNeg += modsFromPct;
m_floatStatPosBuff[stat] = std::round(modPos);
m_floatStatNegBuff[stat] = std::round(modNeg);
UpdateStatBuffModForClient(stat);
}
@@ -5408,6 +5404,18 @@ void Unit::UpdateStatBuffModForClient(Stats stat)
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::StatNegBuff, stat), int32(m_floatStatNegBuff[stat]));
}
void Unit::SetCreateStat(Stats stat, float val)
{
UnitMods const unitMod = static_cast<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
HandleStatFlatModifier(unitMod, BASE_VALUE, val, true);
}
float Unit::GetCreateStat(Stats stat) const
{
UnitMods const unitMod = static_cast<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
return GetFlatModifierValue(unitMod, BASE_VALUE);
}
void Unit::_RegisterDynObject(DynamicObject* dynObj)
{
m_dynObj.push_back(dynObj);
@@ -9891,7 +9899,6 @@ void Unit::HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifie
switch (modifierType)
{
case BASE_VALUE:
case BASE_PCT_EXCLUDE_CREATE:
case TOTAL_VALUE:
m_auraFlatModifiersGroup[unitMod][modifierType] += apply ? amount : -amount;
break;
@@ -10146,18 +10153,18 @@ void Unit::UpdateAllDamagePctDoneMods()
float Unit::GetTotalStatValue(Stats stat) const
{
float createStat = GetCreateStat(stat); // retrieved early to workaround a GCC false positive warning about out of bounds array access (conversion to UnitMods confuses it)
UnitMods unitMod = UnitMods(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
// value = ((base_value * base_pct) + total_value) * total_pct
float value = CalculatePct(GetFlatModifierValue(unitMod, BASE_VALUE), std::max(GetFlatModifierValue(unitMod, BASE_PCT_EXCLUDE_CREATE), -100.0f));
value += createStat;
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
// value = (base_value * base_pct) * total_pct + total_value * total_pct
float baseValue = GetFlatModifierValue(unitMod, BASE_VALUE);
baseValue *= GetPctModifierValue(unitMod, BASE_PCT);
baseValue *= GetPctModifierValue(unitMod, TOTAL_PCT);
return value;
float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
AddPct(totalValue, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_STAT_BONUS_PCT, stat));
totalValue *= GetPctModifierValue(unitMod, TOTAL_PCT);
return baseValue + totalValue;
}
float Unit::GetTotalAuraModValue(UnitMods unitMod) const
@@ -10168,7 +10175,7 @@ float Unit::GetTotalAuraModValue(UnitMods unitMod) const
return 0.0f;
}
float value = CalculatePct(GetFlatModifierValue(unitMod, BASE_VALUE), std::max(GetFlatModifierValue(unitMod, BASE_PCT_EXCLUDE_CREATE), -100.0f));
float value = GetFlatModifierValue(unitMod, BASE_VALUE);
value *= GetPctModifierValue(unitMod, BASE_PCT);
value += GetFlatModifierValue(unitMod, TOTAL_VALUE);
value *= GetPctModifierValue(unitMod, TOTAL_PCT);
@@ -10220,7 +10227,7 @@ void Unit::UpdateResistances(uint32 school)
{
UnitMods unitMod = UnitMods(UNIT_MOD_RESISTANCE_START + school);
float value = CalculatePct(GetFlatModifierValue(unitMod, BASE_VALUE), std::max(GetFlatModifierValue(unitMod, BASE_PCT_EXCLUDE_CREATE), -100.0f));
float value = GetFlatModifierValue(unitMod, BASE_VALUE);
value *= GetPctModifierValue(unitMod, BASE_PCT);
float baseValue = value;
+8 -10
View File
@@ -159,17 +159,16 @@ typedef std::unordered_multimap<uint32 /*type*/, uint32 /*spellId*/> SpellImmune
enum UnitModifierFlatType
{
BASE_VALUE = 0,
BASE_PCT_EXCLUDE_CREATE = 1, // percent modifier affecting all stat values from auras and gear but not player base for level
TOTAL_VALUE = 2,
MODIFIER_TYPE_FLAT_END = 3
BASE_VALUE = 0, // flat value containing only fixed create stats, e.g. player_classlevelstats
TOTAL_VALUE = 1, // flat value containing flat modfiers from gear and auras
MODIFIER_TYPE_FLAT_END
};
enum UnitModifierPctType
{
BASE_PCT = 0,
TOTAL_PCT = 1,
MODIFIER_TYPE_PCT_END = 2
BASE_PCT = 0, // percent modifier affecting only BASE_VALUE
TOTAL_PCT = 1, // percent modifier affecting BASE_VALUE and TOTAL_VALUE
MODIFIER_TYPE_PCT_END
};
enum WeaponDamageRange
@@ -1441,7 +1440,7 @@ class TC_GAME_API Unit : public WorldObject
void UpdateStatBuffMod(Stats stat);
void UpdateStatBuffModForClient(Stats stat);
void SetStatSupportBuff(Stats stat, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::StatSupportBuff, stat), val); }
void SetCreateStat(Stats stat, float val) { m_createStats[stat] = val; }
void SetCreateStat(Stats stat, float val);
void SetCreateHealth(uint32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BaseHealth), val); }
uint32 GetCreateHealth() const { return m_unitData->BaseHealth; }
void SetCreateMana(uint32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BaseMana), val); }
@@ -1449,7 +1448,7 @@ class TC_GAME_API Unit : public WorldObject
virtual int32 GetCreatePowerValue(Powers power) const;
float GetPosStat(Stats stat) const { return m_unitData->StatPosBuff[stat]; }
float GetNegStat(Stats stat) const { return m_unitData->StatNegBuff[stat]; }
float GetCreateStat(Stats stat) const { return m_createStats[stat]; }
float GetCreateStat(Stats stat) const;
uint32 GetChannelSpellId() const { return m_unitData->ChannelData->SpellID; }
void SetChannelSpellId(uint32 channelSpellId)
@@ -1954,7 +1953,6 @@ class TC_GAME_API Unit : public WorldObject
bool m_ControlledByPlayer;
std::array<float, MAX_STATS> m_createStats;
std::array<float, MAX_STATS> m_floatStatPosBuff;
std::array<float, MAX_STATS> m_floatStatNegBuff;
@@ -4137,31 +4137,20 @@ void AuraEffect::HandleModBonusArmorPercent(AuraApplication const* aurApp, uint8
aurApp->GetTarget()->UpdateArmor();
}
void AuraEffect::HandleModStatBonusPercent(AuraApplication const* aurApp, uint8 mode, bool apply) const
void AuraEffect::HandleModStatBonusPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
{
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT)))
return;
Unit* target = aurApp->GetTarget();
if (GetMiscValue() < -1 || GetMiscValue() > 4)
if (GetMiscValue() < 0 || GetMiscValue() >= MAX_STATS)
{
TC_LOG_ERROR("spells", "WARNING: Misc Value for SPELL_AURA_MOD_STAT_BONUS_PCT 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)
{
target->HandleStatFlatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT_EXCLUDE_CREATE, float(GetAmount()), apply);
target->UpdateStatBuffMod(Stats(i));
}
}
target->UpdateStats(Stats(GetMiscValue()));
}
void AuraEffect::HandleOverrideSpellPowerByAttackPower(AuraApplication const* aurApp, uint8 mode, bool apply) const