vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--<< ====================================================================== >>--
-- Constants                                                                  --
--<< ====================================================================== >>--
ACETIMER.COLOR = {
        AQUA    = {0.0,1.0,1.0},
        BLUE    = {0.0,0.0,1.0},
        FUCHSIA = {1.0,0.0,1.0},
        GRAY    = {0.5,0.5,0.5},
        GREEN   = {0.0,0.5,0.0},
        LIME    = {0.0,1.0,0.0},
        MAROON  = {0.5,0.0,0.0},
        NAVY    = {0.0,0.0,0.5},
        OLIVE   = {0.5,0.5,0.0},
        PURPLE  = {0.5,0.0,0.5},
        RED     = {1.0,0.0,0.0},
        TEAL    = {0.0,0.5,0.5},
        WHITE   = {1.0,1.0,1.0},
        YELLOW  = {1.0,1.0,0.0},
}

ACETIMER.COLOR_MAP = {
        [0]="OLIVE",[1]="TEAL",[2]="PURPLE",[3]="GREEN",
}

ACETIMER.COMMANDS = { 
        ACETIMER.CMD_SHORT, 
        ACETIMER.CMD_LONG, 
}

ACETIMER.CMD_OPTIONS = {
        { option = ACETIMER.OPT_ANCHOR, method = "CareOptAnchor", desc = ACETIMER.OPT_ANCHOR_DESC,},
        { option = ACETIMER.OPT_SCALE,  method = "CareOptScale",  desc = ACETIMER.OPT_SCALE_DESC,},
        { option = ACETIMER.OPT_GROW,   method = "CareOptGrow",   desc = ACETIMER.OPT_GROW_DESC,},
}

ACETIMER.SPELL = 1
ACETIMER.EVENT = 2
ACETIMER.SKILL = 3

--<< ====================================================================== >>--
-- Class Setup                                                                --
--<< ====================================================================== >>--
AceTimer = AceAddon:new({
    name          = ACETIMER.NAME,
    description   = ACETIMER.DESCRIPTION, -- Optional; Ace will use the .toc Notes text
    version       = "R4",
    releaseDate   = "",
    aceCompatible = "102", -- Check ACE_COMP_VERSION in Ace.lua for current.
    author        = "Rasmus Kromann-Larsen",
    email         = "rasmus@kromann-larsen.dk",
    website       = "http://www.rasmuskl.dk",
    category      = "combat",
    --optionsFrame  = "AceTimerOptionsFrame",
    db            = AceDatabase:new("AceTimerDB"),
    --defaults      = DEFAULT_OPTIONS,
    cmd           = AceChatCmd:new(ACETIMER.COMMANDS, ACETIMER.CMD_OPTIONS),
        modules       = {},
})

function AceTimer:Initialize()
    -- Helpful closures for accessing the addon's currently loaded profile.
    self.GetOpt = function(var) return self.db:get(self.profilePath,var)    end
    self.SetOpt = function(var,val) self.db:set(self.profilePath,var,val)   end
    self.TogOpt = function(var) return self.db:toggle(self.profilePath,var) end

    self:InitModules()
end

function AceTimer:Enable()
        self.groups = {}
        self.timers = {}
        self.bars = {}
        for i = 1, 20 do self.bars[i] = {} end
        self.gs_patterns = {}
        self.gs_captured = {}
        self:InitAnchor()
        
        self:EnableModules()
end

function AceTimer:Disable()
        self:DisableModules()
        if self.gs_patterns then self.gs_patterns = nil end
        if self.gs_captured then self.gs_captured = nil end
        if self.bars then self:KillBars(); self.bars = nil end
        if self.timers then self.timers = nil end
        if self.groups then self.groups = nil end

        self:UnregisterAllEvents(self)
        self:UnhookAllScripts()
end

--<< ====================================================================== >>--
-- Timer Processing                                                           --
--<< ====================================================================== >>--
function AceTimer:AddGroup(id, forall, color)
        if color then 
                self.groups[id] = { fa=forall, cr=ACETIMER.COLOR[color] }
        else
                self.groups[id] = { fa=forall }
        end
end

function AceTimer:AddTimer(kind, name, duration, targeted, isgain, selforselect, extra)
        if not self.timers[kind]       then self.timers[kind] = {}       end
        if not self.timers[kind][name] then self.timers[kind][name] = {} end
        if not extra then extra = {} end
        targeted = (targeted > 0 ) and 1 or nil
        isgain = (isgain > 0 ) and 1 or nil
        selforselect = (selforselect > 0 ) and 1 or nil
        if extra.cr then extra.cr = ACETIMER.COLOR[extra.cr] 
        elseif extra.gr and self.groups[extra.gr].cr then extra.cr = self.groups[extra.gr].cr
        else
                local ccode = (targeted and 2 or 0) + (isgain and 1 or 0)
                extra.cr = ACETIMER.COLOR[ACETIMER.COLOR_MAP[ccode]]
        end
        self.timers[kind][name] = { d=duration, k={t=targeted,g=isgain,s=selforselect}, x=extra }
end

function AceTimer:StartTimer(timer, name, target, rank)
        if not target then target = "none" end
        if not rank then rank = timer.r or 0 end
        if timer.x.gr then self:CleanGroup(timer.x.gr, target) end
        if timer.x.ea then
                for name, valid in timer.x.ea do 
                        local event = self.timers[ACETIMER.EVENT][name]
                        event.r = rank
                        event.v = GetTime() + valid
                        if target ~= "none" then event.t = target end
                end
        end
        if timer.d == 0 then return end

        local id, slot = name.."-"..target 
        for i = 1, 20 do 
                if self.bars[i].id == id then 
                        TimexBar:StopBar(self.bars[i].id)
                        self:StopBar(self.bars[i].id)
                        break 
                end
        end
        for i = 1, 20 do if not self.bars[i].id then slot = i; break end end
        if not slot or not TimexBar:Get(id) then return end

        self.bars[slot].id     = id
        self.bars[slot].timer  = timer
        self.bars[slot].name   = name
        self.bars[slot].rank   = rank
        self.bars[slot].target = target
        self.bars[slot].group  = timer.x.gr
        self:SetPoint(id, slot)
        TimexBar:SetColor(id, unpack(timer.x.cr or ACETIMER.COLOR.GRAY))
        TimexBar:SetScale(id, self.GetOpt("Scale"))
        TimexBar:SetText(id, target == "none" and name or target)
        TimexBar:SetTexture(id, timer.x.tx or self:GetTexture(name, timer.x))
        TimexBar:SetFunction(id, self.StopBar, self, id)
        TimexBar:SetClickFunction(id, self.OnClick, self, id, timer.x.rc)
        TimexBar:Start(id, timer.x.d and self:GetDuration(timer.d, timer.x.d, rank) or timer.d)
end

function AceTimer:GetDuration(duration, record, rank)
        if record.rt then duration = record.rt[rank] or duration end
        if record.rs then duration = duration + (rank-1) * record.rs end
        if record.cp then duration = duration + (GetComboPoints() - 1) * record.cp end
        if record.tn then
                if type(record.tn) == "string" then record.tn = self:GetTalentPosition(record.tn) end
                local _,_,_,_, tr = GetTalentInfo(unpack(record.tn))
                if tr > 0 then
                        local gain = record.tt and record.tt[tr] or (record.tb + (tr-1) * (record.ts or record.tb))
                        duration = duration + (record.tp and (duration/100) * gain or gain)
                end
        end
        return duration
end

function AceTimer:GetTexture(name, record)
        if record.xn then name = record.xn end
        local i = 1
        while true do
                local nm = GetSpellName(i, BOOKTYPE_SPELL)
                if not nm then break
                elseif nm == name then
                        record.tx = GetSpellTexture(i, BOOKTYPE_SPELL)
                        return record.tx
                end
                i = i + 1
        end
        local tp = self:GetTalentPosition(name)
        if tp then 
                local _, tx = GetTalentInfo(unpack(tp))
                record.tx = tx
                return record.tx
        end
        if HasPetSpells() then
                for i =1, NUM_PET_ACTION_SLOTS do
                        local nm, _, tx = GetPetActionInfo(i)
                        if nm == name then record.tx = tx; return record.tx end
                end
        end
end

function AceTimer:GetTalentPosition(name)
        for i = 1, GetNumTalentTabs() do
                for j = 1, GetNumTalents(i) do
                        if GetTalentInfo(i, j) == name then return {i, j} end
                end
        end
end

--<< ====================================================================== >>--
-- Bar Processing                                                             --
--<< ====================================================================== >>--
function AceTimer:CleanGroup(group, target)
        local forall = self.groups[group].fa
        for i = 1, 20 do
                if self.bars[i].group and self.bars[i].group == group then
                        if forall then 
                                TimexBar:StopBar(self.bars[i].id)
                                self:StopBar(self.bars[i].id)
                        elseif self.bars[i].target == target then 
                                TimexBar:StopBar(self.bars[i].id)
                                self:StopBar(self.bars[i].id)
                                break
                        end
                end
        end
end

function AceTimer:SetPointTOP(id, slot)
        return TimexBar:SetPoint(id, "TOP", "AceTimerAnchor", "TOP", 0, (slot * 20))
end

function AceTimer:SetPointBOTTOM(id, slot)
        return TimexBar:SetPoint(id, "BOTTOM", "AceTimerAnchor", "BOTTOM", 0, (-5 - (slot * 20)))
end

function AceTimer:KillBar(name, unit)
        for i = 1, 20 do
                if self.bars[i].id then
                        if self.bars[i].name == name then
                                if not unit then 
                                        if self.bars[i].timer.k.t then unit = UnitName("player") else unit = "none" end
                                end
                                if self.bars[i].target == unit then
                                        TimexBar:StopBar(self.bars[i].id)
                                        return self:StopBar(self.bars[i].id)
                                end
                        end
                else 
                        break
                end
        end
end

function AceTimer:KillBars(unit)
        if unit and UnitExists("target") and UnitName("target") == unit and not UnitIsDeadOrGhost("target") then return end
        for i = 1, 20 do
                if self.bars[i].id then
                        if not unit or self.bars[i].target == unit then
                                TimexBar:StopBar(self.bars[i].id)
                                self:StopBar(self.bars[i].id)
                        end
                else
                        break
                end
        end
end

function AceTimer:StopBar(id)
        for i = 1, 20 do 
                if self.bars[i].id == id then 
                        for k in self.bars[i] do self.bars[i][k] = nil end
                end
        end
        for i = 1, 19 do
                if not self.bars[i].id then
                        local temp = self.bars[i]
                        for j = i + 1, 20 do
                                if self.bars[j].id then
                                        self.bars[i] = self.bars[j]; self.bars[j] = temp; temp = nil
                                        self:SetPoint(self.bars[i].id, i)
                                        break
                                end
                        end
                        if temp then break end
                end
        end
end

function AceTimer:OnClick(id, reactive)
        if arg1 == "RightButton" then
                TimexBar:StopBar(id) 
                self:StopBar(id)
        elseif arg1 == "LeftButton" and reactive then
                for i = 1, 20 do 
                        if self.bars[i].id == id then
                                if self.bars[i].rank > 0 then
                                        return CastSpellByName(string.format(ACETIMER.FMT_CAST, self.bars[i].name, self.bars[i].rank))
                                else
                                        return CastSpellByName(self.bars[i].name)
                                end
                        end
                end
        end
end

--<< ====================================================================== >>--
-- Globalstrings Processing                                                   --
--<< ====================================================================== >>--
-- self.gs_patterns
-- self.gs_captured
function AceTimer:FindGlobal(text, global)
        local gp = self.gs_patterns[global]
        if not gp then 
                self.gs_patterns[global] = {n=0,}
                gp = self.gs_patterns[global]
                gp.p = string.gsub(global, "([%[%]%(%)])", "%%%1")
                gp.p = string.gsub(gp.p, "%.$", "%%%.")
                gp.p = string.gsub(gp.p, "%%(%S-)([sd])", function (a1, a2) 
                        gp.n = gp.n + 1
                        local _,_, found = string.find(a1, "(%d)%$")
                        if found then gp[gp.n] = tonumber(found) else gp[gp.n] = gp.n end
                        if a2 == "s" then return "(.+)" 
                        elseif a2 == "d" then return "(%d+)" 
                        end
                end)
        end
        for k in self.gs_captured do self.gs_captured[k] = nil end
        return self:OrderCaptured(gp, string.find(text, gp.p))
end

function AceTimer:OrderCaptured(gp, s, e, ...)
        if s then 
                for i = 1, gp.n do self.gs_captured[gp[i]] = arg[i] end
                return TRUE
        end
end

--<< ====================================================================== >>--
-- Anchor Processing                                                          --
--<< ====================================================================== >>--
function AceTimer:InitAnchor()
        local test = {d=10, x={cr=ACETIMER.COLOR.GRAY, tx="Interface\\Icons\\INV_Misc_QuestionMark"} }
        AceTimerAnchorText:SetText(ACETIMER.NAME) 
        AceTimerAnchorTest:SetText(ACETIMER.WORD_TEST)
        AceTimerAnchorTest:SetScript("OnClick", function () return self:StartTimer(test, ACETIMER.WORD_TEST, "none", 0) end)
        AceTimerAnchorHide:SetText(ACETIMER.WORD_HIDE)
        AceTimerAnchorHide:SetScript("OnClick", function () return self:CareOptAnchor() end)
        if not self.GetOpt("Scale") then self.SetOpt("Scale", 0.80) end
        AceTimerAnchorScaleLow:SetText("0.5")
        AceTimerAnchorScaleHigh:SetText("1.5")
        AceTimerAnchorScale:SetScript("OnValueChanged", function() return self:OnScaleChanged() end)
        AceTimerAnchorScale:SetValue(self.GetOpt("Scale"))
        self:SetBarGrowth()
        if not self.GetOpt("AnchorHidden") then AceTimerAnchor:Show() end
end

function AceTimer:OnScaleChanged()
        local scale = AceTimerAnchorScale:GetValue()
        AceTimerAnchorScaleText:SetText(string.format("%.2f", scale))
        self.SetOpt("Scale", scale)
        for i = 1, 20 do
                if self.bars[i].id and TimexBar:CheckBar(self.bars[i].id) then 
                        TimexBar:SetScale(self.bars[i].id, scale) 
                end
        end
end

function AceTimer:OnTestClicked()
        self:StartBar(ACETIMER.WORD_TEST.." 1", "none", 0, nil, 15, nil, ACETIMER.COLOR.RED)
        self:StartBar(ACETIMER.WORD_TEST.." 2", "none", 0, nil, 10, nil, ACETIMER.COLOR.GREEN)
        self:StartBar(ACETIMER.WORD_TEST.." 3", "none", 0, nil,  5, nil, ACETIMER.COLOR.BLUE)
end

function AceTimer:SetBarGrowth()
        if self.GetOpt("GrowUp") then 
                self.SetPoint = self.SetPointTOP
        else
                self.SetPoint = self.SetPointBOTTOM
        end
end

--<< ====================================================================== >>--
-- Command Handlers                                                           --
--<< ====================================================================== >>--
function AceTimer:CareOptAnchor()
        if self.TogOpt("AnchorHidden") then 
                AceTimerAnchor:Hide() else AceTimerAnchor:Show() 
        end
end

function AceTimer:CareOptScale(scale)
        scale = tonumber(scale)
        if not scale then scale = 0.80 else 
                if scale < 0.50 then scale = 0.50 elseif scale > 1.50 then scale = 1.50 end
        end
        AceTimerAnchorScale:SetValue(tonumber(string.format("%.2f", scale)))
        self.cmd:result(ACETIMER.OPT_SCALE_TEXT, "|cfff5f530" .. string.format("%.2f", self.GetOpt("Scale")) .. "|r")
end

function AceTimer:CareOptGrow()
        self.cmd:result(ACETIMER.OPT_GROW_TEXT, ACETIMER.MAP_DOWNUP[self.TogOpt("GrowUp") or 0])
        self:SetBarGrowth()
end

function AceTimer:Report()
        self.cmd:report({})
        self.cmd:result(ACETIMER.OPT_SCALE_TEXT, "|cfff5f530" .. string.format("%.2f", self.GetOpt("Scale")) .. "|r")
        self.cmd:result(ACETIMER.OPT_GROW_TEXT, ACETIMER.MAP_DOWNUP[self.GetOpt("GrowUp") or 0])
        self:ReportModules()
end

--<< ====================================================================== >>--
-- Register the Addon                                                         --
--<< ====================================================================== >>--
AceTimer:RegisterForLoad()