Dragonflight Talent System

From Warcraft Wiki
Jump to navigation Jump to search
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 and nodeInfo.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.

References