WoWInterface SVN LibResInfo

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /tags
    from Rev 72 to Rev 90
    Reverse comparison

Rev 72 → Rev 90

22/LibResInfo-1.0/LibResInfo-1.0.lua New file
0,0 → 1,703
--[[--------------------------------------------------------------------
LibResInfo-1.0
Library to provide information about resurrections in your group.
Copyright (c) 2012-2014 Phanx. All rights reserved.
See the accompanying README and LICENSE files for more information.
http://www.wowinterface.com/downloads/info21467-LibResInfo-1.0.html
http://wow.curseforge.com/addons/libresinfo/
------------------------------------------------------------------------
TODO:
* Handle Reincarnation with some guesswork?
* Clear data when releasing spirit
----------------------------------------------------------------------]]
 
local DEBUG_LEVEL = GetAddOnMetadata("LibResInfo-1.0", "Version") and 1 or 0
local DEBUG_FRAME = ChatFrame3
 
------------------------------------------------------------------------
 
local MAJOR, MINOR = "LibResInfo-1.0", 22
assert(LibStub, MAJOR.." requires LibStub")
assert(LibStub("CallbackHandler-1.0"), MAJOR.." requires CallbackHandler-1.0")
local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
 
------------------------------------------------------------------------
 
local callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
local eventFrame = lib.eventFrame or CreateFrame("Frame")
 
local guidFromUnit = lib.guidFromUnit or {} -- t[unit] = guid -- table lookup is faster than calling UnitGUID
local nameFromGUID = lib.nameFromGUID or {} -- t[guid] = name
local unitFromGUID = lib.unitFromGUID or {} -- t[guid] = unit
 
local castingSingle = lib.castingSingle or {} -- t[casterGUID] = { startTime = <number>, endTime = <number>, target = <guid> }
local castingMass = lib.castingMass or {} -- t[casterGUID] = endTime
local hasPending = lib.hasPending or {} -- t[targetGUID] = endTime
 
local hasSoulstone = lib.hasSoulstone or {} -- t[targetGUID] = <boolean>
local isDead = lib.isDead or {} -- t[targetGUID] = <boolean>
local isGhost = lib.isGhost or {} -- t[targetGUID] = <boolean>
 
------------------------------------------------------------------------
 
lib.callbacks = callbacks
lib.eventFrame = eventFrame
 
lib.guidFromUnit = guidFromUnit
lib.nameFromGUID = nameFromGUID
lib.unitFromGUID = unitFromGUID
 
lib.castingSingle = castingSingle
lib.castingMass = castingMass
lib.hasPending = hasPending
 
lib.hasSoulstone = hasSoulstone
lib.isDead = isDead
lib.isGhost = isGhost
 
------------------------------------------------------------------------
 
local RESURRECT_PENDING_TIME = 60
local RELEASE_PENDING_TIME = 360
local RECENTLY_MASS_RESURRECTED = GetSpellInfo(95223)
local SOULSTONE = GetSpellInfo(20707)
 
local resSpells = {
[2008] = GetSpellInfo(2008), -- Ancestral Spirit (shaman)
[8342] = GetSpellInfo(8342), -- Defibrillate (item: Goblin Jumper Cables)
[22999] = GetSpellInfo(22999), -- Defibrillate (item: Goblin Jumper Cables XL)
[54732] = GetSpellInfo(54732), -- Defibrillate (item: Gnomish Army Knife)
[54732] = GetSpellInfo(164729), -- Defibrillate (item: Ultimate Gnomish Army Knife)
[126393] = GetSpellInfo(126393), -- Eternal Guardian (hunter pet: quilien)
[61999] = GetSpellInfo(61999), -- Raise Ally (death knight)
[20484] = GetSpellInfo(20484), -- Rebirth (druid)
[7328] = GetSpellInfo(7328), -- Redemption (paladin)
[2006] = GetSpellInfo(2006), -- Resurrection (priest)
[115178] = GetSpellInfo(115178), -- Resuscitate (monk)
[50769] = GetSpellInfo(50769), -- Revive (druid)
[982] = GetSpellInfo(982), -- Revive Pet (hunter)
[20707] = GetSpellInfo(20707), -- Soulstone (warlock)
[83968] = GetSpellInfo(83968), -- Mass Resurrection
}
 
------------------------------------------------------------------------
 
local next, pairs, GetNumGroupMembers, GetTime, IsInGroup, IsInRaid, UnitAura, UnitCastingInfo, UnitGUID, UnitHasIncomingResurrection, UnitHealth, UnitIsConnected, UnitIsDead, UnitIsDeadOrGhost, UnitIsGhost, UnitName
= next, pairs, GetNumGroupMembers, GetTime, IsInGroup, IsInRaid, UnitAura, UnitCastingInfo, UnitGUID, UnitHasIncomingResurrection, UnitHealth, UnitIsConnected, UnitIsDead, UnitIsDeadOrGhost, UnitIsGhost, UnitName
 
------------------------------------------------------------------------
 
local function debug(level, text, ...)
if level <= DEBUG_LEVEL then
if ... then
if type(text) == "string" and strfind(text, "%%[dfqsx%d%.]") then
text = format(text, ...)
else
text = strjoin(" ", tostringall(text, ...))
end
else
text = tostring(text)
end
DEBUG_FRAME:AddMessage("|cff00ddba[LRI]|r " .. text)
end
end
 
local newTable, remTable
do
local pool = {}
function newTable()
local t = next(pool)
if t then
pool[t] = nil
return t
end
return {}
end
function remTable(t)
pool[wipe(t)] = true
return nil
end
end
 
------------------------------------------------------------------------
 
lib.callbacksInUse = lib.callbacksInUse or {}
 
eventFrame:SetScript("OnEvent", function(self, event, ...)
return self[event] and self[event](self, event, ...)
end)
 
function callbacks:OnUsed(lib, callback)
if not next(lib.callbacksInUse) then
debug(1, "Callbacks in use! Starting up...")
eventFrame:RegisterEvent("GROUP_ROSTER_UPDATE")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("INCOMING_RESURRECT_CHANGED")
eventFrame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
eventFrame:RegisterEvent("UNIT_SPELLCAST_START")
eventFrame:RegisterEvent("UNIT_SPELLCAST_STOP")
eventFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
eventFrame:RegisterEvent("UNIT_AURA")
eventFrame:RegisterEvent("UNIT_CONNECTION")
eventFrame:RegisterEvent("UNIT_HEALTH")
eventFrame:GROUP_ROSTER_UPDATE("OnUsed")
end
lib.callbacksInUse[callback] = true
end
 
function callbacks:OnUnused(lib, callback)
lib.callbacksInUse[callback] = nil
if not next(lib.callbacksInUse) then
debug(1, "No callbacks in use. Shutting down...")
eventFrame:UnregisterAllEvents()
eventFrame:Hide()
wipe(guidFromUnit)
wipe(nameFromGUID)
wipe(unitFromGUID)
for caster, data in pairs(castingSingle) do
castingSingle[caster] = remTable(data)
end
wipe(castingMass)
wipe(hasPending)
wipe(hasSoulstone)
wipe(isDead)
wipe(isGhost)
end
end
 
------------------------------------------------------------------------
 
function lib.RegisterAllCallbacks(handler, method, includeMassRes)
lib.RegisterCallback(handler, "LibResInfo_ResCastStarted", method)
lib.RegisterCallback(handler, "LibResInfo_ResCastCancelled", method)
lib.RegisterCallback(handler, "LibResInfo_ResCastFinished", method)
 
if includeMassRes then
lib.RegisterCallback(handler, "LibResInfo_MassResStarted", method)
lib.RegisterCallback(handler, "LibResInfo_MassResCancelled", method)
lib.RegisterCallback(handler, "LibResInfo_MassResFinished", method)
lib.RegisterCallback(handler, "LibResInfo_UnitUpdate", method)
end
 
lib.RegisterCallback(handler, "LibResInfo_ResPending", method)
lib.RegisterCallback(handler, "LibResInfo_ResUsed", method)
lib.RegisterCallback(handler, "LibResInfo_ResExpired", method)
end
 
------------------------------------------------------------------------
-- Returns information about the res being cast on the specified unit.
-- Arguments: unit (unitID or GUID)
-- Returns: resType (string), endTime (number), caster (unitID), casterGUID
-- * All returns are nil if no res is being cast on the unit.
-- * resType is one of:
-- - SELFRES if the unit has a Soulstone or other self-res ability available,
-- - PENDING if the unit already has a res available to accept, or
-- - CASTING if a res is being cast on the unit.
-- * caster and casterGUID are nil if the unit is being Mass Ressed.
------------------------------------------------------------------------
 
function lib:UnitHasIncomingRes(unit)
if type(unit) ~= "string" then return end
local guid
if strmatch(unit, "^Player%-") then
guid = unit
unit = unitFromGUID[guid]
else
guid = UnitGUID(unit)
unit = unitFromGUID[guid]
end
if not guid or not unit or not UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then
return
end
if hasPending[guid] then
local state = hasSoulstone[guid] and "SELFRES" or "PENDING"
debug(2, "UnitHasIncomingRes", nameFromGUID[guid], state)
return state, hasPending[guid]
end
 
local state, firstCaster, firstEnd
for caster, data in pairs(castingSingle) do
if data.target == guid then
if not firstEnd or data.endTime < firstEnd then
state, firstCaster, firstEnd = "CASTING", caster, data.endTime
end
end
end
if not UnitDebuff(unit, RECENTLY_MASS_RESURRECTED) then
for caster, endTime in pairs(castingMass) do
if not firstEnd or endTime < firstEnd then
state, firstCaster, firstEnd = "MASSRES", caster, endTime
end
end
end
if state and firstCaster and firstEnd then
debug(2, "UnitHasIncomingRes", nameFromGUID[guid], state, nameFromGUID[firstCaster])
return state, firstEnd, unitFromGUID[firstCaster], firstCaster
end
--debug(3, "UnitHasIncomingRes", nameFromGUID[guid], "nil")
end
 
------------------------------------------------------------------------
-- Return information about the res being cast by the specified unit.
-- Arguments: unit (unitID or GUID)
-- Returns: endTime (number), target (unitID), targetGUID (guid), isFirst (boolean)
-- * all returns are nil if the unit is not casting a res
-- * target and targetGUID are nil if the unit is casting Mass Res
------------------------------------------------------------------------
 
function lib:UnitIsCastingRes(unit)
if type(unit) ~= "string" then return end
local guid
if strmatch(unit, "^Player%-") then
guid = unit
unit = unitFromGUID[guid]
else
guid = UnitGUID(unit)
unit = unitFromGUID[guid]
end
if not guid or not unit then
return
end
 
local casting = castingSingle[guid]
if casting then
local endTime, target, isFirst = casting.endTime, casting.target, true
-- TODO: Handle edge case where this function is called in between the cast start and the target identification?
for caster, data in pairs(castingSingle) do
if data.target == target and data.endTime < endTime then
isFirst = false
break
end
end
debug(2, "UnitIsCastingRes", nameFromGUID[guid], "casting on", nameFromGUID[casting.target], isFirst and "(first)" or "(duplicate)")
return endTime, unitFromGUID[casting.target], casting.target, isFirst
end
 
casting = castingMass[guid]
if casting then
local endTime, isFirst = casting, true
for caster, endTime2 in pairs(castingMass) do
if endTime2 < endTime then
isFirst = false
break
end
end
debug(2, "UnitIsCastingRes", nameFromGUID[guid], "casting Mass Res", isFirst and "(first)" or "(duplicate)")
return endTime, nil, nil, isFirst
end
 
--debug(3, "UnitIsCastingRes", nameFromGUID[guid], "nil")
end
 
------------------------------------------------------------------------
-- Handle group changes:
 
local function AddUnit(unit)
local guid = UnitGUID(unit)
if not guid then return end
guidFromUnit[unit] = guid
nameFromGUID[guid] = UnitName(unit)
unitFromGUID[guid] = unit
-- Check for soulstones:
eventFrame:UNIT_AURA("AddUnit", unit)
end
 
function eventFrame:GROUP_ROSTER_UPDATE(event)
debug(3, event)
 
-- Update guid <==> unit mappings:
wipe(guidFromUnit)
wipe(unitFromGUID)
if IsInRaid() then
for i = 1, GetNumGroupMembers() do
AddUnit("raid"..i)
AddUnit("raidpet"..i)
end
else
AddUnit("player")
AddUnit("pet")
if IsInGroup() then
for i = 1, GetNumGroupMembers() - 1 do
AddUnit("party"..i)
AddUnit("partypet"..i)
end
end
end
 
-- Remove data for single casters no longer in the group:
for caster, data in pairs(castingSingle) do
if not unitFromGUID[caster] then
local target = data.target
castingSingle[caster] = remTable(data)
debug(1, ">> ResCastCancelled on", nameFromGUID[target], "by", nameFromGUID[caster], "(caster left group)")
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, nil, caster)
end
end
 
-- Remove data for mass casters no longer in the group:
for caster in pairs(castingMass) do
if not unitFromGUID[caster] then
castingMass[caster] = nil
debug(1, ">> MassResCancelled by", nameFromGUID[caster], "(left group)")
callbacks:Fire("LibResInfo_MassResCancelled", nil, caster)
end
end
 
-- Remove data for targets no longer in the group:
for caster, data in pairs(castingSingle) do
local target = data.target
if not unitFromGUID[target] then
castingSingle[caster] = remTable(data)
-- TODO: Is this callback needed, or will the cast cancel on its own?
debug(1, ">> ResCastCancelled on", nameFromGUID[target], "by", nameFromGUID[caster], "(target left group)")
callbacks:Fire("LibResInfo_ResCastCancelled", nil, target, unitFromGUID[caster], caster)
end
end
 
-- Remove data for waiters no longer in the group:
for target in pairs(hasPending) do
if not unitFromGUID[target] then
hasPending[target] = nil
debug(1, ">> ResExpired on", nameFromGUID[target], "(left group)")
callbacks:Fire("LibResInfo_ResExpired", nil, target)
end
end
 
-- Unregister unit events and stop the timer if there are no waiters:
if not next(hasPending) then
debug(3, "Nobody pending, stop timer")
self:Hide()
end
 
-- Unregister CLEU if there are no casts:
if not next(castingSingle) and not next(castingMass) then
debug(3, "Nobody casting, unregistering CLEU")
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end
 
-- Remove names no longer in the group:
for guid, name in pairs(nameFromGUID) do
if not unitFromGUID[guid] then
debug(4, name, "is no longer in the group")
nameFromGUID[guid] = nil
end
end
end
 
eventFrame.PLAYER_ENTERING_WORLD = eventFrame.GROUP_ROSTER_UPDATE
 
------------------------------------------------------------------------
 
function eventFrame:INCOMING_RESURRECT_CHANGED(event, unit)
local guid = guidFromUnit[unit]
if not guid then return end
 
local hasRes = UnitHasIncomingResurrection(unit)
debug(3, event, nameFromGUID[guid], hasRes)
 
if hasRes then
-- Unit has a res incoming. Match it to a spell.
local now = GetTime()
for caster, data in pairs(castingSingle) do
if not data.target and data.startTime - now < 10 then
-- Found it!
data.target = guid
debug(1, ">> ResCastStarted on", nameFromGUID[guid], "by", nameFromGUID[caster], "in", event)
callbacks:Fire("LibResInfo_ResCastStarted", unit, guid, unitFromGUID[caster], caster, data.endTime)
break
end
end
-- TODO: Why was I searching for finished casts here???
else
-- Check if unit previously had any resses.
for caster, data in pairs(castingSingle) do
if data.target == guid then
debug(4, nameFromGUID[caster], "was casting...")
if data.startTime then
debug(4, "...and stopped.")
castingSingle[caster] = remTable(data)
debug(1, ">> ResCastCancelled", "on", nameFromGUID[guid], "by", nameFromGUID[casterGUID], "in", event)
callbacks:Fire("LibResInfo_ResCastCancelled", unit, guid, unitFromGUID[casterGUID], casterGUID)
else
debug(4, "...and finished.")
castingSingle[caster] = remTable(data)
hasPending[guid] = nil
debug(1, ">> ResCastFinished", "on", nameFromGUID[guid], "by", nameFromGUID[casterGUID], "in", event)
callbacks:Fire("LibResInfo_ResCastFinished", unit, guid, unitFromGUID[casterGUID], casterGUID)
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end
end
end
end
end
 
------------------------------------------------------------------------
 
function eventFrame:UNIT_SPELLCAST_START(event, unit, spellName, _, _, spellID)
if not resSpells[spellID] then return end
local guid = guidFromUnit[unit]
if not guid then return end
debug(3, event, nameFromGUID[guid], "casting", spellName)
 
local _, _, _, _, startTime, endTime = UnitCastingInfo(unit)
 
if spellID == 83968 then -- Mass Resurrection
castingMass[guid] = endTime / 1000
debug(1, ">> MassResStarted", nameFromGUID[guid])
callbacks:Fire("LibResInfo_MassResStarted", unit, guid, endTime / 1000)
return
end
 
local data = newTable()
data.startTime = startTime / 1000
data.endTime = endTime / 1000
castingSingle[guid] = data
end
 
function eventFrame:UNIT_SPELLCAST_SUCCEEDED(event, unit, spellName, _, _, spellID)
if not resSpells[spellID] then return end
local guid = guidFromUnit[unit]
if not guid then return end
 
debug(3, event, nameFromGUID[guid], "finished", spellName)
 
if spellID == 83968 then -- Mass Resurrection
castingMass[guid] = nil
debug(1, ">> MassResFinished", nameFromGUID[guid])
callbacks:Fire("LibResInfo_MassResFinished", unit, guid)
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
return
end
 
local data = castingSingle[guid]
if data then -- No START event for instant cast spells.
local target = data.target
if not target then
-- Probably Soulstone precast on a live target.
return
end
data.finished = true -- Flag so STOP can ignore this.
debug(1, ">> ResCastFinished", "on", nameFromGUID[target], "by", nameFromGUID[guid], "in", event)
callbacks:Fire("LibResInfo_ResCastFinished", unitFromGUID[target], target, unit, guid)
end
 
debug(3, "Registering CLEU")
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end
 
function eventFrame:UNIT_SPELLCAST_STOP(event, unit, spellName, _, _, spellID)
if not resSpells[spellID] then return end
local guid = guidFromUnit[unit]
if not guid then return end
 
debug(3, event, nameFromGUID[guid], "stopped", spellName)
 
if spellID == 83968 then -- Mass Resurrection
if not castingMass[guid] then return end -- already SUCCEEDED
castingMass[guid] = nil
debug(1, ">> MassResCancelled", nameFromGUID[guid])
callbacks:Fire("LibResInfo_MassResCancelled", unit, guid)
else
local data = castingSingle[guid]
if data then
local target = data.target
local finished = data.finished
castingSingle[guid] = remTable(data)
if finished or not target then
-- no target = Probably Soulstone precast on a live target.
-- finished = Cast finished. Don't fire a callback or unregister CLEU.
return
end
debug(1, ">> ResCastCancelled", "on", nameFromGUID[target], "by", nameFromGUID[guid])
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, unit, guid)
end
end
 
-- Unregister CLEU if there are no casts:
if not next(castingSingle) and not next(castingMass) then
debug(3, "Nobody casting, unregistering CLEU")
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end
end
 
eventFrame.UNIT_SPELLCAST_INTERRUPTED = eventFrame.UNIT_SPELLCAST_STOP
 
------------------------------------------------------------------------
 
function eventFrame:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, combatEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, spellID, spellName, spellSchool)
if combatEvent ~= "SPELL_RESURRECT" then return end
 
local destUnit = unitFromGUID[destGUID]
if not destUnit then return end
debug(3, combatEvent, "on", destName, "by", sourceName)
 
local now = GetTime()
local endTime = now + RESURRECT_PENDING_TIME
 
hasPending[destGUID] = endTime
 
self:Show()
 
debug(1, ">> ResPending", "on", strmatch(destName, "[^%-]+"), "by", strmatch(sourceName, "[^%-]+"))
callbacks:Fire("LibResInfo_ResPending", destUnit, destGUID, endTime)
 
-- Unregister CLEU if there are no casts:
if not next(castingSingle) and not next(castingMass) then
-- TODO: Keep track of number of instant casts?
-- Seems unlikely that multiple casts would end so close together that this would be an issue.
debug(3, "Nobody casting, unregistering CLEU")
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
end
end
 
------------------------------------------------------------------------
 
function eventFrame:UNIT_AURA(event, unit)
local guid = guidFromUnit[unit]
if not guid then return end
debug(5, event, unit)
 
if not isDead[guid] then
local stoned = UnitAura(unit, SOULSTONE)
if stoned ~= hasSoulstone[guid] then
if not stoned and UnitHealth(unit) <= 1 then
return
end
hasSoulstone[guid] = stoned
debug(2, nameFromGUID[guid], stoned and "gained" or "lost", SOULSTONE)
end
return
end
 
if UnitIsGhost(unit) and not isGhost[guid] then
isGhost[guid] = true
if hasPending[guid] then
hasPending[guid] = nil
debug(1, ">> ResExpired", nameFromGUID[guid], "(released)")
callbacks:Fire("LibResInfo_ResExpired", unit, guid)
end
-- No need to check next(castingMass) and fire a UnitUpdate here
-- since Mass Resurrection will still hit units who released.
end
end
 
function eventFrame:UNIT_CONNECTION(event, unit)
local guid = guidFromUnit[unit]
if not guid then return end
debug(4, event, unit)
 
if hasPending[unit] and not UnitIsConnected(unit) then
hasPending[guid] = nil
debug(1, ">> ResExpired", nameFromGUID[guid], "(offline)")
callbacks:Fire("LibResInfo_ResExpired", unit, guid)
elseif next(castingMass) then
for caster, data in pairs(castingSingle) do
if data.target == guid then
return
end
end
debug(1, ">> UnitUpdate", nameFromGUID[guid], "(offline)")
callbacks:Fire("LibResInfo_UnitUpdate", unit, guid)
end
end
 
function eventFrame:UNIT_HEALTH(event, unit)
local guid = guidFromUnit[unit]
if not guid then return end
debug(5, event, unit)
 
local dead = UnitIsDead(unit)
 
if dead and not isDead[guid] then
debug(2, nameFromGUID[guid], "is now dead")
isDead[guid] = true
if hasSoulstone[guid] then
local endTime = GetTime() + RELEASE_PENDING_TIME
hasPending[guid] = endTime
debug(1, ">> ResPending", nameFromGUID[guid], SOULSTONE)
callbacks:Fire("LibResInfo_ResPending", unit, guid, endTime, true)
elseif next(castingMass) then
debug(1, ">> UnitUpdate", nameFromGUID[guid], "(dead)")
callbacks:Fire("LibResInfo_UnitUpdate", unit, guid)
end
 
elseif isDead[guid] and not dead then
debug(2, nameFromGUID[guid], "is now alive")
isDead[guid] = nil
if hasPending[guid] then
isGhost[guid] = nil
hasPending[guid] = nil
debug(1, ">> ResUsed", nameFromGUID[guid])
callbacks:Fire("LibResInfo_ResUsed", unit, guid)
elseif next(castingMass) then
for caster, data in pairs(castingSingle) do
if data.target == guid then
return
end
end
debug(1, ">> UnitUpdate", nameFromGUID[guid], "(alive)")
callbacks:Fire("LibResInfo_UnitUpdate", unit, guid)
end
end
end
 
------------------------------------------------------------------------
 
eventFrame:Hide()
 
local timer, INTERVAL = 0, 0.5
eventFrame:SetScript("OnUpdate", function(self, elapsed)
timer = timer + elapsed
if timer >= INTERVAL then
debug(6, "Timer update")
if not next(hasPending) then
debug(4, "Nobody pending, stop timer")
return self:Hide()
end
local now = GetTime()
for guid, endTime in pairs(hasPending) do
if endTime - now < INTERVAL then -- It will expire before the next update.
local unit = unitFromGUID[guid]
hasPending[guid] = nil
debug(1, ">> ResExpired", nameFromGUID[guid])
callbacks:Fire("LibResInfo_ResExpired", unit, guid, true)
end
end
timer = 0
end
end)
 
eventFrame:SetScript("OnShow", function()
debug(4, "Timer start")
end)
 
eventFrame:SetScript("OnHide", function()
debug(4, "Timer stop")
timer = 0
end)
 
------------------------------------------------------------------------
 
SLASH_LIBRESINFO1 = "/lri"
SlashCmdList.LIBRESINFO = function(input)
input = gsub(input, "[^A-Za-z0-9]", "")
if strlen(input) < 1 then return end
if strmatch(input, "%D") then
local f = _G[input]
if type(f) == "table" and type(f.AddMessage) == "function" then
DEBUG_FRAME = f
debug(0, "Debug frame set to", input)
else
debug(0, input, "is not a valid debug output frame!")
end
else
local v = tonumber(input)
if v and v >= 0 then
DEBUG_LEVEL = v
debug(0, "Debug level set to", input)
else
debug(0, input, "is not a valid debug level!")
end
end
end
\ No newline at end of file
22/LibResInfo-1.0.toc New file
0,0 → 1,18
## Interface: 60000
## Version: @project-revision@
 
## Title: Lib: ResInfo-1.0
## Notes: Library to provide information about resurrections in your group.
## Notes-deDE: Eine Bibliothek, um Informationen über Wiederbelebungen in der Gruppe zu liefern.
## Notes-esES: Una biblioteca para proporcionar información sobre resurreciones en tu grupo.
## Notes-esMX: Una biblioteca para proporcionar información sobre resurreciones en tu grupo.
 
## Author: Phanx
## X-Email: addons@phanx.net
## X-Copyright: Copyright (c) 2012-2014 Phanx. All rights reserved.
## X-Website: https://github.com/Phanx/LibResInfo
 
LibStub\LibStub.lua
CallbackHandler-1.0\CallbackHandler-1.0.lua
 
LibResInfo-1.0\LibResInfo-1.0.lua
\ No newline at end of file
22/README.md New file
0,0 → 1,44
LibResInfo-1.0
=================
 
LibResInfo detects resurrection spell casts and identifies who they are being cast on, and provides that information to addons through callbacks and API methods. It also supports Mass Resurrection and Soulstone. It is fully embeddable, completely locale-independent, and does not require any other players in your group to have anything installed.
 
Requires [LibStub](http://www.wowace.com/addons/libstub/) and [CallbackHandler-1.0](http://www.wowace.com/addons/callbackhandler/).
 
* [Download on CurseForge](http://wow.curseforge.com/addons/libresinfo/)
* [Download on WoWInterface](http://www.wowinterface.com/downloads/info21467-LibResInfo-1.0.html)
 
 
## API Documentation:
 
* [API methods](https://github.com/Phanx/LibResInfo/wiki/API-Methods)
* [Callbacks](https://github.com/Phanx/LibResInfo/wiki/Callbacks)
 
 
## Embedding
 
If you are the CurseForge/WowAce packager, you should use the following URL in your .pkgmeta file:
 
svn://svn.wowinterface.com/LibResInfo-976/trunk/LibResInfo-1.0
 
If you use a Git URL instead, you will get a number of irrelevant documentation and metadata files, and an extra layer of folders, since Git does not support checking out only part of the repository, and the Curse packager does not support checking out from GitHub over SVN. You're welcome to add `libresinfo` to the `tools-used` section of your .pkgmeta file so I get Curse points for the library usage, but it's not required.
 
 
## Additional Notes
 
Support for Reincarnation is under consideration, but would require some guesswork, since until you actually see a shaman resurrect themselves, there's no way to tell if the ability is on cooldown or not.
 
No callbacks are fired for players who die while Mass Resurrection is being cast, but the API will return correct information (that the player has an incoming resurrection) so if it important for your addon, just check the player's status when they die.
 
No callbacks are fired for players who resurrect themselves by returning to their corpse while Mass Resurrection is the only res casting on them, but the API will (again) return correct information (that the player has no incoming resurrection) so if it important for your addon, just check the player's status when they resurrect.
 
 
## Limitations
 
Due to limitations of the WoW API, it is **not possible** to detect:
 
* ...when someone declines a resurrection manually by clicking "Decline" on the dialog box.
* ...when someone has a wait time before they can accept a resurrection. In this case, the 60-second expiration time will be extended by the amount of time they are forced to wait, but the ResExpired callback will be fired at the 60-second mark since there's no way for LRI to know about the wait time.
* ...who a player who joins the group while casting a resurrection spell is targeting.
* ...whether a player who joins the group while dead has a resurrection being cast on them.
* ...whether a player who joins the group while dead has a resurrection already available.
22/LICENSE.txt New file
0,0 → 1,33
Copyright (c) 2012-2014 Phanx. All rights reserved.
 
Permission is granted for anyone to use, read, or otherwise interpret
this software for any purpose, without any restrictions.
 
Permission is granted for anyone to embed or include this software in
another work not derived from this software that makes use of the
interface provided by this software for the purpose of creating a
package of the work and its required libraries, and to distribute such
packages as long as the software is not modified in any way, including
by modifying or removing any files.
 
Permission is granted for anyone to modify this software or sample from
it, and to distribute such modified versions or derivative works as long
as neither the names of this software nor its authors are used in the
name or title of the work or in any other way that may cause it to be
confused with or interfere with the simultaneous use of this software.
 
This software may not be distributed standalone or in any other way, in
whole or in part, modified or unmodified, without specific prior written
permission from the authors of this software.
 
The names of this software and/or its authors may not be used to
promote or endorse works derived from this software without specific
prior written permission from the authors of this software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
22 Property changes : Added: svn:externals + svn://svn.wowace.com/wow/libstub/mainline/tags/1.0 LibStub svn://svn.wowace.com/wow/callbackhandler/mainline/tags/1.0.7/CallbackHandler-1.0 CallbackHandler-1.0