第一次发版v0.1.0
This commit is contained in:
293
CombatLog.lua
Normal file
293
CombatLog.lua
Normal file
@@ -0,0 +1,293 @@
|
||||
NanamiPlates_CombatLog = {}
|
||||
|
||||
local NP = NanamiPlates
|
||||
local SpellDB = NanamiPlates_SpellDB
|
||||
local Auras = NanamiPlates_Auras
|
||||
|
||||
local string_gsub = string.gsub
|
||||
local string_gfind = string.gfind
|
||||
local string_find = string.find
|
||||
local string_sub = string.sub
|
||||
local GetTime = GetTime
|
||||
local UnitExists = UnitExists
|
||||
local UnitName = UnitName
|
||||
local UnitGUID = UnitGUID
|
||||
|
||||
local superwow_active = NP.superwow_active
|
||||
|
||||
local castTracker
|
||||
local recentMeleeHits
|
||||
|
||||
local function InitReferences()
|
||||
castTracker = NP.castTracker
|
||||
recentMeleeHits = NP.recentMeleeHits
|
||||
end
|
||||
|
||||
local function cmatch(str, pattern)
|
||||
if not str or not pattern then return nil end
|
||||
local pat = string_gsub(pattern, "%%%d?%$?s", "(.+)")
|
||||
pat = string_gsub(pat, "%%%d?%$?d", "(%d+)")
|
||||
for a, b, c, d in string_gfind(str, pat) do
|
||||
return a, b, c, d
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local castIcons = {
|
||||
["Fireball"] = "Interface\\Icons\\Spell_Fire_FlameBolt",
|
||||
["Frostbolt"] = "Interface\\Icons\\Spell_Frost_FrostBolt02",
|
||||
["Shadow Bolt"] = "Interface\\Icons\\Spell_Shadow_ShadowBolt",
|
||||
["Greater Heal"] = "Interface\\Icons\\Spell_Holy_GreaterHeal",
|
||||
["Flash Heal"] = "Interface\\Icons\\Spell_Holy_FlashHeal",
|
||||
["Lightning Bolt"] = "Interface\\Icons\\Spell_Nature_Lightning",
|
||||
["Chain Lightning"] = "Interface\\Icons\\Spell_Nature_ChainLightning",
|
||||
["Healing Wave"] = "Interface\\Icons\\Spell_Nature_MagicImmunity",
|
||||
["Fear"] = "Interface\\Icons\\Spell_Shadow_Possession",
|
||||
["Polymorph"] = "Interface\\Icons\\Spell_Nature_Polymorph",
|
||||
["Smite"] = "Interface\\Icons\\Spell_Holy_HolySmite",
|
||||
["Mind Blast"] = "Interface\\Icons\\Spell_Shadow_UnholyFrenzy",
|
||||
["Holy Light"] = "Interface\\Icons\\Spell_Holy_HolyLight",
|
||||
["Starfire"] = "Interface\\Icons\\Spell_Arcane_StarFire",
|
||||
["Wrath"] = "Interface\\Icons\\Spell_Nature_AbolishMagic",
|
||||
["Entangling Roots"] = "Interface\\Icons\\Spell_Nature_StrangleVines",
|
||||
["Moonfire"] = "Interface\\Icons\\Spell_Nature_StarFall",
|
||||
["Regrowth"] = "Interface\\Icons\\Spell_Nature_ResistNature",
|
||||
["Rejuvenation"] = "Interface\\Icons\\Spell_Nature_Rejuvenation",
|
||||
}
|
||||
|
||||
local function ParseCastStart(msg)
|
||||
if not msg then return end
|
||||
if not castTracker then castTracker = NP.castTracker end
|
||||
if not castTracker then return end
|
||||
|
||||
local unit, spell = nil, nil
|
||||
for u, s in string_gfind(msg, "(.+) begins to cast (.+)%.") do
|
||||
unit, spell = u, s
|
||||
end
|
||||
if not unit then
|
||||
for u, s in string_gfind(msg, "(.+) begins to perform (.+)%.") do
|
||||
unit, spell = u, s
|
||||
end
|
||||
end
|
||||
|
||||
if unit and spell then
|
||||
if not castTracker[unit] then castTracker[unit] = {} end
|
||||
table.insert(castTracker[unit], {
|
||||
spell = spell,
|
||||
startTime = GetTime(),
|
||||
duration = 2000,
|
||||
icon = castIcons[spell],
|
||||
})
|
||||
end
|
||||
|
||||
local interruptedUnit = nil
|
||||
for u in string_gfind(msg, "(.+)'s .+ is interrupted%.") do interruptedUnit = u end
|
||||
if not interruptedUnit then
|
||||
for u in string_gfind(msg, "(.+)'s .+ fails%.") do interruptedUnit = u end
|
||||
end
|
||||
if interruptedUnit and castTracker[interruptedUnit] then
|
||||
table.remove(castTracker[interruptedUnit], 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function ParseAttackHit(msg)
|
||||
if not msg then return end
|
||||
local attacker, victim = nil, nil
|
||||
|
||||
if string_sub(msg, 1, 8) == "You hit " then
|
||||
local forPos = string_find(msg, " for ")
|
||||
if forPos then
|
||||
victim = string_sub(msg, 9, forPos - 1)
|
||||
attacker = "You"
|
||||
end
|
||||
elseif string_sub(msg, 1, 9) == "You crit " then
|
||||
local forPos = string_find(msg, " for ")
|
||||
if forPos then
|
||||
victim = string_sub(msg, 10, forPos - 1)
|
||||
attacker = "You"
|
||||
end
|
||||
end
|
||||
|
||||
if attacker == "You" and victim and Auras then
|
||||
Auras:SealHandler(attacker, victim)
|
||||
end
|
||||
|
||||
if not recentMeleeHits then recentMeleeHits = NP.recentMeleeHits end
|
||||
if not recentMeleeHits then return end
|
||||
|
||||
if attacker == "You" and victim then
|
||||
recentMeleeHits[victim] = GetTime()
|
||||
if superwow_active and UnitExists("target") and UnitName("target") == victim then
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid then recentMeleeHits[guid] = GetTime() end
|
||||
end
|
||||
end
|
||||
|
||||
if not victim then
|
||||
for a, v in string_gfind(msg, "(.+) hits (.-) for %d+%.") do
|
||||
attacker, victim = a, v
|
||||
break
|
||||
end
|
||||
end
|
||||
if not victim then
|
||||
for a, v in string_gfind(msg, "(.+) crits (.-) for %d+%.") do
|
||||
attacker, victim = a, v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if attacker == "You" and victim and recentMeleeHits then
|
||||
recentMeleeHits[victim] = GetTime()
|
||||
if superwow_active and UnitExists("target") and UnitName("target") == victim then
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid then recentMeleeHits[guid] = GetTime() end
|
||||
end
|
||||
end
|
||||
|
||||
if attacker and victim and Auras then
|
||||
Auras:SealHandler(attacker, victim)
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle spell-related combat log events
|
||||
function NanamiPlates_CombatLog.HandleSpellEvent(evnt, msg)
|
||||
if not msg then return end
|
||||
|
||||
InitReferences()
|
||||
|
||||
if evnt == "CHAT_MSG_SPELL_AURA_GONE_OTHER" or evnt == "CHAT_MSG_SPELL_AURA_GONE_SELF" then
|
||||
local target, effect
|
||||
for t, e in string_gfind(msg, "(.+) is no longer afflicted by (.+)%.") do
|
||||
target, effect = t, e
|
||||
end
|
||||
if not target then
|
||||
for e in string_gfind(msg, "(.+) fades from .+%.") do effect = e end
|
||||
end
|
||||
if effect and SpellDB and SpellDB.objects then
|
||||
for unit, levels in pairs(SpellDB.objects) do
|
||||
for lvl, effects in pairs(levels) do
|
||||
if effects[effect] then effects[effect] = nil end
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evnt == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER" then
|
||||
if SpellDB then SpellDB:RemovePending() end
|
||||
return
|
||||
end
|
||||
|
||||
-- Check for pending spell resolution
|
||||
if SpellDB and SpellDB.pending and SpellDB.pending[3] then
|
||||
local effect = SpellDB.pending[3]
|
||||
|
||||
-- Check if spell was resisted/missed/etc
|
||||
local removePatterns = NP.REMOVE_PENDING_PATTERNS
|
||||
if removePatterns then
|
||||
for _, pattern in ipairs(removePatterns) do
|
||||
if cmatch(msg, pattern) then
|
||||
SpellDB:RemovePending()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for spell hit/application
|
||||
if string_find(msg, effect) then
|
||||
local affTarget = nil
|
||||
for t in string_gfind(msg, "(.+) is afflicted by " .. effect) do
|
||||
affTarget = t
|
||||
end
|
||||
if affTarget or string_find(msg, "Your " .. effect) then
|
||||
SpellDB:PersistPending(effect)
|
||||
|
||||
if SpellDB.OWNER_BOUND_DEBUFFS and SpellDB.OWNER_BOUND_DEBUFFS[effect] then
|
||||
local targetName = affTarget or (UnitExists("target") and UnitName("target"))
|
||||
if targetName then
|
||||
SpellDB:TrackOwnerBoundDebuff(targetName, effect)
|
||||
if superwow_active and UnitExists("target") and UnitName("target") == targetName then
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid then SpellDB:TrackOwnerBoundDebuff(guid, effect) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback: track debuffs via "afflicted by" messages using recentCasts.
|
||||
-- Handles instant-cast DOTs (e.g. Corruption) whose pending was overwritten
|
||||
-- by a subsequent cast before the combat log confirmed application.
|
||||
do
|
||||
local affTarget, affEffect
|
||||
for t, e in string_gfind(msg, "(.+) is afflicted by (.+)%.") do
|
||||
affTarget, affEffect = t, e
|
||||
end
|
||||
if affEffect and SpellDB and SpellDB.recentCasts then
|
||||
if SpellDB.WARLOCK_CURSES and SpellDB.WARLOCK_CURSES[affEffect] then
|
||||
local hasMalediction = SpellDB.HasMalediction and SpellDB:HasMalediction()
|
||||
for otherCurse, _ in pairs(SpellDB.WARLOCK_CURSES) do
|
||||
if otherCurse ~= affEffect then
|
||||
local otherRecent = SpellDB.recentCasts[otherCurse]
|
||||
local thisRecent = SpellDB.recentCasts[affEffect]
|
||||
if otherRecent and thisRecent and otherRecent.time > thisRecent.time then
|
||||
if hasMalediction and SpellDB:CanCursesCoexist(affEffect, otherCurse) then
|
||||
-- skip: these two curses can coexist
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local recent = SpellDB.recentCasts[affEffect]
|
||||
if recent and (GetTime() - recent.time) < 4 then
|
||||
SpellDB:RefreshEffect(affTarget, 0, affEffect, recent.duration, true)
|
||||
|
||||
if superwow_active and UnitExists("target") and UnitName("target") == affTarget then
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid then
|
||||
SpellDB:RefreshEffect(guid, 0, affEffect, recent.duration, true)
|
||||
end
|
||||
end
|
||||
|
||||
if SpellDB.OWNER_BOUND_DEBUFFS and SpellDB.OWNER_BOUND_DEBUFFS[affEffect] then
|
||||
SpellDB:TrackOwnerBoundDebuff(affTarget, affEffect, recent.duration)
|
||||
if superwow_active and UnitExists("target") and UnitName("target") == affTarget then
|
||||
local guid = UnitGUID and UnitGUID("target")
|
||||
if guid then SpellDB:TrackOwnerBoundDebuff(guid, affEffect, recent.duration) end
|
||||
end
|
||||
end
|
||||
|
||||
if Auras and Auras.timers then
|
||||
Auras.timers[affTarget .. "_" .. affEffect] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse cast starts for non-SuperWoW fallback
|
||||
if NP.SPELL_DAMAGE_EVENTS and NP.SPELL_DAMAGE_EVENTS[evnt] then
|
||||
ParseCastStart(msg)
|
||||
end
|
||||
|
||||
-- Holy Strike handler for Paladin
|
||||
if evnt == "CHAT_MSG_SPELL_SELF_DAMAGE" and Auras then
|
||||
Auras:HolyStrikeHandler(msg)
|
||||
end
|
||||
end
|
||||
|
||||
function NanamiPlates_CombatLog.HandleCombatEvent(evnt, msg)
|
||||
if not msg then return end
|
||||
InitReferences()
|
||||
ParseAttackHit(msg)
|
||||
end
|
||||
|
||||
NanamiPlates_CombatLog.cmatch = cmatch
|
||||
NanamiPlates_CombatLog.castIcons = castIcons
|
||||
NanamiPlates_CombatLog.ParseCastStart = ParseCastStart
|
||||
NanamiPlates_CombatLog.ParseAttackHit = ParseAttackHit
|
||||
NanamiPlates_CombatLog.InitReferences = InitReferences
|
||||
|
||||
NanamiPlates.CombatLog = NanamiPlates_CombatLog
|
||||
Reference in New Issue
Block a user