Add files via upload

This commit is contained in:
Relationship
2026-07-12 14:57:27 +01:00
committed by GitHub
parent d0329d309e
commit 28ef2ed7c9
7 changed files with 621 additions and 105 deletions
+181
View File
@@ -94,6 +94,25 @@ local playerInCombat = false
-- members when the player is NOT in combat themselves.
local groupInCombat = false
-- ============================================================
-- Multi-mob incoming-attack tracking (v2.6.2)
--
-- Vanilla WoW 1.12 exposes NO unit token for a mob that isn't
-- currently targeted / mouseovered / party-targeted. That meant
-- earlier versions only lit the threat bar on the ONE enemy the
-- player had targeted. To show bars for EVERY enemy in combat with
-- the group, we parse the incoming-combat chat events (mob->player
-- and mob->party) and remember which mob names have hit or missed
-- a group member recently. UpdateAllPlates then treats any plate
-- whose mob name is in this table as "in combat" and displays a
-- threat bar even when its health bar hasn't dropped yet and no
-- unit token is available.
-- ============================================================
local mobsAttackingGroup = {}
local mobsAttackingPlayer = {}
local MOB_ATTACK_TTL = 6.0 -- seconds a mob stays flagged as attacking
local INCOMING_PARSERS = {}
-- ============================================================
-- Safe API helpers
-- ============================================================
@@ -414,6 +433,67 @@ local function BuildAllParsers()
" party damage, " .. table.getn(PARTY_HEAL_PARSERS) ..
" party heal, " .. table.getn(PARTY_PERIODIC_PARSERS) ..
" party periodic parsers built")
-- ========================================
-- INCOMING (mob -> player/party) parsers (v2.6.2)
-- Used to detect that a specific mob NAME is actively engaged
-- with the group even though we have no unit token for it.
-- We only care about the first capture (the mob / source name).
-- Any OTHERSELF or OTHEROTHER GlobalString whose first %s is the
-- attacker qualifies. Some of these constants may not exist on
-- every Turtle/OctoWoW build; missing ones are silently skipped.
-- ========================================
local incomingSources = {
-- Melee mob -> player
"COMBATHITOTHERSELF", "COMBATHITCRITOTHERSELF",
"COMBATHITSCHOOLOTHERSELF", "COMBATHITCRITSCHOOLOTHERSELF",
"MISSEDOTHERSELF",
"VSDODGEOTHERSELF", "VSPARRYOTHERSELF", "VSBLOCKOTHERSELF",
"VSABSORBOTHERSELF", "VSIMMUNEOTHERSELF", "VSRESISTOTHERSELF",
"VSDEFLECTOTHERSELF", "VSEVADEOTHERSELF",
-- Spell mob -> player
"SPELLLOGOTHERSELF", "SPELLLOGSCHOOLOTHERSELF",
"SPELLLOGCRITOTHERSELF", "SPELLLOGCRITSCHOOLOTHERSELF",
"SPELLMISSOTHERSELF", "SPELLDODGEDOTHERSELF",
"SPELLPARRIEDOTHERSELF", "SPELLBLOCKEDOTHERSELF",
"SPELLRESISTOTHERSELF", "SPELLIMMUNEOTHERSELF",
"SPELLEVADEDOTHERSELF", "SPELLDEFLECTEDOTHERSELF",
"SPELLREFLECTOTHERSELF",
-- Periodic (mob DoT ticking on player)
"PERIODICAURADAMAGEOTHERSELF",
-- Melee mob -> other (party member)
"COMBATHITOTHEROTHER", "COMBATHITCRITOTHEROTHER",
"COMBATHITSCHOOLOTHEROTHER", "COMBATHITCRITSCHOOLOTHEROTHER",
"MISSEDOTHEROTHER",
"VSDODGEOTHEROTHER", "VSPARRYOTHEROTHER", "VSBLOCKOTHEROTHER",
"VSABSORBOTHEROTHER", "VSIMMUNEOTHEROTHER", "VSRESISTOTHEROTHER",
"VSDEFLECTOTHEROTHER", "VSEVADEOTHEROTHER",
-- Spell mob -> other (party member)
"SPELLLOGOTHEROTHER", "SPELLLOGSCHOOLOTHEROTHER",
"SPELLLOGCRITOTHEROTHER", "SPELLLOGCRITSCHOOLOTHEROTHER",
"SPELLMISSOTHEROTHER", "SPELLDODGEDOTHEROTHER",
"SPELLPARRIEDOTHEROTHER", "SPELLBLOCKEDOTHEROTHER",
"SPELLRESISTOTHEROTHER", "SPELLIMMUNEOTHEROTHER",
-- Periodic (mob DoT ticking on party member)
"PERIODICAURADAMAGEOTHEROTHER",
}
for i = 1, table.getn(incomingSources) do
local gname = incomingSources[i]
local fs = getglobal(gname)
if fs then
local r = FormatToRegex(fs)
if r then
local toPlayer = false
if string.find(gname, "OTHERSELF") or string.find(gname, "TOSELF") then
toPlayer = true
end
INCOMING_PARSERS[table.getn(INCOMING_PARSERS) + 1] =
{ regex = r, globalVar = gname, toPlayer = toPlayer }
end
end
end
addon:ui_print("Threat engine: " .. table.getn(INCOMING_PARSERS) ..
" incoming (mob->group) parsers built")
end
-- ============================================================
@@ -1137,6 +1217,79 @@ end
-- Event handler for combat log events
-- ============================================================
-- Mark a mob name as actively attacking someone in the group.
-- Filters out anything that is obviously a player (self or a cached
-- party/raid member class). Unknown names are assumed to be mobs.
local function MarkMobAttackingGroup(name)
if not name or name == "" then return end
if playerName and name == playerName then return end
if partyClassCache and partyClassCache[name] then return end
mobsAttackingGroup[name] = GetTime()
end
-- Same as MarkMobAttackingGroup, but flags the mob as specifically
-- attacking the player. Used to distinguish "attacking me" from
-- "attacking a party member" in the nameplate fallback branches.
local function MarkMobAttackingPlayer(name)
if not name or name == "" then return end
if playerName and name == playerName then return end
if partyClassCache and partyClassCache[name] then return end
mobsAttackingPlayer[name] = GetTime()
end
-- Parse an incoming combat-log message and, if it matches any known
-- mob->group pattern, flag the first capture (the source mob name).
local function ParseIncomingMessage(msg)
if not msg or not INCOMING_PARSERS then return end
for i = 1, table.getn(INCOMING_PARSERS) do
local p = INCOMING_PARSERS[i]
local _, _, c1 = string.find(msg, p.regex)
if c1 then
MarkMobAttackingGroup(c1)
if p.toPlayer then
MarkMobAttackingPlayer(c1)
end
return
end
end
end
-- Public API: is this mob name currently flagged as attacking the group?
function addon.ThreatEngine:IsMobAttackingGroup(mobName)
if not mobName then return false end
local t = mobsAttackingGroup[mobName]
if not t then return false end
if GetTime() - t > MOB_ATTACK_TTL then
mobsAttackingGroup[mobName] = nil
return false
end
return true
end
-- Public API: is this mob name currently flagged as specifically
-- attacking the player (not just any group member)?
function addon.ThreatEngine:IsMobAttackingPlayer(mobName)
if not mobName then return false end
local t = mobsAttackingPlayer[mobName]
if not t then return false end
if GetTime() - t > MOB_ATTACK_TTL then
mobsAttackingPlayer[mobName] = nil
return false
end
return true
end
-- Public API: return the raw table (name -> lastSeen). Caller must not
-- mutate it. Used mainly by debug commands.
function addon.ThreatEngine:GetMobsAttackingGroup()
return mobsAttackingGroup
end
-- Called by the event frame for any CREATURE_VS_* combat log event.
function addon.ThreatEngine:OnIncomingCombat(ev, msg)
ParseIncomingMessage(msg)
end
function addon.ThreatEngine:OnCombatEvent(ev, msg)
if not msg then return end
if not playerName then return end
@@ -1545,6 +1698,8 @@ end
function addon.ThreatEngine:ClearAll()
threatData = {}
mobsAttackingGroup = {}
mobsAttackingPlayer = {}
end
-- ============================================================
@@ -1809,6 +1964,20 @@ function addon.ThreatEngine:Initialize()
combatFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE")
-- Incoming combat: mob attacking player / party. Used by v2.6.2
-- multi-mob threat display to flag mobs that are engaged with
-- the group but for which we have no unit token.
combatFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
combatFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES")
combatFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS")
combatFrame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_MISSES")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_PARTY_MISSES")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_VS_SELF_DAMAGE")
combatFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_VS_PARTY_DAMAGE")
combatFrame:SetScript("OnEvent", function()
-- Vanilla 1.12: event name is in global 'event', args in global arg1..arg9
-- IMPORTANT: we must NOT shadow 'event' with a local or parameter
@@ -1838,6 +2007,18 @@ function addon.ThreatEngine:Initialize()
elseif event == "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS"
or event == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS" then
addon.ThreatEngine:OnPartyPeriodicEvent(event, arg1)
-- Incoming combat: mob attacking player / party (v2.6.2)
elseif event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS"
or event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES"
or event == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS"
or event == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES"
or event == "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE"
or event == "CHAT_MSG_SPELL_CREATURE_VS_SELF_MISSES"
or event == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE"
or event == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_MISSES"
or event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_VS_SELF_DAMAGE"
or event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_VS_PARTY_DAMAGE" then
addon.ThreatEngine:OnIncomingCombat(event, arg1)
end
-- Handle non-combat events