93 _configIdGenerator = time(
nullptr);
95 std::unordered_map<uint32, std::vector<TraitCondEntry const*>> nodeEntryConditions;
98 nodeEntryConditions[traitNodeEntryXTraitCondEntry->TraitNodeEntryID].push_back(traitCondEntry);
100 std::unordered_map<uint32, std::vector<TraitCostEntry const*>> nodeEntryCosts;
103 nodeEntryCosts[traitNodeEntryXTraitCostEntry->TraitNodeEntryID].push_back(traitCostEntry);
105 std::unordered_map<uint32, std::vector<TraitCondEntry const*>> nodeGroupConditions;
108 nodeGroupConditions[traitNodeGroupXTraitCondEntry->TraitNodeGroupID].push_back(traitCondEntry);
110 std::unordered_map<uint32, std::vector<TraitCostEntry const*>> nodeGroupCosts;
113 nodeGroupCosts[traitNodeGroupXTraitCostEntry->TraitNodeGroupID].push_back(traitCondEntry);
115 std::unordered_multimap<int32, uint32> nodeGroups;
117 nodeGroups.emplace(traitNodeGroupXTraitNodeEntry->TraitNodeID, traitNodeGroupXTraitNodeEntry->TraitNodeGroupID);
119 std::unordered_map<uint32, std::vector<TraitCondEntry const*>> nodeConditions;
122 nodeConditions[traitNodeXTraitCondEntry->TraitNodeID].push_back(traitCondEntry);
124 std::unordered_map<uint32, std::vector<TraitCostEntry const*>> nodeCosts;
127 nodeCosts[traitNodeXTraitCostEntry->TraitNodeID].push_back(traitCostEntry);
129 std::unordered_multimap<uint32, TraitNodeEntryEntry const*> nodeEntries;
132 nodeEntries.emplace(traitNodeXTraitNodeEntryEntry->TraitNodeID, traitNodeEntryEntry);
134 std::unordered_map<int32, std::vector<TraitCostEntry const*>> treeCosts;
137 treeCosts[traitTreeXTraitCostEntry->TraitTreeID].push_back(traitCostEntry);
139 std::unordered_map<uint32, std::vector<TraitTreeXTraitCurrencyEntry const*>> treeCurrencies;
142 treeCurrencies[traitTreeXTraitCurrencyEntry->TraitTreeID].push_back(traitTreeXTraitCurrencyEntry);
144 std::unordered_map<int32, std::vector<int32>> traitTreesIdsByTraitSystem;
148 Tree& tree = _traitTrees[traitTree->ID];
149 tree.Data = traitTree;
152 tree.Costs = std::move(*costs);
156 tree.Currencies.resize(currencies->size());
158 std::ranges::transform(*currencies, tree.Currencies.begin(),
159 [](
uint32 traitCurrencyId) { return sTraitCurrencyStore.AssertEntry(traitCurrencyId); },
163 if (traitTree->TraitSystemID)
165 traitTreesIdsByTraitSystem[traitTree->TraitSystemID].push_back(traitTree->ID);
172 SubTree& subTree = _traitSubTrees[traitSubTree->ID];
173 subTree.Data = traitSubTree;
176 tree->SubTrees.push_back(&subTree);
181 NodeGroup& nodeGroup = _traitGroups[traitNodeGroup->ID];
182 nodeGroup.Data = traitNodeGroup;
185 nodeGroup.Conditions = std::move(*conditions);
188 nodeGroup.Costs = std::move(*costs);
193 Node& node = _traitNodes[traitNode->ID];
194 node.Data = traitNode;
197 tree->Nodes.push_back(&node);
201 NodeEntry& entry = node.Entries.emplace_back();
202 entry.Data = traitNodeEntry;
205 entry.Conditions = std::move(*conditions);
208 entry.Costs = std::move(*costs);
217 nodeGroup->Nodes.push_back(&node);
218 node.Groups.push_back(nodeGroup);
222 node.Conditions = std::move(*conditions);
225 node.Costs = std::move(*costs);
229 subTree->Nodes.push_back(&node);
231 for (NodeEntry
const& nodeEntry : node.Entries)
234 subTree->Currencies.insert(traitCurrency);
236 for (NodeGroup
const* nodeGroup : node.Groups)
239 subTree->Currencies.insert(traitCurrency);
243 subTree->Currencies.insert(traitCurrency);
248 subTree->Currencies.insert(traitCurrency);
259 right->ParentNodes.emplace_back(left,
static_cast<TraitEdgeType>(traitEdgeEntry->Type));
272 _traitTreesBySkillLine[skillLineXTraitTreeEntry->SkillLineID].push_back(tree);
277 if (skillRaceClassInfo->ClassMask & (1 << (i - 1)))
278 _skillLinesByClass[i] = skillLineXTraitTreeEntry->SkillLineID;
286 for (
auto&& [traitSystemId, traitTreeIds] : traitTreesIdsByTraitSystem)
287 for (
int32 traitTreeId : traitTreeIds)
289 _traitTreesByTraitSystem[traitSystemId].push_back(tree);
292 _traitCurrencySourcesByCurrency[traitCurrencySource->TraitCurrencyID].push_back(traitCurrencySource);
295 _traitDefinitionEffectPointModifiers[traitDefinitionEffectPoints->TraitDefinitionID].push_back(traitDefinitionEffectPoints);
297 std::unordered_map<uint32, std::vector<TraitTreeLoadoutEntryEntry const*>> traitTreeLoadoutEntries;
299 traitTreeLoadoutEntries[traitTreeLoadoutEntry->TraitTreeLoadoutID].push_back(traitTreeLoadoutEntry);
307 _traitTreeLoadoutsByChrSpecialization[traitTreeLoadout->ChrSpecializationID] = std::move(*entries);
415 auto hasTraitNodeEntry = [&traitConfig](
int32 traitNodeEntryId)
419 return traitEntry.TraitNodeEntryID == traitNodeEntryId && (traitEntry.Rank > 0 || traitEntry.GrantedRanks > 0);
420 }) != traitConfig.
Entries.end();
423 for (Tree
const* tree : *trees)
427 switch (currency->GetType())
431 int32& amount = currencies[currency->ID];
432 if (player.
GetMoney() >
uint64(std::numeric_limits<int32>::max() - amount))
433 amount = std::numeric_limits<int32>::max();
446 if (currencySource->QuestID && !player.
IsQuestRewarded(currencySource->QuestID))
449 if (currencySource->AchievementID && !player.
HasAchieved(currencySource->AchievementID))
452 if (currencySource->PlayerLevel && player.
GetLevel() < currencySource->PlayerLevel)
455 if (currencySource->TraitNodeEntryID && !hasTraitNodeEntry(currencySource->TraitNodeEntryID))
458 currencies[currencySource->TraitCurrencyID] += currencySource->Amount;
463 if (currency->PlayerDataElementAccountID)
464 currencies[currency->ID] += std::visit([](
auto value) {
return static_cast<int32>(value); }, player.
GetDataElementAccount(currency->CurrencyTypesID));
465 else if (currency->PlayerDataElementCharacterID)
466 currencies[currency->ID] += std::visit([](
auto value) {
return static_cast<int32>(value); }, player.
GetDataElementCharacter(currency->CurrencyTypesID));
682 Optional<std::map<int32, SpentCurrency>>& spentCurrencies)
684 struct ConditionCheckResult
687 bool HasFailedConditions =
false;
690 auto meetsConditions = [&traitConfig, player, &spentCurrencies]<
Trinity::invocable_r<int32> RankSupplier>(std::vector<TraitCondEntry const*>
const& conditions,
TraitConditionType conditionType, RankSupplier getRank) -> ConditionCheckResult
692 ConditionCheckResult result;
697 if (condition->GetCondType() != conditionType)
702 if (!rank.has_value())
705 if (*rank < condition->GrantedRanks)
711 result.HasFailedConditions =
true;
717 result.IsSufficient =
true;
727 bool hasFailedConditions =
false;
728 ConditionCheckResult result = meetsConditions(node->Conditions, conditionType, [&] { return CountTraitNodeRanks(traitConfig, 0, node->Data->ID, traitNodeEntryId).Node; });
729 if (result.IsSufficient)
731 else if (result.HasFailedConditions)
732 hasFailedConditions =
true;
734 for (NodeGroup
const* group : node->Groups)
736 if (group->Conditions.empty())
739 result = meetsConditions(group->Conditions, conditionType, [&]{ return CountTraitNodeRanks(traitConfig, group->Data->ID, node->Data->ID, traitNodeEntryId).Group; });
740 if (result.IsSufficient)
742 if (result.HasFailedConditions)
743 hasFailedConditions =
true;
746 for (NodeEntry
const& entry : node->Entries)
748 if (entry.Data->ID != traitNodeEntryId || entry.Conditions.empty())
751 result = meetsConditions(entry.Conditions, conditionType, [&] { return CountTraitNodeRanks(traitConfig, 0, node->Data->ID, traitNodeEntryId).Entry; });
752 if (result.IsSufficient)
754 if (result.HasFailedConditions)
755 hasFailedConditions =
true;
758 return !hasFailedConditions;
768 std::vector<UF::TraitEntry> entries;
773 auto addGrantedRankToEntry = [&entries](
int32 nodeId, NodeEntry
const& entry,
int32 grantedRanks)
775 auto itr = std::ranges::find_if(entries, [&](
UF::TraitEntry const& traitEntry)
779 if (itr == entries.end())
781 itr = entries.emplace(entries.end());
782 itr->TraitNodeID = nodeId;
783 itr->TraitNodeEntryID =
int32(entry.Data->ID);
785 itr->GrantedRanks = 0;
787 itr->GrantedRanks += grantedRanks;
788 if (itr->GrantedRanks > entry.Data->MaxRanks)
789 itr->GrantedRanks = entry.Data->MaxRanks;
794 for (Tree
const* tree : *trees)
796 for (Node
const* node : tree->Nodes)
798 for (NodeEntry
const& entry : node->Entries)
802 addGrantedRankToEntry(node->Data->ID, entry, condition->GrantedRanks);
806 for (NodeEntry
const& entry : node->Entries)
808 addGrantedRankToEntry(node->Data->ID, entry, condition->GrantedRanks);
810 for (NodeGroup
const* group : node->Groups)
813 for (NodeEntry
const& entry : node->Entries)
815 addGrantedRankToEntry(node->Data->ID, entry, condition->GrantedRanks);
840 auto getNodeEntryCount = [&](
int32 traitNodeId)
845 auto getNodeEntry = [&](
int32 traitNodeId,
int32 traitNodeEntryId)
849 return traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId;
851 return entryItr != traitConfig.
Entries.end() ? &*entryItr :
nullptr;
854 auto isNodeFullyFilled = [&](Node
const* node)
856 auto nodeEntryMatches = [&](NodeEntry
const& nodeEntry)
859 return traitEntry && (traitEntry->
Rank + traitEntry->
GrantedRanks) == nodeEntry.Data->MaxRanks;
863 return std::ranges::any_of(node->Entries, nodeEntryMatches);
865 return std::ranges::all_of(node->Entries, nodeEntryMatches);
878 if (getNodeEntryCount(traitEntry.TraitNodeID) != 1)
884 if (!node->ParentNodes.empty())
886 bool hasAnyParentTrait =
false;
887 for (
auto const& [parentNode, edgeType] : node->ParentNodes)
889 if (!isNodeFullyFilled(parentNode))
897 hasAnyParentTrait =
true;
900 if (!hasAnyParentTrait)
907 for (
auto itr = traitConfig.
Entries.begin(); itr != traitConfig.
Entries.end(); )
912 if (!removeInvalidEntries)
917 if (!itr->GrantedRanks
919 traitConfig.
Entries.erase(itr);
924 itr = traitConfig.
Entries.begin();
930 struct SubtreeValidationData
932 std::vector<WorldPackets::Traits::TraitEntry>
Entries;
933 bool IsSelected =
false;
935 std::unordered_map<int32, SubtreeValidationData> subtrees;
940 auto entryItr = std::ranges::find(node->Entries, traitEntry.TraitNodeEntryID, [](NodeEntry
const& nodeEntry) { return int32(nodeEntry.Data->ID); });
941 ASSERT(entryItr != node->Entries.end());
944 subtrees[entryItr->Data->TraitSubTreeID].IsSelected =
true;
946 if (node->Data->TraitSubTreeID)
947 subtrees[node->Data->TraitSubTreeID].Entries.push_back(traitEntry);
951 subTree.Active =
false;
953 for (
auto&& [selectedSubTreeId, data] : subtrees)
956 if (subtreeDataItr == std::ranges::end(traitConfig.
SubTrees))
959 subtreeDataItr->TraitSubTreeID = selectedSubTreeId;
962 subtreeDataItr->Entries = std::move(data.Entries);
963 subtreeDataItr->Active = data.IsSelected;
966 std::map<int32, int32> grantedCurrencies;
969 for (
auto const& [traitCurrencyId, spentAmount] : *spentCurrencies)
974 if (!spentAmount.Total)
978 if (!grantedCount || *grantedCount < spentAmount.Total)
992 if (!spentAmount || spentAmount->
Total != *grantedAmount)
996 for (
auto&& [selectedTraitSubTreeId, data] : subtrees)
998 if (!data.IsSelected)
1008 if (!spentAmount || spentAmount->
Total != *grantedAmount)