426 lines
9.9 KiB
Lua
426 lines
9.9 KiB
Lua
--[[
|
|
Name: Roster-2.1
|
|
Revision: $Revision: 21071 $
|
|
X-ReleaseDate: $Date: 2006-12-17 13:56:50 -0500 (Sun, 17 Dec 2006) $
|
|
Author: Maia (maia.proudmoore@gmail.com)
|
|
Website: http://www.wowace.com/
|
|
Documentation: http://www.wowace.com/wiki/Roster-2.1
|
|
SVN: http://svn.wowace.com/wowace/trunk/RosterLib/
|
|
Description: Party/raid roster management
|
|
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
|
|
]]
|
|
|
|
local MAJOR_VERSION = "Roster-2.1"
|
|
local MINOR_VERSION = "$Revision: 21071 $"
|
|
|
|
if not AceLibrary then error(MAJOR_VERSION .. " 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 unknownUnits = {}
|
|
local Lib = {}
|
|
local roster
|
|
local new, del
|
|
do
|
|
local cache = setmetatable({}, {__mode='k'})
|
|
function new()
|
|
local t = next(cache)
|
|
if t then
|
|
cache[t] = nil
|
|
return t
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
function del(t)
|
|
for k in pairs(t) do
|
|
t[k] = nil
|
|
end
|
|
cache[t] = true
|
|
return nil
|
|
end
|
|
end
|
|
|
|
------------------------------------------------
|
|
-- activate, enable, disable
|
|
------------------------------------------------
|
|
|
|
local function activate(self, oldLib, oldDeactivate)
|
|
if oldLib then
|
|
if oldLib.roster then self.roster = oldLib.roster end
|
|
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
|
|
instance:embed(self)
|
|
if instance:IsFullyInitialized() then
|
|
self:AceEvent_FullyInitialized()
|
|
else
|
|
self:RegisterEvent("AceEvent_FullyInitialized")
|
|
end
|
|
end
|
|
end
|
|
|
|
------------------------------------------------
|
|
-- Internal functions
|
|
------------------------------------------------
|
|
|
|
function Lib:AceEvent_FullyInitialized()
|
|
self:TriggerEvent("RosterLib_Enabled")
|
|
|
|
self:RegisterBucketEvent({"RAID_ROSTER_UPDATE", "PARTY_MEMBERS_CHANGED"}, 0.2, "ScanFullRoster")
|
|
self:RegisterBucketEvent("UNIT_PET", 0.2, "ScanPet")
|
|
|
|
self:ScanFullRoster()
|
|
end
|
|
|
|
|
|
------------------------------------------------
|
|
-- Unit iterator
|
|
------------------------------------------------
|
|
|
|
local UnitIterator
|
|
do
|
|
local rmem, pmem, step, count
|
|
local function SelfIterator()
|
|
while step do
|
|
local unit
|
|
if step == 1 then
|
|
-- STEP 1: player
|
|
unit = "player"
|
|
step = 2
|
|
elseif step == 2 then
|
|
-- STEP 2: pet
|
|
unit = "pet"
|
|
step = nil
|
|
end
|
|
if unit and UnitExists(unit) then return unit end
|
|
end
|
|
end
|
|
|
|
local function SelfAndPartyIterator()
|
|
while step do
|
|
local unit
|
|
if step <= 2 then
|
|
unit = SelfIterator()
|
|
if not step then step = 3 end
|
|
elseif step == 3 then
|
|
-- STEP 3: party units
|
|
unit = string.format("party%d", count)
|
|
step = 4
|
|
elseif step == 4 then
|
|
-- STEP 4: party pets
|
|
unit = string.format("partypet%d", count)
|
|
count = count + 1
|
|
step = count <= pmem and 3 or nil
|
|
end
|
|
if unit and UnitExists(unit) then return unit end
|
|
end
|
|
end
|
|
|
|
local function RaidIterator()
|
|
while step do
|
|
local unit
|
|
if step == 1 then
|
|
-- STEP 1: raid units
|
|
unit = string.format("raid%d", count)
|
|
step = 2
|
|
elseif step == 2 then
|
|
-- STEP 2: raid pets
|
|
unit = string.format("raidpet%d", count)
|
|
count = count + 1
|
|
step = count <= rmem and 1 or nil
|
|
end
|
|
if unit and UnitExists(unit) then return unit end
|
|
end
|
|
end
|
|
|
|
function UnitIterator()
|
|
rmem = GetNumRaidMembers()
|
|
step = 1
|
|
if rmem == 0 then
|
|
pmem = GetNumPartyMembers()
|
|
if pmem == 0 then
|
|
return SelfIterator, false
|
|
else
|
|
count = 1
|
|
return SelfAndPartyIterator, false
|
|
end
|
|
else
|
|
count = 1
|
|
return RaidIterator, true
|
|
end
|
|
end
|
|
end
|
|
|
|
------------------------------------------------
|
|
-- Roster code
|
|
------------------------------------------------
|
|
|
|
local rosterScanCache = {}
|
|
function Lib:ScanFullRoster()
|
|
local changed
|
|
local it, isInRaid = UnitIterator()
|
|
-- save all units we currently have, this way we can check who to remove from roster later.
|
|
for name in pairs(roster) do
|
|
rosterScanCache[name] = true
|
|
end
|
|
-- update data
|
|
for unitid in it do
|
|
local name = self:CreateOrUpdateUnit(unitid, isInRaid)
|
|
-- we successfully added a unit, so we don't need to remove it next step
|
|
if name then
|
|
rosterScanCache[name] = nil
|
|
changed = true
|
|
end
|
|
end
|
|
-- clear units we had in roster that either left the raid or are unknown for some reason.
|
|
for name in pairs(rosterScanCache) do
|
|
self:RemoveUnit(name)
|
|
rosterScanCache[name] = nil
|
|
changed = true
|
|
end
|
|
self:ProcessRoster()
|
|
if changed then
|
|
self:TriggerEvent("RosterLib_RosterUpdated")
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function Lib:ScanPet(owner_list)
|
|
local changed
|
|
for owner in pairs(owner_list) do
|
|
local unitid = self:GetPetFromOwner(owner)
|
|
if not unitid then return end
|
|
|
|
if 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)
|
|
changed = true
|
|
end
|
|
end
|
|
else
|
|
_, changed = self:CreateOrUpdateUnit(unitid)
|
|
end
|
|
self:ProcessRoster()
|
|
end
|
|
if changed then
|
|
self:TriggerEvent("RosterLib_RosterUpdated")
|
|
end
|
|
end
|
|
|
|
|
|
function Lib: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 owner:find("raid") then
|
|
return owner:gsub("raid", "raidpet")
|
|
elseif owner:find("party") then
|
|
return owner:gsub("party", "partypet")
|
|
elseif owner == "player" then
|
|
return "pet"
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:ScanUnknownUnits()
|
|
local changed
|
|
for unitid in pairs(unknownUnits) do
|
|
local name, c
|
|
if UnitExists(unitid) then
|
|
name, c = self:CreateOrUpdateUnit(unitid)
|
|
else
|
|
unknownUnits[unitid] = nil
|
|
end
|
|
-- some pets never have a name. too bad for them, farewell!
|
|
if not name and unitid:find("pet") then
|
|
unknownUnits[unitid] = nil
|
|
else
|
|
changed = changed or c
|
|
end
|
|
end
|
|
self:ProcessRoster()
|
|
if changed then
|
|
self:TriggerEvent("RosterLib_RosterUpdated")
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:ProcessRoster()
|
|
if next(unknownUnits, nil) then
|
|
self:CancelScheduledEvent("ScanUnknownUnits")
|
|
self:ScheduleEvent("ScanUnknownUnits", self.ScanUnknownUnits, 1, self)
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:CreateOrUpdateUnit(unitid, isInRaid)
|
|
-- check for name
|
|
local name = UnitName(unitid)
|
|
if name and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING and not UnitIsCharmed(unitid) then
|
|
local unit = roster[name]
|
|
local isPet = unitid:find("pet")
|
|
|
|
-- 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 isPet and unit and unit.class ~= "PET" then
|
|
return name
|
|
end
|
|
-- save old data if existing
|
|
local old_name, old_unitid, old_class, old_rank, old_subgroup, old_online
|
|
if unit then
|
|
old_name = unit.name
|
|
old_unitid = unit.unitid
|
|
old_class = unit.class
|
|
old_rank = unit.rank
|
|
old_subgroup = unit.subgroup
|
|
old_online = unit.online
|
|
else
|
|
-- object
|
|
unit = new()
|
|
roster[name] = unit
|
|
end
|
|
|
|
local new_name, new_unitid, new_class, new_rank, new_subgroup, new_online
|
|
|
|
-- name
|
|
new_name = name
|
|
-- unitid
|
|
new_unitid = unitid
|
|
-- class
|
|
if isPet then
|
|
new_class = "PET"
|
|
else
|
|
_, new_class = UnitClass(unitid)
|
|
end
|
|
if isInRaid == nil and GetNumRaidMembers() > 0 then
|
|
isInRaid = true
|
|
end
|
|
|
|
-- subgroup and rank
|
|
new_subgroup = 1
|
|
new_rank = 0 -- XXX Support party leader?
|
|
if isInRaid then
|
|
local _,_,num = unitid:find("(%d+)")
|
|
if num then
|
|
_, new_rank, new_subgroup = GetRaidRosterInfo(num)
|
|
end
|
|
end
|
|
-- online/offline status
|
|
new_online = UnitIsConnected(unitid)
|
|
|
|
-- compare data
|
|
if not old_name
|
|
or new_name ~= old_name
|
|
or new_unitid ~= old_unitid
|
|
or new_class ~= old_class
|
|
or new_subgroup ~= old_subgroup
|
|
or new_rank ~= old_rank
|
|
or new_online ~= old_online
|
|
then
|
|
unit.name = new_name
|
|
unit.unitid = new_unitid
|
|
unit.class = new_class
|
|
unit.subgroup = new_subgroup
|
|
unit.rank = new_rank
|
|
unit.online = new_online
|
|
self:TriggerEvent("RosterLib_UnitChanged",
|
|
new_unitid, new_name, new_class, new_subgroup, new_rank,
|
|
old_name, old_unitid, old_class, old_subgroup, old_rank)
|
|
return name, true
|
|
end
|
|
return name
|
|
else
|
|
unknownUnits[unitid] = true
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:RemoveUnit(name)
|
|
local r = roster[name]
|
|
roster[name] = nil
|
|
self:TriggerEvent("RosterLib_UnitChanged",
|
|
nil, nil, nil, nil, nil,
|
|
r.name, r.unitid, r.class, r.subgroup, r.rank)
|
|
r = del(r)
|
|
end
|
|
|
|
|
|
------------------------------------------------
|
|
-- API
|
|
------------------------------------------------
|
|
|
|
function Lib:GetUnitIDFromName(name)
|
|
if roster[name] then
|
|
return roster[name].unitid
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:GetUnitIDFromUnit(unit)
|
|
local name = UnitName(unit)
|
|
if name and roster[name] then
|
|
return roster[name].unitid
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
|
|
function Lib:GetUnitObjectFromName(name)
|
|
return roster[name]
|
|
end
|
|
|
|
|
|
function Lib:GetUnitObjectFromUnit(unit)
|
|
local name = UnitName(unit)
|
|
return roster[name]
|
|
end
|
|
|
|
|
|
local function iter(t)
|
|
local key = t.key
|
|
local pets = t.pets
|
|
repeat
|
|
key = next(roster, key)
|
|
if not key then
|
|
t = del(t)
|
|
return nil
|
|
end
|
|
until (pets or roster[key].class ~= "PET")
|
|
t.key = key
|
|
return roster[key]
|
|
end
|
|
|
|
function Lib:IterateRoster(pets)
|
|
local t = new()
|
|
t.pets = pets
|
|
return iter, t
|
|
end
|
|
|
|
AceLibrary:Register(Lib, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|
|
|