diff --git a/AscensionExporter/AscensionExporter.toc b/AscensionExporter/AscensionExporter.toc index 920f99c..d55109e 100644 --- a/AscensionExporter/AscensionExporter.toc +++ b/AscensionExporter/AscensionExporter.toc @@ -7,6 +7,7 @@ Core.lua Util\Json.lua +Util\TalentEncoder.lua UI\ExportFrame.lua Collectors\Talents.lua Collectors\Gear.lua diff --git a/AscensionExporter/Core.lua b/AscensionExporter/Core.lua index b1feb72..e658d85 100644 --- a/AscensionExporter/Core.lua +++ b/AscensionExporter/Core.lua @@ -337,6 +337,34 @@ function AE:GenerateMarkdownEnchants() return table.concat(lines, "\n") end +-- Markdown talents export generator +function AE:GenerateMarkdownTalents() + local talents = self.CollectTalents and self.CollectTalents() or { selected = {} } + local _, class = UnitClass("player") + + local lines = {} + table.insert(lines, "## Talents\n") + + if #(talents.selected or {}) == 0 then + table.insert(lines, "*No talents selected.*\n") + return table.concat(lines, "\n") + end + + -- Generate the talent calc URL + local url = self.GenerateTalentCalcURL and self.GenerateTalentCalcURL(talents, class) + if url then + table.insert(lines, string.format("[View in Talent Calculator](%s)\n", url)) + end + + -- Optional: embed iframe for Wiki.js + local build = self.EncodeTalentsToURL and self.EncodeTalentsToURL(talents, class) + if build then + table.insert(lines, string.format('\n', build)) + end + + return table.concat(lines, "\n") +end + -- Slash command handling SLASH_ASCX1 = "/ascx" SLASH_ASCX2 = "/asxc" -- alias: both map to the same handler key "ASCX" @@ -348,7 +376,7 @@ SlashCmdList["ASCX"] = function(msg) -- avoid referencing buttons that won't be present in the fallback window. local hasButtons = type(_G.AscensionExporter_ShowExportFrame) == "function" local prefix = hasButtons and "Use the buttons above or type:" or "Type one of the commands:" - local help = prefix .. "\n/ascx export all|talents|gear|enchants|mdgear\n/ascx sv on|off (SavedVariables is currently " .. (AscensionExporterConfig.enableSavedVariables and "ON" or "OFF") .. ")\n/ascx debug (show collector status)\n" + local help = prefix .. "\n/ascx export all|talents|gear|enchants|mdgear|mdtalents\n/ascx sv on|off (SavedVariables is currently " .. (AscensionExporterConfig.enableSavedVariables and "ON" or "OFF") .. ")\n/ascx debug (show collector status)\n" AE:ShowExport(help, "Ascension Export - Tools & Help") return end @@ -359,6 +387,9 @@ SlashCmdList["ASCX"] = function(msg) if rest == "mdgear" or rest == "md" then local md = AE:GenerateMarkdownGear() AE:ShowExport(md, "Ascension Export - Markdown Gear - Copy All (Ctrl+C)") + elseif rest == "mdtalents" then + local md = AE:GenerateMarkdownTalents() + AE:ShowExport(md, "Ascension Export - Markdown Talents - Copy All (Ctrl+C)") elseif rest == "all" or rest == "talents" or rest == "gear" or rest == "enchants" then AE:Export(rest) else diff --git a/AscensionExporter/UI/ExportFrame.lua b/AscensionExporter/UI/ExportFrame.lua index e6ca930..bed3f3e 100644 --- a/AscensionExporter/UI/ExportFrame.lua +++ b/AscensionExporter/UI/ExportFrame.lua @@ -66,9 +66,22 @@ local function CreateOrGetFrame() end end) + -- Second row for additional buttons + local x2 = 0 + makeBtn("MD Talents", x2, function() + if AscensionExporter and AscensionExporter.GenerateMarkdownTalents then + local md = AscensionExporter:GenerateMarkdownTalents() or "" + if AscensionExporter.ShowExport then + AscensionExporter:ShowExport(md, "Ascension Export - Markdown Talents - Copy All (Ctrl+C)") + else + AscensionExporter_ShowExportFrame(md, "Ascension Export - Markdown Talents - Copy All (Ctrl+C)") + end + end + end):SetPoint("TOPLEFT", 16 + x2, -62) + -- ScrollFrame + EditBox local scrollFrame = CreateFrame("ScrollFrame", "AscensionExporterScrollFrame", frame, "UIPanelScrollFrameTemplate") - scrollFrame:SetPoint("TOPLEFT", 16, -64) + scrollFrame:SetPoint("TOPLEFT", 16, -90) scrollFrame:SetPoint("BOTTOMRIGHT", -32, 16) local editBox = CreateFrame("EditBox", "AscensionExporterEditBox", scrollFrame) diff --git a/AscensionExporter/Util/TalentEncoder.lua b/AscensionExporter/Util/TalentEncoder.lua new file mode 100644 index 0000000..3883636 --- /dev/null +++ b/AscensionExporter/Util/TalentEncoder.lua @@ -0,0 +1,93 @@ +-- AscensionExporter - Talent URL Encoder +-- Encodes talents into URL format for https://exil.es/en/wow/talent-calc?build=... + +AscensionExporter = _G.AscensionExporter or {} +_G.AscensionExporter = AscensionExporter +local AE = AscensionExporter + +local BASE36_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz" + +local function encodeRank(rank) + if rank < 10 then + return tostring(rank) + end + -- For ranks >= 10, use base36 (a=10, b=11, etc) + if rank <= 35 then + return BASE36_CHARS:sub(rank + 1, rank + 1) + end + return "0" +end + +local function toBase36(num) + if num == 0 then return "0" end + local result = "" + while num > 0 do + local remainder = num % 36 + result = BASE36_CHARS:sub(remainder + 1, remainder + 1) .. result + num = math.floor(num / 36) + end + return result +end + +-- Encode talents to URL format +-- Format: `${class}.${tree0}.${tree1}.${tree2}` +-- Each tree is encoded as "index_rank" pairs separated by underscores +-- Example: "0_3_b_5" means slot 0 has rank 3, slot 11 (b in base36) has rank 5 +function AE.EncodeTalentsToURL(talentsData, className) + if not talentsData or not className then + return nil + end + + -- Normalize class name to lowercase + className = className:lower() + + -- Build a structure: 3 talent trees with slots + -- Each WoW class has 3 talent trees + local trees = {{}, {}, {}} + + -- Group talents by their tabIndex (1-based in Lua, but we want 0-based for array) + for _, t in ipairs(talentsData.selected or {}) do + local tabIndex = t.tabIndex or 1 + local talentIndex = (t.talentIndex or 1) - 1 -- Convert to 0-based index + local rank = t.rank or 0 + + if rank > 0 and tabIndex >= 1 and tabIndex <= 3 then + table.insert(trees[tabIndex], { + index = talentIndex, + rank = rank + }) + end + end + + -- Sort each tree by index for consistent encoding + for _, tree in ipairs(trees) do + table.sort(tree, function(a, b) return a.index < b.index end) + end + + -- Encode each tree + local treeParts = {} + for _, tree in ipairs(trees) do + local pairs = {} + for _, talent in ipairs(tree) do + local idxStr = toBase36(talent.index) + local rankStr = encodeRank(talent.rank) + table.insert(pairs, idxStr .. rankStr) + end + table.insert(treeParts, table.concat(pairs, "_")) + end + + -- Build final URL format: class.tree0.tree1.tree2 + return className .. "." .. table.concat(treeParts, ".") +end + +-- Generate full talent calc URL +function AE.GenerateTalentCalcURL(talentsData, className) + local build = AE.EncodeTalentsToURL(talentsData, className) + if not build then + return nil + end + return "https://exil.es/en/wow/talent-calc?build=" .. build +end + +-- Mark module as loaded +AE._loadedTalentEncoder = true