local ex = Examiner; -- Module local mod = ex:CreateModule("Talent",TALENTS); mod.help = "The Inspected Player's Talent Specialization"; mod.page = CreateFrame("Frame",nil,ex); mod:HasButton(true); mod.canCache = true; mod.details = ex:CreateDetailObject(); -- Variables local cfg, cache; local activeTab = 1; local activeSpec; local isInspect; local arrowIndex, branchIndex; local points = {}; local BRANCH_ARRAY = {}; -- Init Talent Branches for tier = 1, MAX_NUM_TALENT_TIERS do BRANCH_ARRAY[tier] = {}; for column = 1, NUM_TALENT_COLUMNS do BRANCH_ARRAY[tier][column] = {}; end end -------------------------------------------------------------------------------------------------------- -- Module Scripts -- -------------------------------------------------------------------------------------------------------- -- OnInspect function mod:OnInitialize() cfg = Examiner_Config; cache = Examiner_Cache; end -- OnInspect function mod:OnInspect(unit) if (ex.isSelf) then isInspect = nil; self:HasData(true); self:InitTalents(); end end -- OnInspectReady function mod:OnInspectReady(unit) if (not ex.isSelf) then isInspect = true; self:HasData(ex.canInspect); self:InitTalents(); end end -- OnCacheLoaded function mod:OnCacheLoaded(entry,unit) if (entry.talentPoints or entry.talents) and (entry.talentSpec) then self.details:Clear(); self.details:Add("Talents"); self.details:Add("Specialization",entry.talentSpec or UNKNOWN); self.details:Add("Points",entry.talentPoints or entry.talents); -- Az: backward compatibility with old cache entries self.details:Update(); end end -- OnCache function mod:OnCache(entry,unit) if (self:CanCache()) and (self.hasData) then entry.talentSpec = self.talentSpec; entry.talentPoints = self.talentPoints; entry.level = ex.info.level; end end -- OnClearInspect function mod:OnClearInspect() self:HasData(nil); self.details:Clear(); self:ClearObjects(); end -------------------------------------------------------------------------------------------------------- -- Menu -- -------------------------------------------------------------------------------------------------------- -- Menu Init Items function mod.MenuInit(parent,list) local group = GetActiveTalentGroup(true); local _, _, _, _, p1 = GetTalentTabInfo(1,isInspect,nil,2); local _, _, _, _, p2 = GetTalentTabInfo(2,isInspect,nil,2); local _, _, _, _, p3 = GetTalentTabInfo(3,isInspect,nil,2); local noSecondary = (p1 + p2 + p3 == 0); list[1].text = "Shown Spec"; list[1].header = 1; list[2].text = "Active"; list[2].value = nil; list[2].checked = (cfg.shownSpec == nil); list[3].text = (group == 1 and "|cff80ff80" or "").."Primary"; list[3].value = 1; list[3].checked = (cfg.shownSpec == 1); list[4].text = (group == 2 and "|cff80ff80" or noSecondary and "|cffff8080" or "").."Secondary"; list[4].value = 2; list[4].checked = (cfg.shownSpec == 2); end -- Menu Select Item function mod.MenuSelect(parent,entry) cfg.shownSpec = entry.value; if (mod:IsShown()) then mod:UpdateTalents(); end end -------------------------------------------------------------------------------------------------------- -- Talent Button Functions -- -------------------------------------------------------------------------------------------------------- -- Select Tab local function TalentsTab_OnClick(self,button) activeTab = self.id; PanelTemplates_SetTab(ex,activeTab); mod:UpdateTalents(); end -- Talent OnClick local function TalentButton_OnClick(self,button) local editBox = ChatEdit_GetActiveWindow(); if (IsModifiedClick("CHATLINK")) and (editBox) and (editBox:IsVisible()) then editBox:Insert(GetTalentLink(activeTab,self.id,isInspect,nil,activeSpec)); end end -- Talent OnEnter local function TalentButton_OnEnter(self,motion) GameTooltip:SetOwner(self,"ANCHOR_RIGHT"); GameTooltip:SetTalent(activeTab,self.id,isInspect,nil,activeSpec); end -- Create Talent Button local function CreateTalentButton(parent,index) local btn = CreateFrame("Button",nil,parent); btn:SetWidth(37); btn:SetHeight(37); btn:SetScript("OnClick",TalentButton_OnClick); btn:SetScript("OnEnter",TalentButton_OnEnter); btn:SetScript("OnLeave",ex.HideGTT); btn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square"); btn.id = index; btn.icon = btn:CreateTexture(nil,"BORDER"); btn.icon:SetPoint("TOPLEFT",1,-1); btn.icon:SetPoint("BOTTOMRIGHT",-1,1); btn.slot = btn:CreateTexture(nil,"BACKGROUND"); btn.slot:SetTexture("Interface\\Buttons\\UI-EmptySlot-White"); btn.slot:SetWidth(64); btn.slot:SetHeight(64); btn.slot:SetPoint("CENTER"); btn.rankBorder = btn:CreateTexture(nil,"OVERLAY"); btn.rankBorder:SetTexture("Interface\\TalentFrame\\TalentFrame-RankBorder"); btn.rankBorder:SetWidth(32); btn.rankBorder:SetHeight(32); btn.rankBorder:SetPoint("CENTER",btn,"BOTTOMRIGHT"); btn.goldBorder = btn:CreateTexture(nil,"ARTWORK"); btn.goldBorder:SetTexture("Interface\\TalentFrame\\TalentFrame-Parts"); btn.goldBorder:SetTexCoord(0.40625000,0.57812500,0.68359375,0.76953125) btn.goldBorder:SetWidth(56); btn.goldBorder:SetHeight(56); btn.goldBorder:SetPoint("CENTER"); btn.rank = btn:CreateFontString(nil,"OVERLAY","GameFontNormalSmall"); btn.rank:SetPoint("CENTER",btn.rankBorder,0,1); return btn; end -------------------------------------------------------------------------------------------------------- -- Widget Creation -- -------------------------------------------------------------------------------------------------------- -- OnShow local function OnShow(self) mod:UpdateTalents(); ex.model:Hide(); ex.details:Hide(); ex.guild:Hide(); ex:ShowBackground(true); end -- OnHide local function OnHide(self) ex.model:Show(); ex.details:Show(); ex.guild:Show(); ex:ShowBackground(); ex:SetBackgroundTexture(); end -- Talent Page mod.page:SetWidth(320); mod.page:SetHeight(354); mod.page:SetPoint("BOTTOM",-11,10); mod.page:Hide(); mod.page:SetScript("OnShow",OnShow); mod.page:SetScript("OnHide",OnHide); -- Scroll local sc = CreateFrame("ScrollFrame","ExaminerTalentsScrollChild",mod.page,"UIPanelScrollFrameTemplate"); sc:SetPoint("TOPLEFT",ex.model,0,-2); sc:SetPoint("BOTTOMRIGHT",ex.model,-25,25); -- Scroll Child Frame local scf = CreateFrame("Frame","ExaminerScrollChildFrame"); scf:SetWidth(320); scf:SetHeight(1); sc:SetScrollChild(scf); -- Talent Tabs for i = 1, _G.GetNumTalentTabs() do local tab = CreateFrame("Button","ExaminerTab"..i,mod.page,"TabButtonTemplate"); tab.id = i; tab:SetScript("OnClick",TalentsTab_OnClick); if (i == 1) then tab:SetPoint("TOPLEFT",45,36); else tab:SetPoint("LEFT","ExaminerTab"..(i - 1),"RIGHT"); end end ex.selectedTab = 1; PanelTemplates_SetNumTabs(ex,3); PanelTemplates_UpdateTabs(ex); -- Talent Buttons -- Az: workaround, if I create these dynamically with a metatable (see commented out line below), the framelevel/strata of the arrowframe screws up, seems like a blizzard ui bug local talentBtns = {}; for i = 1, 40 do talentBtns[i] = CreateTalentButton(scf,i); end -- ArrowFrame -- To make them appear above talent buttons local af = CreateFrame("Frame",nil,scf); af:SetAllPoints(); -- Talent Buttons + Arrows + Branches --local talentBtns = setmetatable({},{ __index = function(t,k) t[k] = CreateTalentButton(scf,k); return t[k]; end }); local branches = setmetatable({},{ __index = function(t,k) t[k] = scf:CreateTexture(nil,"BACKGROUND","TalentBranchTemplate"); return t[k]; end }); local arrows = setmetatable({},{ __index = function(t,k) t[k] = af:CreateTexture(nil,"OVERLAY","TalentArrowTemplate"); return t[k]; end }); -------------------------------------------------------------------------------------------------------- -- Functions -- -------------------------------------------------------------------------------------------------------- -- Branch local function SetBranchTexture(coords,x,y) local branch = branches[branchIndex]; branch:SetTexCoord(unpack(coords)); branch:SetPoint("TOPLEFT",x,y); branch:Show(); branchIndex = (branchIndex + 1); end -- Arrows local function SetArrowTexture(coords,x,y) local arrow = arrows[arrowIndex]; arrow:SetTexCoord(unpack(coords)); arrow:SetPoint("TOPLEFT",x,y); arrow:Show(); arrowIndex = (arrowIndex + 1); end -- CalculateBranchLines local function CalculateBranchLines(tier,column,rank,...) local type = (rank > 0 and 1 or -1); for i = 1, select("#",...), 4 do local preTier, preColumn = select(i,...); local left = min(preColumn,column); local right = max(preColumn,column); -- Same Column if (preColumn == column) then for i = preTier, tier - 1 do BRANCH_ARRAY[i][column].down = type; if (i + 1 <= tier - 1) then BRANCH_ARRAY[i + 1][column].up = type; end end BRANCH_ARRAY[tier][column].topArrow = type; -- Same Tier elseif (preTier == tier) then for i = left, right - 1 do BRANCH_ARRAY[tier][i].right = type; BRANCH_ARRAY[tier][i + 1].left = type; end BRANCH_ARRAY[tier][column][preColumn < column and "leftArrow" or "rightArrow"] = type -- Diagonal else local blocked; for i = preTier, tier - 1 do if (BRANCH_ARRAY[i][column].id) then blocked = true; -- Az: debug, going to keep this in, one day talent trees might change so this is needed, this way people can see Examiner is the cause AzMsg("|2Examiner Talent Module:|r blocked path |1"..tier.." x "..i.."|r left = |1"..left.."|r, right = |1"..right); break; end end -- Top Connection -- Az: Currently, as of patch 3.1, no classes actually has a blocking talent above, so I haven't been able to test it, and it will most likely not be correct if (blocked) then for i = preTier, tier - 1 do BRANCH_ARRAY[i][column].up = type; BRANCH_ARRAY[i + 1][column].down = type; end BRANCH_ARRAY[tier][column][preColumn < column and "leftArrow" or "rightArrow"] = type -- Left/Right Connection else for i = preTier, tier - 1 do BRANCH_ARRAY[i][column].down = type; BRANCH_ARRAY[i + 1][column].up = type; end for i = left, right - 1 do BRANCH_ARRAY[preTier][i].right = type; BRANCH_ARRAY[preTier][i + 1].left = type; end BRANCH_ARRAY[tier][column].topArrow = type; end end end end -- Clear Things function mod:ClearObjects() sc:Hide(); for i = 1, _G.GetNumTalentTabs() do _G["ExaminerTab"..i]:Hide(); end end -- Init Talents function mod:InitTalents() if (self:IsShown()) then self:UpdateTalents(); end -- Gather Talent Info local maxTree = 1; local combined = 0; for i = 1, _G.GetNumTalentTabs() do local _, _, pointsSpent = GetTalentTabInfo(i,isInspect); points[i] = pointsSpent; combined = (combined + pointsSpent); if (pointsSpent > points[maxTree]) then maxTree = i; end end local talentPoints = points[1].."/"..points[2].."/"..points[3]; -- Fancy level estimate since we can determine it from talents if (ex.info.level == -1) and (combined > 0) then if (combined >= 36) then ex.info.level = (80 + combined - 36); else ex.info.level = ((combined - 1) * 2 + 10); end ex:SetUnitDetailString(); end -- Details local _, talentSpec = GetTalentTabInfo(maxTree,isInspect,nil,group); self.details:Add("Talents"); self.details:Add("Specialization",combined > 0 and talentSpec or "No Talents"); self.details:Add("Points",talentPoints); self.details:Update(); -- Backup Values for Caching later self.talentSpec = talentSpec; self.talentPoints = talentPoints; end -- Update Talents function mod:UpdateTalents() --activeSpec = cfg.shownSpec or GetActiveTalentGroup(isInspect); --local uniqueId, tabName, description, icon, pointsSpent, background, previewPointsSpent, bool = GetTalentTabInfo(activeTab,isInspect,nil,activeSpec); --ex:SetBackgroundTexture("Interface\\TalentFrame\\"..background.."-"); -- Show Objects sc:Show(); for i = 1, _G.GetNumTalentTabs() do _G["ExaminerTab"..i]:Show(); end -- Update Tabs for i = 1, _G.GetNumTalentTabs() do local tab = _G["ExaminerTab"..i]; local tabName, _, pointsSpent = _G.GetTalentTabInfo(i,isInspect); tab:SetFormattedText("%s |cff00ff00%d",tabName,pointsSpent); PanelTemplates_TabResize(tab,-18); end -- Reset Prereq arrowIndex = 1; branchIndex = 1; for tier = 1, MAX_NUM_TALENT_TIERS do for column = 1, NUM_TALENT_COLUMNS do wipe(BRANCH_ARRAY[tier][column]); end end -- Talents local numTalents = GetNumTalents(activeTab,isInspect,nil); for i = 1, numTalents do local name, iconTexture, tier, column, rank, maxRank, meetsPrereq, previewRank, meetsPreviewPrereq, isExceptional, goldBorder = GetTalentInfo(activeTab,i,isInspect,nil,activeSpec); local btn = talentBtns[i]; if (not name) then btn:Hide(); -- Az: For some silly reason, we are able to get a nil entry before we reach the end. This happens for mages/arcane as of patch 4.0.3a else BRANCH_ARRAY[tier][column].id = i; CalculateBranchLines(tier,column,rank,GetTalentPrereqs(activeTab,i,isInspect,nil,activeSpec)); btn:Show(); btn:ClearAllPoints(); btn:SetPoint("TOPLEFT",(column - 1) * 63 + 35,(tier - 1) * -63 - 20); btn.icon:SetTexture(iconTexture); btn.icon:SetTexCoord(0.07,0.93,0.07,0.93); if (isExceptional) then btn.goldBorder:Show(); else btn.goldBorder:Hide(); end if (rank == 0) then btn.icon:SetDesaturated(true); btn.goldBorder:SetDesaturated(true); btn.rankBorder:Hide(); btn.rank:Hide(); btn.slot:SetVertexColor(0.5, 0.5, 0.5); else btn.icon:SetDesaturated(false); btn.goldBorder:SetDesaturated(false); btn.rankBorder:Show(); btn.rank:Show(); btn.rank:SetText(rank); if (rank < maxRank) then btn.slot:SetVertexColor(0.1,1,0.1); btn.rank:SetTextColor(0,1,0); else btn.slot:SetVertexColor(1,0.82,0); btn.rank:SetTextColor(1,0.82,0); end end end end -- Hide Remaining Objects for i = numTalents + 1, #talentBtns do talentBtns[i]:Hide(); end for i = branchIndex, #branches do branches[i]:Hide(); end for i = arrowIndex, #arrows do arrows[i]:Hide(); end end