Dragonflight Talent System
- For talents in general, see Talent.
The main focus of this guide is the new player talent tree system[1]. Professions and "Generic" trees are sometimes mentioned, but professions in particular have some differences that this guide won't dive into. Currently, the only "Generic" tree, is the dragonriding talent tree. If the system turns out to be a success, we can expect more generic trees in the future.
Talent Build String
With the re-introduction of talent trees, Blizzard has implemented a way to share your talent choices. This is done through a Talent Build String. This string is a base64 encoded binary format, containing the build's spec, a checksum of the tree itself, and selected talent information.
The format for this string, has been described by blizzard in Blizzard_ClassTalentImportExport.lua. This file also contains the code that Blizzard uses to encode and decode talent strings.
Besides manually encoding a string, you can also use C_Traits.GenerateImportString and C_Traits.GenerateInspectImportString to generate a Talent Build String. See also Hyperlinks-talentbuild.
Overview
C_Traits contains all the generic APIs and interacts with C_ClassTalents for talent tree specific APIs, and C_ProfSpecs for profession specific APIs.
The hierarchy in the new talent system is:
[system] - config >< tree < node < nodeEntry - nodeDefinition - spell
In other words (with db2 links added when they line up, however, there are usually multiple tables involved for each entity):
- TraitSystem.db2 - specifies Generic trees, e.g. dragonriding talents. This is not part of the hierarchy for player talent trees or professions
- TraitConfig - when you apply your talent choices, you apply it to the config. Switching talent loadouts basically boils down to switching TraitConfig. A config can contain multiple TraitTrees (this is the case for professions, for talent trees there is only 1 TraitTree).
- TraitTree.db2 - a tree is basically a list of TraitNodes, and currency cost information. For talent trees, while visually there's a separate class and spec tree, in the API it's all 1 tree. There's 1 TraitTree per class. Professions have a TraitTree for each profession specialization tab.
- TraitNode.db2 - a node is roughly equivalent to a button in the tree UI. Nodes can have multiple entries, in particular, choice nodes will have 2, whereas single option nodes have 1.
- TraitNodeEntry.db2 - an entry holds information about a specific talent choice itself, including how many ranks it has.
- TraitDefinition.db2 - is directly related to a TraitNodeEntry, and will contain the spellID and/or other relevant information, of what a talent will actually do for a player.
Some other entities include
- TraitCurrency.db2 - nodes generally cost a specific TraitCurrency to purchase. These TraitCurrencies can be based on regular currencies (such as profession knowlegde, or dragonriding glyphs), or have own tracking and spending (as is the case for player talents).
- TraitSubTree.db2 - a The War Within Hero Tree, it's not really part of the hierarchy directly, but instead, has relations to trees, nodes, and nodeEntries.
All of the entities above can be queried with the C_Traits API, which is listed below
Hero Talent Trees
The War Within introduced a new concept into talent trees: Hero Trees. In APIs, these are called subTrees instead. Various APIs have been updated to include subTree information, but generally, the inclusion of subtrees does not affect the overall methodology of working with the C_ClassTalents and C_Traits APIs. Conceptually, SubTrees are implemented in a fashion, that allows the system to be used in applications other than Hero Talents. Time will tell whether that will happen.
Choosing a subTree
Each TraitTree has 1 hidden talent node per specialization, which defines the chosen subTree. This node has the type Enum.TraitNodeType.SubTreeSelection
, and has 2 entryIDs. These EntryIDs' entryInfo table, includes a entryInfo.subTreeID
value, which corresponds to the SubTreeID.
Choosing a subTree, is as simple as changing the selected node, via C_Traits.SetSelection. This is also how subTree selection is stored in a Talent Build String, encoding the SubTreeSelection type node the same way as a regular Selection type node.
The SubTreeSelection Node has no cost attached to it, only a TraitCondition requirement of level 71.
Purchasing subTree talents
Each subTree in a class has their own TraitCurrency. Note that different classes might share the same TraitCurrencyID. Once a player reaches level 70, they'll stop earning any class or spec TraitCurrency, and will start earning subTree TraitCurrency instead. The player will earn 1 amount of each subTree TraitCurrency every level, up to the level cap. This allows you to purchase talents in both subTrees at once, though only 1 subTree will ever be active. Purchasing subTree talents, functions the exact same way as any other talent, its cost is just a subTree TraitCurrency, rather than a class/spec TraitCurrency.
Checking whether a subTree talent is active
It is assumed that you can use IsPlayerSpell to find out whether a talent in a subTree is purchased and active. Alternatively, you can check C_Traits.GetNodeInfo nodeInfo.subTreeActive
to check whether the node's subTree is active.
SubTree APIs
- C_ClassTalents.GetHeroTalentSpecsForClassSpec returns the available subTreeIDs for a given spec, and the required player level to activate a subTree. Note it requires a configID as an argument, it is not yet confirmed whether this imposes restrictions on the returned value.
- C_Traits.GetSubTreeInfo returns TraitSubTreeInfo for a given subTreeID, this info includes all the info required to display the subTree in the UI, and includes the TraitCurrencyID and SubTreeSelection nodeID. Note it requires a configID as an argument, it is not yet confirmed whether this imposes restrictions on the returned value.
- C_Traits.GetNodeInfo is updated to include
nodeInfo.subTreeID
andnodeInfo.subTreeActive
properties for subTree talents, for other nodes, these are both nil. - C_Traits.GetEntryInfo is updated to include
entryInfo.subTreeID
, which only exists on SubTreeSelection node entries, and specified the subTree that is activated if this entry is chosen. - TRAIT_SUB_TREE_CHANGED is fired when a SubTreeSelection node entry is updated, telling the UI to change its display. This does not mean that the new subTree is actually active, this only happens after the pending change is committed.
Visual positioning implementation details
Each Node has it's own posX and posY information. For subTree nodes, these should not be seen in relation to regular nodes. Instead, subTree nodes' positions should only be viewed in relation to themselves. In fact, the default talent UI will normalize the subTree node positions through TalentFrameUtil.GetNormalizedSubTreeNodePosition
.
A given subTree, has SubTreeInfo, which returns the Center X for all its subTree nodes, and the top Y for its top subTree node. This allows the UI to use these numbers to make a general offset for subTree nodes.
Effectively, you take the top-center subTree node, and places it in your desired position, taking the subTree posX and posY as offsets, and position all subTree nodes based on (nodePosX / 10) - subTreePosX
and (-nodePosY / 10) + subTreePosY
Other implementation details
SubTreeSelection nodes, have TraitCond.db2 applied through TraitNodeXTraitCond.db2, which specifies a SpecSetMember.db2 requirement, which maps to the required specializationID. TraitCurrencySource.db2 specifies the sources for SubTree TraitCurrency.db2, which also shows there are only 4 currencies total, so all classes use the same currencies for the SubTrees, with most classes using the same 3. Other than that, all the info lives in TraitSubTree.db2, and the rest of the implementation is effectively identical to the spec and class nodes
Commands
Several script and slash commands[2] have been added for accessibility and general use in ClassTalentHelper.lua
- Activate loadout by name:
/run ClassTalentHelper.SwitchToLoadoutByName("loadoutName")
/loadoutname loadoutName
/lon loadoutName
- Activate loadout by dropdown list order, starting with 1:
/run ClassTalentHelper.SwitchToLoadoutByIndex(1)
/loadoutindex 2
/loi 3
- Activate specialization by name:
/run ClassTalentHelper.SwitchToSpecializationByName("specName")
/specname specName
/spn specName
- Activate specialization by order within the Specializations tab, starting with 1:
/run ClassTalentHelper.SwitchToSpecializationByIndex(1)
/specindex 2
/spi 3
Examples
- Extracts the list of SpellIDs for the player's current specialization
function GetSpellIDList()
local list = {}
local configID = C_ClassTalents.GetActiveConfigID()
if configID == nil then return end
local configInfo = C_Traits.GetConfigInfo(configID)
if configInfo == nil then return end
for _, treeID in ipairs(configInfo.treeIDs) do -- in the context of talent trees, there is only 1 treeID
local nodes = C_Traits.GetTreeNodes(treeID)
for i, nodeID in ipairs(nodes) do
local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID)
for _, entryID in ipairs(nodeInfo.entryIDs) do -- each node can have multiple entries (e.g. choice nodes have 2)
local entryInfo = C_Traits.GetEntryInfo(configID, entryID)
if entryInfo and entryInfo.definitionID then
local definitionInfo = C_Traits.GetDefinitionInfo(entryInfo.definitionID)
if definitionInfo.spellID then
table.insert(list, definitionInfo.spellID)
end
end
end
end
end
return list
end
- Change a selection node choice, and apply the changes.
function SwitchDragonridingTalentSelection()
local DRAGONRIDING_TRAIT_SYSTEM_ID = 1
local nodeID = 64062 -- vigor regen node
local configID = C_Traits.GetConfigIDBySystemID(DRAGONRIDING_TRAIT_SYSTEM_ID)
-- for class talents, this should be: configID = C_ClassTalents.GetActiveConfigID()
if not configID then return end
local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID)
if not nodeInfo or not nodeInfo.entryIDs or 2 ~= #nodeInfo.entryIDs then return end
local currentEntryID = nodeInfo.entryIDsWithCommittedRanks[1] or nil
local currentSelection
for index, entryID in ipairs(nodeInfo.entryIDs) do
if entryID == currentEntryID then currentSelection = index end
end
local success = C_Traits.SetSelection(configID, nodeID, nodeInfo.entryIDs[currentSelection == 2 and 1 or 2]) -- defaults to setting the second option, if not currently set
if not success then return end
local commitSuccess = C_Traits.CommitConfig(configID)
-- for class talents, this should be: commitSuccess = C_ClassTalents.CommitConfig(selectedLoadoutID)
end
- Get the currently selected loadout configID. Currently, there is no authoritative API to tell you which loadout is currently selected, so you have to do a best-effort guess instead.
function GetSelectedLoadoutConfigID()
local lastSelected = PlayerUtil.GetCurrentSpecID() and C_ClassTalents.GetLastSelectedSavedConfigID(PlayerUtil.GetCurrentSpecID())
local selectionID = ClassTalentFrame and ClassTalentFrame.TalentsTab and ClassTalentFrame.TalentsTab.LoadoutDropDown and ClassTalentFrame.TalentsTab.LoadoutDropDown.GetSelectionID and ClassTalentFrame.TalentsTab.LoadoutDropDown:GetSelectionID()
-- the priority in authoritativeness is [default UI's dropdown] > [API] > ['ActiveConfigID'] > nil
return selectionID or lastSelected or C_ClassTalents.GetActiveConfigID() or nil -- nil happens when you don't have any spec selected, e.g. on a freshly created character
end
- Checks if a spellID is a learned talent, note that this is an example to explain the API, and that it's generally encouraged to use IsPlayerSpell instead.
function IsSpellTalented(spellID) -- this could be made to be a lot more efficient, if you already know the relevant nodeID and entryID
local configID = C_ClassTalents.GetActiveConfigID()
if configID == nil then return end
local configInfo = C_Traits.GetConfigInfo(configID)
if configInfo == nil then return end
for _, treeID in ipairs(configInfo.treeIDs) do -- in the context of talent trees, there is only 1 treeID
local nodes = C_Traits.GetTreeNodes(treeID)
for i, nodeID in ipairs(nodes) do
local nodeInfo = C_Traits.GetNodeInfo(configID, nodeID)
for _, entryID in ipairs(nodeInfo.entryIDsWithCommittedRanks) do -- there should be 1 or 0
local entryInfo = C_Traits.GetEntryInfo(configID, entryID)
if entryInfo and entryInfo.definitionID then
local definitionInfo = C_Traits.GetDefinitionInfo(entryInfo.definitionID)
if definitionInfo.spellID == spellID then
return true
end
end
end
end
end
return false
end
Relevant DB2 tables
The overview section already mentions a few DB2 tables, this is a full list with a short explanation of its contents and use. Due to the nature of things, this list is highly subject to changes.
- SkillLineXTraitTree.db2 maps TraitTrees to SkillLines, including player classes and professions.
- TraitCond.db2 TraitCondition info, defining node visibility, granting, and availability, and the requirements for each condition
- TraitCost.db2 the TraitCurrencyID cost for a given TraitCostID, which is often shared by multiple nodes
- TraitCurrency.db2 TraitCurrency info, and a relation to CurrencyTypeID in case the TraitCurrency is based on a currency
- TraitCurrencySource.db2 defines TraitCurrency sources, for the currencies not tied to a currency
- TraitDefinition.db2 the actual effect of a TraitNodeEntry, generally a spell or crafting effect
- TraitDefinitionEffectPoints.db2 related to the TraitNodeDefinition and a Curve.db2
- TraitEdge.db2 defines the arrows to be drawn between arrows, and whether these arrows impose availability constraints
- TraitNode.db2 a node's TraitTreeID, position, type, and TraitSubTreeID
- TraitNodeEntry.db2 maps to TraitDefinition, details max ranks, its type, and optionally a TraitSubTreeID
- TraitNodeEntryXTraitCost.db2 maps a TraitNodeEntry and TraitCost
- TraitNodeGroup.db2 specifies a NodeGroupID, this is used to apply costs and conditions to several nodes at once
- TraitNodeGroupXTraitCond.db2 maps groups with conditions
- TraitNodeGroupXTraitCost.db2 maps group with costs
- TraitNodeGroupXTraitNode.db2 maps nodes with groups
- TraitNodeXTraitCond.db2 maps nodes with conditions
- TraitNodeXTraitCost.db2 maps nodes with costs
- TraitNodeXTraitNodeEntry.db2 maps nodes with their entries
- TraitSubTree.db2 currently defines Hero Talent Trees, with their name, description, texture atlas, and parent TraitTreeID
- TraitSystem.db2 used for generic talent trees, such as dragonriding
- TraitTree.db2 tree, with optional conditions, flags, reference to TraitSystem
- TraitTreeLoadout.db2 contains information for starter builds - there are dupplicates in the data, and it's currently unknown how blizzard associates a loadout with a specific spec
- TraitTreeLoadoutEntry.db2 contains starter build selections and their order
- TraitTreeXTraitCurrency.db2 maps trees and currencies, with information in which order the API should return the TraitCurrencyIDs
References
|