Files
coa-altoholic/Altoholic/Frames/Talents.lua
T
florian.berthold b8d619c3bb
release / release (push) Successful in 5s
coa.16: Talents view no longer crashes on CoA custom classes
DataStore_Talents._GetTreeReference degrades to nil (was assert) when a custom class
(MONK, etc.) has no/partial talent reference data; Talents.lua skips background render
when GetTreeInfo returns no bg.
2026-05-29 19:36:11 +02:00

632 lines
17 KiB
Lua

local addonName = ...
local addon = _G[addonName]
local L = LibStub("AceLocale-3.0"):GetLocale(addonName)
local WHITE = "|cFFFFFFFF"
local GREEN = "|cFF00FF00"
addon.Talents = {}
addon.Glyphs = {}
local tns = addon.Talents -- tns = talents namespace
local gns = addon.Glyphs -- gns = glyphs namespace
local currentTalentGroup
-- ** Arrows **
local INITIAL_OFFSET_X = 25 -- constants used for positioning talents
local INITIAL_OFFSET_Y = 15
local TALENT_OFFSET_X = 62
local TALENT_OFFSET_Y = 55
local numArrows
local function ResetArrowCount()
numArrows = 1
end
local function HideUnusedArrows()
while numArrows <= 30 do
_G["AltoholicFrameTalents_Arrow" .. numArrows]:Hide()
numArrows = numArrows + 1
end
numArrows = nil
end
local function DrawArrow(tier, column, prereqTier, prereqColumn, blocked)
local arrowType -- algorithm taken from TalentFrameBase.lua, adjusted for my needs
if (column == prereqColumn) then -- Same column ? ==> TOP
arrowType = "top"
elseif (tier == prereqTier) then -- Same tier ? ==> LEFT or RIGHT
if (column < prereqColumn) then
arrowType = "right"
else
arrowType = "left"
end
else -- None of these ? ==> diagonal
if not blocked then
arrowType = "top"
else
if (column < prereqColumn) then
arrowType = "right"
else
arrowType = "left"
end
end
end
if not arrowType then
return
end
local x, y
if arrowType == "top" then
x = 2
y = 18
elseif arrowType == "left" then
x = -17
y = -2
elseif arrowType == "right" then
x = 22
y = -2
end
x = x + INITIAL_OFFSET_X + ((column-1) * TALENT_OFFSET_X)
y = y - (INITIAL_OFFSET_Y + ((tier-1) * TALENT_OFFSET_Y))
local arrow = _G["AltoholicFrameTalents_Arrow" .. numArrows]
local tc = TALENT_ARROW_TEXTURECOORDS[arrowType][1]
arrow:SetTexCoord(tc[1], tc[2], tc[3], tc[4]);
arrow:SetPoint("TOPLEFT", tns.Parent, "TOPLEFT", x, y)
arrow:Show()
numArrows = numArrows + 1
end
-- ** Buttons **
local numButtons
local function ResetButtonCount()
numButtons = 1
end
local function HideUnusedButtons()
while numButtons <= 40 do
_G["AltoholicFrameTalents_ScrollFrameTalent" .. numButtons]:Hide()
_G["AltoholicFrameTalents_ScrollFrameTalent" .. numButtons]:SetID(0)
numButtons = numButtons + 1
end
numButtons = nil
end
local function DrawTalent(texture, tier, column, count, id)
local itemName = "AltoholicFrameTalents_ScrollFrameTalent" .. numButtons
local itemButton = _G[itemName]
itemButton:SetPoint("TOPLEFT", itemButton:GetParent(), "TOPLEFT",
INITIAL_OFFSET_X + ((column-1) * TALENT_OFFSET_X),
-(INITIAL_OFFSET_Y + ((tier-1) * TALENT_OFFSET_Y)))
itemButton:SetID(id)
if texture then
addon:SetItemButtonTexture(itemName, texture, 37, 37)
end
local itemCount = _G[itemName .. "Count"]
local itemTexture = _G[itemName .. "IconTexture"]
if count and count > 0 then
itemCount:SetText(GREEN .. count)
itemCount:Show()
itemTexture:SetDesaturated(0)
else
itemTexture:SetDesaturated(1)
itemCount:Hide()
end
itemButton:Show()
numButtons = numButtons + 1
end
-- ** Branches **
local numBranches
local branchArray -- a 2-dimensional array to hold branches
local function ResetBranchCount()
numBranches = 1
end
local function InitializeBranchArray()
branchArray = branchArray or {}
wipe(branchArray)
for i = 1, MAX_NUM_TALENT_TIERS do
branchArray[i] = {};
for j = 1, NUM_TALENT_COLUMNS do
branchArray[i][j] = {};
end
end
end
local function ClearBranchArray()
wipe(branchArray)
branchArray = nil
end
local function InitBranch(tier, column, prereqTier, prereqColumn, blocked)
-- algorithm taken from TalentFrameBase.lua, adjusted for my needs
local left = min(column, prereqColumn);
local right = max(column, prereqColumn);
if (column == prereqColumn) then -- Same column ? ==> TOP
for i = prereqTier, tier - 1 do
branchArray[i][column].down = true;
if ( (i + 1) <= (tier - 1) ) then
branchArray[i+1][column].up = true;
end
end
return
end
if (tier == prereqTier) then -- Same tier ? ==> LEFT or RIGHT
for i = left, right-1 do
branchArray[prereqTier][i].right = true;
branchArray[prereqTier][i+1].left = true;
end
return
end
-- None of these ? ==> diagonal
if not blocked then
branchArray[prereqTier][column].down = true;
branchArray[tier][column].up = true;
for i = prereqTier, tier - 1 do
branchArray[i][column].down = true;
branchArray[i + 1][column].up = true;
end
for i = left, right - 1 do
branchArray[prereqTier][i].right = true;
branchArray[prereqTier][i+1].left = true;
end
else
for i=prereqTier, tier-1 do
branchArray[i][column].up = true;
branchArray[i + 1][column].down = true;
end
end
end
local function SetBranchTexture(branchType, x, y)
local branch = _G["AltoholicFrameTalents_Branch" .. numBranches]
local tc = TALENT_BRANCH_TEXTURECOORDS[branchType][1]
branch:SetTexCoord(tc[1], tc[2], tc[3], tc[4]);
branch:SetPoint("TOPLEFT", tns.Parent, "TOPLEFT", x, y)
branch:Show()
numBranches = numBranches + 1
end
local function DrawBranches()
local x, y
local ignoreUp
for i = 1, MAX_NUM_TALENT_TIERS do
for j = 1, NUM_TALENT_COLUMNS do
local p = branchArray[i][j]
x = INITIAL_OFFSET_X + ((j-1) * TALENT_OFFSET_X) + 2
y = -(INITIAL_OFFSET_Y + ((i-1) * TALENT_OFFSET_Y)) - 2
if p.node then -- there's a talent there
if p.up then
if not ignoreUp then
SetBranchTexture("up", x, y + TALENT_BUTTON_SIZE)
else
ignoreUp = nil
end
end
if p.down then
SetBranchTexture("down", x, y - TALENT_BUTTON_SIZE + 1)
end
if p.left then
SetBranchTexture("left", x - TALENT_BUTTON_SIZE, y)
end
if p.right then
SetBranchTexture("right", x + TALENT_BUTTON_SIZE, y)
end
else
if p.up and p.left and p.right then
SetBranchTexture("tup", x, y)
elseif p.down and p.left and p.right then
SetBranchTexture("tdown", x, y)
elseif p.left and p.down then
SetBranchTexture("topright", x, y)
SetBranchTexture("down", x, y-32)
elseif p.left and p.up then
SetBranchTexture("bottomright", x, y)
elseif p.left and p.right then
SetBranchTexture("right", x + TALENT_BUTTON_SIZE, y)
SetBranchTexture("left", x+1, y)
elseif p.right and p.down then
SetBranchTexture("topleft", x, y)
SetBranchTexture("down", x, y-32)
elseif p.right and p.up then
SetBranchTexture("bottomleft", x, y)
elseif p.up and p.down then
SetBranchTexture("up", x, y)
SetBranchTexture("down", x, y-32)
ignoreUp = true
end
end
p.up = nil -- clear after use
p.left = nil
p.right = nil
p.down = nil
p.node = nil
end
end
end
local function HideUnusedBranches()
while numBranches <= 30 do
_G["AltoholicFrameTalents_Branch" .. numBranches]:Hide()
numBranches = numBranches + 1
end
numBranches = nil
end
-- *** TALENTS ***
function tns:Update(treeIndex)
gns:Update()
treeIndex = treeIndex or 1
AltoholicFrameTalents_ScrollFrameScrollBar:SetMinMaxValues(0, 330);
-- stop all autocast
for i = 1, 3 do
AutoCastShine_AutoCastStop(_G[ "AltoholicFrameTalents_SpecIcon" .. i .. "Shine" ]);
end
AltoholicFrameTalents:Hide()
local character = addon.Tabs.Characters:GetCurrent()
if not character then return end
local DS = DataStore
if DS:GetActiveTalents(character) == 1 then
AltoholicFrameTalents_PrimaryText:SetText(format("%s (%s)",WHITE..TALENT_SPEC_PRIMARY..GREEN, TALENT_ACTIVE_SPEC_STATUS ))
AltoholicFrameTalents_SecondaryText:SetText(WHITE..TALENT_SPEC_SECONDARY)
else
AltoholicFrameTalents_PrimaryText:SetText(WHITE..TALENT_SPEC_PRIMARY)
AltoholicFrameTalents_SecondaryText:SetText(format("%s (%s)",WHITE..TALENT_SPEC_SECONDARY..GREEN, TALENT_ACTIVE_SPEC_STATUS ))
end
local _, class = DS:GetCharacterClass(character)
if not DS:IsClassKnown(class) then return end
local level = DS:GetCharacterLevel(character)
if not level or level < 10 then return end
local treeName = DS:GetTreeNameByID(class, treeIndex)
tns.Parent = _G["AltoholicFrameTalents_ScrollFrameTalent1"]:GetParent()
local index = 1
for tree in DS:GetClassTrees(class) do -- draw spec icons
local itemName = "AltoholicFrameTalents_SpecIcon"..index
local itemButton = _G[itemName]
local itemCount = _G[itemName .."Count"]
local icon = DS:GetTreeInfo(class, tree)
addon:SetItemButtonTexture(itemName, icon, 30, 30)
itemCount:SetText(WHITE .. DS:GetNumPointsSpent(character, tree, currentTalentGroup))
itemCount:Show()
itemButton:Show()
index = index + 1
end
local isActiveTalentGroup = currentTalentGroup == DS:GetActiveTalents(character)
-- textures are 90.625% of the original size
local _, bg = DS:GetTreeInfo(class, treeName)
if not bg then return end -- CoA: no talent-tree background for this class (e.g. custom class with no reference data)
AltoholicFrameTalents_bgTopLeft:SetTexture(bg.."-TopLeft")
AltoholicFrameTalents_bgTopRight:SetTexture(bg.."-TopRight")
AltoholicFrameTalents_bgBottomLeft:SetTexture(bg.."-BottomLeft")
AltoholicFrameTalents_bgBottomRight:SetTexture(bg.."-BottomRight")
SetDesaturation(AltoholicFrameTalents_bgTopLeft, not isActiveTalentGroup)
SetDesaturation(AltoholicFrameTalents_bgTopRight, not isActiveTalentGroup)
SetDesaturation(AltoholicFrameTalents_bgBottomLeft, not isActiveTalentGroup)
SetDesaturation(AltoholicFrameTalents_bgBottomRight, not isActiveTalentGroup)
AutoCastShine_AutoCastStart(_G[ "AltoholicFrameTalents_SpecIcon" .. treeIndex .. "Shine" ]);
AltoholicFrameTalents_ScrollFrame:SetID(treeIndex)
ResetButtonCount()
ResetArrowCount()
ResetBranchCount()
InitializeBranchArray()
-- draw all icons in their respective slot
for i = 1, DS:GetNumTalents(class, treeName) do
local _, _, texture, tier, column = DS:GetTalentInfo(class, treeName, i)
local rank = DS:GetTalentRank(character, treeName, currentTalentGroup, i)
DrawTalent(texture, tier, column, rank, i)
branchArray[tier][column].node = true;
-- Draw arrows & branches where applicable
local prereqTier, prereqColumn = DS:GetTalentPrereqs(class, treeName, i)
if prereqTier and prereqColumn then
local left = min(column, prereqColumn);
local right = max(column, prereqColumn);
if ( left == prereqColumn ) then -- Don't check the location of the current button
left = left + 1;
else
right = right - 1;
end
local blocked -- Check for blocking talents
for j = 1, DS:GetNumTalents(class, treeName) do
local _, _, _, searchedTier, searchedColumn = DS:GetTalentInfo(class, treeName, j)
if searchedTier == prereqTier then -- do nothing if lower tier, process if same tier, exit if higher tier
if (searchedColumn >= left) and (searchedColumn <= right) then
blocked = true
break
end
elseif searchedTier > prereqTier then
break
end
end
DrawArrow(tier, column, prereqTier, prereqColumn, blocked)
InitBranch(tier, column, prereqTier, prereqColumn, blocked)
end
end
DrawBranches()
HideUnusedButtons()
HideUnusedArrows()
HideUnusedBranches()
ClearBranchArray()
AltoholicFrameTalents:Show()
end
function tns:Icon_OnEnter(frame)
local DS = DataStore
local character = addon.Tabs.Characters:GetCurrent()
local _, class = DS:GetCharacterClass(character)
local treeName = DS:GetTreeNameByID(class, frame:GetID())
if treeName then
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT");
AltoTooltip:AddLine(treeName,1,1,1);
AltoTooltip:Show();
end
end
local function GetTalentLink(frame)
local DS = DataStore
local treeIndex = frame:GetParent():GetParent():GetID()
local character = addon.Tabs.Characters:GetCurrent()
local _, class = DS:GetCharacterClass(character)
local treeName = DS:GetTreeNameByID(class, treeIndex)
local spellNumber = frame:GetID()
local id, name = DS:GetTalentInfo(class, treeName, spellNumber)
local rank = DS:GetTalentRank(character, treeName, currentTalentGroup, spellNumber)
return DS:GetTalentLink(id, rank, name)
end
function tns:Button_OnEnter(frame)
local link = GetTalentLink(frame)
if not link then return end
AltoTooltip:ClearLines();
AltoTooltip:SetOwner(frame, "ANCHOR_RIGHT");
AltoTooltip:SetHyperlink(link);
AltoTooltip:Show();
end
function tns:Button_OnClick(frame, button)
if ( button == "LeftButton" ) and ( IsShiftKeyDown() ) then
local chat = ChatEdit_GetLastActiveWindow()
if chat:IsShown() then
local link = GetTalentLink(frame)
if link then
chat:Insert(link)
end
end
end
end
function tns:SetCurrentGroup(group)
currentTalentGroup = group
end
function tns:OnUpdate()
if AltoholicFrameTalents:IsVisible() then
tns:Update()
end
end
-- *** GLYPHS ***
local glyphSlotTexCoord = {
-- copied from Blizzard_GlyphUI.lua, no idea why they're not visible from here .. :/
-- Empty Texture
[0] = { left = 0.78125, right = 0.91015625, top = 0.69921875, bottom = 0.828125 },
[1] = { left = 0, right = 0.12890625, top = 0.87109375, bottom = 1 },
[2] = { left = 0.130859375, right = 0.259765625, top = 0.87109375, bottom = 1 },
[3] = { left = 0.392578125, right = 0.521484375, top = 0.87109375, bottom = 1 },
[4] = { left = 0.5234375, right = 0.65234375, top = 0.87109375, bottom = 1 },
[5] = { left = 0.26171875, right = 0.390625, top = 0.87109375, bottom = 1 },
[6] = { left = 0.654296875, right = 0.783203125, top = 0.87109375, bottom = 1 }
}
local function DrawGlyph(id)
local name = "AltoholicFrameTalentsGlyph" .. id
local glyph = _G[name]
local DS = DataStore
local character = addon.Tabs.Characters:GetCurrent()
local enabled, glyphType, spell, icon = DS:GetGlyphInfo(character, currentTalentGroup, id)
if glyphType == 1 then
glyph.glyph:SetVertexColor(1, 0.25, 0);
else
glyph.glyph:SetVertexColor(0, 0.25, 1);
end
if enabled == 0 then
glyph.shine:Hide();
glyph.background:Hide();
glyph.glyph:Hide();
glyph.ring:Hide();
glyph.setting:SetTexture("Interface\\Spellbook\\UI-GlyphFrame-Locked");
glyph.setting:SetTexCoord(.1, .9, .1, .9);
elseif not spell then
local tc = glyphSlotTexCoord[0]
glyph.shine:Show();
glyph.background:Show();
glyph.background:SetTexCoord(tc.left, tc.right, tc.top, tc.bottom);
glyph.glyph:Hide();
glyph.ring:Show();
glyph.setting:SetTexture("Interface\\Spellbook\\UI-GlyphFrame");
glyph.setting:SetTexCoord(0.740234375, 0.953125, 0.484375, 0.697265625);
else
local tc = glyphSlotTexCoord[id]
glyph.shine:Show();
glyph.background:Show();
glyph.background:SetAlpha(1);
glyph.background:SetTexCoord(tc.left, tc.right, tc.top, tc.bottom);
glyph.glyph:Show();
glyph.glyph:SetTexture(icon);
glyph.ring:Show();
glyph.setting:SetTexture("Interface\\Spellbook\\UI-GlyphFrame");
glyph.setting:SetTexCoord(0.740234375, 0.953125, 0.484375, 0.697265625);
end
end
function gns:Update()
-- GLYPHTYPE_MAJOR = 1;
-- GLYPHTYPE_MINOR = 2;
-- 1
-- 3 5
-- 6 4
-- 2
for i = 1, 6 do
DrawGlyph(i)
end
end
function gns:Button_OnLoad(frame)
local name = frame:GetName()
local id = frame:GetID()
local glyph = _G[name]
glyph.glyph = _G[name .. "Glyph"]
glyph.setting = _G[name .. "Setting"]
glyph.highlight = _G[name .. "Highlight"]
glyph.background = _G[name .. "Background"]
glyph.ring = _G[name .. "Ring"]
glyph.shine = _G[name .. "Shine"]
local ratio
if (id == 1) or (id == 4) or (id == 6) then -- major
ratio = 0.85
else
ratio = 0.70
end
glyph.glyph:SetWidth(63 * ratio);
glyph.glyph:SetHeight(63 * ratio);
glyph.setting:SetWidth(108 * ratio);
glyph.setting:SetHeight(108 * ratio);
glyph.setting:SetTexture("Interface\\Spellbook\\UI-GlyphFrame");
glyph.setting:SetTexCoord(0.740234375, 0.953125, 0.484375, 0.697265625);
glyph.highlight:SetWidth(108 * ratio);
glyph.highlight:SetHeight(108 * ratio);
glyph.highlight:SetTexCoord(0.740234375, 0.953125, 0.484375, 0.697265625);
glyph.ring:SetWidth(82 * ratio);
glyph.ring:SetHeight(82 * ratio);
glyph.ring:SetPoint("CENTER", glyph, "CENTER", 0, -1);
glyph.ring:SetTexCoord(0.767578125, 0.92578125, 0.32421875, 0.482421875);
glyph.shine:SetTexCoord(0.9609375, 1, 0.9609375, 1);
glyph.background:SetWidth(70 * ratio);
glyph.background:SetHeight(70 * ratio);
end
function gns:Button_OnEnter(frame)
local id = frame:GetID()
local DS = DataStore
local character = addon.Tabs.Characters:GetCurrent()
local enabled, glyphType, spell, _, glyphID = DS:GetGlyphInfo(character, currentTalentGroup, id)
local glyphTypeText
if tonumber(glyphType) == 1 then
glyphTypeText = "|cFF69CCF0" .. MAJOR_GLYPH
else
glyphTypeText = "|cFF69CCF0" .. MINOR_GLYPH
end
AltoTooltip:SetOwner(frame, "ANCHOR_LEFT");
AltoTooltip:ClearLines();
if enabled == 0 then
AltoTooltip:AddLine("|cFFFF0000" .. GLYPH_LOCKED);
AltoTooltip:AddLine(glyphTypeText);
AltoTooltip:AddLine(_G["GLYPH_SLOT_TOOLTIP"..id]);
AltoTooltip:Show();
return
elseif not spell then
AltoTooltip:AddLine("|cFF808080" .. GLYPH_EMPTY);
AltoTooltip:AddLine(glyphTypeText);
AltoTooltip:AddLine(GLYPH_EMPTY_DESC);
AltoTooltip:Show();
return
end
local link = DS:GetGlyphLink(id, spell, glyphID)
AltoTooltip:SetHyperlink(link);
AltoTooltip:Show();
end
function gns:Button_OnClick(frame, button)
local id = frame:GetID()
local DS = DataStore
local character = addon.Tabs.Characters:GetCurrent()
local enabled, glyphType, spell, _, glyphID = DS:GetGlyphInfo(character, currentTalentGroup, id)
if not spell then return end
if ( button == "LeftButton" ) and ( IsShiftKeyDown() ) then
local chat = ChatEdit_GetLastActiveWindow()
if chat:IsShown() then
local link = DS:GetGlyphLink(id, spell, glyphID)
chat:Insert(link)
end
end
end