38 std::vector<TraitCostEntry const*>
Costs;
45 std::vector<NodeGroup const*>
Groups;
46 std::vector<std::pair<Node const*, TraitEdgeType>>
ParentNodes;
48 std::vector<TraitCostEntry const*>
Costs;
55 std::vector<TraitCostEntry const*>
Costs;
62 std::vector<Node const*>
Nodes;
63 std::vector<TraitCostEntry const*>
Costs;
68std::unordered_map<int32, NodeGroup> _traitGroups;
69std::unordered_map<int32, Node> _traitNodes;
70std::unordered_map<int32, Tree> _traitTrees;
71std::array<int32, MAX_CLASSES> _skillLinesByClass;
72std::unordered_map<int32, std::vector<Tree const*>> _traitTreesBySkillLine;
73std::unordered_map<int32, std::vector<Tree const*>> _traitTreesByTraitSystem;
74int32 _configIdGenerator = 0;
75std::unordered_map<int32, std::vector<TraitCurrencySourceEntry const*>> _traitCurrencySourcesByCurrency;
76std::unordered_map<int32, std::vector<TraitDefinitionEffectPointsEntry const*>> _traitDefinitionEffectPointModifiers;
77std::unordered_map<int32, std::vector<TraitTreeLoadoutEntryEntry const*>> _traitTreeLoadoutsByChrSpecialization;
82 _configIdGenerator = time(
nullptr);
84 std::unordered_map<int32, std::vector<TraitCondEntry const*>> nodeEntryConditions;
87 nodeEntryConditions[traitNodeEntryXTraitCondEntry->TraitNodeEntryID].push_back(traitCondEntry);
89 std::unordered_map<int32, std::vector<TraitCostEntry const*>> nodeEntryCosts;
92 nodeEntryCosts[traitNodeEntryXTraitCostEntry->TraitNodeEntryID].push_back(traitCostEntry);
94 std::unordered_map<int32, std::vector<TraitCondEntry const*>> nodeGroupConditions;
97 nodeGroupConditions[traitNodeGroupXTraitCondEntry->TraitNodeGroupID].push_back(traitCondEntry);
99 std::unordered_map<int32, std::vector<TraitCostEntry const*>> nodeGroupCosts;
102 nodeGroupCosts[traitNodeGroupXTraitCostEntry->TraitNodeGroupID].push_back(traitCondEntry);
104 std::unordered_multimap<int32, int32> nodeGroups;
106 nodeGroups.emplace(traitNodeGroupXTraitNodeEntry->TraitNodeID, traitNodeGroupXTraitNodeEntry->TraitNodeGroupID);
108 std::unordered_map<int32, std::vector<TraitCondEntry const*>> nodeConditions;
111 nodeConditions[traitNodeXTraitCondEntry->TraitNodeID].push_back(traitCondEntry);
113 std::unordered_map<int32, std::vector<TraitCostEntry const*>> nodeCosts;
116 nodeCosts[traitNodeXTraitCostEntry->TraitNodeID].push_back(traitCostEntry);
118 std::unordered_multimap<int32, TraitNodeEntryEntry const*> nodeEntries;
121 nodeEntries.emplace(traitNodeXTraitNodeEntryEntry->TraitNodeID, traitNodeEntryEntry);
123 std::unordered_map<int32, std::vector<TraitCostEntry const*>> treeCosts;
126 treeCosts[traitTreeXTraitCostEntry->TraitTreeID].push_back(traitCostEntry);
128 std::unordered_map<int32, std::vector<TraitCurrencyEntry const*>> treeCurrencies;
131 treeCurrencies[traitTreeXTraitCurrencyEntry->TraitTreeID].push_back(traitCurrencyEntry);
133 std::unordered_map<int32, std::vector<int32>> traitTreesIdsByTraitSystem;
137 Tree& tree = _traitTrees[traitTree->ID];
138 tree.Data = traitTree;
141 tree.Costs = std::move(*costs);
144 tree.Currencies = std::move(*currencies);
146 if (traitTree->TraitSystemID)
148 traitTreesIdsByTraitSystem[traitTree->TraitSystemID].push_back(traitTree->ID);
155 NodeGroup& nodeGroup = _traitGroups[traitNodeGroup->ID];
156 nodeGroup.Data = traitNodeGroup;
159 nodeGroup.Conditions = std::move(*conditions);
162 nodeGroup.Costs = std::move(*costs);
167 Node& node = _traitNodes[traitNode->ID];
168 node.Data = traitNode;
171 tree->Nodes.push_back(&node);
175 NodeEntry& entry = node.Entries.emplace_back();
176 entry.Data = traitNodeEntry;
179 entry.Conditions = std::move(*conditions);
182 entry.Costs = std::move(*costs);
191 nodeGroup->Nodes.push_back(&node);
192 node.Groups.push_back(nodeGroup);
196 node.Conditions = std::move(*conditions);
199 node.Costs = std::move(*costs);
209 right->ParentNodes.emplace_back(left,
static_cast<TraitEdgeType>(traitEdgeEntry->Type));
222 _traitTreesBySkillLine[skillLineXTraitTreeEntry->SkillLineID].push_back(tree);
227 if (skillRaceClassInfo->ClassMask & (1 << (i - 1)))
228 _skillLinesByClass[i] = skillLineXTraitTreeEntry->SkillLineID;
236 for (
auto&& [traitSystemId, traitTreeIds] : traitTreesIdsByTraitSystem)
237 for (
int32 traitTreeId : traitTreeIds)
239 _traitTreesByTraitSystem[traitSystemId].push_back(tree);
242 _traitCurrencySourcesByCurrency[traitCurrencySource->TraitCurrencyID].push_back(traitCurrencySource);
245 _traitDefinitionEffectPointModifiers[traitDefinitionEffectPoints->TraitDefinitionID].push_back(traitDefinitionEffectPoints);
247 std::unordered_map<int32, std::vector<TraitTreeLoadoutEntryEntry const*>> traitTreeLoadoutEntries;
249 traitTreeLoadoutEntries[traitTreeLoadoutEntry->TraitTreeLoadoutID].push_back(traitTreeLoadoutEntry);
257 return left->OrderIndex < right->OrderIndex;
260 _traitTreeLoadoutsByChrSpecialization[traitTreeLoadout->ChrSpecializationID] = std::move(*entries);
271 if (_configIdGenerator == std::numeric_limits<int32>::max())
272 _configIdGenerator = 0;
274 return ++_configIdGenerator;
283 return tree->ConfigType;
293 switch (traitConfig.
Type)
312 auto getCurrencyCount = [&](
int32 currencyId)
315 return count ? *count : 0;
319 for (NodeGroup
const* group : node->Groups)
324 auto nodeEntryItr = std::find_if(node->Entries.begin(), node->Entries.end(), [&entry](NodeEntry
const& nodeEntry) { return int32(nodeEntry.Data->ID) == entry.TraitNodeEntryID; });
325 if (nodeEntryItr != node->Entries.end())
345 for (NodeGroup
const* group : node->Groups)
349 auto nodeEntryItr = std::find_if(node->Entries.begin(), node->Entries.end(), [&entry](NodeEntry
const& nodeEntry) { return int32(nodeEntry.Data->ID) == entry.TraitNodeEntryID; });
350 if (nodeEntryItr != node->Entries.end())
368 auto hasTraitNodeEntry = [&traitConfig](
int32 traitNodeEntryId)
372 return traitEntry.TraitNodeEntryID == traitNodeEntryId && (traitEntry.Rank > 0 || traitEntry.GrantedRanks > 0);
373 }) != traitConfig.
Entries.end();
376 for (Tree
const* tree : *trees)
384 int32& amount = currencies[currency->
ID];
385 if (player.
GetMoney() >
uint64(std::numeric_limits<int32>::max() - amount))
386 amount = std::numeric_limits<int32>::max();
399 if (currencySource->QuestID && !player.
IsQuestRewarded(currencySource->QuestID))
402 if (currencySource->AchievementID && !player.
HasAchieved(currencySource->AchievementID))
405 if (currencySource->PlayerLevel && player.
GetLevel() < currencySource->PlayerLevel)
408 if (currencySource->TraitNodeEntryID && !hasTraitNodeEntry(currencySource->TraitNodeEntryID))
411 currencies[currencySource->TraitCurrencyID] += currencySource->Amount;
425 for (NodeGroup
const* group : node->Groups)
429 auto nodeEntryItr = std::find_if(node->Entries.begin(), node->Entries.end(), [&entry](NodeEntry
const& nodeEntry) { return int32(nodeEntry.Data->ID) == entry.TraitNodeEntryID; });
430 if (nodeEntryItr != node->Entries.end())
449 Optional<std::map<int32, int32>>& cachedCurrencies)
469 if (!cachedCurrencies)
474 auto itr = cachedCurrencies->try_emplace(condition->
TraitCurrencyID, 0).first;
480 auto itr = cachedCurrencies->try_emplace(condition->
TraitCurrencyID, 0).first;
494 std::vector<UF::TraitEntry> entries;
499 auto getOrCreateEntry = [&entries](
int32 nodeId,
int32 entryId)
501 auto itr = std::find_if(entries.begin(), entries.end(), [&](
UF::TraitEntry const& traitEntry)
503 return traitEntry.TraitNodeID == nodeId && traitEntry.TraitNodeEntryID == entryId;
505 if (itr == entries.end())
507 itr = entries.emplace(entries.end());
508 itr->TraitNodeID = nodeId;
509 itr->TraitNodeEntryID = entryId;
511 itr->GrantedRanks = 0;
518 for (Tree
const* tree : *trees)
520 for (Node
const* node : tree->Nodes)
522 for (NodeEntry
const& entry : node->Entries)
525 getOrCreateEntry(node->Data->ID, entry.Data->ID)->GrantedRanks += condition->
GrantedRanks;
529 for (NodeEntry
const& entry : node->Entries)
530 getOrCreateEntry(node->Data->ID, entry.Data->ID)->GrantedRanks += condition->
GrantedRanks;
532 for (NodeGroup
const* group : node->Groups)
535 for (NodeEntry
const& entry : node->Entries)
536 getOrCreateEntry(node->Data->ID, entry.Data->ID)->GrantedRanks += condition->
GrantedRanks;
549 auto entryItr = std::find_if(node->Entries.begin(), node->Entries.end(), [&](NodeEntry
const& entry) { return entry.Data->ID == uint32(traitEntry.TraitNodeEntryID); });
550 if (entryItr == node->Entries.end())
561 auto getNodeEntryCount = [&](
int32 traitNodeId)
565 return traitEntry.TraitNodeID == traitNodeId;
569 auto getNodeEntry = [&](
int32 traitNodeId,
int32 traitNodeEntryId)
573 return traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId;
575 return entryItr != traitConfig.
Entries.end() ? &*entryItr :
nullptr;
578 auto isNodeFullyFilled = [&](Node
const* node)
581 return std::any_of(node->Entries.begin(), node->Entries.end(), [&](NodeEntry
const& nodeEntry)
584 return traitEntry && (traitEntry->
Rank + traitEntry->
GrantedRanks) == nodeEntry.Data->MaxRanks;
587 return std::all_of(node->Entries.begin(), node->Entries.end(), [&](NodeEntry
const& nodeEntry)
589 WorldPackets::Traits::TraitEntry const* traitEntry = getNodeEntry(node->Data->ID, nodeEntry.Data->ID);
590 return traitEntry && (traitEntry->Rank + traitEntry->GrantedRanks) == nodeEntry.Data->MaxRanks;
597 auto meetsConditions = [&](std::vector<TraitCondEntry const*>
const& conditions)
599 bool hasConditions =
false;
607 hasConditions =
true;
611 return !hasConditions;
621 if (getNodeEntryCount(traitEntry.
TraitNodeID) != 1)
624 for (NodeEntry
const& entry : node->Entries)
625 if (!meetsConditions(entry.Conditions))
628 if (!meetsConditions(node->Conditions))
631 for (NodeGroup
const* group : node->Groups)
632 if (!meetsConditions(group->Conditions))
635 if (!node->ParentNodes.empty())
637 bool hasAnyParentTrait =
false;
638 for (
auto const& [parentNode, edgeType] : node->ParentNodes)
640 if (!isNodeFullyFilled(parentNode))
648 hasAnyParentTrait =
true;
651 if (!hasAnyParentTrait)
656 std::map<int32, int32> grantedCurrencies;
659 for (
auto [traitCurrencyId, spentAmount] : *spentCurrencies)
668 if (!grantedCount || *grantedCount < spentAmount)
674 for (
auto [traitCurrencyId, grantedAmount] : grantedCurrencies)
680 if (!spentAmount || *spentAmount != grantedAmount)
708 std::map<int32, int32> currencies;
717 return traitEntry.TraitNodeID == traitNodeId && traitEntry.TraitNodeEntryID == traitNodeEntryId;
719 return entryItr != config.
Entries.end() ? &*entryItr :
nullptr;
724 int32 addedRanks = loadoutEntry->NumPoints;
728 newEntry.
TraitNodeID = loadoutEntry->SelectedTraitNodeID;
736 addedRanks -= entryInConfig->
Rank;
738 newEntry.
Rank = addedRanks;
744 entryInConfig->
Rank += addedRanks;
746 traitConfig.
Entries.push_back(newEntry);
DB2Storage< TraitCurrencySourceEntry > sTraitCurrencySourceStore("TraitCurrencySource.db2", &TraitCurrencySourceLoadInfo::Instance)
DB2Storage< SkillLineEntry > sSkillLineStore("SkillLine.db2", &SkillLineLoadInfo::Instance)
DB2Storage< TraitTreeXTraitCostEntry > sTraitTreeXTraitCostStore("TraitTreeXTraitCost.db2", &TraitTreeXTraitCostLoadInfo::Instance)
DB2Storage< TraitNodeGroupXTraitCondEntry > sTraitNodeGroupXTraitCondStore("TraitNodeGroupXTraitCond.db2", &TraitNodeGroupXTraitCondLoadInfo::Instance)
DB2Storage< TraitNodeGroupXTraitNodeEntry > sTraitNodeGroupXTraitNodeStore("TraitNodeGroupXTraitNode.db2", &TraitNodeGroupXTraitNodeLoadInfo::Instance)
DB2Storage< TraitNodeEntry > sTraitNodeStore("TraitNode.db2", &TraitNodeLoadInfo::Instance)
DB2Storage< SkillLineXTraitTreeEntry > sSkillLineXTraitTreeStore("SkillLineXTraitTree.db2", &SkillLineXTraitTreeLoadInfo::Instance)
DB2Storage< TraitCostEntry > sTraitCostStore("TraitCost.db2", &TraitCostLoadInfo::Instance)
DB2Storage< TraitTreeLoadoutEntry > sTraitTreeLoadoutStore("TraitTreeLoadout.db2", &TraitTreeLoadoutLoadInfo::Instance)
DB2Storage< TraitNodeXTraitNodeEntryEntry > sTraitNodeXTraitNodeEntryStore("TraitNodeXTraitNodeEntry.db2", &TraitNodeXTraitNodeEntryLoadInfo::Instance)
DB2Storage< TraitNodeGroupXTraitCostEntry > sTraitNodeGroupXTraitCostStore("TraitNodeGroupXTraitCost.db2", &TraitNodeGroupXTraitCostLoadInfo::Instance)
DB2Storage< TraitTreeEntry > sTraitTreeStore("TraitTree.db2", &TraitTreeLoadInfo::Instance)
DB2Storage< TraitCondEntry > sTraitCondStore("TraitCond.db2", &TraitCondLoadInfo::Instance)
DB2Storage< TraitTreeXTraitCurrencyEntry > sTraitTreeXTraitCurrencyStore("TraitTreeXTraitCurrency.db2", &TraitTreeXTraitCurrencyLoadInfo::Instance)
DB2Storage< ChrSpecializationEntry > sChrSpecializationStore("ChrSpecialization.db2", &ChrSpecializationLoadInfo::Instance)
DB2Storage< TraitNodeEntryEntry > sTraitNodeEntryStore("TraitNodeEntry.db2", &TraitNodeEntryLoadInfo::Instance)
DB2Storage< TraitDefinitionEffectPointsEntry > sTraitDefinitionEffectPointsStore("TraitDefinitionEffectPoints.db2", &TraitDefinitionEffectPointsLoadInfo::Instance)
DB2Storage< TraitEdgeEntry > sTraitEdgeStore("TraitEdge.db2", &TraitEdgeLoadInfo::Instance)
DB2Storage< TraitNodeEntryXTraitCondEntry > sTraitNodeEntryXTraitCondStore("TraitNodeEntryXTraitCond.db2", &TraitNodeEntryXTraitCondLoadInfo::Instance)
DB2Storage< TraitCurrencyEntry > sTraitCurrencyStore("TraitCurrency.db2", &TraitCurrencyLoadInfo::Instance)
DB2Storage< TraitNodeXTraitCostEntry > sTraitNodeXTraitCostStore("TraitNodeXTraitCost.db2", &TraitNodeXTraitCostLoadInfo::Instance)
DB2Storage< TraitNodeXTraitCondEntry > sTraitNodeXTraitCondStore("TraitNodeXTraitCond.db2", &TraitNodeXTraitCondLoadInfo::Instance)
DB2Storage< TraitNodeGroupEntry > sTraitNodeGroupStore("TraitNodeGroup.db2", &TraitNodeGroupLoadInfo::Instance)
DB2Storage< TraitNodeEntryXTraitCostEntry > sTraitNodeEntryXTraitCostStore("TraitNodeEntryXTraitCost.db2", &TraitNodeEntryXTraitCostLoadInfo::Instance)
DB2Storage< TraitTreeLoadoutEntryEntry > sTraitTreeLoadoutEntryStore("TraitTreeLoadoutEntry.db2", &TraitTreeLoadoutEntryLoadInfo::Instance)
@ RequiredForAvailability
std::optional< T > Optional
Optional helper class to wrap optional values within.
std::vector< TraitCondEntry const * > Conditions
TraitConfigType ConfigType
std::vector< NodeGroup const * > Groups
std::vector< Node const * > Nodes
std::vector< std::pair< Node const *, TraitEdgeType > > ParentNodes
std::vector< TraitCurrencyEntry const * > Currencies
std::vector< TraitCostEntry const * > Costs
std::vector< NodeEntry > Entries
std::vector< Tree const * > const * GetTreesForConfig(WorldPackets::Traits::TraitConfig const &traitConfig)
Finds relevant TraitTree identifiers.
void FillSpentCurrenciesMap(WorldPackets::Traits::TraitEntry const &entry, std::map< int32, int32 > &cachedCurrencies)
void InitializeStarterBuildTraitConfig(WorldPackets::Traits::TraitConfig &traitConfig, PlayerDataAccessor player)
std::vector< TraitDefinitionEffectPointsEntry const * > const * GetTraitDefinitionEffectPointModifiers(int32 traitDefinitionId)
int32 GenerateNewTraitConfigId()
LearnResult ValidateConfig(WorldPackets::Traits::TraitConfig const &traitConfig, PlayerDataAccessor player, bool requireSpendingAllCurrencies)
bool IsValidEntry(WorldPackets::Traits::TraitEntry const &traitEntry)
void TakeCurrencyCost(WorldPackets::Traits::TraitEntry const &entry, std::map< int32, int32 > ¤cies)
@ NotEnoughTalentsInPrimaryTree
std::vector< UF::TraitEntry > GetGrantedTraitEntriesForConfig(WorldPackets::Traits::TraitConfig const &traitConfig, PlayerDataAccessor player)
void FillOwnedCurrenciesMap(WorldPackets::Traits::TraitConfig const &traitConfig, PlayerDataAccessor player, std::map< int32, int32 > ¤cies)
TraitConfigType GetConfigTypeForTree(int32 traitTreeId)
bool HasEnoughCurrency(WorldPackets::Traits::TraitEntry const &entry, std::map< int32, int32 > const ¤cies)
bool MeetsTraitCondition(WorldPackets::Traits::TraitConfig const &traitConfig, PlayerDataAccessor player, TraitCondEntry const *condition, Optional< std::map< int32, int32 > > &cachedCurrencies)
auto MapEqualRange(M &map, typename M::key_type const &key)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
TraitConditionType GetCondType() const
int32 SpentAmountRequired
TraitCurrencyType GetType() const
int32 GetCurrencyQuantity(int32 currencyId) const
uint32 GetPrimarySpecialization() const
bool HasAchieved(int32 achievementId) const
bool IsQuestRewarded(int32 questId) const
int32 ChrSpecializationID
std::vector< TraitEntry > Entries