mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Players: Fixed trait config validation
This commit is contained in:
@@ -4197,6 +4197,7 @@ struct TraitCondEntry
|
||||
int32 TraitCondAccountElementID;
|
||||
|
||||
TraitConditionType GetCondType() const { return static_cast<TraitConditionType>(CondType); }
|
||||
EnumFlag<TraitCondFlags> GetFlags() const { return static_cast<TraitCondFlags>(Flags); }
|
||||
};
|
||||
|
||||
struct TraitCostEntry
|
||||
|
||||
@@ -3059,7 +3059,8 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent
|
||||
|
||||
if (traitDefinitionId)
|
||||
if (TraitDefinitionEntry const* traitDefinition = sTraitDefinitionStore.LookupEntry(*traitDefinitionId))
|
||||
AddOverrideSpell(traitDefinition->OverridesSpellID, spellId);
|
||||
if (traitDefinition->OverridesSpellID)
|
||||
AddOverrideSpell(traitDefinition->OverridesSpellID, spellId);
|
||||
|
||||
// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
|
||||
if (uint32 freeProfs = GetFreePrimaryProfessionPoints())
|
||||
@@ -28662,8 +28663,7 @@ void Player::ApplyTraitEntryChanges(int32 editedConfigId, WorldPackets::Traits::
|
||||
if (consumeCurrencies)
|
||||
{
|
||||
std::map<int32, int32> currencies;
|
||||
for (WorldPackets::Traits::TraitEntry const& costEntry : costEntries)
|
||||
TraitMgr::FillSpentCurrenciesMap(costEntry, currencies);
|
||||
TraitMgr::FillSpentCurrenciesMap(costEntries, currencies);
|
||||
|
||||
for (auto [traitCurrencyId, amount] : currencies)
|
||||
{
|
||||
|
||||
@@ -466,30 +466,30 @@ void FillOwnedCurrenciesMap(WorldPackets::Traits::TraitConfig const& traitConfig
|
||||
}
|
||||
}
|
||||
|
||||
void FillSpentCurrenciesMap(WorldPackets::Traits::TraitEntry const& entry, std::map<int32, int32>& cachedCurrencies)
|
||||
void AddSpentCurrenciesForEntry(WorldPackets::Traits::TraitEntry const& entry, std::map<int32, int32>& cachedCurrencies, int32 multiplier)
|
||||
{
|
||||
Node const* node = Trinity::Containers::MapGetValuePtr(_traitNodes, entry.TraitNodeID);
|
||||
for (NodeGroup const* group : node->Groups)
|
||||
for (TraitCostEntry const* cost : group->Costs)
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank;
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank * multiplier;
|
||||
|
||||
auto nodeEntryItr = std::ranges::find_if(node->Entries, [&entry](NodeEntry const& nodeEntry) { return int32(nodeEntry.Data->ID) == entry.TraitNodeEntryID; });
|
||||
if (nodeEntryItr != node->Entries.end())
|
||||
for (TraitCostEntry const* cost : nodeEntryItr->Costs)
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank;
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank * multiplier;
|
||||
|
||||
for (TraitCostEntry const* cost : node->Costs)
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank;
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank * multiplier;
|
||||
|
||||
if (Tree const* tree = Trinity::Containers::MapGetValuePtr(_traitTrees, node->Data->TraitTreeID))
|
||||
for (TraitCostEntry const* cost : tree->Costs)
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank;
|
||||
cachedCurrencies[cost->TraitCurrencyID] += cost->Amount * entry.Rank * multiplier;
|
||||
}
|
||||
|
||||
void FillSpentCurrenciesMap(WorldPackets::Traits::TraitConfig const& traitConfig, std::map<int32, int32>& cachedCurrencies)
|
||||
void FillSpentCurrenciesMap(std::vector<WorldPackets::Traits::TraitEntry> const& traitEntries, std::map<int32, int32>& cachedCurrencies)
|
||||
{
|
||||
for (WorldPackets::Traits::TraitEntry const& entry : traitConfig.Entries)
|
||||
FillSpentCurrenciesMap(entry, cachedCurrencies);
|
||||
for (WorldPackets::Traits::TraitEntry const& entry : traitEntries)
|
||||
AddSpentCurrenciesForEntry(entry, cachedCurrencies, 1);
|
||||
}
|
||||
|
||||
std::array<int32, 2> GetClassAndSpecTreeCurrencies(WorldPackets::Traits::TraitConfig const& traitConfig)
|
||||
@@ -538,7 +538,7 @@ bool MeetsTraitCondition(WorldPackets::Traits::TraitConfig const& traitConfig, P
|
||||
if (condition->TraitCurrencyID && condition->SpentAmountRequired)
|
||||
{
|
||||
if (!cachedCurrencies)
|
||||
FillSpentCurrenciesMap(traitConfig, cachedCurrencies.emplace());
|
||||
FillSpentCurrenciesMap(traitConfig.Entries, cachedCurrencies.emplace());
|
||||
|
||||
if (condition->TraitNodeGroupID || condition->TraitNodeID || condition->TraitNodeEntryID)
|
||||
{
|
||||
@@ -554,6 +554,67 @@ bool MeetsTraitCondition(WorldPackets::Traits::TraitConfig const& traitConfig, P
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NodeMeetsTraitConditions(WorldPackets::Traits::TraitConfig const& traitConfig, Node const* node, uint32 traitNodeEntryId, PlayerDataAccessor player, Optional<std::map<int32, int32>>& spentCurrencies)
|
||||
{
|
||||
auto meetsConditions = [&](std::vector<TraitCondEntry const*> const& conditions)
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool IsSufficient = false;
|
||||
bool HasFailedConditions = false;
|
||||
} result;
|
||||
|
||||
for (TraitCondEntry const* condition : conditions)
|
||||
{
|
||||
if (condition->GetCondType() == TraitConditionType::Available || condition->GetCondType() == TraitConditionType::Visible)
|
||||
{
|
||||
if (MeetsTraitCondition(traitConfig, player, condition, spentCurrencies))
|
||||
{
|
||||
if (condition->GetFlags().HasFlag(TraitCondFlags::IsSufficient))
|
||||
{
|
||||
result.IsSufficient = true;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
result.HasFailedConditions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
bool hasFailedConditions = false;
|
||||
for (NodeEntry const& entry : node->Entries)
|
||||
{
|
||||
if (entry.Data->ID == traitNodeEntryId)
|
||||
{
|
||||
auto [IsSufficient, HasFailedConditions] = meetsConditions(entry.Conditions);
|
||||
if (IsSufficient)
|
||||
return true;
|
||||
if (HasFailedConditions)
|
||||
hasFailedConditions = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto [IsSufficient, HasFailedConditions] = meetsConditions(node->Conditions); IsSufficient)
|
||||
return true;
|
||||
else if (HasFailedConditions)
|
||||
hasFailedConditions = true;
|
||||
|
||||
for (NodeGroup const* group : node->Groups)
|
||||
{
|
||||
auto [IsSufficient, HasFailedConditions] = meetsConditions(group->Conditions);
|
||||
if (IsSufficient)
|
||||
return true;
|
||||
if (HasFailedConditions)
|
||||
hasFailedConditions = true;
|
||||
}
|
||||
|
||||
return !hasFailedConditions;
|
||||
};
|
||||
|
||||
std::vector<UF::TraitEntry> GetGrantedTraitEntriesForConfig(WorldPackets::Traits::TraitConfig const& traitConfig, PlayerDataAccessor player)
|
||||
{
|
||||
std::vector<UF::TraitEntry> entries;
|
||||
@@ -654,24 +715,7 @@ LearnResult ValidateConfig(WorldPackets::Traits::TraitConfig& traitConfig, Playe
|
||||
};
|
||||
|
||||
Optional<std::map<int32, int32>> spentCurrencies;
|
||||
FillSpentCurrenciesMap(traitConfig, spentCurrencies.emplace());
|
||||
|
||||
auto meetsConditions = [&](std::vector<TraitCondEntry const*> const& conditions)
|
||||
{
|
||||
bool hasConditions = false;
|
||||
for (TraitCondEntry const* condition : conditions)
|
||||
{
|
||||
if (condition->GetCondType() == TraitConditionType::Available || condition->GetCondType() == TraitConditionType::Visible)
|
||||
{
|
||||
if (MeetsTraitCondition(traitConfig, player, condition, spentCurrencies))
|
||||
return true;
|
||||
|
||||
hasConditions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !hasConditions;
|
||||
};
|
||||
FillSpentCurrenciesMap(traitConfig.Entries, spentCurrencies.emplace());
|
||||
|
||||
auto isValidTraitEntry = [&](WorldPackets::Traits::TraitEntry const& traitEntry)
|
||||
{
|
||||
@@ -683,17 +727,9 @@ LearnResult ValidateConfig(WorldPackets::Traits::TraitConfig& traitConfig, Playe
|
||||
if (getNodeEntryCount(traitEntry.TraitNodeID) != 1)
|
||||
return LearnResult::Unknown;
|
||||
|
||||
for (NodeEntry const& entry : node->Entries)
|
||||
if (int32(entry.Data->ID) == traitEntry.TraitNodeEntryID && !meetsConditions(entry.Conditions))
|
||||
return LearnResult::Unknown;
|
||||
|
||||
if (!meetsConditions(node->Conditions))
|
||||
if (!NodeMeetsTraitConditions(traitConfig, node, traitEntry.TraitNodeEntryID, player, spentCurrencies))
|
||||
return LearnResult::Unknown;
|
||||
|
||||
for (NodeGroup const* group : node->Groups)
|
||||
if (!meetsConditions(group->Conditions))
|
||||
return LearnResult::Unknown;
|
||||
|
||||
if (!node->ParentNodes.empty())
|
||||
{
|
||||
bool hasAnyParentTrait = false;
|
||||
@@ -725,6 +761,8 @@ LearnResult ValidateConfig(WorldPackets::Traits::TraitConfig& traitConfig, Playe
|
||||
if (!removeInvalidEntries)
|
||||
return result;
|
||||
|
||||
AddSpentCurrenciesForEntry(*itr, *spentCurrencies, -1);
|
||||
|
||||
if (!itr->GrantedRanks // fully remove entries that don't have granted ranks
|
||||
|| !itr->Rank) // ... or entries that do have them and don't have any additional spent ranks (can happen if the same entry is revalidated after first removing all spent ranks)
|
||||
traitConfig.Entries.erase(itr);
|
||||
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
void Load();
|
||||
int32 GenerateNewTraitConfigId();
|
||||
TraitConfigType GetConfigTypeForTree(int32 traitTreeId);
|
||||
void FillSpentCurrenciesMap(WorldPackets::Traits::TraitEntry const& entry, std::map<int32, int32>& cachedCurrencies);
|
||||
void FillSpentCurrenciesMap(std::vector<WorldPackets::Traits::TraitEntry> const& traitEntries, std::map<int32, int32>& cachedCurrencies);
|
||||
std::vector<UF::TraitEntry> GetGrantedTraitEntriesForConfig(WorldPackets::Traits::TraitConfig const& traitConfig, PlayerDataAccessor player);
|
||||
bool IsValidEntry(WorldPackets::Traits::TraitEntry const& traitEntry);
|
||||
LearnResult ValidateConfig(WorldPackets::Traits::TraitConfig& traitConfig, PlayerDataAccessor player, bool requireSpendingAllCurrencies = false, bool removeInvalidEntries = false);
|
||||
|
||||
Reference in New Issue
Block a user