local NanamiDPS = NanamiDPS local Parser = NanamiDPS.Parser -- Pattern utility functions (from ShaguDPS) local sanitize_cache = {} local function sanitize(pattern) if not sanitize_cache[pattern] then local ret = pattern ret = gsub(ret, "([%+%-%*%(%)%?%[%]%^])", "%%%1") ret = gsub(ret, "%d%$", "") ret = gsub(ret, "(%%%a)", "%(%1+%)") ret = gsub(ret, "%%s%+", ".+") ret = gsub(ret, "%(.%+%)%(%%d%+%)", "%(.-%)%(%%d%+%)") sanitize_cache[pattern] = ret end return sanitize_cache[pattern] end local capture_cache = {} local function captures(pat) local r = capture_cache if not r[pat] then r[pat] = { nil, nil, nil, nil, nil } for a, b, c, d, e in string.gfind(gsub(pat, "%((.+)%)", "%1"), gsub(pat, "%d%$", "%%(.-)$")) do r[pat][1] = tonumber(a) r[pat][2] = tonumber(b) r[pat][3] = tonumber(c) r[pat][4] = tonumber(d) r[pat][5] = tonumber(e) end end return r[pat][1], r[pat][2], r[pat][3], r[pat][4], r[pat][5] end local ra, rb, rc, rd, re, a, b, c, d, e, match, num, va, vb, vc, vd, ve local function cfind(str, pat) a, b, c, d, e = captures(pat) match, num, va, vb, vc, vd, ve = string.find(str, sanitize(pat)) ra = e == 1 and ve or d == 1 and vd or c == 1 and vc or b == 1 and vb or va rb = e == 2 and ve or d == 2 and vd or c == 2 and vc or a == 2 and va or vb rc = e == 3 and ve or d == 3 and vd or a == 3 and va or b == 3 and vb or vc rd = e == 4 and ve or a == 4 and va or c == 4 and vc or b == 4 and vb or vd re = a == 5 and va or d == 5 and vd or c == 5 and vc or b == 5 and vb or ve return match, num, ra, rb, rc, rd, re end ----------------------------------------------------------------------- -- Combat log string groups ----------------------------------------------------------------------- local combatlog_strings = { -- Damage: melee ["Hit Damage (self vs. other)"] = { COMBATHITSELFOTHER, COMBATHITSCHOOLSELFOTHER, COMBATHITCRITSELFOTHER, COMBATHITCRITSCHOOLSELFOTHER, }, ["Hit Damage (other vs. self)"] = { COMBATHITOTHERSELF, COMBATHITCRITOTHERSELF, COMBATHITSCHOOLOTHERSELF, COMBATHITCRITSCHOOLOTHERSELF, }, ["Hit Damage (other vs. other)"] = { COMBATHITOTHEROTHER, COMBATHITCRITOTHEROTHER, COMBATHITSCHOOLOTHEROTHER, COMBATHITCRITSCHOOLOTHEROTHER, }, -- Damage: spell ["Spell Damage (self vs. self/other)"] = { SPELLLOGSCHOOLSELFSELF, SPELLLOGCRITSCHOOLSELFSELF, SPELLLOGSELFSELF, SPELLLOGCRITSELFSELF, SPELLLOGSCHOOLSELFOTHER, SPELLLOGCRITSCHOOLSELFOTHER, SPELLLOGSELFOTHER, SPELLLOGCRITSELFOTHER, }, ["Spell Damage (other vs. self)"] = { SPELLLOGSCHOOLOTHERSELF, SPELLLOGCRITSCHOOLOTHERSELF, SPELLLOGOTHERSELF, SPELLLOGCRITOTHERSELF, }, ["Spell Damage (other vs. other)"] = { SPELLLOGSCHOOLOTHEROTHER, SPELLLOGCRITSCHOOLOTHEROTHER, SPELLLOGOTHEROTHER, SPELLLOGCRITOTHEROTHER, }, -- Damage: shields ["Shield Damage (self vs. other)"] = { DAMAGESHIELDSELFOTHER, }, ["Shield Damage (other vs. self/other)"] = { DAMAGESHIELDOTHERSELF, DAMAGESHIELDOTHEROTHER, }, -- Damage: periodic ["Periodic Damage (self/other vs. other)"] = { PERIODICAURADAMAGESELFOTHER, PERIODICAURADAMAGEOTHEROTHER, }, ["Periodic Damage (self/other vs. self)"] = { PERIODICAURADAMAGESELFSELF, PERIODICAURADAMAGEOTHERSELF, }, -- Healing ["Heal (self vs. self/other)"] = { HEALEDCRITSELFSELF, HEALEDSELFSELF, HEALEDCRITSELFOTHER, HEALEDSELFOTHER, }, ["Heal (other vs. self/other)"] = { HEALEDCRITOTHERSELF, HEALEDOTHERSELF, HEALEDCRITOTHEROTHER, HEALEDOTHEROTHER, }, ["Periodic Heal (self/other vs. other)"] = { PERIODICAURAHEALSELFOTHER, PERIODICAURAHEALOTHEROTHER, }, ["Periodic Heal (other vs. self/other)"] = { PERIODICAURAHEALSELFSELF, PERIODICAURAHEALOTHERSELF, }, } ----------------------------------------------------------------------- -- Event -> pattern mapping ----------------------------------------------------------------------- local combatlog_events = { -- Damage: melee ["CHAT_MSG_COMBAT_SELF_HITS"] = combatlog_strings["Hit Damage (self vs. other)"], ["CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS"] = combatlog_strings["Hit Damage (other vs. self)"], ["CHAT_MSG_COMBAT_PARTY_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], ["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], ["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], ["CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], ["CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], ["CHAT_MSG_COMBAT_PET_HITS"] = combatlog_strings["Hit Damage (other vs. other)"], -- Damage: spell ["CHAT_MSG_SPELL_SELF_DAMAGE"] = combatlog_strings["Spell Damage (self vs. self/other)"], ["CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE"] = combatlog_strings["Spell Damage (other vs. self)"], ["CHAT_MSG_SPELL_PARTY_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], ["CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], ["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], ["CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], ["CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], ["CHAT_MSG_SPELL_PET_DAMAGE"] = combatlog_strings["Spell Damage (other vs. other)"], -- Damage: shields ["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] = combatlog_strings["Shield Damage (self vs. other)"], ["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] = combatlog_strings["Shield Damage (other vs. self/other)"], -- Damage: periodic ["CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE"] = combatlog_strings["Periodic Damage (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] = combatlog_strings["Periodic Damage (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE"] = combatlog_strings["Periodic Damage (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE"] = combatlog_strings["Periodic Damage (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] = combatlog_strings["Periodic Damage (self/other vs. self)"], -- Healing ["CHAT_MSG_SPELL_SELF_BUFF"] = combatlog_strings["Heal (self vs. self/other)"], ["CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF"] = combatlog_strings["Heal (other vs. self/other)"], ["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] = combatlog_strings["Heal (other vs. self/other)"], ["CHAT_MSG_SPELL_PARTY_BUFF"] = combatlog_strings["Heal (other vs. self/other)"], ["CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS"] = combatlog_strings["Periodic Heal (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS"] = combatlog_strings["Periodic Heal (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] = combatlog_strings["Periodic Heal (self/other vs. other)"], ["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] = combatlog_strings["Periodic Heal (other vs. self/other)"], } ----------------------------------------------------------------------- -- Pattern -> parser function mapping ----------------------------------------------------------------------- local combatlog_parser = { -- Spell damage: self vs self [SPELLLOGSCHOOLSELFSELF] = function(d, attack, value, school) return d.source, attack, d.target, value, school, "damage" end, [SPELLLOGCRITSCHOOLSELFSELF] = function(d, attack, value, school) return d.source, attack, d.target, value, school, "damage" end, [SPELLLOGSELFSELF] = function(d, attack, value) return d.source, attack, d.target, value, d.school, "damage" end, [SPELLLOGCRITSELFSELF] = function(d, attack, value) return d.source, attack, d.target, value, d.school, "damage" end, [PERIODICAURADAMAGESELFSELF] = function(d, value, school, attack) return d.source, attack, d.target, value, school, "damage" end, -- Spell damage: self vs other [SPELLLOGSCHOOLSELFOTHER] = function(d, attack, target, value, school) return d.source, attack, target, value, school, "damage" end, [SPELLLOGCRITSCHOOLSELFOTHER] = function(d, attack, target, value, school) return d.source, attack, target, value, school, "damage" end, [SPELLLOGSELFOTHER] = function(d, attack, target, value) return d.source, attack, target, value, d.school, "damage" end, [SPELLLOGCRITSELFOTHER] = function(d, attack, target, value) return d.source, attack, target, value, d.school, "damage" end, [PERIODICAURADAMAGESELFOTHER] = function(d, target, value, school, attack) return d.source, attack, target, value, school, "damage" end, -- Melee damage: self vs other [COMBATHITSELFOTHER] = function(d, target, value) return d.source, d.attack, target, value, d.school, "damage" end, [COMBATHITCRITSELFOTHER] = function(d, target, value) return d.source, d.attack, target, value, d.school, "damage" end, [COMBATHITSCHOOLSELFOTHER] = function(d, target, value, school) return d.source, d.attack, target, value, school, "damage" end, [COMBATHITCRITSCHOOLSELFOTHER] = function(d, target, value, school) return d.source, d.attack, target, value, school, "damage" end, -- Shield damage: self vs other [DAMAGESHIELDSELFOTHER] = function(d, value, school, target) return d.source, "Reflect (" .. school .. ")", target, value, school, "damage" end, -- Spell damage: other vs self [SPELLLOGSCHOOLOTHERSELF] = function(d, source, attack, value, school) return source, attack, d.target, value, school, "damage" end, [SPELLLOGCRITSCHOOLOTHERSELF] = function(d, source, attack, value, school) return source, attack, d.target, value, school, "damage" end, [SPELLLOGOTHERSELF] = function(d, source, attack, value) return source, attack, d.target, value, d.school, "damage" end, [SPELLLOGCRITOTHERSELF] = function(d, source, attack, value) return source, attack, d.target, value, d.school, "damage" end, [PERIODICAURADAMAGEOTHERSELF] = function(d, value, school, source, attack) return source, attack, d.target, value, school, "damage" end, -- Melee damage: other vs self [COMBATHITOTHERSELF] = function(d, source, value) return source, d.attack, d.target, value, d.school, "damage" end, [COMBATHITCRITOTHERSELF] = function(d, source, value) return source, d.attack, d.target, value, d.school, "damage" end, [COMBATHITSCHOOLOTHERSELF] = function(d, source, value, school) return source, d.attack, d.target, value, school, "damage" end, [COMBATHITCRITSCHOOLOTHERSELF] = function(d, source, value, school) return source, d.attack, d.target, value, school, "damage" end, -- Spell damage: other vs other [SPELLLOGSCHOOLOTHEROTHER] = function(d, source, attack, target, value, school) return source, attack, target, value, school, "damage" end, [SPELLLOGCRITSCHOOLOTHEROTHER] = function(d, source, attack, target, value, school) return source, attack, target, value, school, "damage" end, [SPELLLOGOTHEROTHER] = function(d, source, attack, target, value) return source, attack, target, value, d.school, "damage" end, [SPELLLOGCRITOTHEROTHER] = function(d, source, attack, target, value, school) return source, attack, target, value, school, "damage" end, [PERIODICAURADAMAGEOTHEROTHER] = function(d, target, value, school, source, attack) return source, attack, target, value, school, "damage" end, -- Melee damage: other vs other [COMBATHITOTHEROTHER] = function(d, source, target, value) return source, d.attack, target, value, d.school, "damage" end, [COMBATHITCRITOTHEROTHER] = function(d, source, target, value) return source, d.attack, target, value, d.school, "damage" end, [COMBATHITSCHOOLOTHEROTHER] = function(d, source, target, value, school) return source, d.attack, target, value, school, "damage" end, [COMBATHITCRITSCHOOLOTHEROTHER] = function(d, source, target, value, school) return source, d.attack, target, value, school, "damage" end, -- Shield damage: other [DAMAGESHIELDOTHERSELF] = function(d, source, value, school) return source, "Reflect (" .. school .. ")", d.target, value, school, "damage" end, [DAMAGESHIELDOTHEROTHER] = function(d, source, value, school, target) return source, "Reflect (" .. school .. ")", target, value, school, "damage" end, -- Healing: other vs self [HEALEDCRITOTHERSELF] = function(d, source, spell, value) return source, spell, d.target, value, d.school, "heal" end, [HEALEDOTHERSELF] = function(d, source, spell, value) return source, spell, d.target, value, d.school, "heal" end, [PERIODICAURAHEALOTHERSELF] = function(d, value, source, spell) return source, spell, d.target, value, d.school, "heal" end, -- Healing: self vs self [HEALEDCRITSELFSELF] = function(d, spell, value) return d.source, spell, d.target, value, d.school, "heal" end, [HEALEDSELFSELF] = function(d, spell, value) return d.source, spell, d.target, value, d.school, "heal" end, [PERIODICAURAHEALSELFSELF] = function(d, value, spell) return d.source, spell, d.target, value, d.school, "heal" end, -- Healing: self vs other [HEALEDCRITSELFOTHER] = function(d, spell, target, value) return d.source, spell, target, value, d.school, "heal" end, [HEALEDSELFOTHER] = function(d, spell, target, value) return d.source, spell, target, value, d.school, "heal" end, [PERIODICAURAHEALSELFOTHER] = function(d, target, value, spell) return d.source, spell, target, value, d.school, "heal" end, -- Healing: other vs other [HEALEDCRITOTHEROTHER] = function(d, source, spell, target, value) return source, spell, target, value, d.school, "heal" end, [HEALEDOTHEROTHER] = function(d, source, spell, target, value) return source, spell, target, value, d.school, "heal" end, [PERIODICAURAHEALOTHEROTHER] = function(d, target, value, source, spell) return source, spell, target, value, d.school, "heal" end, } ----------------------------------------------------------------------- -- Register combat log events ----------------------------------------------------------------------- for evt in pairs(combatlog_events) do Parser:RegisterEvent(evt) end -- Preload patterns for pattern in pairs(combatlog_parser) do sanitize(pattern) end ----------------------------------------------------------------------- -- Death events ----------------------------------------------------------------------- Parser:RegisterEvent("CHAT_MSG_COMBAT_FRIENDLY_DEATH") Parser:RegisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH") ----------------------------------------------------------------------- -- Interrupt patterns ----------------------------------------------------------------------- local interrupt_strings = {} if SPELLINTERRUPTSELFOTHER then table.insert(interrupt_strings, SPELLINTERRUPTSELFOTHER) end if SPELLINTERRUPTOTHERSELF then table.insert(interrupt_strings, SPELLINTERRUPTOTHERSELF) end if SPELLINTERRUPTOTHEROTHER then table.insert(interrupt_strings, SPELLINTERRUPTOTHEROTHER) end local interrupt_parser = {} if SPELLINTERRUPTSELFOTHER then interrupt_parser[SPELLINTERRUPTSELFOTHER] = function(d, spell, target, interrupted) return d.source, spell, target, interrupted end end if SPELLINTERRUPTOTHERSELF then interrupt_parser[SPELLINTERRUPTOTHERSELF] = function(d, source, spell, interrupted) return source, spell, d.target, interrupted end end if SPELLINTERRUPTOTHEROTHER then interrupt_parser[SPELLINTERRUPTOTHEROTHER] = function(d, source, spell, target, interrupted) return source, spell, target, interrupted end end for pat in pairs(interrupt_parser) do sanitize(pat) end ----------------------------------------------------------------------- -- Dispel patterns ----------------------------------------------------------------------- local dispel_strings = {} local dispel_parser = {} if AURADISPELSELF2 then table.insert(dispel_strings, AURADISPELSELF2) dispel_parser[AURADISPELSELF2] = function(d, spell, aura) return d.source, spell, d.target, aura end end if AURADISPELOTHER2 then table.insert(dispel_strings, AURADISPELOTHER2) dispel_parser[AURADISPELOTHER2] = function(d, source, spell, target, aura) return source, spell, target, aura end end for pat in pairs(dispel_parser) do sanitize(pat) end ----------------------------------------------------------------------- -- Aura gain patterns for threat-clearing abilities ----------------------------------------------------------------------- local auraGainPatterns = {} if AURAADDEDSELFHELPFUL then table.insert(auraGainPatterns, { pat = sanitize(AURAADDEDSELFHELPFUL), self = true }) end if AURAADDEDOTHERHELPFUL then table.insert(auraGainPatterns, { pat = sanitize(AURAADDEDOTHERHELPFUL), self = false }) end Parser:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS") Parser:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS") Parser:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS") Parser:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_SELF") Parser:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_OTHER") Parser:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_PARTY") ----------------------------------------------------------------------- -- Main event handler ----------------------------------------------------------------------- local defaults = {} local absorb = ABSORB_TRAILER and sanitize(ABSORB_TRAILER) or nil local resist = RESIST_TRAILER and sanitize(RESIST_TRAILER) or nil local empty, physical, autohit = "", "physical", "Auto Hit" local player = UnitName("player") Parser:SetScript("OnEvent", function() if not arg1 then return end -- Death events if event == "CHAT_MSG_COMBAT_FRIENDLY_DEATH" or event == "CHAT_MSG_COMBAT_HOSTILE_DEATH" then if UNITDIESOTHER then local deathPat = sanitize(UNITDIESOTHER) local _, _, deadName = string.find(arg1, deathPat) if deadName then Parser:ProcessDeath(deadName) end end if UNITDIESSELF then local selfPat = sanitize(UNITDIESSELF) if string.find(arg1, selfPat) then Parser:ProcessDeath(player) end end return end -- Threat-clearing aura detection (Feign Death, Vanish, etc.) local TC = NanamiDPS.ThreatCoefficients local TE = NanamiDPS.ThreatEngine if TC and TE and TC.threatWipeEvents then for auraName, wipeType in pairs(TC.threatWipeEvents) do if string.find(arg1, auraName, 1, true) then local who = nil if string.find(arg1, player, 1, true) or (AURAADDEDSELFHELPFUL and string.find(arg1, "You gain", 1, true)) then who = player else for aIdx, aPat in pairs(auraGainPatterns) do if not aPat.self then local _, _, aName = string.find(arg1, aPat.pat) if aName then who = aName; break end end end end if who then TE:WipePlayerThreat(who, wipeType) local DS = NanamiDPS.DataStore if DS then DS:AddThreat(who, -(DS:GetPlayerThreat(who) or 0)) end end return end end end -- Strip absorb/resist suffixes if absorb then arg1 = string.gsub(arg1, absorb, empty) end if resist then arg1 = string.gsub(arg1, resist, empty) end defaults.source = player defaults.target = player defaults.school = physical defaults.attack = autohit defaults.spell = UNKNOWN or "Unknown" defaults.value = 0 -- Check interrupt patterns first (they share spell damage events) for _, pat in ipairs(interrupt_strings) do local result, num, a1, a2, a3, a4, a5 = cfind(arg1, pat) if result then local src, spell, tgt, interrupted = interrupt_parser[pat](defaults, a1, a2, a3, a4, a5) Parser:ProcessInterrupt(src, spell, tgt, interrupted) return end end -- Check dispel patterns for _, pat in ipairs(dispel_strings) do local result, num, a1, a2, a3, a4, a5 = cfind(arg1, pat) if result then local src, spell, tgt, aura = dispel_parser[pat](defaults, a1, a2, a3, a4, a5) Parser:ProcessDispel(src, spell, tgt, aura) return end end -- Standard combat log patterns local patterns = combatlog_events[event] if not patterns then return end for _, pattern in pairs(patterns) do local result, num, a1, a2, a3, a4, a5 = cfind(arg1, pattern) if result then local source, spell, target, value, school, dtype = combatlog_parser[pattern](defaults, a1, a2, a3, a4, a5) if dtype == "damage" then Parser:ProcessDamage(source, spell, target, value, school) elseif dtype == "heal" then Parser:ProcessHealing(source, spell, target, value, school) end return end end end)