vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: SpecialEvents-Aura-2.0
Revision: $Rev: 10488 $
Author: Tekkub Stoutwrithe (tekkub@gmail.com)
Website: http://wiki.wowace.com/index.php/SpecialEvents_Addon
Documentation: http://wiki.wowace.com/index.php/SpecialEvents-Aura-2.0
SVN: svn://svn.wowace.com/root/trunk/SpecialEventsEmbed/SpecialEvents-Aura-2.0
Description: Special events for Aura's, (de)buffs gained, lost etc.
Dependencies: AceLibrary, AceEvent-2.0, Gratuity-2.0
]]


local vmajor, vminor = "SpecialEvents-Aura-2.0", "$Revision: 10488 $"

if not AceLibrary then error(vmajor .. " requires AceLibrary.") end
if not AceLibrary:HasInstance("AceEvent-2.0") then error(vmajor .. " requires AceEvent-2.0.") end
if not AceLibrary:IsNewVersion(vmajor, vminor) then return end

local lib = {}
AceLibrary("AceEvent-2.0"):embed(lib)

local gratuity = AceLibrary("Gratuity-2.0")
local RL
local debuffTextureCache = {}
local buffTextureCache = {}


----------------------------
--      Compost Heap      --
----------------------------

local heap = {}
setmetatable(heap, {__mode = "kv"})


local function acquire(a1,a2,a3,a4,a5)
        local t = next(heap)
        if t then
                heap[t] = nil
                assert(not next(t), "A table in the compost heap has been modified!")
        end
        t = t or {}

        if a1 ~= nil then table.insert(t, a1) end
        if a2 ~= nil then table.insert(t, a2) end
        if a3 ~= nil then table.insert(t, a3) end
        if a4 ~= nil then table.insert(t, a4) end
        if a5 ~= nil then table.insert(t, a5) end
        return t
end


local function reclaim(t, d)
        if type(t) ~= "table" then return end

        if d and d > 0 then
                for i in pairs(t) do
                        if type(t[i]) == "table" then reclaim(t[i], d - 1) end
                end
        end

        for i in pairs(t) do t[i] = nil end
        t.reset = 1
        t.reset = nil
        table.setn(t, 0)

        heap[t] = true
end


----------------------------
--     Initialization     --
----------------------------

local function registerevents(self)
        self:RegisterEvent("PLAYER_LEAVING_WORLD")
        self:RegisterEvent("UNIT_AURA", "AuraScan")
        self:RegisterEvent("UNIT_AURASTATE", "AuraScan")
        self:RegisterEvent("PLAYER_AURAS_CHANGED")
        self:RegisterEvent("PLAYER_TARGET_CHANGED")
        self:RegisterEvent("PLAYER_REGEN_ENABLED")
        -- check if RosterLib exists. We need to do that here and not earlier.
        RL = AceLibrary:HasInstance("RosterLib-2.0") and AceLibrary("RosterLib-2.0") or nil
        if RL then 
                RL:Enable()
                self:RegisterBucketEvent("RosterLib_UnitChanged",0.2)
        else
                self:RegisterEvent("PARTY_MEMBERS_CHANGED",1)
                self:RegisterEvent("RAID_ROSTER_UPDATE",1)
        end
end


-- Activate a new instance of this library
function activate(self, oldLib, oldDeactivate)
        if oldLib then
                self.vars = oldLib.vars
                if oldLib:IsEventRegistered("UNIT_AURA") then registerevents(self) end
                if oldLib:IsEventRegistered("RosterLib_UnitChanged") then
                        if RL then RL:Disable() end
                        oldLib:UnregisterAllEvents()
                end     
        else self.vars = {buffs = {}, debuffs = {}} end

        self:RegisterEvent("PLAYER_ENTERING_WORLD")
        
        if oldDeactivate then oldDeactivate(oldLib) end
end


function lib:PLAYER_ENTERING_WORLD()
        self:ScanAllAuras()
        registerevents(self)
end


function lib:PLAYER_LEAVING_WORLD()
        self:UnregisterAllEvents()
        self:RegisterEvent("PLAYER_ENTERING_WORLD")
        if RL then 
                RL:Disable()
        end
end


--------------------------------
--      Tracking methods      --
--------------------------------

function lib:PLAYER_AURAS_CHANGED()
        self:AuraScan("player")
        if GetNumRaidMembers() > 0 then
                local u
                for i=1,GetNumRaidMembers() do
                        if UnitIsUnit("raid"..i, "player") then u = "raid"..i end
                end
                self:AuraScan(u)
        end
end


function lib:PLAYER_TARGET_CHANGED()
        self:AuraScan("target")
        self:TriggerEvent("SpecialEvents_AuraTargetChanged")
end


function lib:RosterLib_UnitChanged(units)
        for unit in pairs(units) do
                if unit and UnitExists(unit) then
                        self:AuraScan(unit)
                end
        end
        if GetNumRaidMembers() > 0 then
                self:TriggerEvent("SpecialEvents_AuraRaidRosterUpdate")
        else
                self:TriggerEvent("SpecialEvents_AuraPartyMembersChanged")
        end
end


function lib:PARTY_MEMBERS_CHANGED()
        if UnitExists("pet") then self:AuraScan("pet") end

        for i=1,4 do
                if UnitExists("party"..i) then self:AuraScan("party"..i) end
                if UnitExists("partypet"..i) then self:AuraScan("partypet"..i) end
        end
        self:TriggerEvent("SpecialEvents_AuraPartyMembersChanged")
end


function lib:RAID_ROSTER_UPDATE()
        for i=1,40 do
                if UnitExists("raid"..i) then self:AuraScan("raid"..i) end
                if UnitExists("raidpet"..i) then self:AuraScan("raidpet"..i) end
        end
        self:TriggerEvent("SpecialEvents_AuraRaidRosterUpdate")
end

function lib:PLAYER_REGEN_ENABLED()
        reclaim(debuffTextureCache)
        debuffTextureCache = acquire()
        reclaim(buffTextureCache)
        buffTextureCache = acquire()
end

function lib:ScanAllAuras()
        if UnitExists("player") then self:AuraScan("player") end
        if UnitExists("pet") then self:AuraScan("pet") end

        for i=1,4 do
                if UnitExists("party"..i) then self:AuraScan("party"..i) end
                if UnitExists("partypet"..i) then self:AuraScan("partypet"..i) end
        end

        for i=1,40 do
                if UnitExists("raid"..i) then self:AuraScan("raid"..i) end
                if UnitExists("raidpet"..i) then self:AuraScan("raidpet"..i) end
        end

        if UnitExists("target") then self:AuraScan("target") end
--~~    if UnitExists("mouseover") then self:AuraScan("mouseover") end
end


function lib:AuraScan(targ)
        local maxbuffs, maxdebuffs, t = 16, 16, targ or arg1
        local oldd, oldb = self.vars.debuffs[t], self.vars.buffs[t]
        local newd, newb = acquire(), acquire()

        if t == "player" then
                for i=0,(maxbuffs-1) do
                        local bidx = GetPlayerBuff(i, "HELPFUL")
                        if bidx and bidx ~= -1 then
                                gratuity:SetPlayerBuff(bidx)
                                local txt = gratuity:GetLine(1)
                                if txt then newb[txt] = i end
                        end
                end

                for i=0,(maxdebuffs-1) do
                        local didx = GetPlayerBuff(i, "HARMFUL")
                        if didx and didx ~= -1 then
                                gratuity:SetPlayerBuff(didx)
                                local txt, txtr = gratuity:GetLine(1)

                                local apps = GetPlayerBuffApplications(didx)
                                local texture = GetPlayerBuffTexture(didx)
                                local dbtype = GetPlayerBuffDispelType(didx)

                                if txt then
                                        local txtindex = txt
                                        if texture then txtindex = txtindex..texture end
                                        newd[txtindex] = acquire(i, txt, apps, dbtype, texture)
                                end
                        end
                end
        else
                for i=1,maxbuffs do
                        local txt
                        local texture, apps = UnitBuff (t, i)
                        if texture then 
                                if not buffTextureCache[texture] then
                                        gratuity:SetUnitBuff(t,i)
                                        txt = gratuity:GetLine(1)
                                        buffTextureCache[texture] = txt
                                else
                                        txt = buffTextureCache[texture]
                                end
                                if txt then newb[txt] = i end
                        end
                end

                for i=1,maxdebuffs do
                        local txt
                        local texture, apps, dbtype = UnitDebuff (t, i)
                        if texture then
                                if not debuffTextureCache[texture] then
                                        gratuity:SetUnitDebuff(t,i)
                                        txt = gratuity:GetLine(1)
                                        debuffTextureCache[texture] = txt
                                else
                                        txt = debuffTextureCache[texture]
                                end
                                if txt then
                                        local txtindex = txt .. texture
                                        reclaim(newd[txtindex])
                                        newd[txtindex] = acquire(i, txt, apps, dbtype, texture)
                                end
                        end
                end
        end

        self.vars.buffs[t] = newb
        self.vars.debuffs[t] = newd

        if oldb then
                for b,i in oldb do
                        if not newb[b] then
                                self:TriggerEvent("SpecialEvents_UnitBuffLost", t, b)
                                if t == "player" then self:TriggerEvent("SpecialEvents_PlayerBuffLost", b) end
                        end
                end
        end

        if oldd then
                for d,i in pairs(oldd) do
                        if type(i) ~= "table" then
                                local t = ""
                                for d,i in pairs(oldd) do t = t.." "..d.."="..i end
                                error(string.format("Debuff error! Unit: %s, Table dump:%s", targ, t))
                        elseif not newd[d] then
                                self:TriggerEvent("SpecialEvents_UnitDebuffLost", t, i[2], i[3], i[4], i[5])
                                if t == "player" then self:TriggerEvent("SpecialEvents_PlayerDebuffLost", i[2], i[3], i[4], i[5]) end
                        end
                end
        end

        for b,i in pairs(newb) do
                if (not oldb or not oldb[b]) then
                        self:TriggerEvent("SpecialEvents_UnitBuffGained", t, b)
                        if t == "player" then self:TriggerEvent("SpecialEvents_PlayerBuffGained", b, i) end
                end
        end

        for d,i in pairs(newd) do
                assert(type(i) == "table", string.format("Debuff: %s - Value not a table: %s", d, type(i) == "table" and "table" or i or "nil"))

                local o2 = oldd and type(oldd[d]) == "table" and oldd[d][2]
                if not o2 or o2 ~= i[2] then
                        self:TriggerEvent("SpecialEvents_UnitDebuffGained", t, i[2], i[3], i[4], i[5])
                        if t == "player" then self:TriggerEvent("SpecialEvents_PlayerDebuffGained", i[2], i[3], i[4], i[5]) end
                end
        end

        reclaim(oldb)
        reclaim(oldd, 1)
end


-----------------------------
--      Query Methods      --
-----------------------------

function lib:UnitHasBuff(targ, buff)
        if self.vars.buffs[targ] then return self.vars.buffs[targ][buff] end
end


function lib:UnitHasDebuff(targ, debuff)
        if self.vars.debuffs[targ] then
                for i, v in pairs(self.vars.debuffs[targ]) do
                        if v[2] == debuff then
                                return v[1], v[3]
                        end
                end
        end
end


function lib:UnitHasDebuffType(targ, debufftype)
        if self.vars.debuffs[targ] then
                for i,v in pairs(self.vars.debuffs[targ]) do
                        if v[4] == debufftype then return v end
                end
        end
end


function lib:BuffIter(unitid)
        local f = function(unitid, i)
                if not self.vars.buffs[unitid] then return end
                local idx = next(self.vars.buffs[unitid], i)
                local v = self.vars.buffs[unitid][idx]
                if v then return idx, v end
        end
        return f, unitid, nil
end

function lib:DebuffIter(unitid)
        local idx = nil
        local f = function(unitid)
                if not self.vars.debuffs[unitid] then return end
                idx = next(self.vars.debuffs[unitid], idx)
                local v = self.vars.debuffs[unitid][idx]
                if v then return v[2], v[3], v[4], v[5] end
        end
        return f, unitid, nil
end

--------------------------------
--      Load this bitch!      --
--------------------------------
AceLibrary:Register(lib, vmajor, vminor, activate)