diff --git a/CHANGELOG.md b/CHANGELOG.md index bd821d6b..b4faaacc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format of this changelog is based on [Keep a Changelog](https://keepachangel ### Added * Add support for Cataclysm Classic 4.4.0 +* Add new talent selection UI for Cataclysm Classic ## [1.14.6] - 2024-04-07 diff --git a/Clicked/Config/Bindings.lua b/Clicked/Config/Bindings.lua index 3c0ca7c9..b361e306 100644 --- a/Clicked/Config/Bindings.lua +++ b/Clicked/Config/Bindings.lua @@ -21,6 +21,7 @@ local GetAddOnEnableState = C_AddOns.GetAddOnEnableState or function(n, c) retur local AceGUI = LibStub("AceGUI-3.0") local LibTalentInfo = LibStub("LibTalentInfo-1.0") +local LibTalentInfoClassic = LibStub("LibTalentInfoClassic-1.0") local LibMacroSyntaxHighlight = LibStub("LibMacroSyntaxHighlight-1.0") --- @class ClickedInternal @@ -126,42 +127,80 @@ end --- @param specIndices? integer[] --- @return integer[] local function GetRelevantSpecializationIds(classNames, specIndices) - local specializationIds = {} + if Addon:IsGameVersionAtleast("RETAIL") then + local specializationIds = {} - if specIndices == nil then - specIndices = {} + if specIndices == nil then + specIndices = {} - if classNames == nil or #classNames == 1 and classNames[1] == select(2, UnitClass("player")) then - specIndices[1] = GetSpecialization() - else - for _, class in ipairs(classNames) do - local specs = LibTalentInfo:GetClassSpecIDs(class) + if classNames == nil or #classNames == 1 and classNames[1] == select(2, UnitClass("player")) then + specIndices[1] = GetSpecialization() + else + for _, class in ipairs(classNames) do + local specs = LibTalentInfo:GetClassSpecIDs(class) - for specIndex in pairs(specs) do - table.insert(specIndices, specIndex) + for specIndex in pairs(specs) do + table.insert(specIndices, specIndex) + end end end end - end - if classNames == nil then - classNames = {} - classNames[1] = select(2, UnitClass("player")) - end + if classNames == nil then + classNames = {} + classNames[1] = select(2, UnitClass("player")) + end - for i = 1, #classNames do - local class = classNames[i] - local specs = LibTalentInfo:GetClassSpecIDs(class) + for i = 1, #classNames do + local class = classNames[i] + local specs = LibTalentInfo:GetClassSpecIDs(class) - for j = 1, #specIndices do - local specIndex = specIndices[j] - local specId = specs[specIndex] + for j = 1, #specIndices do + local specIndex = specIndices[j] + local specId = specs[specIndex] - table.insert(specializationIds, specId) + table.insert(specializationIds, specId) + end + end + + return specializationIds + else + local specializationIds = {} + + if specIndices == nil then + specIndices = {} + if classNames == nil or #classNames == 1 and classNames[1] == select(2, UnitClass("player")) then + specIndices[1] = GetPrimaryTalentTree() + else + for _, class in ipairs(classNames) do + local specs = LibTalentInfoClassic:GetClassSpecializations(class) + + for specIndex in pairs(specs) do + table.insert(specIndices, specIndex) + end + end + end + end + + if classNames == nil then + classNames = {} + classNames[1] = select(2, UnitClass("player")) + end + + for i = 1, #classNames do + local class = classNames[i] + local specs = LibTalentInfoClassic:GetClassSpecializations(class) + + for j = 1, #specIndices do + local specIndex = specIndices[j] + local spec = specs[specIndex] + + table.insert(specializationIds, spec.id) + end end - end - return specializationIds + return specializationIds + end end --- @param type '"LoadOption"'|'"TriStateLoadOption"' @@ -357,7 +396,7 @@ local function HijackSpellButton_UpdateButton(self) button:SetID(parent:GetID()) button:SetScript("OnEnter", function(_, motion) - if Addon:IsGameVersionAtleast("RETAIL") then + if Addon:IsGameVersionAtleast("CATA") then parent:OnEnter(motion) else SpellButton_OnEnter(parent, motion) @@ -365,7 +404,7 @@ local function HijackSpellButton_UpdateButton(self) end) button:SetScript("OnLeave", function() - if Addon:IsGameVersionAtleast("RETAIL") then + if Addon:IsGameVersionAtleast("CATA") then parent:OnLeave() else SpellButton_OnLeave(parent) @@ -376,7 +415,7 @@ local function HijackSpellButton_UpdateButton(self) local slot = SpellBook_GetSpellBookSlot(parent); local name, subName = GetSpellBookItemName(slot, SpellBookFrame.bookType) - if mouseButton ~= "RightButton" and (not Addon:IsGameVersionAtleast("RETAIL") and not Addon:IsStringNilOrEmpty(subName)) then + if mouseButton ~= "RightButton" and (not Addon:IsGameVersionAtleast("CATA") and not Addon:IsStringNilOrEmpty(subName)) then name = string.format("%s(%s)", name, subName) end @@ -1080,10 +1119,14 @@ local function DrawTalentSelectOption(container, title, specializations, data, m if data.selected then local talents - if mode == "talents" then - talents = Addon:GetLocalizedTalents(specializations) - elseif mode == "pvp_talents" then - talents = Addon:GetLocalizedPvPTalents(specializations) + if Addon:IsGameVersionAtleast("RETAIL") then + if mode == "talents" then + talents = Addon:GetLocalizedTalents(specializations) + elseif mode == "pvp_talents" then + talents = Addon:GetLocalizedPvPTalents(specializations) + end + elseif Addon:IsGameVersionAtleast("CATA") then + talents = Addon:Cata_GetTalents(specializations) end --- @param name string @@ -1125,7 +1168,13 @@ local function DrawTalentSelectOption(container, title, specializations, data, m ticker = C_Timer.NewTimer(Addon.TOOLTIP_SHOW_DELAY, function() GameTooltip:SetOwner(widget.frame, "ANCHOR_TOPLEFT") - GameTooltip:SetSpellByID(talent.spellId) + + if talent.spellId ~= nil then + GameTooltip:SetSpellByID(talent.spellId) + else + GameTooltip:SetText(talent.text) + end + GameTooltip:Show() end) end @@ -1358,7 +1407,7 @@ local function DrawSpellItemAuraSelection(container, action, mode) end if mode == Addon.BindingTypes.SPELL then - local hasRank = not Addon:IsGameVersionAtleast("RETAIL") and id ~= nil and string.find(name, "%((.+)%)") + local hasRank = not Addon:IsGameVersionAtleast("CATA") and id ~= nil and string.find(name, "%((.+)%)") -- pick from spellbook button do @@ -1389,7 +1438,7 @@ local function DrawSpellItemAuraSelection(container, action, mode) local tooltip = Addon.L["Click on a spell book entry to select it."] - if not Addon:IsGameVersionAtleast("RETAIL") then + if not Addon:IsGameVersionAtleast("CATA") then tooltip = tooltip .. "\n" .. Addon.L["Right click to use the max rank."] end @@ -2366,8 +2415,9 @@ local function DrawBindingMacroConditionsPage(container, binding) if Addon:IsGameVersionAtleast("RETAIL") then DrawMacroAdvancedFlyable(container, load.advancedFlyable) - DrawMacroBonusBar(container, load.bonusbar) end + + DrawMacroBonusBar(container, load.bonusbar) end end @@ -2411,7 +2461,14 @@ end --- @param specialization Binding.TriStateLoadOption --- @param classNames string[] local function DrawLoadSpecialization(container, specialization, classNames) - local items, order = Addon:GetLocalizedSpecializations(classNames) + local items, order + + if Addon:IsGameVersionAtleast("RETAIL") then + items, order = Addon:GetLocalizedSpecializations(classNames) + else + items, order = Addon:Cata_GetLocalizedSpecializations(classNames) + end + DrawTristateLoadOption(container, Addon.L["Talent specialization"], items, order, specialization) end @@ -2619,15 +2676,18 @@ local function DrawBindingLoadConditionsPage(container, binding) DrawLoadClass(container, load.class) DrawLoadRace(container, load.race) - if Addon:IsGameVersionAtleast("RETAIL") then + if Addon:IsGameVersionAtleast("CATA") then local classNames = GetTriStateLoadOptionValue(load.class) local specIndices = GetTriStateLoadOptionValue(load.specialization) local specializationIds = GetRelevantSpecializationIds(classNames, specIndices) DrawLoadSpecialization(container, load.specialization, classNames) DrawLoadTalent(container, load.talent --[[@as Binding.MutliFieldLoadOption]], specializationIds) - DrawLoadPvPTalent(container, load.pvpTalent, specializationIds) - DrawLoadWarMode(container, load.warMode) + + if Addon:IsGameVersionAtleast("RETAIL") then + DrawLoadPvPTalent(container, load.pvpTalent, specializationIds) + DrawLoadWarMode(container, load.warMode) + end elseif Addon:IsGameVersionAtleast("WOTLK") then local classNames = GetTriStateLoadOptionValue(load.class) @@ -2894,8 +2954,15 @@ local function CreateFromItemTemplate(identifier) elseif identifier == ITEM_TEMPLATE_IMPORT_ACTIONBAR then local group - if Addon:IsGameVersionAtleast("RETAIL") then - local _, name, _, icon = GetSpecializationInfo(GetSpecialization()) + if Addon:IsGameVersionAtleast("CATA") then + local name, icon + + if Addon:IsGameVersionAtleast("RETAIL") then + _, name, _, icon = GetSpecializationInfo(GetSpecialization()) + else + local class = select(3, UnitClass("player")) + _, name, _, icon = GetSpecializationInfoForClassID(class, GetPrimaryTalentTree()) + end --- @type Group for _, g in Clicked:IterateGroups() do @@ -2971,9 +3038,9 @@ local function CreateFromItemTemplate(identifier) binding.load.class.selected = 1 binding.load.class.single = select(2, UnitClass("player")) - if Addon:IsGameVersionAtleast("RETAIL") then + if Addon:IsGameVersionAtleast("CATA") then binding.load.specialization.selected = 1 - binding.load.specialization.single = GetSpecialization() + binding.load.specialization.single = GetSpecialization and GetSpecialization() or GetPrimaryTalentTree() end end end @@ -3375,7 +3442,7 @@ function Addon:BindingConfig_Initialize() HijackSpellButton_UpdateButton(nil) end) - if Addon:IsGameVersionAtleast("RETAIL") then + if Addon:IsGameVersionAtleast("CATA") then for i = 1, SPELLS_PER_PAGE do local currSpellButton = _G["SpellButton" .. i]; hooksecurefunc(currSpellButton, "UpdateButton", HijackSpellButton_UpdateButton) diff --git a/Clicked/Core/BindingProcessor.lua b/Clicked/Core/BindingProcessor.lua index 2c026159..606833df 100644 --- a/Clicked/Core/BindingProcessor.lua +++ b/Clicked/Core/BindingProcessor.lua @@ -875,6 +875,15 @@ function Addon:UpdateTalentCacheAndReloadBindings(delay, ...) end end end + elseif Addon:IsGameVersionAtleast("CATA") then + wipe(talentCache) + + for tab = 1, GetNumTalentTabs() do + for index = 1, GetNumTalents(tab) do + local name, _, _, _, rank = GetTalentInfo(tab, index) + talentCache[name] = rank > 0 + end + end end Clicked:ReloadBindings(...) @@ -1018,67 +1027,6 @@ function Addon:UpdateBindingLoadState(binding, options) end if Addon:IsGameVersionAtleast("RETAIL") then - -- specialization - if ShouldPerformStateCheck("PLAYER_TALENT_UPDATE") then - local function IsSpecializationIndexSelected(index) - return index == GetSpecialization() - end - - state.specialization = ValidateTriStateLoadOption(load.specialization, IsSpecializationIndexSelected) - end - - -- talent selected - if ShouldPerformStateCheck("TRAIT_CONFIG_CREATED", "TRAIT_CONFIG_UPDATED", "PLAYER_TALENT_UPDATE") then - --- @param entries Binding.MutliFieldLoadOption.Entry[] - --- @return boolean - local function IsTalentMatrixValid(entries) - if #entries == 0 then - return false - end - - if next(talentCache) == nil then - return false - end - - local compounds = {{}} - - for _, entry in ipairs(entries) do - if entry.operation == "OR" then - table.insert(compounds, {}) - end - - table.insert(compounds[#compounds], { - negated = entry.negated, - value = entry.value - }) - end - - for _, compound in ipairs(compounds) do - local valid = true - - for _, item in ipairs(compound) do - if #item.value > 0 then - if talentCache[item.value] and item.negated or not talentCache[item.value] and not item.negated then - valid = false - end - end - end - - if valid then - return true - end - end - - return false - end - - if load.talent.selected then - state.talent = IsTalentMatrixValid(load.talent.entries) - else - state.talent = nil - end - end - -- pvp talent selected if ShouldPerformStateCheck("PLAYER_PVP_TALENT_UPDATE") then local cache = {} @@ -1162,27 +1110,72 @@ function Addon:UpdateBindingLoadState(binding, options) state.warMode = ValidateLoadOption(load.warMode, IsWarMode) end - elseif Addon:IsGameVersionAtleast("WOTLK") then - -- specialization (classic) + end + + if Addon:IsGameVersionAtleast("WOTLK") then + -- specialization if ShouldPerformStateCheck("PLAYER_TALENT_UPDATE") then local function IsSpecializationIndexSelected(index) - return index == GetActiveTalentGroup() + local retail = GetSpecialization and GetSpecialization() + local cata = GetPrimaryTalentTree and GetPrimaryTalentTree() + local wotlk = GetActiveTalentGroup and GetActiveTalentGroup() + local selected = retail or cata or wotlk + return index == selected end state.specialization = ValidateTriStateLoadOption(load.specialization, IsSpecializationIndexSelected) end + end - -- talent selected (classic) - if ShouldPerformStateCheck("CHARACTER_POINTS_CHANGED", "PLAYER_TALENT_UPDATE") then - local function IsTalentIndexSelected(index) - local tab = math.ceil(index / MAX_NUM_TALENTS) - local talentIndex = (index - 1) % MAX_NUM_TALENTS + 1 + -- talent selected + if ShouldPerformStateCheck("CHARACTER_POINTS_CHANGED", "PLAYER_TALENT_UPDATE") then + --- @param entries Binding.MutliFieldLoadOption.Entry[] + --- @return boolean + local function IsTalentMatrixValid(entries) + if #entries == 0 then + return false + end + + if next(talentCache) == nil then + return false + end + + local compounds = {{}} + + for _, entry in ipairs(entries) do + if entry.operation == "OR" then + table.insert(compounds, {}) + end + + table.insert(compounds[#compounds], { + negated = entry.negated, + value = entry.value + }) + end + + for _, compound in ipairs(compounds) do + local valid = true - local _, _, _, _, rank = GetTalentInfo(tab, talentIndex) - return rank and rank > 0 + for _, item in ipairs(compound) do + if #item.value > 0 then + if talentCache[item.value] and item.negated or not talentCache[item.value] and not item.negated then + valid = false + end + end + end + + if valid then + return true + end end - state.talent = ValidateTriStateLoadOption(load.talent --[[@as Binding.TriStateLoadOption]], IsTalentIndexSelected) + return false + end + + if load.talent.selected then + state.talent = IsTalentMatrixValid(load.talent.entries) + else + state.talent = nil end end diff --git a/Clicked/Core/Database.lua b/Clicked/Core/Database.lua index 701d1d85..3a180fee 100644 --- a/Clicked/Core/Database.lua +++ b/Clicked/Core/Database.lua @@ -369,21 +369,22 @@ function Addon:GetNewBindingTemplate() } if Addon:IsGameVersionAtleast("RETAIL") then - --- @type number local specIndex = GetSpecialization() - - -- Initial spec - if specIndex == 5 then - specIndex = 1 - end - + specIndex = specIndex == 5 and 1 or specIndex -- Initial spec + template.load.specialization = GetTriStateLoadOptionTemplate(specIndex) + elseif Addon:IsGameVersionAtleast("CATA") then + --- @type number + local specIndex = GetPrimaryTalentTree() template.load.specialization = GetTriStateLoadOptionTemplate(specIndex) - template.load.talent = GetMultiFieldLoadOptionTemplate("") elseif Addon:IsGameVersionAtleast("WOTLK") then local specIndex = GetActiveTalentGroup() template.load.specialization = GetTriStateLoadOptionTemplate(specIndex) end + if Addon:IsGameVersionAtleast("CATA") then + template.load.talent = GetMultiFieldLoadOptionTemplate("") + end + return template end diff --git a/Clicked/Core/Init.lua b/Clicked/Core/Init.lua index c5cba139..6490e076 100644 --- a/Clicked/Core/Init.lua +++ b/Clicked/Core/Init.lua @@ -102,6 +102,8 @@ function Addon:IsGameVersionAtleast(version) if version == "RETAIL" and isRetail then return true + elseif version == "CATA" and isCata then + return true elseif version == "WOTLK" and isWOTLK then return true elseif version == "BC" and isBC then diff --git a/Clicked/Core/LocaleUtils.lua b/Clicked/Core/LocaleUtils.lua index c792d105..317122df 100644 --- a/Clicked/Core/LocaleUtils.lua +++ b/Clicked/Core/LocaleUtils.lua @@ -652,6 +652,193 @@ if Addon:IsGameVersionAtleast("RETAIL") then end end + return items, order + end +elseif Addon:IsGameVersionAtleast("CATA") then + --- Get a localized list of all available specializations for the given class names. If the `classNames` parameter is `nil` it will return results for the + --- player's current class. + --- + --- @param classNames? string[] + --- @return table items + --- @return integer[] order + function Addon:Cata_GetLocalizedSpecializations(classNames) + --- @type table + local items = {} + + --- @type integer[] + local order = {} + + if classNames == nil then + classNames = {} + classNames[1] = select(2, UnitClass("player")) + end + + if #classNames == 1 then + local class = classNames[1] + local specs = LibTalentInfoClassic:GetClassSpecializations(class) + + for specIndex, spec in pairs(specs) do + local _, name, _, icon = GetSpecializationInfoForSpecID(spec.id) + local key = specIndex + + if not Addon:IsStringNilOrEmpty(name) then + items[key] = string.format("", icon, name) + table.insert(order, key) + end + end + else + local max = 0 + + -- Find class with the most specializations out of all available classes + if #classNames == 0 then + for _, class in ipairs(allClasses) do + local count = LibTalentInfoClassic:GetNumSpecializationsForClass(class) + + if count > max then + max = count + end + end + -- Find class with the most specializations out of the selected classes + else + for i = 1, #classNames do + local count = LibTalentInfoClassic:GetNumSpecializationsForClass(classNames[i]) + + if count > max then + max = count + end + end + end + + for i = 1, max do + local key = i + + items[key] = string.format("", Addon.L["Specialization %s"]:format(i)) + table.insert(order, key) + end + end + + return items, order + end + + --- Get a localized list of all available talents for the + --- given specialization IDs. If the `specializations` parameter + --- is `nil` it will return results for the player's current specialization. + --- + --- @param specializations? integer[] + --- @return TalentInfo[] + function Addon:Cata_GetTalents(specializations) + --- @type TalentInfo[] + local result = {} + + if specializations == nil then + local class = select(2, UnitClass("player")) + specializations = {} + specializations[1] = LibTalentInfoClassic:GetClassSpecialization(class, GetPrimaryTalentTree()).id + end + + --- @type table + local found = {} + + for _, specialization in ipairs(specializations) do + local count = LibTalentInfoClassic:GetNumTalentsForSpec(specialization) + + for i = 1, count do + local id, name, texture = LibTalentInfoClassic:GetTalentInfoBySpecID(specialization, i) + + if name ~= nil and not found[name] then + found[name] = true + table.insert(result, { + entryId = id, + text = name, + icon = texture, + }) + end + end + end + + return result + end + + --- Get a localized list of all available classic shapeshift forms for the given class names. + --- If the `classNames` parameter is `nil` it will return results for the player's current class. + --- + --- @param classes? string[] + --- @return table items + --- @return integer[] order + function Addon:Classic_GetLocalizedForms(classes) + --- @type table + local items = {} + + --- @type integer[] + local order = {} + + if classes == nil then + classes = {} + classes[1] = select(2, UnitClass("player")) + end + + if #classes == 1 then + local className = classes[1] + local defaultForm = Addon.L["None"] + + if className == "DRUID" then + defaultForm = Addon.L["Humanoid Form"] + end + + do + local key = #order + 1 + + items[key] = string.format("", defaultForm) + table.insert(order, key) + end + + for _, spellIds in Addon:Classic_IterateShapeshiftForms(className) do + local key = #order + 1 + local targetSpellId = spellIds[#spellIds] + + -- Find first available form to set name + for _, spellId in ipairs(spellIds) do + if IsSpellKnown(spellId) then + targetSpellId = spellId + break + end + end + + local name, _, icon = Addon:GetSpellInfo(targetSpellId, false) + items[key] = string.format("", icon, name) + + table.insert(order, key) + end + else + local max = 0 + + -- Find specialization with the highest number of forms + if #classes == 0 then + for _, forms in Addon:Classic_IterateShapeshiftClasses() do + if #forms > max then + max = #forms + end + end + -- Find specialization with the highest number of forms out of the selected specializations + else + for _, className in ipairs(classes) do + local forms = Addon:Classic_GetShapeshiftForms(className) + + if #forms > max then + max = #forms + end + end + end + + -- start at 0 because [form:0] is no form + for i = 0, max do + local key = #order + 1 + + items[key] = string.format("", Addon.L["Stance %s"]:format(i)) + table.insert(order, key) + end + end + return items, order end elseif Addon:IsGameVersionAtleast("CLASSIC") then diff --git a/Clicked/Core/Upgrader.lua b/Clicked/Core/Upgrader.lua index 2fb0099d..6dc85c05 100644 --- a/Clicked/Core/Upgrader.lua +++ b/Clicked/Core/Upgrader.lua @@ -3,7 +3,7 @@ --- @class ClickedInternal local Addon = select(2, ...) -Addon.DATA_VERSION = 6 +Addon.DATA_VERSION = 7 -- Local support functions @@ -798,6 +798,27 @@ local function Upgrade(db, from) end end end + + if from < 7 then + for _, binding in ipairs(db.bindings) do + if Addon.EXPANSION_LEVEL <= Addon.EXPANSION.CATA then + binding.load.talent = { + selected = false, + entries = { + { + operation = "AND", + negated = false, + value = "" + } + } + } + + if Addon.EXPANSION_LEVEL > Addon.EXPANSION.CLASSIC then + Addon:ShowInformationPopup("Clicked: Binding talent load options have been reset, sorry for the inconvenience.") + end + end + end + end end -- Private addon API diff --git a/Clicked/Core/Utils.lua b/Clicked/Core/Utils.lua index 2b062991..a3779414 100644 --- a/Clicked/Core/Utils.lua +++ b/Clicked/Core/Utils.lua @@ -589,7 +589,7 @@ function Addon:GetSpellInfo(input, addSubText) addSubText = true end - if addSubText and not Addon:IsGameVersionAtleast("RETAIL") then + if addSubText and not Addon:IsGameVersionAtleast("CATA") then --- @diagnostic disable-next-line: redundant-parameter local subtext = GetSpellSubtext(spellId) diff --git a/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.lua b/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.lua index c10f926d..215968a9 100644 --- a/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.lua +++ b/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.lua @@ -1,14 +1,20 @@ +local VERSION_MAJOR = "LibTalentInfoClassic-1.0" +local VERSION_MINOR = 1 + if LibStub == nil then - error("LibTalentInfoClassic-1.0 requires LibStub") + error(VERSION_MAJOR .. " requires LibStub") end --- @class LibTalentInfoClassic-1.0 -local LibTalentInfoClassic = LibStub:NewLibrary("LibTalentInfoClassic-1.0", 1) +local LibTalentInfoClassic = LibStub:NewLibrary(VERSION_MAJOR, VERSION_MINOR) + +--- @alias SpecializationInfo { id: integer, name: string, icon: integer} --- @class TalentProvider --- @field public version integer ---- @field public classes string[]? ---- @field public talents table +--- @field public specializations table? +--- @field public talents table + if LibTalentInfoClassic == nil then return @@ -37,69 +43,175 @@ function LibTalentInfoClassic:GetTalentProviderVersion() return talentProvider.version end -function LibTalentInfoClassic:GetNumTalentsForTab(class, tabIndex) - assert(type(class) == "string", "bad argument #1: expected string, got " .. type(class)) - assert(type(tabIndex) == "number", "bad argument #2: expected number, got " .. type(tabIndex)) +--- Get all specialization IDs for the given class. +--- +--- @param classFileName string +--- @return {[integer]: SpecializationInfo} +function LibTalentInfoClassic:GetClassSpecializations(classFileName) + if talentProvider == nil then + error("No talent provider registered, register one first using 'RegisterTalentProvider'.") + end + + if classFileName == nil or talentProvider.specializations[classFileName] == nil then + return {} + end + + local specializationIDs = talentProvider.specializations[classFileName] + local result = {} + + for index, spec in pairs(specializationIDs) do + result[index] = { id = spec.id, name = spec.name, icon = spec.icon } + end + + return result +end + +--- Get a specialization for the given class. +--- +--- @param classFileName string +--- @param tabIndexOrSpecID integer +--- @return SpecializationInfo +function LibTalentInfoClassic:GetClassSpecialization(classFileName, tabIndexOrSpecID) + if talentProvider == nil then + error("No talent provider registered, register one first using 'RegisterTalentProvider'.") + end + + if classFileName == nil or talentProvider.specializations[classFileName] == nil then + return {} + end + local specializationIDs = talentProvider.specializations[classFileName] + + if tabIndexOrSpecID > 0 and tabIndexOrSpecID <= MAX_TALENT_TABS then + local spec = specializationIDs[tabIndexOrSpecID] + return { id = spec.id, name = spec.name, icon = spec.icon, } + end + + for _, spec in pairs(specializationIDs) do + if spec.id == tabIndexOrSpecID then + return { id = spec.id, name = spec.name, icon = spec.icon } + end + end + + error("No specialization found for class " .. classFileName .. " with index or ID " .. tabIndexOrSpecID) +end + +--- Get the number of specializations for a class. +--- +--- @param classFileName string +--- @return integer +function LibTalentInfoClassic:GetNumSpecializationsForClass(classFileName) if talentProvider == nil then error("No talent provider registered, register one first using 'RegisterTalentProvider'.") end - if talentProvider.talents[class] == nil then + if classFileName == nil or talentProvider.specializations[classFileName] == nil then return 0 end - if tabIndex <= 0 or tabIndex > MAX_TALENT_TABS then + local specializationIDs = talentProvider.specializations[classFileName] or {} + return #specializationIDs +end + +--- Get the amount of talents available in the talent tree for a specific specialization. +--- +--- @param classFileName string +--- @param tabIndex integer +--- @return integer +function LibTalentInfoClassic:GetNumTalentsForTab(classFileName, tabIndex) + assert(type(classFileName) == "string", "bad argument #1: expected string, got " .. type(classFileName)) + assert(type(tabIndex) == "number", "bad argument #2: expected number, got " .. type(tabIndex)) + + if talentProvider == nil then + error("No talent provider registered, register one first using 'RegisterTalentProvider'.") + end + + local specs = talentProvider.specializations[classFileName] + if specs == nil then + error("No talents found for class: " .. classFileName) + end + + local spec = specs[tabIndex] + + if tabIndex <= 0 or tabIndex > MAX_TALENT_TABS or spec == nil then error("Talent tab is out of range: " .. tabIndex .. ". Must be an integer between 1 and " .. MAX_TALENT_TABS) end - local talents = talentProvider.talents[class][tabIndex] or {} + local talents = talentProvider.talents[spec.id] or {} + return #talents +end + +--- Get the amount of talents available in the talent tree for a specific specialization. +--- +--- @param specID integer +--- @return integer +function LibTalentInfoClassic:GetNumTalentsForSpec(specID) + assert(type(specID) == "number", "bad argument #2: expected number, got " .. type(specID)) + + if talentProvider == nil then + error("No talent provider registered, register one first using 'RegisterTalentProvider'.") + end + + local talents = talentProvider.talents[specID] or {} + if talents == nil then + error("No talents found for spec ID: " .. specID) + end + return #talents end --- Get the info for a talent of the specified class. --- ---- @param class string The class file name obtained by `UnitClass`. ---- @param tabIndex integer An integer value between 1 and `MAX_TALENT_TABS`. +--- @param specID integer An integer value between 1 and `MAX_TALENT_TABS`, or the specialization ID. --- @param talentIndex integer An integer value between 1 and `MAX_NUM_TALENTS`. --- @return integer? talentID --- @return string? name --- @return integer? texture ---- @return boolean? selected ---- @return boolean? available ---- @return integer? spellID ---- @return nil ---- @return integer? row ---- @return integer? column ---- @return boolean? known ---- @return boolean? grantedByAura -function LibTalentInfoClassic:GetTalentInfo(class, tabIndex, talentIndex) - assert(type(class) == "string", "bad argument #1: expected string, got " .. type(class)) - assert(type(tabIndex) == "number", "bad argument #2: expected number, got " .. type(tabIndex)) +function LibTalentInfoClassic:GetTalentInfoBySpecID(specID, talentIndex) + assert(type(specID) == "number", "bad argument #2: expected number, got " .. type(specID)) assert(type(talentIndex) == "number", "bad argument #3: expected number, got " .. type(talentIndex)) if talentProvider == nil then error("No talent provider registered, register one first using 'RegisterTalentProvider'.") end - if talentProvider.talents[class] == nil then - return nil - end - - if tabIndex <= 0 or tabIndex > MAX_TALENT_TABS then - error("Talent tab is out of range: " .. tabIndex .. ". Must be an integer between 1 and " .. MAX_TALENT_TABS) - end - if talentIndex <= 0 or talentIndex > MAX_NUM_TALENTS then error("Talent index is out of range: " .. talentIndex .. ". Must be an integer between 1 and " .. MAX_NUM_TALENTS) end - local talents = talentProvider.talents[class][tabIndex] or {} + local talents = talentProvider.talents[specID] if talentIndex > #talents then - return nil + return end - local talentID = talents[talentIndex] - return talentID, GetTalentInfo(tabIndex, talentIndex) + local talent = talents[talentIndex] + return talent.id, talent.name, talent.icon +end + +--- Get the info for a talent of the specified class. +--- +--- @param classFileName string The class file name obtained by `UnitClass`. +--- @param tabIndex integer An integer value between 1 and `MAX_TALENT_TABS`. +--- @param talentIndex integer An integer value between 1 and `MAX_NUM_TALENTS`. +--- @return integer? talentID +--- @return string? name +--- @return integer? texture +function LibTalentInfoClassic:GetTalentInfoByTab(classFileName, tabIndex, talentIndex) + assert(type(classFileName) == "string", "bad argument #1: expected string, got " .. type(classFileName)) + assert(type(tabIndex) == "number", "bad argument #2: expected number, got " .. type(tabIndex)) + assert(type(talentIndex) == "number", "bad argument #3: expected number, got " .. type(talentIndex)) + + if talentProvider == nil then + error("No talent provider registered, register one first using 'RegisterTalentProvider'.") + end + + if tabIndex <= 0 or tabIndex > MAX_TALENT_TABS then + error("Talent tab is out of range: " .. tabIndex .. ". Must be an integer between 1 and " .. MAX_TALENT_TABS) + end + + local specializations = talentProvider.specializations[classFileName] or {} + local spec = specializations[tabIndex].id + + return self:GetTalentInfoBySpecID(spec, talentIndex) end diff --git a/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.xml b/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.xml index 8e88adfb..9aba743b 100644 --- a/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.xml +++ b/Clicked/Libs/LibTalentInfoClassic-1.0/LibTalentInfoClassic-1.0.xml @@ -1,5 +1,5 @@