Core/Players: Fixed traits not updating aura values when changing rank

This commit is contained in:
Shauren
2025-06-14 14:55:06 +02:00
parent f94d87b00f
commit e6b553b913
4 changed files with 81 additions and 71 deletions

View File

@@ -2754,7 +2754,7 @@ WorldLocation const* Player::GetStoredAuraTeleportLocation(uint32 spellId) const
return nullptr;
}
bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, int32 fromSkill /*= 0*/, bool favorite /*= false*/, Optional<int32> traitDefinitionId /*= {}*/)
bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading /*= false*/, int32 fromSkill /*= 0*/, bool favorite /*= false*/, Optional<PlayerSpellTrait> trait /*= {}*/)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE);
if (!spellInfo)
@@ -2834,13 +2834,13 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
dependent_set = true;
}
if (itr->second.TraitDefinitionId != traitDefinitionId)
if (itr->second.Trait != trait)
{
if (itr->second.TraitDefinitionId)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(*itr->second.TraitDefinitionId))
if (itr->second.Trait)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(itr->second.Trait->DefinitionId))
RemoveOverrideSpell(traitDefinition->OverridesSpellID, spellId);
itr->second.TraitDefinitionId = traitDefinitionId;
itr->second.Trait = trait;
}
itr->second.favorite = favorite;
@@ -2918,7 +2918,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
LearnSpell(prev_spell, true, fromSkill);
}
std::pair<PlayerSpellMap::iterator, bool> inserted = m_spells.emplace(std::piecewise_construct, std::forward_as_tuple(spellId), std::forward_as_tuple());
std::pair<PlayerSpellMap::iterator, bool> inserted = m_spells.try_emplace(spellId);
PlayerSpell& newspell = inserted.first->second;
// learning a previous rank might have given us this spell already from a skill autolearn, most likely with PLAYERSPELL_NEW state
// we dont want to do double insert if this happened during load from db so we force state to CHANGED, just in case
@@ -2927,8 +2927,7 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
newspell.dependent = dependent;
newspell.disabled = disabled;
newspell.favorite = favorite;
if (traitDefinitionId)
newspell.TraitDefinitionId = *traitDefinitionId;
newspell.Trait = trait;
// replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
if (newspell.active && !newspell.disabled && spellInfo->IsRanked())
@@ -2994,48 +2993,13 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
if (castSpell)
{
CastSpellExtraArgs args;
args.SetTriggerFlags(TRIGGERED_FULL_MASK);
if (traitDefinitionId)
{
if (UF::TraitConfig const* traitConfig = GetTraitConfig(m_activePlayerData->ActiveCombatTraitConfigID))
{
int32 traitEntryIndex = traitConfig->Entries.FindIndexIf([traitDefinitionId](UF::TraitEntry const& traitEntry)
{
return sTraitNodeEntryStore.AssertEntry(traitEntry.TraitNodeEntryID)->TraitDefinitionID == traitDefinitionId;
});
int32 rank = 0;
if (traitEntryIndex >= 0)
rank = traitConfig->Entries[traitEntryIndex].Rank + traitConfig->Entries[traitEntryIndex].GrantedRanks;
if (rank > 0)
{
if (std::vector<TraitDefinitionEffectPointsEntry const*> const* traitDefinitionEffectPoints = TraitMgr::GetTraitDefinitionEffectPointModifiers(*traitDefinitionId))
{
for (TraitDefinitionEffectPointsEntry const* traitDefinitionEffectPoint : *traitDefinitionEffectPoints)
{
if (traitDefinitionEffectPoint->EffectIndex >= int32(spellInfo->GetEffects().size()))
continue;
float basePoints = sDB2Manager.GetCurveValueAt(traitDefinitionEffectPoint->CurveID, rank);
if (traitDefinitionEffectPoint->GetOperationType() == TraitPointsOperationType::Multiply)
basePoints *= spellInfo->GetEffect(SpellEffIndex(traitDefinitionEffectPoint->EffectIndex)).CalcBaseValue(this, nullptr, 0, -1);
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + traitDefinitionEffectPoint->EffectIndex), basePoints);
}
}
}
}
}
CastSpell(this, spellId, args);
CastSpell(this, spellId, true);
if (spellInfo->HasEffect(SPELL_EFFECT_SKILL_STEP))
return false;
}
if (traitDefinitionId)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(*traitDefinitionId))
if (trait)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(trait->DefinitionId))
if (traitDefinition->OverridesSpellID)
AddOverrideSpell(traitDefinition->OverridesSpellID, spellId);
@@ -3206,7 +3170,7 @@ bool Player::HandlePassiveSpellLearn(SpellInfo const* spellInfo)
return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)));
}
void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/, bool suppressMessaging /*= false*/, Optional<int32> traitDefinitionId /*= {}*/)
void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/, bool suppressMessaging /*= false*/, Optional<PlayerSpellTrait> trait /*= {}*/)
{
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
@@ -3214,7 +3178,7 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/
bool active = disabled ? itr->second.active : true;
bool favorite = itr != m_spells.end() ? itr->second.favorite : false;
bool learning = AddSpell(spell_id, active, true, dependent, false, false, fromSkill, favorite, traitDefinitionId);
bool learning = AddSpell(spell_id, active, true, dependent, false, false, fromSkill, favorite, trait);
// prevent duplicated entires in spell book, also not send if not in world (loading)
if (learning && IsInWorld())
@@ -3223,7 +3187,8 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill /*= 0*/
WorldPackets::Spells::LearnedSpellInfo& learnedSpellInfo = learnedSpells.ClientLearnedSpellData.emplace_back();
learnedSpellInfo.SpellID = spell_id;
learnedSpellInfo.Favorite = favorite;
learnedSpellInfo.TraitDefinitionID = traitDefinitionId;
if (trait)
learnedSpellInfo.TraitDefinitionID = int32(trait->DefinitionId);
learnedSpells.SuppressMessaging = suppressMessaging;
SendDirectMessage(learnedSpells.Write());
}
@@ -3278,7 +3243,7 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled /*= false*/, bool learn_
bool cur_active = itr->second.active;
bool cur_dependent = itr->second.dependent;
Optional<int32> traitDefinitionId = itr->second.TraitDefinitionId;
Optional<PlayerSpellTrait> trait = itr->second.Trait;
if (disabled)
{
@@ -3437,8 +3402,8 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled /*= false*/, bool learn_
}
}
if (traitDefinitionId)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(*traitDefinitionId))
if (trait)
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(trait->DefinitionId))
RemoveOverrideSpell(traitDefinition->OverridesSpellID, spell_id);
m_overrideSpells.erase(spell_id);
@@ -17643,9 +17608,6 @@ void Player::_LoadEquipmentSets(PreparedQueryResult result)
if (ObjectGuid::LowType guid = fields[6 + i].GetUInt64())
eqSet.Data.Pieces[i] = ObjectGuid::Create<HighGuid::Item>(guid);
eqSet.Data.Appearances.fill(0);
eqSet.Data.Enchants.fill(0);
if (eqSet.Data.SetID >= MAX_EQUIPMENT_SET_INDEX) // client limit
continue;
@@ -17676,7 +17638,6 @@ void Player::_LoadTransmogOutfits(PreparedQueryResult result)
eqSet.Data.SetIcon = fields[3].GetString();
eqSet.Data.IgnoreMask = fields[4].GetUInt32();
eqSet.State = EQUIPMENT_SET_UNCHANGED;
eqSet.Data.Pieces.fill(ObjectGuid::Empty);
for (uint32 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
eqSet.Data.Appearances[i] = fields[5 + i].GetInt32();
@@ -28789,7 +28750,7 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
// remove traits not found in new config
std::set<int32, std::greater<>> entryIndicesToRemove;
for (int32 i = 0; i < int32(editedConfig.Entries.size()); ++i)
for (int32 i = 0; i < std::ssize(editedConfig.Entries); ++i)
{
UF::TraitEntry const& oldEntry = editedConfig.Entries[i];
auto entryItr = std::ranges::find_if(newConfig.Entries, makeTraitEntryFinder(oldEntry.TraitNodeID, oldEntry.TraitNodeEntryID));
@@ -28812,9 +28773,8 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
std::vector<WorldPackets::Traits::TraitEntry> costEntries;
// apply new traits
for (std::size_t i = 0; i < newConfig.Entries.size(); ++i)
for (WorldPackets::Traits::TraitEntry const& newEntry : newConfig.Entries)
{
WorldPackets::Traits::TraitEntry const& newEntry = newConfig.Entries[i];
int32 oldEntryIndex = editedConfig.Entries.FindIndexIf(makeTraitEntryFinder(newEntry.TraitNodeID, newEntry.TraitNodeEntryID));
if (oldEntryIndex < 0)
{
@@ -28851,7 +28811,10 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
.ModifyValue(&UF::TraitEntry::GrantedRanks), newEntry.GrantedRanks);
if (applyTraits)
{
ApplyTraitEntry(newEntry.TraitNodeEntryID, 0, 0, false);
ApplyTraitEntry(newEntry.TraitNodeEntryID, newEntry.Rank, newEntry.GrantedRanks, true);
}
}
}
@@ -28880,9 +28843,8 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
}
}
for (std::size_t i = 0; i < newConfig.SubTrees.size(); ++i)
for (WorldPackets::Traits::TraitSubTreeCache const& newSubTree : newConfig.SubTrees)
{
WorldPackets::Traits::TraitSubTreeCache const& newSubTree = newConfig.SubTrees[i];
int32 oldSubTreeIndex = editedConfig.SubTrees.FindIndexIf([&](UF::TraitSubTreeCache const& ufSubTree) { return ufSubTree.TraitSubTreeID == newSubTree.TraitSubTreeID; });
std::vector<UF::TraitEntry> subTreeEntries;
subTreeEntries.resize(newSubTree.Entries.size());
@@ -28894,6 +28856,7 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
newUfEntry.Rank = newSubTree.Entries[j].Rank;
newUfEntry.GrantedRanks = newSubTree.Entries[j].GrantedRanks;
}
if (oldSubTreeIndex < 0)
{
UF::TraitSubTreeCache& newUfSubTree = AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData)
@@ -28972,7 +28935,7 @@ void Player::ApplyTraitConfig(int32 configId, bool apply)
ApplyTraitEntry(traitEntry.TraitNodeEntryID, traitEntry.Rank, traitEntry.GrantedRanks, apply);
}
void Player::ApplyTraitEntry(int32 traitNodeEntryId, int32 /*rank*/, int32 /*grantedRanks*/, bool apply)
void Player::ApplyTraitEntry(int32 traitNodeEntryId, int32 rank, int32 grantedRanks, bool apply)
{
TraitNodeEntryEntry const* traitNodeEntry = sTraitNodeEntryStore.LookupEntry(traitNodeEntryId);
if (!traitNodeEntry)
@@ -28984,8 +28947,10 @@ void Player::ApplyTraitEntry(int32 traitNodeEntryId, int32 /*rank*/, int32 /*gra
if (traitDefinition->SpellID)
{
ASSERT(traitNodeEntry->TraitDefinitionID <= 0xFFFFFF && rank + grantedRanks <= 0xFF);
if (apply)
LearnSpell(traitDefinition->SpellID, true, 0, false, traitNodeEntry->TraitDefinitionID);
LearnSpell(traitDefinition->SpellID, true, 0, false, PlayerSpellTrait{ .DefinitionId = traitNodeEntry->TraitDefinitionID, .Rank = rank + grantedRanks });
else
RemoveSpell(traitDefinition->SpellID);
}
@@ -29064,6 +29029,13 @@ void Player::SetTraitConfigUseSharedActionBars(int32 traitConfigId, bool usesSha
m_traitConfigStates[traitConfigId] = PLAYERSPELL_CHANGED;
}
Optional<PlayerSpellTrait> Player::GetTraitInfoForSpell(uint32 spellId) const
{
if (PlayerSpell const* spell = Trinity::Containers::MapGetValuePtr(m_spells, spellId))
return spell->Trait;
return {};
}
void Player::SetReputation(uint32 factionentry, int32 value)
{
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value);

View File

@@ -200,6 +200,14 @@ enum PlayerSpellState : uint8
PLAYERSPELL_TEMPORARY = 4
};
struct PlayerSpellTrait
{
int32 DefinitionId : 24;
int32 Rank : 8;
friend bool operator==(PlayerSpellTrait const&, PlayerSpellTrait const&) noexcept = default;
};
struct PlayerSpell
{
PlayerSpellState state;
@@ -207,7 +215,7 @@ struct PlayerSpell
bool dependent : 1; // learned as result another spell learn, skill grow, quest reward, etc
bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks
bool favorite : 1;
Optional<int32> TraitDefinitionId;
Optional<PlayerSpellTrait> Trait;
};
struct StoredAuraTeleportLocation
@@ -1891,8 +1899,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void SendProficiency(ItemClass itemClass, uint32 itemSubclassMask) const;
void SendKnownSpells();
void SendUnlearnSpells();
bool AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, int32 fromSkill = 0, bool favorite = false, Optional<int32> traitDefinitionId = {});
void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill = 0, bool suppressMessaging = false, Optional<int32> traitDefinitionId = {});
bool AddSpell(uint32 spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, int32 fromSkill = 0, bool favorite = false, Optional<PlayerSpellTrait> trait = {});
void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill = 0, bool suppressMessaging = false, Optional<PlayerSpellTrait> trait = {});
void RemoveSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true, bool suppressMessaging = false);
void ResetSpells(bool myClassOnly = false);
void LearnCustomSpells();
@@ -1986,6 +1994,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void SetActiveCombatTraitConfigID(int32 traitConfigId) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::ActiveCombatTraitConfigID), traitConfigId); }
void SetTraitConfigUseStarterBuild(int32 traitConfigId, bool useStarterBuild);
void SetTraitConfigUseSharedActionBars(int32 traitConfigId, bool usesSharedActionBars, bool isLastSelectedSavedConfig);
Optional<PlayerSpellTrait> GetTraitInfoForSpell(uint32 spellId) const;
uint32 GetFreePrimaryProfessionPoints() const { return m_activePlayerData->CharacterPoints; }
void SetFreePrimaryProfessions(uint16 profs) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::CharacterPoints), profs); }

View File

@@ -47,7 +47,7 @@ void WorldSession::HandleTraitsCommitConfig(WorldPackets::Traits::TraitsCommitCo
auto findEntry = [](WorldPackets::Traits::TraitConfig& config, int32 traitNodeId, int32 traitNodeEntryId) -> WorldPackets::Traits::TraitEntry*
{
auto entryItr = std::find_if(config.Entries.begin(), config.Entries.end(), [=](WorldPackets::Traits::TraitEntry const& traitEntry)
auto entryItr = std::ranges::find_if(config.Entries, [=](WorldPackets::Traits::TraitEntry const& traitEntry)
{
return traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId;
});
@@ -118,10 +118,10 @@ void WorldSession::HandleTraitsCommitConfig(WorldPackets::Traits::TraitsCommitCo
if (newEntry.Rank)
traitEntry->Rank = newEntry.Rank;
else
newConfigState.Entries.erase(std::remove_if(newConfigState.Entries.begin(), newConfigState.Entries.end(), [&newEntry](WorldPackets::Traits::TraitEntry const& traitEntry)
std::erase_if(newConfigState.Entries, [&newEntry](WorldPackets::Traits::TraitEntry const& traitEntry)
{
return traitEntry.TraitNodeID == newEntry.TraitNodeID && traitEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID;
}), newConfigState.Entries.end());
});
}
else
newConfigState.Entries.emplace_back() = newEntry;
@@ -152,7 +152,7 @@ void WorldSession::HandleClassTalentsRequestNewConfig(WorldPackets::Traits::Clas
if ((classTalentsRequestNewConfig.Config.CombatConfigFlags & TraitCombatConfigFlags::ActiveForSpec) != TraitCombatConfigFlags::None)
return;
int64 configCount = std::count_if(_player->m_activePlayerData->TraitConfigs.begin(), _player->m_activePlayerData->TraitConfigs.end(), [](UF::TraitConfig const& traitConfig)
int64 configCount = std::ranges::count_if(_player->m_activePlayerData->TraitConfigs, [](UF::TraitConfig const& traitConfig)
{
return static_cast<TraitConfigType>(*traitConfig.Type) == TraitConfigType::Combat
&& (static_cast<TraitCombatConfigFlags>(*traitConfig.CombatConfigFlags) & TraitCombatConfigFlags::ActiveForSpec) == TraitCombatConfigFlags::None;
@@ -179,7 +179,7 @@ void WorldSession::HandleClassTalentsRequestNewConfig(WorldPackets::Traits::Clas
for (UF::TraitEntry const& grantedEntry : TraitMgr::GetGrantedTraitEntriesForConfig(classTalentsRequestNewConfig.Config, _player))
{
auto entryItr = std::find_if(classTalentsRequestNewConfig.Config.Entries.begin(), classTalentsRequestNewConfig.Config.Entries.end(),
auto entryItr = std::ranges::find_if(classTalentsRequestNewConfig.Config.Entries,
[&](WorldPackets::Traits::TraitEntry const& entry) { return entry.TraitNodeID == grantedEntry.TraitNodeID && entry.TraitNodeEntryID == grantedEntry.TraitNodeEntryID; });
WorldPackets::Traits::TraitEntry& newEntry = entryItr != classTalentsRequestNewConfig.Config.Entries.end() ? *entryItr : classTalentsRequestNewConfig.Config.Entries.emplace_back();

View File

@@ -35,6 +35,7 @@
#include "Spell.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "TraitMgr.h"
#include "Vehicle.h"
#include <G3D/g3dmath.h>
#include <bit>
@@ -503,7 +504,35 @@ int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32
Unit const* casterUnit = nullptr;
if (caster)
{
casterUnit = caster->ToUnit();
if (Player const* playerCaster = caster->ToPlayer())
{
if (Optional<PlayerSpellTrait> trait = playerCaster->GetTraitInfoForSpell(_spellInfo->Id))
{
if (std::vector<TraitDefinitionEffectPointsEntry const*> const* traitDefinitionEffectPoints = TraitMgr::GetTraitDefinitionEffectPointModifiers(trait->DefinitionId))
{
auto pointsOverride = std::ranges::find(*traitDefinitionEffectPoints, EffectIndex, &TraitDefinitionEffectPointsEntry::EffectIndex);
if (pointsOverride != traitDefinitionEffectPoints->end())
{
float traitBasePoints = sDB2Manager.GetCurveValueAt((*pointsOverride)->CurveID, trait->Rank);
switch ((*pointsOverride)->GetOperationType())
{
case TraitPointsOperationType::Set:
value = traitBasePoints;
break;
case TraitPointsOperationType::Multiply:
value *= traitBasePoints;
break;
case TraitPointsOperationType::None:
default:
break;
}
}
}
}
}
}
if (Scaling.Variance)
{