vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: RosterLib-2.0
Revision: $Revision: 9839 $
X-ReleaseDate: "$Date: 2006-08-10 08:55:29 +0200 (Thu, 10 Aug 2006) $
Author: Maia (maia.proudmoore@gmail.com)
Website: http://wiki.wowace.com/index.php/RosterLib-2.0
Documentation: http://wiki.wowace.com/index.php/RosterLib-2.0_API_Documentation
SVN: http://svn.wowace.com/root/trunk/RosterLib-2.0/
Description: party/raid roster management
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
]]

local MAJOR_VERSION = "RosterLib-2.0"
local MINOR_VERSION = "$Revision: 9839 $"

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

local updatedUnits = {}
local unknownUnits = {}
local RosterLib = {}
local roster

------------------------------------------------
-- activate, enable, disable
------------------------------------------------

local function print(text)
        ChatFrame3:AddMessage(text)
end

local function activate(self, oldLib, oldDeactivate)
        RosterLib = self
        if oldLib then
                self.roster = oldLib.roster
                oldLib:UnregisterAllEvents()
                oldLib:CancelAllScheduledEvents()
        end
        if not self.roster then self.roster = {} end
        if oldDeactivate then oldDeactivate(oldLib) end
        roster = self.roster
end


local function external(self, major, instance)
        if major == "AceEvent-2.0" then
                AceEvent = instance
                AceEvent:embed(self)
                self:UnregisterAllEvents()
                self:CancelAllScheduledEvents()
                if AceEvent:IsFullyInitialized() then
                        self:AceEvent_FullyInitialized()
                else
                        self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true)
                end             
        elseif major == "Compost-2.0" then
                Compost = instance
        end
end


function RosterLib:Enable()
-- not used anymore, but as addons still might be calling this method, we're keeping it.
end


function RosterLib:Disable()
-- not used anymore, but as addons still might be calling this method, we're keeping it.
end

------------------------------------------------
-- Internal functions
------------------------------------------------

function RosterLib:AceEvent_FullyInitialized()
        self:TriggerEvent("RosterLib_Enabled")
        self:RegisterEvent("RAID_ROSTER_UPDATE","ScanFullRoster")
        self:RegisterEvent("PARTY_MEMBERS_CHANGED","ScanFullRoster")
        self:RegisterEvent("UNIT_PET","ScanPet")
        self:ScanFullRoster()
end


------------------------------------------------
-- Unit iterator
------------------------------------------------

local playersent, petsent, unitcount, petcount, pmem, rmem, unit

local function NextUnit()
        -- STEP 1: pet
        if not petsent then
                petsent = true
                if rmem == 0 then
                        unit = "pet"
                        if UnitExists(unit) then return unit end
                end
        end
        -- STEP 2: player
        if not playersent then
                playersent = true
                if rmem == 0 then
                        unit = "player"
                        if UnitExists(unit) then return unit end
                end
        end
        -- STEP 3: raid units
        if rmem > 0 then
                -- STEP 3a: pet units
                for i = petcount, rmem do
                        unit = string.format("raidpet%d", i)
                        petcount = petcount + 1
                        if UnitExists(unit) then return unit end
                end
                -- STEP 3b: player units
                for i = unitcount, rmem do
                        unit = string.format("raid%d", i)
                        unitcount = unitcount + 1
                        if UnitExists(unit) then return unit end
                end
        -- STEP 4: party units
        elseif pmem > 0 then
                -- STEP 3a: pet units
                for i = petcount, pmem do
                        unit = string.format("partypet%d", i)
                        petcount = petcount + 1
                        if UnitExists(unit) then return unit end
                end
                -- STEP 3b: player units
                for i = unitcount, pmem do
                        unit = string.format("party%d", i)
                        unitcount = unitcount + 1
                        if UnitExists(unit) then return unit end
                end
        end
end

local function UnitIterator()
        playersent, petsent, unitcount, petcount, pmem, rmem = false, false, 1, 1, GetNumPartyMembers(), GetNumRaidMembers()
        return NextUnit
end

------------------------------------------------
-- Roster code
------------------------------------------------


function RosterLib:ScanFullRoster()
        -- save all units we currently have, this way we can check who to remove from roster later.
        local temp = Compost and Compost:Acquire() or {}
        for name in pairs(roster) do 
                temp[name] = true
        end
        -- update data
        for unitid in UnitIterator() do
                local name = self:CreateOrUpdateUnit(unitid)
                -- we successfully added a unit, so we don't need to remove it next step
                if name then temp[name] = nil end
        end
        -- clear units we had in roster that either left the raid or are unknown for some reason.
        for name in pairs(temp) do
                self:RemoveUnit(name)
        end
        if Compost then Compost:Reclaim(temp) end
        self:ProcessRoster()
end


function RosterLib:ScanPet(owner)
        local unitid = self:GetPetFromOwner(owner)
        if not unitid then
                return
        elseif not UnitExists(unitid) then
                unknownUnits[unitid] = nil
                -- find the pet in the roster we need to delete
                for _,u in pairs(roster) do
                        if u.unitid == unitid then
                                self:RemoveUnit(u.name)
                        end
                end
        else
                self:CreateOrUpdateUnit(unitid)
        end
        self:ProcessRoster()
end


function RosterLib:GetPetFromOwner(id)
        -- convert party3 crap to raid IDs when in raid.
        local owner = self:GetUnitIDFromUnit(id)
        if not owner then
                return
        end
        -- get ID
        if string.find(owner,"raid") then
                return string.gsub(owner, "raid", "raidpet")
        elseif string.find(owner,"party") then
                return string.gsub(owner, "party", "partypet")
        elseif owner == "player" then
                return "pet"
        else
                return nil
        end
end


function RosterLib:ScanUnknownUnits()
        local name
        for unitid in pairs(unknownUnits) do 
                if UnitExists(unitid) then
                        name = self:CreateOrUpdateUnit(unitid)
                else
                        unknownUnits[unitid] = nil
                end
                -- some pets never have a name. too bad for them, farewell!
                if not name and string.find(unitid,"pet") then
                        unknownUnits[unitid] = nil
                end
        end
        self:ProcessRoster()
end


function RosterLib:ProcessRoster()
        if next(updatedUnits, nil) then
                self:TriggerEvent("RosterLib_RosterChanged", updatedUnits)
                for name in pairs(updatedUnits) do
                        local u = updatedUnits[name]
                        self:TriggerEvent("RosterLib_UnitChanged", u.unitid, u.name, u.class, u.subgroup, u.rank, u.oldname, u.oldunitid, u.oldclass, u.oldsubgroup, u.oldrank)
                        if Compost then Compost:Reclaim(updatedUnits[name]) end
                        updatedUnits[name] = nil
                end
        end
        if next(unknownUnits, nil) then
                self:CancelScheduledEvent("ScanUnknownUnits")
                self:ScheduleEvent("ScanUnknownUnits",self.ScanUnknownUnits, 1, self)
        end
end


function RosterLib:CreateOrUpdateUnit(unitid)
        local old = nil
        -- check for name
        local name = UnitName(unitid)
        if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING and not UnitIsCharmed(unitid) then
                -- clear stuff
                unknownUnits[unitid] = nil
                -- return if a pet attempts to replace a player name
                -- this doesnt fix the problem with 2 pets overwriting each other FIXME
                if string.find(unitid,"pet") then
                        if roster[name] and roster[name].class ~= "pet" then
                                return name
                        end
                end
                -- save old data if existing
                if roster[name] then
                        old          = Compost and Compost:Acquire() or {}
                        old.name     = roster[name].name
                        old.unitid   = roster[name].unitid
                        old.class    = roster[name].class
                        old.rank     = roster[name].rank
                        old.subgroup = roster[name].subgroup
                end
                -- object
                if not roster[name] then
                        roster[name] = Compost and Compost:Acquire() or {}
                end
                -- name
                roster[name].name = name
                -- unitid
                roster[name].unitid = unitid
                -- class
                if string.find(unitid,"pet") then
                        roster[name].class = "PET"
                else
                        _,roster[name].class = UnitClass(unitid)
                end
                -- subgroup and rank
                if GetNumRaidMembers() > 0 then
                        local _,_,num = string.find(unitid, "(%d+)")
                        _,roster[name].rank,roster[name].subgroup = GetRaidRosterInfo(num)
                else
                        roster[name].subgroup = 1
                        roster[name].rank = 0
                end
                -- compare data
                if not old
                or roster[name].name     ~= old.name
                or roster[name].unitid   ~= old.unitid
                or roster[name].class    ~= old.class
                or roster[name].subgroup ~= old.subgroup
                or roster[name].rank     ~= old.rank
                then
                        updatedUnits[name]             = Compost and Compost:Acquire() or {}
                        updatedUnits[name].oldname     = (old and old.name) or nil
                        updatedUnits[name].oldunitid   = (old and old.unitid) or nil
                        updatedUnits[name].oldclass    = (old and old.class) or nil
                        updatedUnits[name].oldsubgroup = (old and old.subgroup) or nil
                        updatedUnits[name].oldrank     = (old and old.rank) or nil
                        updatedUnits[name].name        = roster[name].name
                        updatedUnits[name].unitid      = roster[name].unitid
                        updatedUnits[name].class       = roster[name].class
                        updatedUnits[name].subgroup    = roster[name].subgroup
                        updatedUnits[name].rank        = roster[name].rank
                end
                -- compost our table
                if old and Compost then
                        Compost:Reclaim(old)
                end
                return name
        else
                unknownUnits[unitid] = true
                return false
        end
end


function RosterLib:RemoveUnit(name)
        updatedUnits[name]             = Compost and Compost:Acquire() or {}
        updatedUnits[name].oldname     = roster[name].name
        updatedUnits[name].oldunitid   = roster[name].unitid
        updatedUnits[name].oldclass    = roster[name].class
        updatedUnits[name].oldsubgroup = roster[name].subgroup
        updatedUnits[name].oldrank     = roster[name].rank
        if Compost then Compost:Reclaim(roster[name]) end
        roster[name] = nil
end


------------------------------------------------
-- API
------------------------------------------------

function RosterLib:GetUnitIDFromName(name)
        if roster[name] then
                return roster[name].unitid
        else
                return nil
        end
end


function RosterLib:GetUnitIDFromUnit(unit)
        local name = UnitName(unit)
        if name and roster[name] then
                return roster[name].unitid
        else
                return nil
        end
end


function RosterLib:GetUnitObjectFromName(name)
        if roster[name] then
                return roster[name]
        else
                return nil
        end
end


function RosterLib:GetUnitObjectFromUnit(unit)
        local name = UnitName(unit)
        if roster[name] then
                return roster[name]
        else
                return nil
        end
end


function RosterLib:IterateRoster(pets)
        local key
        return function()
                key = next(roster, key)
                if key and (pets or roster[key].class ~= "PET") then
                        return roster[key]
                end
        end
end


AceLibrary:Register(RosterLib, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)