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, "Used")]] |
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") |
-- Test for Soulstone support: |
eventFrame:RegisterEvent("UNIT_AURA") |
eventFrame:RegisterEvent("UNIT_CONNECTION") |
eventFrame:RegisterEvent("UNIT_HEALTH")--[[ |
end |
lib.callbacksInUse[callback] = true |
end |
|
function callbacks:OnUnused(lib, callback) |
lib.callbacksInUse[callback] = nil |
if not next(lib.callbacksInUse) then |
debug(1, "Unused") |
eventFrame:UnregisterAllEvents() |
eventFrame:Hide() |
wipe(guidFromUnit) |
wipe(nameFromGUID) |
wipe(unitFromGUID) |
wipe(castingMass) |
wipe(hasPending) |
wipe(isGhost) |
for caster, data in pairs(castingSingle) do |
castingSingle[caster] = remTable(data) |
end |
end |
end]] |
|
------------------------------------------------------------------------ |
|
function lib.RegisterAllCallbacks(handler, method) |
lib.RegisterCallback(handler, "LibResInfo_ResCastStarted", method) |
lib.RegisterCallback(handler, "LibResInfo_ResCastCancelled", method) |
lib.RegisterCallback(handler, "LibResInfo_ResCastFinished", method) |
|
lib.RegisterCallback(handler, "LibResInfo_MassResStarted", method) |
lib.RegisterCallback(handler, "LibResInfo_MassResCancelled", method) |
lib.RegisterCallback(handler, "LibResInfo_MassResFinished", method) |
|
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 |
local guid |
if strmatch(unit, "^0x") then |
guid = unit |
unit = unitFromGUID[unit] |
else |
guid = UnitGUID(unit) |
unit = unitFromGUID[guid] |
if type(unit) ~= "string" then return end |
local guid |
if strmatch(unit, "^0x") 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 |
if guid and unit and UnitIsDeadOrGhost(unit) and UnitIsConnected(unit) then |
if resPending[guid] then |
debug(2, "UnitHasIncomingRes", nameFromGUID[guid], "PENDING") |
return "PENDING", resPending[guid] |
else |
local firstCaster, firstEnd |
for casterGUID, targetGUID in pairs(castTarget) do |
if targetGUID == guid then |
local endTime = castEnd[casterGUID] |
if endTime and (not firstEnd or endTime < firstEnd) then |
firstCaster, firstEnd = casterGUID, endTime |
end |
end |
end |
if not UnitDebuff(unit, RECENTLY_MASS_RESURRECTED) then |
for casterGUID in pairs(castMass) do |
local endTime = castEnd[casterGUID] |
if endTime and (not firstEnd or endTime < firstEnd) then |
firstCaster, firstEnd = casterGUID, endTime |
end |
end |
end |
if firstCaster and firstEnd then |
debug(2, "UnitHasIncomingRes", nameFromGUID[guid], "CASTING", nameFromGUID[firstCaster]) |
return "CASTING", firstEnd, unitFromGUID[firstCaster], firstCaster |
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 |
debug(3, "UnitHasIncomingRes", nameFromGUID[guid], "nil") |
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 |
local guid |
if strmatch(unit, "^0x") then |
guid = unit |
unit = unitFromGUID[unit] |
else |
guid = UnitGUID(unit) |
unit = unitFromGUID[guid] |
if type(unit) ~= "string" then return end |
local guid |
if strmatch(unit, "^0x") 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 |
if guid and unit then |
debug(4, unit, guid, nameFromGUID[guid]) |
local isFirst, targetGUID, endTime = true, castTarget[guid], castEnd[guid] |
if targetGUID then |
if resPending[targetGUID] then |
isFirst = nil |
else |
for k, v in pairs(castTarget) do |
if k ~= guid and v == targetGUID and castEnd[k] < endTime then |
isFirst = nil |
break |
end |
end |
for k in pairs(castMass) do |
if k ~= guid and castEnd[k] < endTime then |
isFirst = nil |
break |
end |
end |
end |
debug(2, "UnitIsCastingRes", nameFromGUID[guid], nameFromGUID[targetGUID], isFirst) |
return endTime, unitFromGUID[targetGUID], targetGUID, isFirst |
debug(2, "UnitIsCastingRes", nameFromGUID[guid], "casting on", nameFromGUID[casting.target], isFirst and "(first)" or "(duplicate)") |
return endTime, unitFromGUID[casting.target], casting.target, isFirst |
end |
|
elseif castMass[guid] then |
for k in pairs(castMass) do |
if not castEnd[k] then |
castMass[k], castStart[k] = nil, nil |
elseif k ~= guid and castEnd[k] < endTime then |
isFirst = nil |
break |
end |
end |
debug(2, "UnitIsCastingRes", nameFromGUID[guid], "MASS", isFirst) |
return endTime, nil, nil, isFirst |
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 |
return debug(3, "UnitIsCastingRes", nameFromGUID[guid], "NIL") |
end |
return debug(3, "UnitIsCastingRes", unit, "INVALID") |
debug(2, "UnitIsCastingRes", nameFromGUID[guid], "casting Mass Res", isFirst and "(first)" or "(duplicate)") |
return endTime, nil, nil, isFirst |
end |
debug(3, "UnitIsCastingRes", tostring(unit), "INVALID", type(unit)) |
|
debug(3, "UnitIsCastingRes", nameFromGUID[guid], "nil") |
end |
|
------------------------------------------------------------------------ |
-- Handle group changes: |
|
local unitFromGUID_old, guidFromUnit_old = {}, {} |
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 f:GROUP_ROSTER_UPDATE() |
debug(3, "GROUP_ROSTER_UPDATE") |
function eventFrame:GROUP_ROSTER_UPDATE(event) |
debug(3, event) |
|
wipe(guidFromUnit_old) |
wipe(unitFromGUID_old) |
for guid, unit in pairs(unitFromGUID) do |
unitFromGUID_old[guid] = unit |
unitFromGUID[guid] = nil |
end |
for unit, guid in pairs(guidFromUnit) do |
guidFromUnit_old[unit] = guid |
guidFromUnit[unit] = nil |
end |
|
-- Update guid <==> unit mappings: |
wipe(guidFromUnit) |
wipe(unitFromGUID) |
if IsInRaid() then |
debug(4, "raid") |
for i = 1, GetNumGroupMembers() do |
local unit = "raid"..i |
local guid = UnitGUID(unit) |
if guid then |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
end |
unit = "raidpet"..i |
guid = UnitGUID(unit) |
if guid then |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
end |
AddUnit("raid"..i) |
AddUnit("raidpet"..i) |
end |
else |
local unit, guid = "player", UnitGUID("player") |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
|
unit, guid = "pet", UnitGUID("pet") |
if guid then |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
end |
|
AddUnit("player") |
AddUnit("pet") |
if IsInGroup() then |
debug(4, "party") |
for i = 1, GetNumGroupMembers() - 1 do |
unit = "party"..i |
guid = UnitGUID(unit) |
if guid then |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
end |
unit = "partypet"..i |
guid = UnitGUID(unit) |
if guid then |
guidFromUnit[unit] = guid |
nameFromGUID[guid] = UnitName(unit) |
unitFromGUID[guid] = unit |
end |
AddUnit("party"..i) |
AddUnit("partypet"..i) |
end |
else |
debug(4, "solo") |
end |
end |
|
-- Someone left the group while casting a res. |
-- Find who they were casting on and cancel it. |
for caster in pairs(castEnd) do |
-- Remove data for single casters no longer in the group: |
for caster, data in pairs(castingSingle) do |
if not unitFromGUID[caster] then |
debug(4, nameFromGUID[caster], "left while casting on", nameFromGUID[target]) |
local startTime, endTime = castStart[caster] or 0, castEnd[caster] or 0 |
castStart[caster], castEnd[caster] = nil, nil |
local target = castTarget[caster] |
if target then |
if resCasting[target] and resCasting[target] > 1 then |
resCasting[target] = resCasting[target] - 1 |
else |
resCasting[target] = nil |
end |
castTarget[caster] = nil |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[target], "by", nameFromGUID[caster], resCasting[target]) |
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, unitFromGUID_old[caster], caster) |
elseif castMass[caster] then |
castMass[caster] = nil |
for guid, unit in pairs(guidFromUnit) do |
if UnitIsDeadOrGhost(unit) and UnitIsConnected(unit) and not UnitDebuff(unit, RECENTLY_MASS_RESURRECTED) then |
if resCasting[guid] and resCasting[guid] > 1 then |
resCasting[guid] = resCasting[guid] - 1 |
else |
resCasting[guid] = nil |
end |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[caster], "by", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, unitFromGUID_old[caster], caster) |
end |
end |
end |
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 |
|
-- Someone left the group while a res was being cast on them. |
-- Find the cast and cancel it. |
for target, n in pairs(resCasting) do |
-- 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 |
debug(4, nameFromGUID[target], "left while incoming.") |
for caster, castertarget in pairs(castTarget) do |
if target == castertarget then |
resCasting[target] = nil |
castTarget[caster], castStart[caster], castEnd[caster] = nil, nil, nil |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[target], "by", nameFromGUID[caster]) |
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, unitFromGUID_old[caster], caster) |
end |
end |
for caster in pairs(castMass) do |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[target], "by", nameFromGUID[caster]) |
callbacks:Fire("LibResInfo_ResCastCancelled", unitFromGUID[target], target, unitFromGUID_old[caster], caster) |
end |
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 |
|
-- Someone left the group when they had a res available. |
-- Find the res and cancel it. |
for target in pairs(resPending) do |
-- Remove data for waiters no longer in the group: |
for target in pairs(hasPending) do |
if not unitFromGUID[target] then |
debug(3, nameFromGUID[target], "left while pending.") |
resPending[target] = nil |
total.pending = total.pending - 1 |
debug(1, ">> ResExpired", "on", nameFromGUID[target]) |
callbacks:Fire("LibResInfo_ResExpired", unitFromGUID_old[target], target) |
hasPending[target] = nil |
debug(1, ">> ResExpired on", nameFromGUID[target], "(left group)") |
callbacks:Fire("LibResInfo_ResExpired", nil, target) |
end |
end |
|
-- Check events |
debug(3, "# pending:", total.pending) |
if total.pending == 0 then |
self:UnregisterEvent("UNIT_HEALTH") |
-- Unregister unit events and stop the timer if there are no waiters: |
if not next(hasPending) then |
--debug(3, "Nobody pending, unregistering unit state events") |
--self:UnregisterEvent("UNIT_AURA") |
--self:UnregisterEvent("UNIT_CONNECTION") |
--self:UnregisterEvent("UNIT_HEALTH") |
self:Hide() |
end |
|
local most = 0 |
for _, n in pairs(resCasting) do |
most = max(n, most) |
end |
debug(3, "highest # casting:", most) |
if most < 2 then |
-- 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 f:INCOMING_RESURRECT_CHANGED(event, unit) |
if guidFromUnit[unit] then |
local guid = UnitGUID(unit) |
local hasRes = UnitHasIncomingResurrection(unit) |
debug(3, "INCOMING_RESURRECT_CHANGED", nameFromGUID[guid], hasRes) |
function eventFrame:INCOMING_RESURRECT_CHANGED(event, unit) |
local guid = guidFromUnit[unit] |
if not guid then return end |
|
if hasRes then |
local now, found = GetTime() |
for casterGUID, startTime in pairs(castStart) do |
if startTime - now < 10 and not castTarget[casterGUID] and not castMass[casterGUID] then -- time in ms between cast start and res gain |
if not castMass[casterGUID] then |
castTarget[casterGUID] = guid |
end |
resCasting[guid] = 1 + (resCasting[guid] or 0) |
local casterUnit = unitFromGUID[casterGUID] |
debug(1, ">> ResCastStarted", "on", nameFromGUID[guid], "by", nameFromGUID[casterGUID], "DIFF", floor(now - startTime * 100) / 100, "#", resCasting[guid]) |
callbacks:Fire("LibResInfo_ResCastStarted", unit, guid, casterUnit, casterGUID, castEnd[casterGUID]) |
found = true |
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, caster, unitFromGUID[caster], data.endTime) |
break |
end |
if not found then |
debug(4, "No new cast found.") |
end |
for casterGUID, endTime in pairs(castEnd) do |
if endTime - now < 10 and not castStart[casterGUID] and not castMass[casterGUID] then -- time in ms between cast end and res loss |
local targetGUID = castTarget[casterGUID] |
if targetGUID == guid then |
local casterUnit = unitFromGUID[casterGUID] |
castTarget[casterGUID], castEnd[casterGUID] = nil, nil |
debug(1, ">> ResCastFinished", "on",nameFromGUID[guid], "by", nameFromGUID[casterGUID], "#", resCasting[guid], "hasRes") |
callbacks:Fire("LibResInfo_ResCastFinished", unit, guid, casterUnit, casterGUID) |
end |
total.casting = total.casting + 1 |
debug(4, n, "casting, waiting for CLEU.") |
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 |
|
elseif resCasting[guid] then |
-- had a res, doesn't anymore |
local finished, stopped |
for casterGUID, targetGUID in pairs(castTarget) do |
if targetGUID == guid then |
debug(4, nameFromGUID[casterGUID], "was casting...") |
if castStart[casterGUID] then |
debug(4, "...and stopped.") |
castStart[casterGUID], castEnd[casterGUID], castTarget[casterGUID] = nil, nil, nil |
|
debug(1, ">> ResCastCancelled", "on", nameFromGUID[guid], "by", nameFromGUID[casterGUID], "#", resCasting[guid] - 1) |
resCasting[guid] = nil |
callbacks:Fire("LibResInfo_ResCastCancelled", unit, guid, unitFromGUID[casterGUID], casterGUID) |
else |
debug(4, "...and finished.") |
castStart[casterGUID], castEnd[casterGUID], castTarget[casterGUID] = nil, nil, nil |
|
debug(1, ">> ResCastFinished", "on", nameFromGUID[guid], "by", nameFromGUID[casterGUID], "#", resCasting[guid] - 1, "resCasting") |
callbacks:Fire("LibResInfo_ResCastFinished", unit, guid, unitFromGUID[casterGUID], casterGUID) |
|
total.casting = total.casting + 1 |
debug(3, n, "casting, waiting for CLEU") |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
break |
end |
end |
end |
end |
end |
|
------------------------------------------------------------------------ |
|
function f:UNIT_SPELLCAST_START(event, unit, spellName, _, _, spellID) |
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) |
|
if spellID == 83968 then -- Mass Resurrection |
local _, _, _, _, startTime, endTime = UnitCastingInfo(unit) |
castMass[guid] = endTime |
debug(1, ">> MassResStarted", "by", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_MassResStarted", nil, nil, unit, guid, endTime / 1000) |
local _, _, _, _, startTime, endTime = UnitCastingInfo(unit) |
|
elseif resSpells[spellID] then |
local _, _, _, _, startTime, endTime = UnitCastingInfo(unit) |
debug(3, event, "=>", nameFromGUID[guid], "=>", spellName) |
castStart[guid] = startTime / 1000 |
castEnd[guid] = endTime / 1000 |
if spellID == 83968 then -- Mass Resurrection |
castingMass[guid] = endTime / 1000 |
debug(1, ">> MassResStarted", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_MassResStarted", unit, guid) |
return |
end |
|
local data = newTable() |
data.startTime = startTime / 1000 |
data.endTime = endTime / 1000 |
castingSingle[guid] = data |
end |
|
function f:UNIT_SPELLCAST_SUCCEEDED(event, unit, spellName, _, _, spellID) |
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 |
|
if castMass[guid] then -- Mass Resurrection |
debug(3, event, nameFromGUID[guid], "finished", spellName) |
|
for targetGUID, targetUnit in pairs(unitFromGUID) do |
if UnitIsDeadOrGhost(targetUnit) and UnitIsConnected(targetUnit) and not UnitDebuff(targetUnit, RECENTLY_MASS_RESURRECTED) then |
debug(1, ">> ResCastFinished", "on", nameFromGUID[targetGUID], "by", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResCastFinished", targetUnit, targetGUID, unit, guid) |
n = n + 1 |
end |
end |
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 |
|
if n > 0 then |
debug(4, n, "casting, waiting for CLEU") |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
local data = castingSingle[guid] |
if data then -- No START event for instant cast spells. |
local target = data.target |
castingSingle[guid] = remTable(data) -- TODO: Why was I removing only startTime here and waiting for IRC? |
if not target then |
-- Probably Soulstone precast on a live target. |
return |
end |
debug(1, ">> ResCastFinished", "on", nameFromGUID[target], "by", nameFromGUID[guid], "in", event) |
callbacks:Fire("LibResInfo_ResCastFinished", unitFromGUID[target], target, unit, guid) |
end |
if guidFromUnit[unit] and resSpells[spellID] then |
local guid = UnitGUID(unit) |
if castStart[guid] then |
debug(3, event, "=>", nameFromGUID[guid], "=>", spellName) |
castStart[guid] = nil |
else |
-- instant cast |
resCasting[guid] = 1 + (resCasting[guid] or 1) |
debug(3, event, "=>", nameFromGUID[guid], "=>", spellName) |
debug(3, "instant cast, waiting for CLEU") |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
end |
|
debug(3, "Registering CLEU") |
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
|
function f:UNIT_SPELLCAST_STOP(event, unit, spellName, _, _, spellID) |
if guidFromUnit[unit] and resSpells[spellID] then |
local guid = UnitGUID(unit) |
if castStart[guid] then |
debug(3, event, "=>", nameFromGUID[guid], "=>", spellName) |
local targetGUID = castTarget[guid] |
if targetGUID then |
local n = resCasting[targetGUID] |
if n and n > 1 then |
-- someone else is still casting, send cancellation here |
local targetUnit = unitFromGUID[targetGUID] |
castStart[guid], castEnd[guid], castTarget[guid] = nil, nil |
resCasting[targetGUID] = n - 1 |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[targetGUID], "by", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResCastCancelled", targetUnit, targetGUID, unit, guid) |
else |
debug(4, "Waiting for IRC.") |
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 |
|
elseif castMass[guid] then -- Mass Resurrection |
castStart[guid], castEnd[guid], castMass[guid] = nil, nil, nil |
for targetGUID, targetUnit in pairs(unitFromGUID) do |
if UnitIsDeadOrGhost(targetUnit) and UnitIsConnected(targetUnit) and not UnitDebuff(targetUnit, RECENTLY_MASS_RESURRECTED) then |
local n = resCasting[targetGUID] |
if n and n > 1 then |
resCasting[targetGUID] = n - 1 |
else |
resCasting[targetGUID] = nil |
end |
debug(1, ">> ResCastCancelled", "on", nameFromGUID[targetGUID], "by", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResCastCancelled", targetUnit, targetGUID, unit, guid) |
end |
end |
debug(3, event, nameFromGUID[guid], "stopped", spellName) |
|
if spellID == 83968 and castingMass[guid] then -- Mass Resurrection |
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 |
castingSingle[guid] = remTable(data) |
if not target then |
-- Probably Soulstone precast on a live target. |
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 |
|
f.UNIT_SPELLCAST_INTERRUPTED = f.UNIT_SPELLCAST_STOP |
eventFrame.UNIT_SPELLCAST_INTERRUPTED = eventFrame.UNIT_SPELLCAST_STOP |
|
------------------------------------------------------------------------ |
|
function f:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, combatEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, spellID, spellName, spellSchool) |
if combatEvent == "SPELL_RESURRECT" then |
local destUnit = unitFromGUID[destGUID] |
if destUnit then |
local now = GetTime() |
debug(3, combatEvent, "=>", sourceName, "=>", spellName, "=>", destName) |
function eventFrame:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, combatEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, spellID, spellName, spellSchool) |
if event ~= "SPELL_RESURRECT" then return end |
|
local casting = resCasting[destGUID] |
if casting or castMass[sourceGUID] then |
debug(4, "yes") |
local destUnit = unitFromGUID[destGUID] |
if not destUnit then return end |
debug(3, combatEvent, "on", destName, "by", sourceName) |
|
if casting and casting > 1 then |
resCasting[destGUID] = casting - 1 |
else |
resCasting[destGUID] = nil |
end |
local now = GetTime() |
local endTime = now + RESURRECT_PENDING_TIME |
|
local new = not resPending[destGUID] |
resPending[destGUID] = now + RESURRECT_PENDING_TIME |
hasPending[guid] = endTime |
--isGhost[guid] = UnitIsGhost(destUnit) |
|
total.casting = total.casting - 1 |
if total.casting == 0 then |
debug(4, "0 casting, unregister CLEU") |
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") |
end |
--debug(3, "Registering unit state events") |
--self:RegisterEvent("UNIT_AURA") |
--self:RegisterEvent("UNIT_CONNECTION") |
--self:RegisterEvent("UNIT_HEALTH") |
self:Show() |
|
if castMass[sourceGUID] then |
local n = 0 |
for guid, unit in pairs(unitFromGUID) do |
if guid ~= destGUID and (not resPending[guid] or resPending[guid] - now > RESURRECT_PENDING_TIME) and UnitIsDeadOrGhost(unit) and UnitIsConnected(unit) and not UnitDebuff(unit, RECENTLY_MASS_RESURRECTED) then |
n = n + 1 |
end |
end |
if n == 0 then |
castMass[sourceGUID] = nil |
end |
debug(4, n, "mass targets left") |
end |
debug(1, ">> ResPending", "on", strmatch(destName, "[^%-]+"), "by", strmatch(sourceName, "[^%-]+")) |
callbacks:Fire("LibResInfo_ResPending", destUnit, destGUID, endTime) |
|
ghost[destGUID] = UnitIsGhost(destUnit) |
|
if new then |
total.pending = total.pending + 1 |
debug(4, total.pending, "pending, start timer, register UNIT_HEALTH/AURA") |
self:RegisterEvent("UNIT_HEALTH") |
self:RegisterEvent("UNIT_AURA") |
self:Show() |
else |
debug(4, total.pending, "pending, timer already running") |
end |
|
debug(1, ">> ResPending", "on", strmatch(destName, "[^%-]+"), "by", strmatch(sourceName, "[^%-]+")) |
callbacks:Fire("LibResInfo_ResPending", destUnit, destGUID, now + RESURRECT_PENDING_TIME) |
end |
end |
-- 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 f:UNIT_HEALTH(event, unit) |
function eventFrame:UNIT_AURA(event, unit) |
local guid = guidFromUnit[unit] |
if guidFromUnit[unit] then |
if resPending[guid] then |
debug(3, event, nameFromGUID[guid], "Dead", UnitIsDead(unit), "Ghost", UnitIsGhost(unit), "Offline", not UnitIsConnected(unit)) |
if not guid then return end |
--debug(5, event, unit) |
|
local callback |
if not isDead[guid] then |
local stoned = UnitAura(unit, SOULSTONE) |
if stoned ~= hasSoulstone[guid] then |
hasSoulstone[guid] = stoned |
debug(2, nameFromGUID[guid], stoned and "gained" or "lost", SOULSTONE) |
end |
return |
end |
|
if not UnitIsConnected(unit) then |
callback = "LibResInfo_ResExpired" |
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 |
end |
--[[ |
if not next(hasPending) then |
debug(4, "Unregistering unit state events") |
--self:UnregisterEvent("UNIT_AURA") |
--self:UnregisterEvent("UNIT_CONNECTION") |
--self:UnregisterEvent("UNIT_HEALTH") |
end]] |
end |
|
elseif UnitIsGhost(unit) and not ghost[guid] then |
ghost[guid] = true |
callback = "LibResInfo_ResExpired" |
function eventFrame:UNIT_CONNECTION(unit) |
local guid = guidFromUnit[unit] |
if not guid then return end |
debug(4, event, unit) |
|
elseif not UnitIsDead(unit) then |
ghost[guid] = nil |
callback = "LibResInfo_ResUsed" |
end |
if hasPending[unit] and not UnitIsConnected(unit) then |
hasPending[guid] = nil |
debug(1, ">> ResExpired", nameFromGUID[guid], "(offline)") |
callbacks:Fire("LibResInfo_ResExpired", unit, guid) |
end |
end |
|
if callback then |
resPending[guid] = nil |
debug(1, ">>", callback, nameFromGUID[guid]) |
callbacks:Fire(callback, unit, guid) |
function eventFrame:UNIT_HEALTH(unit) |
local guid = guidFromUnit[unit] |
if not guid then return end |
--debug(5, event, unit) |
|
total.pending = total.pending - 1 |
if total.pending == 0 then |
debug(4, "0 pending, unregister UNIT_HEALTH/AURA") |
self:UnregisterEvent("UNIT_AURA") |
self:UnregisterEvent("UNIT_HEALTH") |
end |
end |
local dead = UnitIsDead(unit) |
if dead and not isDead[guid] then |
isDead[guid] = true |
if hasSoulstone[guid] then |
local endTime = GetTime() + RESURRECT_PENDING_TIME |
hasPending[guid] = endTime |
debug(1, ">> ResPending", nameFromGUID[guid], SOULSTONE) |
callbacks:Fire("LibResInfo_ResPending", unit, guid, endTime, true) |
end |
|
elseif not dead then |
isDead[guid] = nil |
if hasPending[guid] then |
isGhost[guid] = nil |
hasPending[guid] = nil |
debug(1, ">> ResUsed", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResUsed", unit, guid) |
end |
end |
end |
|
f.UNIT_AURA = f.UNIT_HEALTH |
|
------------------------------------------------------------------------ |
|
f:Hide() |
eventFrame:Hide() |
|
local timer = 0 |
local INTERVAL = 0.5 |
f:SetScript("OnUpdate", function(self, elapsed) |
local timer, INTERVAL = 0, 0.5 |
eventFrame:SetScript("OnUpdate", function(self, elapsed) |
timer = timer + elapsed |
if timer >= INTERVAL then |
debug(6, "timer update") |
debug(6, "Timer update") |
if not next(hasPending) then |
--debug(4, "Nobody pending, unregistering unit state events") |
--self:UnregisterEvent("UNIT_AURA") |
--self:UnregisterEvent("UNIT_CONNECTION") |
--self:UnregisterEvent("UNIT_HEALTH") |
return self:Hide() |
end |
local now = GetTime() |
for guid, expiry in pairs(resPending) do |
if expiry - now < INTERVAL then -- will expire before next update |
for guid, endTime in pairs(hasPending) do |
if endTime - now < INTERVAL then -- It will expire before the next update. |
local unit = unitFromGUID[guid] |
resPending[guid] = nil |
hasPending[guid] = nil |
debug(1, ">> ResExpired", nameFromGUID[guid]) |
callbacks:Fire("LibResInfo_ResExpired", unit, guid, true) |
|
total.pending = total.pending - 1 |
if total.pending == 0 then |
self:UnregisterEvent("UNIT_HEALTH") |
self:Hide() |
end |
end |
end |
timer = 0 |
end |
end) |
|
f:SetScript("OnShow", function() |
debug(4, "timer start") |
eventFrame:SetScript("OnShow", function() |
debug(4, "Timer start") |
end) |
|
f:SetScript("OnHide", function() |
debug(4, "timer stop") |
eventFrame:SetScript("OnHide", function() |
debug(4, "Timer stop") |
timer = 0 |
end) |
|