aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/DataStores/DB2Structure.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp6
-rw-r--r--src/server/game/Spells/TraitMgr.cpp110
-rw-r--r--src/server/game/Spells/TraitMgr.h2
4 files changed, 79 insertions, 40 deletions
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 6b83ba2cb17..2f6317409ee 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -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
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 6ee68e5f7aa..e3d0705b943 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -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)
{
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);
diff --git a/src/server/game/Spells/TraitMgr.h b/src/server/game/Spells/TraitMgr.h
index 5e446a56e62..212abdf0351 100644
--- a/src/server/game/Spells/TraitMgr.h
+++ b/src/server/game/Spells/TraitMgr.h
@@ -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);