-- Based on RecountGuessedAbsorbs by Elsia local L = LibStub("AceLocale-3.0"):GetLocale("Skada", false) local Skada = Skada local mod = Skada:NewModule(L["Absorbs"]) local playermod = Skada:NewModule(L["Absorb details"]) local combined = Skada:NewModule(L["Absorbs and healing"]) -- This bit shamelessly copied straight from RecountGuessedAbsorbs - thanks! local AbsorbSpellDuration = { -- Death Knight [48707] = 5, -- Anti-Magic Shell (DK) Rank 1 -- Does not currently seem to show tracable combat log events. It shows energizes which do not reveal the amount of damage absorbed [51052] = 10, -- Anti-Magic Zone (DK)( Rank 1 (Correct spellID?) -- Does DK Spell Deflection show absorbs in the CL? [51271] = 20, -- Unbreakable Armor (DK) -- Druid [62606] = 10, -- Savage Defense proc. (Druid) Tooltip of the original spell doesn't clearly state that this is an absorb, but the buff does. -- Mage [11426] = 60, -- Ice Barrier (Mage) Rank 1 [13031] = 60, [13032] = 60, [13033] = 60, [27134] = 60, [33405] = 60, [43038] = 60, [43039] = 60, -- Rank 8 [6143] = 30, -- Frost Ward (Mage) Rank 1 [8461] = 30, [8462] = 30, [10177] = 30, [28609] = 30, [32796] = 30, [43012] = 30, -- Rank 7 [1463] = 60, -- Mana shield (Mage) Rank 1 [8494] = 60, [8495] = 60, [10191] = 60, [10192] = 60, [10193] = 60, [27131] = 60, [43019] = 60, [43020] = 60, -- Rank 9 [543] = 30 , -- Fire Ward (Mage) Rank 1 [8457] = 30, [8458] = 30, [10223] = 30, [10225] = 30, [27128] = 30, [43010] = 30, -- Rank 7 -- Paladin [58597] = 6, -- Sacred Shield (Paladin) proc (Fixed, thanks to Julith) -- Priest [17] = 30, -- Power Word: Shield (Priest) Rank 1 [592] = 30, [600] = 30, [3747] = 30, [6065] = 30, [6066] = 30, [10898] = 30, [10899] = 30, [10900] = 30, [10901] = 30, [25217] = 30, [25218] = 30, [48065] = 30, [48066] = 30, -- Rank 14 [47509] = 12, -- Divine Aegis (Priest) Rank 1 [47511] = 12, [47515] = 12, -- Divine Aegis (Priest) Rank 3 (Some of these are not actual buff spellIDs) [47753] = 12, -- Divine Aegis (Priest) Rank 1 [54704] = 12, -- Divine Aegis (Priest) Rank 1 [47788] = 10, -- Guardian Spirit (Priest) (50 nominal absorb, this may not show in the CL) -- Warlock [7812] = 30, -- Sacrifice (warlock) Rank 1 [19438] = 30, [19440] = 30, [19441] = 30, [19442] = 30, [19443] = 30, [27273] = 30, [47985] = 30, [47986] = 30, -- rank 9 [6229] = 30, -- Shadow Ward (warlock) Rank 1 [11739] = 30, [11740] = 30, [28610] = 30, [47890] = 30, [47891] = 30, -- Rank 6 -- Consumables [29674] = 86400, -- Lesser Ward of Shielding [29719] = 86400, -- Greater Ward of Shielding (these have infinite duration, set for a day here :P) [29701] = 86400, [28538] = 120, -- Major Holy Protection Potion [28537] = 120, -- Major Shadow [28536] = 120, -- Major Arcane [28513] = 120, -- Major Nature [28512] = 120, -- Major Frost [28511] = 120, -- Major Fire [7233] = 120, -- Fire [7239] = 120, -- Frost [7242] = 120, -- Shadow Protection Potion [7245] = 120, -- Holy [6052] = 120, -- Nature Protection Potion [53915] = 120, -- Mighty Shadow Protection Potion [53914] = 120, -- Mighty Nature Protection Potion [53913] = 120, -- Mighty Frost Protection Potion [53911] = 120, -- Mighty Fire [53910] = 120, -- Mighty Arcane [17548] = 120, -- Greater Shadow [17546] = 120, -- Greater Nature [17545] = 120, -- Greater Holy [17544] = 120, -- Greater Frost [17543] = 120, -- Greater Fire [17549] = 120, -- Greater Arcane [28527] = 15, -- Fel Blossom [29432] = 3600, -- Frozen Rune usage (Naxx classic) -- Item usage [36481] = 4, -- Arcane Barrier (TK Kael'Thas) Shield [57350] = 6, -- Darkmoon Card: Illusion [17252] = 30, -- Mark of the Dragon Lord (LBRS epic ring) usage [25750] = 15, -- Defiler's Talisman/Talisman of Arathor Rank 1 [25747] = 15, [25746] = 15, [23991] = 15, [31000] = 300, -- Pendant of Shadow's End Usage [30997] = 300, -- Pendant of Frozen Flame Usage [31002] = 300, -- Pendant of the Null Rune [30999] = 300, -- Pendant of Withering [30994] = 300, -- Pendant of Thawing [31000] = 300, -- [23506]= 20, -- Arena Grand Master Usage (Aura of Protection) [12561] = 60, -- Goblin Construction Helmet usage [31771] = 20, -- Runed Fungalcap usage [21956] = 10, -- Mark of Resolution usage [29506] = 20, -- The Burrower's Shell [4057] = 60, -- Flame Deflector [4077] = 60, -- Ice Deflector [39228] = 20, -- Argussian Compass (may not be an actual absorb) -- Item procs [27779] = 30, -- Divine Protection - Priest dungeon set 1/2 Proc [11657] = 20, -- Jang'thraze (Zul Farrak) proc [10368] = 15, -- Uther's Strength proc [37515] = 15, -- Warbringer Armor Proc [42137] = 86400, -- Greater Rune of Warding Proc [26467] = 30, -- Scarab Brooch proc [26470] = 8, -- Scarab Brooch proc (actual) [27539] = 6, -- Thick Obsidian Breatplate proc [28810] = 30, -- Faith Set Proc Armor of Faith [54808] = 12, -- Noise Machine proc Sonic Shield [55019] = 12, -- Sonic Shield (one of these too ought to be wrong) [64411] = 15, -- Blessing of the Ancient (Val'anyr Hammer of Ancient Kings equip effect) [64413] = 8, -- Val'anyr, Hammer of Ancient Kings proc Protection of Ancient Kings -- Misc [40322] = 30, -- Teron's Vengeful Spirit Ghost - Spirit Shield -- Boss abilities [65874] = 15, -- Twin Val'kyr's Shield of Darkness 175000 [67257] = 15, -- 300000 [67256] = 15, -- 700000 [67258] = 15, -- 1200000 [65858] = 15, -- Twin Val'kyr's Shield of Lights 175000 [67260] = 15, -- 300000 [67259] = 15, -- 700000 [67261] = 15, -- 1200000 } local shields = {} local function AuraApplied(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) local spellId, spellName, spellSchool, auraType = ... if AbsorbSpellDuration[spellId] then shields[dstName] = shields[dstName] or {} shields[dstName][spellId] = shields[dstName][spellId] or {} shields[dstName][spellId][srcName] = timestamp + AbsorbSpellDuration[spellId] end end local function AuraRemoved(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) local spellId, spellName, spellSchool, auraType = ... if AbsorbSpellDuration[spellId] then if shields[dstName] and shields[dstName][spellId] and shields[dstName][spellId][dstName] then -- As advised in RecountGuessedAbsorbs, do not remove shields straight away as an absorb can come after the aura removed event. shields[dstName][spellId][dstName] = timestamp + 0.1 end end end local function log_absorb(set, srcName, dstName, absorbed) -- Get the player. local player = Skada:get_player(set, UnitGUID(srcName), UnitName(srcName)) if player then player.totalabsorbs = player.totalabsorbs + absorbed player.absorbs[dstName] = (player.absorbs[dstName] or 0) + absorbed set.absorbs = set.absorbs + absorbed end end local function consider_absorb(absorbed, dstName, srcName, timestamp) local mintime = nil local found_shield_src for shield_id, spells in pairs(shields[dstName]) do for shield_src, ts in pairs(spells) do if ts - timestamp > 0 and (mintime == nil or ts - timestamp < mintime) then found_shield_src = shield_src end end end if found_shield_src then log_absorb(Skada.current, found_shield_src, dstName, absorbed) log_absorb(Skada.total, found_shield_src, dstName, absorbed) end end local function SwingDamage(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) local samount, soverkill, sschool, sresisted, sblocked, absorbed, scritical, sglancing, scrushing = ... if absorbed and absorbed > 0 and dstName and shields[dstName] and srcName then --Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName) consider_absorb(absorbed, dstName, srcName, timestamp) end end local function SpellDamage(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) local spellId, spellName, spellSchool, samount, soverkill, sschool, sresisted, sblocked, absorbed, scritical, sglancing, scrushing = ... if absorbed and absorbed > 0 and dstName and shields[dstName] and srcName then --Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName) consider_absorb(absorbed, dstName, srcName, timestamp) end end local function SpellMissed(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) local spellId, spellName, spellSchool, misstype, amount = ... if misstype == "ABSORB" and amount > 0 and dstName and shields[dstName] and srcName then --Skada:Print(dstName.." absorbed "..absorbed.." from "..srcName.." (MISS)") consider_absorb(amount, dstName, srcName, timestamp) end end local function SwingMissed(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, ...) SpellMissed(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, nil, nil, nil, ...) end function mod:Update(win, set) local nr = 1 local max = 0 for i, player in ipairs(set.players) do if player.totalabsorbs > 0 then local d = win.dataset[nr] or {} win.dataset[nr] = d d.id = player.id d.value = player.totalabsorbs d.label = player.name d.valuetext = Skada:FormatNumber(player.totalabsorbs)..(" (%02.1f%%)"):format(player.totalabsorbs / set.absorbs * 100) d.class = player.class if player.totalabsorbs > max then max = player.totalabsorbs end nr = nr + 1 end end win.metadata.maxvalue = max end function playermod:Enter(win, id, label) playermod.playerid = id playermod.title = label..L["'s Absorbs"] end function playermod:Update(win, set) local player = Skada:find_player(set, self.playerid) local nr = 1 local max = 0 if player then for dstName, absorbed in pairs(player.absorbs) do local d = win.dataset[nr] or {} win.dataset[nr] = d d.id = dstName d.value = absorbed d.label = dstName d.valuetext = Skada:FormatNumber(absorbed)..(" (%02.1f%%)"):format(absorbed / player.totalabsorbs * 100) d.class = select(2, UnitClass(dstName)) if absorbed > max then max = absorbed end nr = nr + 1 end win.metadata.maxvalue = max end end local function getAHPS(set, player) local totaltime = Skada:PlayerActiveTime(set, player) return (player.totalabsorbs + (player.healing or 0)) / math.max(1,totaltime) end function combined:Update(win, set) local nr = 1 local max = 0 for i, player in ipairs(set.players) do if player.totalabsorbs > 0 or player.healing > 0 then local d = win.dataset[nr] or {} win.dataset[nr] = d d.id = player.id d.value = player.totalabsorbs + (player.healing or 0) d.label = player.name -- d.valuetext = Skada:FormatNumber(player.totalabsorbs + (player.healing or 0))..(" (%02.1f%%)"):format((player.totalabsorbs + (player.healing or 0)) / (set.absorbs + (set.healing or 0)) * 100) d.valuetext = Skada:FormatNumber(player.totalabsorbs + (player.healing or 0))..string.format(" (%02.1f,", getAHPS(set, player))..string.format(" %02.1f%%)", (player.totalabsorbs + (player.healing or 0)) / (set.absorbs + (set.healing or 0)) * 100) d.class = player.class if (player.totalabsorbs + player.healing) > max then max = player.totalabsorbs + player.healing end nr = nr + 1 end end win.metadata.maxvalue = max end function mod:OnEnable() combined.metadata = {showspots = 1, click1 = playermod} mod.metadata = {showspots = 1, click1 = playermod} playermod.metadata = {} Skada:RegisterForCL(AuraApplied, 'SPELL_AURA_REFRESH', {src_is_interesting_nopets = true}) Skada:RegisterForCL(AuraApplied, 'SPELL_AURA_APPLIED', {src_is_interesting_nopets = true}) Skada:RegisterForCL(AuraRemoved, 'SPELL_AURA_REMOVED', {src_is_interesting_nopets = true}) Skada:RegisterForCL(SpellDamage, 'DAMAGE_SHIELD', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellDamage, 'SPELL_DAMAGE', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellDamage, 'SPELL_PERIODIC_DAMAGE', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellDamage, 'SPELL_BUILDING_DAMAGE', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellDamage, 'RANGE_DAMAGE', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SwingDamage, 'SWING_DAMAGE', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellMissed, 'SPELL_MISSED', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellMissed, 'SPELL_PERIODIC_MISSED', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellMissed, 'SPELL_BUILDING_MISSED', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SpellMissed, 'RANGE_MISSED', {dst_is_interesting_nopets = true}) Skada:RegisterForCL(SwingMissed, 'SWING_MISSED', {dst_is_interesting_nopets = true}) Skada:AddMode(self) Skada:AddMode(combined) end function mod:OnDisable() Skada:RemoveMode(self) Skada:RemoveMode(combined) end function mod:AddToTooltip(set, tooltip) end function mod:GetSetSummary(set) return Skada:FormatNumber(set.absorbs) end function combined:GetSetSummary(set) return Skada:FormatNumber(set.absorbs + set.healing) end -- Called by Skada when a new player is added to a set. function mod:AddPlayerAttributes(player) if not player.absorbs then player.absorbs = {} player.totalabsorbs = 0 end end -- Called by Skada when a new set is created. -- Conveniently this is when a new fight starts, so we can clear our shield cache. function mod:AddSetAttributes(set) if not set.absorbs then set.absorbs = 0 end shields = {} end