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

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

No files matched your search

@@ -631,7 +631,15 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
SetStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(cInfo->resistance[SPELL_SCHOOL_ARCANE]));
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
@@ -194,8 +194,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr, bool updateLevel = true);
bool UpdateStats(StatType stat) override;
bool UpdateAllStats() override;
void UpdateResistances(uint32 school) override;
void UpdateArmor() override;
void UpdateMaxHealth() override;
@@ -380,7 +380,7 @@ bool Minion::IsWarlockMinion() const
Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject)
, 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)
{
@@ -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
+14 -15
View File
@@ -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);
}
+101 -72
View File
@@ -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));
-2
View File
@@ -1742,8 +1742,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
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;
-2
View File
@@ -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 { }
+12 -176
View File
@@ -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;
+311
View File
@@ -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<int32>(flatStatValue * basePctMultiplier);
if (flatStatValue < baseTotalValue)
posStatValue += baseTotalValue - flatStatValue;
else
negStatValue += flatStatValue - baseTotalValue;
// ==== Calculate the total value (Total value buff auras)
int32 totalModifier = _totalModifiers[AsUnderlyingType(statType)];
if (totalModifier > 0)
posStatValue += totalModifier;
else
negStatValue += totalModifier;
int32 totalValue = baseTotalValue + baseModifier;
// ==== Calculate the total value (Total pct multipliers)
double totalPctMultiplier = _totalPctMultipliers[AsUnderlyingType(statType)];
int32 statValue = static_cast<int32>(totalValue * totalPctMultiplier);
if (totalValue < statValue)
posStatValue += statValue - totalValue;
else
negStatValue += statValue - totalValue;
// ==== Apply the stats
_owner->SetInt32Value(UNIT_FIELD_STAT0 + AsUnderlyingType(statType), statValue);
_owner->SetInt32Value(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(statType), posStatValue);
_owner->SetInt32Value(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(statType), negStatValue);
switch (statType)
{
case StatType::Strength:
_owner->UpdateAttackPowerAndDamage();
break;
case StatType::Agility:
_owner->UpdateAttackPowerAndDamage(false);
_owner->UpdateAttackPowerAndDamage(true);
_owner->UpdateArmor();
if (Player* player = Object::ToPlayer(_owner))
{
player->UpdateAllCritPercentages();
player->UpdateDodgePercentage();
}
break;
case StatType::Stamina:
_owner->UpdateMaxHealth();
break;
case StatType::Intellect:
_owner->UpdateMaxPower(POWER_MANA);
_owner->UpdatePowerRegeneration(POWER_MANA);
if (Player* player = Object::ToPlayer(_owner))
{
player->UpdateAllSpellCritChances();
player->UpdateSpellDamageAndHealingBonus();
}
break;
case StatType::Spirit:
_owner->UpdatePowerRegeneration(POWER_MANA);
break;
default:
break;
}
}
float Stats::getAuraMultiplierForStatType(AuraType auraType, StatType statType) const
{
float multiplier = _owner->GetTotalAuraMultiplier(auraType, [&](AuraEffect const* aurEff)
{
StatType auraStatType = static_cast<StatType>(aurEff->GetMiscValue());
if (auraStatType == StatType::AllStats || auraStatType == statType)
return true;
if (auraStatType == StatType::PrimaryStat && statType == GetPrimaryStat())
return true;
return false;
});
return multiplier;
}
int32 Stats::getAuraModifierForStatType(AuraType auraType, StatType statType) const
{
int32 modifier = _owner->GetTotalAuraModifier(auraType, [&](AuraEffect const* aurEff)
{
StatType auraStatType = static_cast<StatType>(aurEff->GetMiscValue());
if (auraStatType == StatType::AllStats || auraStatType == statType)
return true;
if (auraStatType == StatType::PrimaryStat && statType == GetPrimaryStat())
return true;
return false;
});
return modifier;
}
+50 -1
View File
@@ -19,10 +19,34 @@
#define _Stats_h__
#include "Define.h"
#include "Util.h"
#include <array>
class Unit;
enum AuraType : uint32;
class TC_GAME_API //Stats
enum class StatType : int8
{
PrimaryStat = -2,
AllStats = -1,
Strength = 0,
Agility = 1,
Stamina = 2,
Intellect = 3,
Spirit = 4,
Max
};
static constexpr std::array<StatType, AsUnderlyingType(StatType::Max)> AllStats =
{
StatType::Strength,
StatType::Agility,
StatType::Stamina,
StatType::Intellect,
StatType::Spirit
};
class TC_GAME_API Stats
{
public:
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<int32, AsUnderlyingType(StatType::Max)> _baseStats;
std::array<int32, AsUnderlyingType(StatType::Max)> _baseStatModifiers;
std::array<double, AsUnderlyingType(StatType::Max)> _basePctMultipliers;
std::array<int32, AsUnderlyingType(StatType::Max)> _totalModifiers;
std::array<double, AsUnderlyingType(StatType::Max)> _totalPctMultipliers;
void updateStat(StatType statType);
float getAuraMultiplierForStatType(AuraType auraType, StatType statType) const;
int32 getAuraModifierForStatType(AuraType auraType, StatType statType) const;
};
#endif // _Stats_h__
+18 -121
View File
@@ -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<MotionMaster>(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<SpellHistory>(this))
i_AI(nullptr), m_aiLocked(false), m_spellHistory(std::make_unique<SpellHistory>(this)),
_stats(std::make_unique<Stats>(this))
{
m_objectType |= TYPEMASK_UNIT;
m_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<UnitMods>(UNIT_MOD_STAT_START + AsUnderlyingType(stat));
// includes value from items and enchantments
float baseModValue = GetFlatModifierValue(unitMod, BASE_VALUE);
if (IsGuardian())
baseModValue = static_cast<Guardian*>(this)->GetBonusStatFromOwner(stat);
if (baseModValue >= 0.0f)
modPos = baseModValue;
else
modNeg = baseModValue;
previousValue = baseModValue;
// SPELL_AURA_MOD_PERCENT_STAT affects the base_value as well as the create stat
previousValue += GetCreateStat(stat);
float multiplier = GetTotalAuraMultiplier(SPELL_AURA_MOD_PERCENT_STAT, [stat](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() == -1 || aurEff->GetMiscValue() == stat)
return true;
return false;
});
currentValue = previousValue * multiplier;
if (G3D::fuzzyGe(currentValue, previousValue))
modPos += currentValue - previousValue;
else
modNeg -= previousValue - currentValue;
// total_value offsets
previousValue += GetTotalAuraModifier(SPELL_AURA_MOD_STAT, [&](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetMiscValue() < 0 || aurEff->GetMiscValue() == stat)
{
if (aurEff->GetAmount() > 0)
modPos += aurEff->GetAmount();
else
modNeg += aurEff->GetAmount();
return true;
}
return false;
});
// total_pct multiplier
multiplier = GetTotalAuraMultiplier(SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE, [stat](AuraEffect const* aurEff) -> bool
{
if (!aurEff->GetMiscValueB() || aurEff->GetMiscValueB() & (1 << stat))
return true;
return false;
});
currentValue = previousValue * multiplier;
if (G3D::fuzzyGe(currentValue, previousValue))
modPos += currentValue - previousValue;
else
modNeg -= previousValue - currentValue;
SetFloatValue(UNIT_FIELD_POSSTAT0 + AsUnderlyingType(stat), modPos);
SetFloatValue(UNIT_FIELD_NEGSTAT0 + AsUnderlyingType(stat), modNeg);
}
void Unit::_RegisterDynObject(DynamicObject* dynObj)
{
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));
}
+17 -29
View File
@@ -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 <array>
#include <forward_list>
#include <map>
#include <memory>
#include <stack>
#include "CombatManager.h"
#include "Object.h"
#include "SpellAuraDefines.h"
#include "SpellDefines.h"
#include "Stats.h"
#include "ThreatManager.h"
#include "Timer.h"
#include "UnitDefines.h"
#include "Util.h"
constexpr uint32 WORLD_TRIGGER = 12999;
constexpr 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<float, MAX_STATS> m_createStats;
AttackerSet m_attackers;
Unit* m_attacking;
@@ -1819,6 +1801,12 @@ class TC_GAME_API Unit : public WorldObject
std::unordered_map<MovementChangeType, PlayerMovementPendingChange> m_pendingMovementChanges;
/* Player Movement fields END*/
private:
std::unique_ptr<Stats> _stats;
public:
Stats& GetStats() const;
int32 GetStat(StatType statType) const;
};
namespace Trinity
+55 -55
View File
@@ -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<int16, MAX_STATS> StatModifier = { };
std::array<int16, AsUnderlyingType(StatType::Max)> StatModifier = { };
};
std::array<RaceStats, MAX_RACES> 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<PlayerLevelInfo[]>(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));
}
}
}
+2 -2
View File
@@ -672,7 +672,7 @@ typedef std::vector<PlayerCreateInfoItem> PlayerCreateInfoItems;
struct PlayerLevelInfo
{
uint16 stats[MAX_STATS] = { };
uint16 stats[5] = { };
};
typedef std::vector<uint32> 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;
+1 -1
View File
@@ -405,7 +405,7 @@ namespace WorldPackets
int32 Level = 0;
int32 HealthDelta = 0;
std::array<int32, 6> PowerDelta = { };
std::array<int32, MAX_STATS> StatDelta = { };
std::array<int32, 5> StatDelta = { };
};
class BindPointUpdate final : public ServerPacket
@@ -16,6 +16,11 @@
*/
#include "SpellAuraEffects.h"
#include <G3D/g3dmath.h>
#include <numeric>
#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 <G3D/g3dmath.h>
#include <numeric>
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<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{
TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return;
}
Unit* target = aurApp->GetTarget();
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<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{
TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return;
}
Unit* target = aurApp->GetTarget();
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<StatType>(GetMiscValue());
if (stat < StatType::PrimaryStat || stat >= StatType::Max)
{
TC_LOG_ERROR("spells", "WARNING: Misc Value for SPELL_AURA_MOD_PERCENT_STAT not valid");
TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue());
return;
}
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<uint32>(CalculatePct(target->GetMaxHealth(), healthPct), (zeroHealth ? 0 : 1)));
target->GetStats().UpdateTotalStatMultiplier(stat);
}
void AuraEffect::HandleAuraModResistenceOfStatPercent(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
+2 -2
View File
@@ -4388,7 +4388,7 @@ class spell_gen_vengeance : public AuraScript
if (!caster->GetAura(SPELL_VENGEANCE_TRIGGERED, caster->GetGUID()))
{
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<int32>(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<int32>(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));
+6 -6
View File
@@ -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;
+6 -6
View File
@@ -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);
+1 -1
View File
@@ -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;
}
-11
View File
@@ -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,