aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/TraitMgr.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-08-28 12:16:33 +0200
committerShauren <shauren.trinity@gmail.com>2024-08-28 12:16:33 +0200
commit1f0a62bde5b927392a58e95a87d4d8bd93438d7e (patch)
tree474f9e9fcdb0ce0903a1ccba5257395f90f9566d /src/server/game/Spells/TraitMgr.cpp
parentc0b31a9f7672fc8f9c957444452d288077845246 (diff)
Core/Players: Fixed trait config validation
Diffstat (limited to 'src/server/game/Spells/TraitMgr.cpp')
-rw-r--r--src/server/game/Spells/TraitMgr.cpp110
1 files changed, 74 insertions, 36 deletions
diff --git a/src/server/game/Spells/TraitMgr.cpp b/src/server/game/Spells/TraitMgr.cpp
index 7733a0ac2dc..80251a08dd3 100644
--- a/src/server/game/Spells/TraitMgr.cpp
+++ b/src/server/game/Spells/TraitMgr.cpp
@@ -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);