diff options
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Spells/TraitMgr.cpp | 110 | ||||
-rw-r--r-- | src/server/game/Spells/TraitMgr.h | 2 |
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); |