vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
MAX_TALENT_TABS = 5;
MAX_NUM_TALENTS = 20;
MAX_NUM_TALENT_TIERS = 8;
NUM_TALENT_COLUMNS = 4;
SPECIAL_TALENT_BRANCH_ARRAY = {};
SPECIAL_TALENT_BUTTON_SIZE = 32;
MAX_NUM_BRANCH_TEXTURES = 30;
MAX_NUM_ARROW_TEXTURES = 30;
INITIAL_SPECIAL_TALENT_OFFSET_X = 53;
INITIAL_SPECIAL_TALENT_OFFSET_Y = 45;
TALENT_POINTS_AT_60 = 51;
CYAN_FONT_COLOR_CODE = "|cff00ffff";

TALENT_BRANCH_TEXTURECOORDS = {
        up = {
                [1] = {0.12890625, 0.25390625, 0 , 0.484375},
                [-1] = {0.12890625, 0.25390625, 0.515625 , 1.0}
        },
        down = {
                [1] = {0, 0.125, 0, 0.484375},
                [-1] = {0, 0.125, 0.515625, 1.0}
        },
        left = {
                [1] = {0.2578125, 0.3828125, 0, 0.5},
                [-1] = {0.2578125, 0.3828125, 0.5, 1.0}
        },
        right = {
                [1] = {0.2578125, 0.3828125, 0, 0.5},
                [-1] = {0.2578125, 0.3828125, 0.5, 1.0}
        },
        topright = {
                [1] = {0.515625, 0.640625, 0, 0.5},
                [-1] = {0.515625, 0.640625, 0.5, 1.0}
        },
        topleft = {
                [1] = {0.640625, 0.515625, 0, 0.5},
                [-1] = {0.640625, 0.515625, 0.5, 1.0}
        },
        bottomright = {
                [1] = {0.38671875, 0.51171875, 0, 0.5},
                [-1] = {0.38671875, 0.51171875, 0.5, 1.0}
        },
        bottomleft = {
                [1] = {0.51171875, 0.38671875, 0, 0.5},
                [-1] = {0.51171875, 0.38671875, 0.5, 1.0}
        },
        tdown = {
                [1] = {0.64453125, 0.76953125, 0, 0.5},
                [-1] = {0.64453125, 0.76953125, 0.5, 1.0}
        },
        tup = {
                [1] = {0.7734375, 0.8984375, 0, 0.5},
                [-1] = {0.7734375, 0.8984375, 0.5, 1.0}
        },
};

TALENT_ARROW_TEXTURECOORDS = {
        top = {
                [1] = {0, 0.5, 0, 0.5},
                [-1] = {0, 0.5, 0.5, 1.0}
        },
        right = {
                [1] = {1.0, 0.5, 0, 0.5},
                [-1] = {1.0, 0.5, 0.5, 1.0}
        },
        left = {
                [1] = {0.5, 1.0, 0, 0.5},
                [-1] = {0.5, 1.0, 0.5, 1.0}
        },
};

--UIPanelWindows["SpecialTalentFrame"] = { area = "doublewide", pushable = 6, whileDead = 1 };

function SpecialTalentFrame_ToggleFrame()
        if ( SpecialTalentFrame:IsVisible() ) then
                HideUIPanel(SpecialTalentFrame);
        else
                ShowUIPanel(SpecialTalentFrame);
        end
end

function SpecialTalentFrame_ToggleDragged()
        if ( SpecialTalentFrame:IsVisible() ) then
                SpecialTalentFrame:Hide();
        else
                SpecialTalentFrame:Show();
        end
end

function SpecialTalentFrame_OnLoad()
        this:RegisterEvent("CHARACTER_POINTS_CHANGED");
        this:RegisterEvent("SPELLS_CHANGED");
        this:RegisterEvent("UNIT_PORTRAIT_UPDATE");
        this:RegisterEvent("ADDON_LOADED");
        for tab=1, MAX_TALENT_TABS do
                SPECIAL_TALENT_BRANCH_ARRAY[tab]={};
                for i=1, MAX_NUM_TALENT_TIERS do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i] = {};
                        for j=1, NUM_TALENT_COLUMNS do
                                SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j] = {id=nil, up=0, left=0, right=0, down=0, leftArrow=0, rightArrow=0, topArrow=0};
                        end
                end
        end
        
        this.learnMode = "learned";
        PlayerOfRealm = UnitName("player").." of "..GetRealmName();
        SpecialTalentFrame_Toggle = SpecialTalentFrame_ToggleFrame;
        
        if ( tp_webdata ) then
                SPECIAL_TALENT_CLASSES = { Druid=1, Hunter=2, Mage=3, Paladin=4, Priest=5, Rogue=6, Shaman=7, Warlock=8, Warrior=9, };
        end
end

function SpecialTalentFrame_OnShow()
        -- Stop buttons from flashing after skill up
        SetButtonPulse(TalentMicroButton, 0, 1);

        PlaySound("TalentScreenOpen");
        UpdateMicroButtons();

        SpecialTalentFrameTitleText:SetText(SPECIAL_TALENT.." - "..UnitClass("player"));
        SetPortraitTexture(SpecialTalentFramePortrait, "player");
        --show total talent points
        local tpoints = max(UnitLevel("player")-9, 0);
        SpecialTalentFrameTalentPointsText:SetText(tpoints);
        --set Planned label blue
        SpecialTalentFramePlannedCheckButtonText:SetTextColor(0,1,1);

        SpecialTalentFrame_Update();
end

function SpecialTalentFrame_OnHide()
        UpdateMicroButtons();
        PlaySound("TalentScreenClose");
end

function SpecialTalentFrame_Minimize()
        SpecialTalentFrameSaved.frameMinimized=1;
        if ( not SpecialTalentFrameSaved.tabShown ) then
                SpecialTalentFrameSaved.tabShown=1;
        end
        SpecialTalentFrameMinimizeButton:SetText("^");
        UIPanelWindows["SpecialTalentFrame"] = { area = "left", pushable = 6, whileDead = 1 };
        SpecialTalentFrame:SetWidth(345);
        SpecialTalentFrame:SetHeight(586);
        for i=2, 4 do
                getglobal("SpecialTalentFrameBorder_TopLeft"..i):Hide();
                getglobal("SpecialTalentFrameBorder_BottomLeft"..i):Hide();             
        end
        for i=1, MAX_TALENT_TABS do
                local button=getglobal("SpecialTalentFrameTab"..i);
                if ( not button ) then
                        break;
                end
                button:Show();
                
                getglobal("SpecialTalentFrameTabFrame"..i):SetPoint("TOPLEFT", SpecialTalentFrame, "TOPLEFT", 0, -80);
        end

        SpecialTalentFrameTalentPointsText:Hide();
        SpecialTalentFrameTalentPoints:Hide();
        SpecialTalentFrameUnspentPointsText:SetPoint("LEFT", SpecialTalentFrame, "TOP", -145, -84);
        --SpecialTalentFrameUnspentPoints:Hide();

        SpecialTalentFrameLearnedPointsText:SetPoint("RIGHT", SpecialTalentFrame, "TOP", 122, -65);
        SpecialTalentFramePlannedPointsText:SetPoint("RIGHT", SpecialTalentFrame, "TOP", 122, -84);
        SpecialTalentFrameLearnedCheckButton:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -19, -65);
        SpecialTalentFramePlannedCheckButton:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -19, -84);

        SpecialTalentFrameForceShiftCheckButton:SetPoint("LEFT", SpecialTalentFrame, "TOPLEFT", 215, -46);
end

function SpecialTalentFrame_Maximize()
        SpecialTalentFrameSaved.frameMinimized=nil;
        SpecialTalentFrameMinimizeButton:SetText("v");
        UIPanelWindows["SpecialTalentFrame"] = { area = "doublewide", pushable = 6, whileDead = 1 };
        SpecialTalentFrame:SetWidth(900);
        SpecialTalentFrame:SetHeight(586);
        for i=2, 4 do
                getglobal("SpecialTalentFrameBorder_TopLeft"..i):Show();
                getglobal("SpecialTalentFrameBorder_BottomLeft"..i):Show();             
        end
        for i=1, MAX_TALENT_TABS do
                local button=getglobal("SpecialTalentFrameTab"..i);
                if ( not button ) then
                        break;
                end
                button:Hide();
                
                getglobal("SpecialTalentFrameTabFrame"..i):SetPoint("TOPLEFT", SpecialTalentFrame, "TOPLEFT", (i-1)*278, -80);
        end

        SpecialTalentFrameTalentPointsText:Show();
        SpecialTalentFrameTalentPoints:Show();
        SpecialTalentFrameUnspentPointsText:SetPoint("LEFT", SpecialTalentFrame, "TOP", 25, -81);
        --SpecialTalentFrameUnspentPoints:SetPoint("RIGHT");

        SpecialTalentFrameLearnedPointsText:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -25, -60);
        SpecialTalentFramePlannedPointsText:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -25, -81);
        SpecialTalentFrameLearnedCheckButton:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -165, -60);
        SpecialTalentFramePlannedCheckButton:SetPoint("RIGHT", SpecialTalentFrame, "TOP", -165, -81);

        SpecialTalentFrameForceShiftCheckButton:SetPoint("LEFT", SpecialTalentFrame, "TOPLEFT", 215, -60);
end

function SpecialTalentFrame_OnEvent()
        if ( (event == "CHARACTER_POINTS_CHANGED") or (event == "SPELLS_CHANGED") ) then
                SpecialTalentFrame_Update();
        elseif ( event == "UNIT_PORTRAIT_UPDATE" ) then
                if ( arg1 == "player" ) then
                        SetPortraitTexture(SpecialTalentFramePortrait, "player");
                end
        elseif ( event=="ADDON_LOADED" and arg1=="SpecialTalentUI" ) then
                this:UnregisterEvent("ADDON_LOADED");
                if ( not SpecialTalentFrameSaved ) then
                        SpecialTalentFrameSaved={};
                end
                SpecialTalent_LoadPlannedSaved();
                SpecialTalentFrame_CheckDragged();
                SpecialTalentFrameTabs_Initialize();
        end
        
end

function SpecialTalentFrameTalent_OnEvent()
        if ( GameTooltip:IsOwned(this) ) then
                GameTooltip:SetTalent(this.tabID, this:GetID());
        end
end

function SpecialTalentFrame_OnDrag()
        if ( not SpecialTalentFrameSaved ) then
                SpecialTalentFrameSaved={};
        end
        SpecialTalentFrameSaved.frameDragged = 1;
        SpecialTalentFrameSaved.frameLeft = SpecialTalentFrame:GetLeft();
        SpecialTalentFrameSaved.frameTop = SpecialTalentFrame:GetTop();
        SpecialTalentFrame:SetUserPlaced(0);
        SpecialTalentFrame_CheckDragged();
end

function SpecialTalentFrame_Update()
        local numTabs = GetNumTalentTabs();
        local learnedText = SpecialTalentFrameLearnedPointsText;
        local plannedText = SpecialTalentFramePlannedPointsText;
        local learned = "";
        local planned = "";
        local player = PlayerOfRealm;
        
        SpecialTalentFrame_UpdateTalentPoints();

        for f=1, MAX_TALENT_TABS do -- for each tab frame

                local talentTabName = GetTalentTabInfo(f);
                if ( not talentTabName ) then
                        break;
                end
                getglobal("SpecialTalentFrameTabFrame"..f):Hide();
                local base;
                local name, iconTexture, pointsSpent, fileName = GetTalentTabInfo(f);
                if ( talentTabName ) then
                        base = "Interface\\TalentFrame\\"..fileName.."-";
                else
                        -- temporary default for classes without talents poor guys
                        base = "Interface\\TalentFrame\\MageFire-";
                end
                getglobal("SpecialTalentFrameTabFrame"..f.."BackgroundTopLeft"):SetTexture(base.."TopLeft");
                getglobal("SpecialTalentFrameTabFrame"..f.."BackgroundTopRight"):SetTexture(base.."TopRight");
                getglobal("SpecialTalentFrameTabFrame"..f.."BackgroundBottomLeft"):SetTexture(base.."BottomLeft");
                getglobal("SpecialTalentFrameTabFrame"..f.."BackgroundBottomRight"):SetTexture(base.."BottomRight");

                if ( f > 1 ) then
                        learned = learned.."/";
                        planned = planned.."/";
                end
                learned = learned..pointsSpent;
                planned = planned..SpecialTalentPlannedSaved[player][f].points;
                getglobal("SpecialTalentFrameTabFrame"..f.."SpentPoints"):SetText(format(MASTERY_POINTS_SPENT, name).." "..NORMAL_FONT_COLOR_CODE..pointsSpent..FONT_COLOR_CODE_CLOSE);
                getglobal("SpecialTalentFrameTabFrame"..f.."SpentPoints"):SetText(CYAN_FONT_COLOR_CODE .. SpecialTalentPlannedSaved[player][f].points .. "|r :"..RED_FONT_COLOR_CODE..talentTabName.."|r: "..NORMAL_FONT_COLOR_CODE..pointsSpent.."|r");
                getglobal("SpecialTalentFrameTabFrame"..f).pointsSpent = pointsSpent;

                local numTalents = GetNumTalents(f);
                -- Just a reminder error if there are more talents than available buttons
                if ( numTalents > MAX_NUM_TALENTS ) then
                        message("Too many talents in talent frame!");
                end

                SpecialTalentFrame_ResetBranches(f);
                local tier, column, rank, maxRank, isExceptional, isLearnable;
                local forceDesaturated, tierUnlocked;
                local button;

                getglobal("SpecialTalentFrameTabFrame"..f).greatestTier = 0;
                
                if ( not SpecialTalentFrameSaved.frameMinimized or SpecialTalentFrameSaved.tabShown==f ) then
                        getglobal("SpecialTalentFrameTabFrame"..f):Show();
                        for i=1, MAX_NUM_TALENTS do
                                button = getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i);
                                if ( i <= numTalents ) then
                                        -- Set the button info
                                        name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(f, i);

                                        -- Show planned points and border if necessary
                                        local plannedPoints = SpecialTalentPlannedSaved[player][f][i];
                                        if ( not plannedPoints or plannedPoints<1 ) then
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Planned"):Hide();
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."PlannedBorder"):Hide();
                                        else
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Planned"):SetText(plannedPoints);
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Planned"):SetTextColor(0,1,1);
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Planned"):Show();
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."PlannedBorder"):Show();
                                        end

                                        -- Show learned points and border if necessary
                                        if ( rank > 0 ) then
                                                if ( rank < maxRank ) then
                                                        getglobal( "SpecialTalentFrameTabFrame"..f.."Talent"..i.."Rank" ):SetTextColor( GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b );
                                                else
                                                        getglobal( "SpecialTalentFrameTabFrame"..f.."Talent"..i.."Rank" ):SetTextColor( NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b );
                                                end
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Rank"):SetText(rank);
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."RankBorder"):Show();
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Rank"):Show();
                                        else
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."RankBorder"):Hide();
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Rank"):Hide();
                                        end

                                        SetSpecialTalentButtonLocation(button, tier, column);
                                        SPECIAL_TALENT_BRANCH_ARRAY[f][tier][column].id = button:GetID();
                                        SetItemButtonTexture(button, iconTexture);

                                        -- Determine learning or planning mode
                                        local tabPointsSpent, talentPoints, requirementsMet;
                                        if ( SpecialTalentFrame.learnMode == "learned" ) then
                                                tabPointsSpent = getglobal("SpecialTalentFrameTabFrame"..f).pointsSpent;
                                                talentPoints = SpecialTalentFrame.talentPoints;
                                        elseif ( SpecialTalentFrame.learnMode == "planned" ) then
                                                tabPointsSpent = SpecialTalentPlannedSaved[player][f].points;
                                                talentPoints = TALENT_POINTS_AT_60 - SpecialTalentPlannedSaved[player].points;
                                                rank = SpecialTalentPlannedSaved[player][f][i] or 0;
                                        end

                                        -- If player has no talent points then show only talents with points in them
                                        if ( talentPoints <= 0 and rank == 0  ) then
                                                forceDesaturated = 1;
                                        else
                                                forceDesaturated = nil;
                                        end

                                        -- If the player has spent at least 5 talent points in the previous tier
                                        if ( (tier - 1) * 5 <= tabPointsSpent ) then
                                                tierUnlocked = 1;
                                        else
                                                tierUnlocked = nil;
                                        end
                                        -- compare highest tier
                                        if ( tier > getglobal("SpecialTalentFrameTabFrame"..f).greatestTier and rank>0 ) then
                                                getglobal("SpecialTalentFrameTabFrame"..f).greatestTier = tier;
                                        end

                                        requirementsMet = SpecialTalentFrame_SetPrereqs(tier, column, forceDesaturated, tierUnlocked, f, SpecialTalent_GetTalentPrereqs(f, i)) and meetsPrereq;

                                        -- Talent must meet prereqs or the player must have no points to spend
                                        if ( requirementsMet ) then
                                                --SetItemButtonDesaturated(button, nil);
                                                getglobal(button:GetName().."IconTexture"):SetAlpha(1);
                                                button.clickable = 1;

                                                if ( rank < maxRank ) then
                                                        -- Rank is green if not maxed out
                                                        getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Slot"):SetVertexColor(0.1, 1.0, 0.1);
                                                else
                                                        getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Slot"):SetVertexColor(1.0, 0.82, 0);
                                                end
                                        else
                                                --SetItemButtonDesaturated(button, 2, .65, .65, .65);
                                                getglobal(button:GetName().."IconTexture"):SetAlpha(.3);
                                                button.clickable = nil;
                                                getglobal("SpecialTalentFrameTabFrame"..f.."Talent"..i.."Slot"):SetVertexColor(0.5, 0.5, 0.5);
                                        end

                                        button:Show();
                                else    
                                        button:Hide();
                                end
                        end
                end
        
                -- Draw the prereq branches
                local node;
                local textureIndex = 1;
                local xOffset, yOffset;
                local texCoords;
                -- Variable that decides whether or not to ignore drawing pieces
                local ignoreUp;
                local tempNode;
                SpecialTalentFrame_ResetBranchTextureCount(f);
                SpecialTalentFrame_ResetArrowTextureCount(f);
                for i=1, MAX_NUM_TALENT_TIERS do
                        for j=1, NUM_TALENT_COLUMNS do
                                node = SPECIAL_TALENT_BRANCH_ARRAY[f][i][j];
                        
                                -- Setup offsets
                                xOffset = ((j - 1) * SPECIAL_TALENT_BUTTON_SIZE * 2) + INITIAL_SPECIAL_TALENT_OFFSET_X ;
                                yOffset = -((i - 1) * SPECIAL_TALENT_BUTTON_SIZE * 1.75) - INITIAL_SPECIAL_TALENT_OFFSET_Y + 4;
                
                                if ( node.id ) then
                                        -- Has talent
                                        if ( node.up ~= 0 ) then
                                                if ( not ignoreUp ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["up"][node.up], xOffset, yOffset + SPECIAL_TALENT_BUTTON_SIZE, f);
                                                else
                                                        ignoreUp = nil;
                                                end
                                        end
                                        if ( node.down ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset, yOffset - SPECIAL_TALENT_BUTTON_SIZE + 1, f);
                                        end
                                        if ( node.left ~= 0 ) then
                                                        SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["left"][node.left], xOffset - SPECIAL_TALENT_BUTTON_SIZE, yOffset, f);
                                        end
                                        if ( node.right ~= 0 ) then
                                                -- See if any connecting branches are gray and if so color them gray
                                                tempNode = SPECIAL_TALENT_BRANCH_ARRAY[f][i][j+1];      
                                                if ( tempNode.left ~= 0 and tempNode.down < 0 ) then
                                                        SpecialTalentFrame_SetBranchTexture(i, j-1, TALENT_BRANCH_TEXTURECOORDS["right"][tempNode.down], xOffset + SPECIAL_TALENT_BUTTON_SIZE, yOffset, f);
                                                else
                                                        SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["right"][node.right], xOffset + SPECIAL_TALENT_BUTTON_SIZE + 1, yOffset, f);
                                                end
                                        end
                                        -- Draw arrows
                                        if ( node.rightArrow ~= 0 ) then
                                                SpecialTalentFrame_SetArrowTexture(i, j, TALENT_ARROW_TEXTURECOORDS["right"][node.rightArrow], xOffset + SPECIAL_TALENT_BUTTON_SIZE/2 + 5, yOffset, f);
                                        end
                                        if ( node.leftArrow ~= 0 ) then
                                                SpecialTalentFrame_SetArrowTexture(i, j, TALENT_ARROW_TEXTURECOORDS["left"][node.leftArrow], xOffset - SPECIAL_TALENT_BUTTON_SIZE/2 - 5, yOffset, f);
                                        end
                                        if ( node.topArrow ~= 0 ) then
                                                SpecialTalentFrame_SetArrowTexture(i, j, TALENT_ARROW_TEXTURECOORDS["top"][node.topArrow], xOffset, yOffset + SPECIAL_TALENT_BUTTON_SIZE/2, f);
                                        end
                                else
                                        -- Doesn't have a talent
                                        if ( node.up ~= 0 and node.left ~= 0 and node.right ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["tup"][node.up], xOffset, yOffset, f);
                                        elseif ( node.down ~= 0 and node.left ~= 0 and node.right ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["tdown"][node.down], xOffset, yOffset, f);
                                        elseif ( node.left ~= 0 and node.down ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["topright"][node.left], xOffset , yOffset, f);
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset , yOffset - 32, f);
                                        elseif ( node.left ~= 0 and node.up ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["bottomright"][node.left], xOffset , yOffset, f);
                                        elseif ( node.left ~= 0 and node.right ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["right"][node.right], xOffset + SPECIAL_TALENT_BUTTON_SIZE, yOffset, f);
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["left"][node.left], xOffset + 1, yOffset, f);
                                        elseif ( node.right ~= 0 and node.down ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["topleft"][node.right], xOffset , yOffset, f);
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset , yOffset - 32, f);
                                        elseif ( node.right ~= 0 and node.up ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["bottomleft"][node.right], xOffset , yOffset, f);
                                        elseif ( node.up ~= 0 and node.down ~= 0 ) then
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["up"][node.up], xOffset, yOffset, f);
                                                SpecialTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset , yOffset - 32, f);
                                                ignoreUp = 1;
                                        end
                                end
                        end -- for
                        -- Hide any unused branch textures
                        for i=SpecialTalentFrame_GetBranchTextureCount(f), MAX_NUM_BRANCH_TEXTURES do
                                getglobal("SpecialTalentFrameTabFrame"..f.."Branch"..i):Hide();
                        end
                        -- Hide and unused arrow textures
                        for i=SpecialTalentFrame_GetArrowTextureCount(f), MAX_NUM_ARROW_TEXTURES do
                                getglobal("SpecialTalentFrameTabFrame"..f.."ArrowFrameArrow"..i):Hide();
                        end
                end -- if
        end --- for each tab frame
        local maxpoints = max(UnitLevel("player")-9, 0);
        local avail = maxpoints - SpecialTalentFrame.talentPoints;
        learned = learned.." = "..avail.."/"..maxpoints;
        learnedText:SetText(learned);
        planned = planned.." = "..SpecialTalentPlannedSaved[player].points.."/"..TALENT_POINTS_AT_60;
        plannedText:SetText(planned);
end

function SpecialTalentFrame_SetArrowTexture(tier, column, texCoords, xOffset, yOffset, tab)
        local arrowTexture = SpecialTalentFrame_GetArrowTexture(tab);
        arrowTexture:SetTexCoord(texCoords[1], texCoords[2], texCoords[3], texCoords[4]);
        arrowTexture:SetPoint("TOPLEFT", "SpecialTalentFrameTabFrame"..tab.."ArrowFrame", "TOPLEFT", xOffset, yOffset);
end

function  SpecialTalentFrame_SetBranchTexture(tier, column, texCoords, xOffset, yOffset, tab)
        local branchTexture =  SpecialTalentFrame_GetBranchTexture(tab);
        branchTexture:SetTexCoord(texCoords[1], texCoords[2], texCoords[3], texCoords[4]);
        branchTexture:SetPoint("TOPLEFT", "SpecialTalentFrameTabFrame"..tab, "TOPLEFT", xOffset, yOffset);
end

function SpecialTalentFrame_GetArrowTexture(tab)
        local tabframe = getglobal("SpecialTalentFrameTabFrame"..tab);
        local index = tabframe.arrowIndex;
        local arrowTexture = getglobal("SpecialTalentFrameTabFrame"..tab.."ArrowFrameArrow".. index);
        getglobal("SpecialTalentFrameTabFrame"..tab).arrowIndex = index + 1;
        if ( not arrowTexture ) then
                message("Not enough arrow textures");
        else
                arrowTexture:Show();
                return arrowTexture;
        end
end

function SpecialTalentFrame_GetBranchTexture(tab)
        local tabframe = getglobal("SpecialTalentFrameTabFrame"..tab);
        local index = tabframe.textureIndex;
        local branchTexture = getglobal("SpecialTalentFrameTabFrame"..tab.."Branch"..index);
         tabframe.textureIndex = index + 1;
        if ( not branchTexture ) then
                message("Not enough branch textures");
        else
                branchTexture:Show();
                return branchTexture;
        end
end

function SpecialTalentFrame_ResetArrowTextureCount(tab)
         getglobal("SpecialTalentFrameTabFrame"..tab).arrowIndex = 1;
end

function SpecialTalentFrame_ResetBranchTextureCount(tab)
         getglobal("SpecialTalentFrameTabFrame"..tab).textureIndex = 1;
end

function SpecialTalentFrame_GetArrowTextureCount(tab)
        return  getglobal("SpecialTalentFrameTabFrame"..tab).arrowIndex;
end

function SpecialTalentFrame_GetBranchTextureCount(tab)
        return getglobal("SpecialTalentFrameTabFrame"..tab).textureIndex;
end

function SpecialTalentFrame_SetPrereqs(...)
        local buttonTier = arg[1];
        local buttonColumn = arg[2];
        local forceDesaturated = arg[3];
        local tierUnlocked = arg[4];
        local tab = arg[5];
        local tier, column, isLearnable;
        local requirementsMet;
        if ( tierUnlocked and not forceDesaturated ) then
                requirementsMet = 1;
        else
                requirementsMet = nil;
        end
        for i=6, arg.n, 3 do
                tier = arg[i];
                column = arg[i+1];
                isLearnable = arg[i+2];
                if ( not isLearnable or forceDesaturated ) then
                        requirementsMet = nil;
                end
                SpecialTalentFrame_DrawLines(buttonTier, buttonColumn, tier, column, requirementsMet, tab);
        end
        return requirementsMet;
end

function SpecialTalentFrame_DrawLines(buttonTier, buttonColumn, tier, column, requirementsMet, tab)
        if ( requirementsMet ) then
                requirementsMet = 1;
        else
                requirementsMet = -1;
        end
        
        -- Check to see if are in the same column
        if ( buttonColumn == column ) then
                -- Check for blocking talents
                if ( (buttonTier - tier) > 1 ) then
                        -- If more than one tier difference
                        for i=tier + 1, buttonTier - 1 do
                                if ( SPECIAL_TALENT_BRANCH_ARRAY[tab][i][buttonColumn].id ) then
                                        -- If there's an id, there's a blocker
                                        message("Error this layout is blocked vertically "..SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][i].id);
                                        return;
                                end
                        end
                end
                
                -- Draw the lines
                for i=tier, buttonTier - 1 do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][buttonColumn].down = requirementsMet;
                        if ( (i + 1) <= (buttonTier - 1) ) then
                                SPECIAL_TALENT_BRANCH_ARRAY[tab][i + 1][buttonColumn].up = requirementsMet;
                        end
                end
                
                -- Set the arrow
                SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].topArrow = requirementsMet;
                return;
        end
        -- Check to see if they're in the same tier
        if ( buttonTier == tier ) then
                local left = min(buttonColumn, column);
                local right = max(buttonColumn, column);
                
                -- See if the distance is greater than one space
                if ( (right - left) > 1 ) then
                        -- Check for blocking talents
                        for i=left + 1, right - 1 do
                                if ( SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i].id ) then
                                        -- If there's an id, there's a blocker
                                        message("there's a blocker");
                                        return;
                                end
                        end
                end
                -- If we get here then we're in the clear
                for i=left, right - 1 do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i].right = requirementsMet;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i+1].left = requirementsMet;
                end
                -- Determine where the arrow goes
                if ( buttonColumn < column ) then
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].rightArrow = requirementsMet;
                else
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].leftArrow = requirementsMet;
                end
                return;
        end
        -- Now we know the prereq is diagonal from us
        local left = min(buttonColumn, column);
        local right = max(buttonColumn, column);
        -- Don't check the location of the current button
        if ( left == column ) then
                left = left + 1;
        else
                right = right - 1;
        end
        -- Check for blocking talents
        local blocked = nil;
        for i=left, right do
                if ( SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i].id ) then
                        -- If there's an id, there's a blocker
                        blocked = 1;
                end
        end
        left = min(buttonColumn, column);
        right = max(buttonColumn, column);
        if ( not blocked ) then
                SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][buttonColumn].down = requirementsMet;
                SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].up = requirementsMet;
                
                for i=tier, buttonTier - 1 do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][buttonColumn].down = requirementsMet;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i + 1][buttonColumn].up = requirementsMet;
                end

                for i=left, right - 1 do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i].right = requirementsMet;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][i+1].left = requirementsMet;
                end
                -- Place the arrow
                SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].topArrow = requirementsMet;
                return;
        end
        -- If we're here then we were blocked trying to go vertically first so we have to go over first, then up
        if ( left == buttonColumn ) then
                left = left + 1;
        else
                right = right - 1;
        end
        -- Check for blocking talents
        for i=left, right do
                if ( SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][i].id ) then
                        -- If there's an id, then throw an error
                        message("Error, this layout is undrawable "..SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][i].id);
                        return;
                end
        end
        -- If we're here we can draw the line
        left = min(buttonColumn, column);
        right = max(buttonColumn, column);
        --SPECIAL_TALENT_BRANCH_ARRAY[tab][tier][column].down = requirementsMet;
        --SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][column].up = requirementsMet;

        for i=tier, buttonTier-1 do
                SPECIAL_TALENT_BRANCH_ARRAY[tab][i][column].up = requirementsMet;
                SPECIAL_TALENT_BRANCH_ARRAY[tab][i+1][column].down = requirementsMet;
        end

        -- Determine where the arrow goes
        if ( buttonColumn < column ) then
                SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].rightArrow =  requirementsMet;
        else
                SPECIAL_TALENT_BRANCH_ARRAY[tab][buttonTier][buttonColumn].leftArrow =  requirementsMet;
        end
end

function SpecialTalent_PlanTalent( tabID, talentID )
        --local tabID, talentID = this.tabID, this:GetID();
        
        -- Get talent info
        local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tabID, talentID);
        local saved = SpecialTalentPlannedSaved[PlayerOfRealm];
        local tab = saved[tabID];
        local plannedPoints = tab[talentID];
        local tabPoints = tab.points;
        local talentPoints = saved.points;
        
        if ( talentPoints<TALENT_POINTS_AT_60 ) then
                if ( not plannedPoints ) then
                        tab[talentID] = 1;
                        tab.points = tabPoints + 1;
                        saved.points = talentPoints + 1;
                elseif ( plannedPoints < maxRank ) then
                        plannedPoints = plannedPoints + 1;
                        tab[talentID] = plannedPoints;
                        tab.points = tabPoints + 1;
                        saved.points = talentPoints + 1;
                end
        end
        SpecialTalentFrame_Update();
end

function SpecialTalent_UnplanTalent( tabID, talentID )
        --local tabID, talentID = this.tabID, this:GetID();
        
        -- Get talent info
        local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tabID, talentID);
        local saved = SpecialTalentPlannedSaved[PlayerOfRealm];
        local tab = saved[tabID];
        local plannedPoints = tab[talentID];
        local tabPoints = tab.points;
        local talentPoints = saved.points;
        
        if ( plannedPoints and plannedPoints > 0 ) then
                -- check if tiers below and enough talentPoints to spare
                local hiTier = getglobal("SpecialTalentFrameTabFrame"..tabID).greatestTier;
                local tierPoints, tierFine, tierTotal = 0, 1, {};
                for i=1, hiTier do
                        tierTotal[i]=0;
                end
                for i=1, GetNumTalents(tabID) do
                        local _,_,t,_,r = SpecialTalent_GetTalentInfo(tabID, i);
                        if ( t > hiTier  ) then break; end
                        tierTotal[t]=tierTotal[t]+r;
                        -- check dependent talents
                        local rt, rc = SpecialTalent_GetTalentPrereqs(tabID, i);
                        if ( rt ) then
                                if ( r>0 and (rt==tier and rc==column) ) then
                                        tierFine=nil;
                                end
                        end
                end
                if ( tierFine ) then
                        for i=tier+1, hiTier do
                                tierPoints=0;
                                for j=1, i-1 do
                                        tierPoints=tierPoints + tierTotal[j];
                                end
                                if ( tierPoints <= (i-1)*5 ) then
                                        tierFine=nil;
                                end
                        end
                end
                if ( tier==hiTier or tierFine ) then
                        plannedPoints = plannedPoints > 1 and plannedPoints-1 or nil;
                        tab[talentID] = plannedPoints;
                        tab.points = tabPoints-1;
                        saved.points = talentPoints-1;
                end
        end
        SpecialTalentFrame_Update();
end

function SpecialTalentFrameTalent_OnClick()
        local tabID, talentID = this.tabID, this:GetID();
        local force = SpecialTalentFrameSaved and SpecialTalentFrameSaved.forceShift;
        if ( SpecialTalentFrame.learnMode=="learned" ) then
                if ( not force or IsShiftKeyDown() ) then
                        LearnTalent(tabID, talentID);
                        SpecialTalentFrame_Update();
                end
        elseif ( SpecialTalentFrame.learnMode=="planned" ) then
                if ( IsShiftKeyDown() and force ) then
                        LearnTalent(tabID, talentID);
                        SpecialTalentFrame_Update();
                        return;
                end
                -- check if requirements met
                if ( not this.clickable ) then
                        return;
                end
                
                if ( arg1=="LeftButton" ) then
                        SpecialTalent_PlanTalent( tabID, talentID );
                elseif ( arg1=="RightButton" ) then
                        SpecialTalent_UnplanTalent( tabID, talentID );
                end
        end
        SpecialTalentButton_OnEnter();
end

function SpecialTalentFrameTalent_OnMouseWheel(value)
        -- check if requirements met
        if ( SpecialTalentFrame.learnMode=="planned" and this.clickable ) then
                if ( value > 0 ) then
                        SpecialTalent_PlanTalent( this.tabID, this:GetID() );
                elseif ( value < 0 ) then
                        SpecialTalent_UnplanTalent( this.tabID, this:GetID() );
                end
                SpecialTalentButton_OnEnter();
        end
end

-- Helper functions
function SpecialTalentFrame_UpdateTalentPoints()
        local cp1, cp2 = UnitCharacterPoints("player");
        SpecialTalentFrameUnspentPointsText:SetText(cp1);
        SpecialTalentFrame.talentPoints = cp1;
end

function SetSpecialTalentButtonLocation(button, tier, column)
        column = ((column - 1) * SPECIAL_TALENT_BUTTON_SIZE * 2) + INITIAL_SPECIAL_TALENT_OFFSET_X;
        tier = -((tier - 1) * SPECIAL_TALENT_BUTTON_SIZE * 1.75) - INITIAL_SPECIAL_TALENT_OFFSET_Y ;
        button:SetPoint("TOPLEFT", button:GetParent(), "TOPLEFT", column, tier);
end

function SpecialTalentFrame_ResetBranches(tab)
        for i=1, MAX_NUM_TALENT_TIERS do
                for j=1, NUM_TALENT_COLUMNS do
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].id = nil;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].up = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].down = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].left = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].right = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].rightArrow = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].leftArrow = 0;
                        SPECIAL_TALENT_BRANCH_ARRAY[tab][i][j].topArrow = 0;
                end
        end
end

function SpecialTalent_LoadPlannedSaved()
        local player= PlayerOfRealm;
        if ( not SpecialTalentPlannedSaved ) then
                SpecialTalentPlannedSaved={};
        end
        if ( not SpecialTalentPlannedSaved[player] ) then
                SpecialTalentPlannedSaved[player]={};
        end
        for t=1, MAX_TALENT_TABS do
                if ( GetTalentTabInfo(t) and not SpecialTalentPlannedSaved[player][t] ) then
                        SpecialTalentPlannedSaved[player].points = 0;
                        SpecialTalentPlannedSaved[player][t]={points=0};
                end
        end
end

function SpecialTalentFrameTabs_Initialize()
        for i=1, MAX_TALENT_TABS do
                local button = getglobal("SpecialTalentFrameTab"..i);
                if ( not button ) then
                        break;
                end
                local name, texture = GetTalentTabInfo(i);
                button.tooltip = name;
                button:SetNormalTexture(texture);
                if ( button:GetID() == SpecialTalentFrameSaved.tabShown ) then
                        button:SetChecked(1);
                end
        end
end

function SpecialTalentFrameTab_OnClick()
        for i=1, MAX_TALENT_TABS do
                local button = getglobal("SpecialTalentFrameTab"..i);
                if ( not button ) then
                        break;
                end
                button:SetChecked(0);
        end
        this:SetChecked(1);
        
        SpecialTalentFrameSaved.tabShown = this:GetID();
        SpecialTalentFrame_Update();
end

function SpecialTalentFrame_CheckDragged()
        if ( SpecialTalentFrameSaved and SpecialTalentFrameSaved.frameDragged == 1 ) then
                local left = SpecialTalentFrameSaved.frameLeft;
                local top = SpecialTalentFrameSaved.frameTop;
                SpecialTalentFrame:ClearAllPoints();
                SpecialTalentFrame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top);
                UIPanelWindows["SpecialTalentFrame"] = nil;
                SpecialTalentFrame_Toggle = SpecialTalentFrame_ToggleDragged;
                if ( SpecialTalentFrameSaved.frameMinimized ) then
                        SpecialTalentFrame_Minimize();
                else
                        SpecialTalentFrame_Maximize();
                end
        else
                SpecialTalentFrame_ResetDrag();
        end
end

function SpecialTalentFrame_ResetDrag()
        if ( SpecialTalentFrameSaved and SpecialTalentFrameSaved.frameDragged ) then
                SpecialTalentFrameSaved.frameDragged = nil;
                SpecialTalentFrameSaved.frameLeft = nil;
                SpecialTalentFrameSaved.frameTop = nil;
        end
        if ( SpecialTalentFrameSaved.frameMinimized ) then
                SpecialTalentFrame_Minimize();
        else
                SpecialTalentFrame_Maximize();
        end
        SpecialTalentFrame_Toggle = SpecialTalentFrame_ToggleFrame;
        SpecialTalentFrame_Toggle();
        SpecialTalentFrame_Toggle();
        --SpecialTalentFrame:Hide();
        --ShowUIPanel(SpecialTalentFrame);
end

function SpecialTalentFrame_SetForceShift( force )
        if ( force ) then
                if ( not SpecialTalentFrameSaved ) then
                        SpecialTalentFrameSaved = {};
                end
                SpecialTalentFrameSaved.forceShift = 1;
                SpecialTalentFrameForceShiftCheckButton:SetChecked(1);
        else
                if ( SpecialTalentFrameSaved and SpecialTalentFrameSaved.forceShift ) then
                        SpecialTalentFrameSaved.forceShift = nil;
                end
                SpecialTalentFrameForceShiftCheckButton:SetChecked(0);
        end
end

function SpecialTalent_GetTalentInfo(tabID, talentID, planned)
        -- Set the button info
        local name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq = GetTalentInfo(tabID, talentID);
        if ( SpecialTalentFrame.learnMode == "planned" or planned ) then
                rank = SpecialTalentPlannedSaved[PlayerOfRealm][tabID][talentID] or 0;
        end
        return name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq;
end

function SpecialTalent_GetTalentPrereqs(tabID, talentID, planned)
        local prereqs = {GetTalentPrereqs(tabID, talentID)};
        local i=1;
        while prereqs[i] do
                local tier, column, isLearnable = prereqs[i], prereqs[i+1], prereqs[i+2];
                local _, _, _, _, rank, maxRank = SpecialTalent_GetTalentInfo(tabID, SPECIAL_TALENT_BRANCH_ARRAY[tabID][tier][column].id, (SpecialTalentFrame.learnMode == "planned" or planned) );
                if ( rank==maxRank ) then
                        prereqs[i+2]=1;
                else
                        prereqs[i+2]=nil;
                end
                i=i+3;
        end
        return unpack(prereqs);
end

function SpecialTalentButton_OnEnter()
        local tabID, talentID = this.tabID, this:GetID()
        GameTooltip:Hide();
        GameTooltip:SetOwner(this, "ANCHOR_RIGHT");
        GameTooltip:SetTalent(tabID, talentID);
        if ( tp_webdata ) then -- requires either Talent Planner addon or WebData.lua in SpecialTalentUI folder
                local name, texture, row, col, rank, maxRank = GetTalentInfo( tabID, talentID );
                local class = SPECIAL_TALENT_CLASSES[UnitClass("player")];
                local goalRank = SpecialTalentPlannedSaved[PlayerOfRealm][tabID][talentID];
                if ( not goalRank or not class ) then return; end
                if ( (goalRank==1 and rank==0) or (goalRank==rank) ) then
                        local text=GameTooltipTextLeft2:GetText();
                        GameTooltipTextLeft2:SetText( text.."\n"..CYAN_FONT_COLOR_CODE..format(PLANNED_RANK, goalRank, maxRank).."|r" );
                elseif ( goalRank==rank+1 ) then
                        GameTooltipTextLeft5:SetText( format(PLANNED_RANK, goalRank, maxRank));
                        GameTooltipTextLeft5:SetTextColor(0, 1.0, 1.0);
                else
                        GameTooltip:AddLine( " " );
                        GameTooltip:AddLine( format(PLANNED_RANK, goalRank, maxRank), 0, 1.0, 1.0 );
                        GameTooltip:AddLine( tp_webdata[class][tabID][talentID][goalRank], 1.0, 0.82, 0.0, 1.0, 1 );
                end
                GameTooltip:Show();
        end
end

Generated by GNU Enscript 1.6.5.90.