From 0a1b593ec6336b3cf77e4da53b12d7b8ffb8abe1 Mon Sep 17 00:00:00 2001 From: Nick Towle Date: Thu, 10 Oct 2024 18:45:14 -0400 Subject: [PATCH] Core/BossPrototype: Add `:RegisterEngageMob` (#1872) --- Core/BossPrototype.lua | 69 ++++++++++++++++++++++++++++++++++ Core/BossPrototype_Classic.lua | 69 ++++++++++++++++++++++++++++++++++ gen_option_values.lua | 10 ++++- 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/Core/BossPrototype.lua b/Core/BossPrototype.lua index ff6a1316fd..0c912f6d32 100644 --- a/Core/BossPrototype.lua +++ b/Core/BossPrototype.lua @@ -57,6 +57,8 @@ local myLocale = GetLocale() local hasVoice = BigWigsAPI:HasVoicePack() local bossUtilityFrame = CreateFrame("Frame") local petUtilityFrame = CreateFrame("Frame") +local activeNameplateUtilityFrame, inactiveNameplateUtilityFrame = CreateFrame("Frame"), CreateFrame("Frame") +local engagedGUIDs, activeNameplates = {}, {} local enabledModules, unitTargetScans = {}, {} local allowedEvents = {} local difficulty, maxPlayers @@ -456,6 +458,11 @@ function boss:Disable(isWipe) if #enabledModules == 0 then bossUtilityFrame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") petUtilityFrame:UnregisterEvent("UNIT_PET") + activeNameplateUtilityFrame:UnregisterEvent("NAME_PLATE_UNIT_ADDED") + inactiveNameplateUtilityFrame:UnregisterEvent("NAME_PLATE_UNIT_REMOVED") + activeNameplateUtilityFrame.nameplateWatcher:Stop() + engagedGUIDs = {} + activeNameplates = {} unitTargetScans = {} else for i = #unitTargetScans, 1, -1 do @@ -793,6 +800,64 @@ do allowedEvents.UNIT_DIED = true bossUtilityFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") end + do + local UnitAffectingCombat = UnitAffectingCombat + activeNameplateUtilityFrame:SetScript("OnEvent", function(_, _, unit) + activeNameplates[unit] = true + end) + inactiveNameplateUtilityFrame:SetScript("OnEvent", function(_, _, unit) + activeNameplates[unit] = nil + end) + local nameplateWatcher = activeNameplateUtilityFrame:CreateAnimationGroup() + nameplateWatcher:SetLooping("REPEAT") + local anim = nameplateWatcher:CreateAnimation() + anim:SetDuration(0.5) + activeNameplateUtilityFrame.nameplateWatcher = nameplateWatcher + nameplateWatcher:SetScript("OnLoop", function() + for unit in next, activeNameplates do + local guid = UnitGUID(unit) + local engaged = engagedGUIDs[guid] + if not engaged and UnitAffectingCombat(unit) then + engagedGUIDs[guid] = true + local _, _, _, _, _, id = strsplit("-", guid) + local mobId = tonumber(id) + if mobId then + for i = #enabledModules, 1, -1 do + local self = enabledModules[i] + local m = eventMap[self]["UNIT_ENTERING_COMBAT"] + if m and m[mobId] then + self:Debug("UNIT_ENTERING_COMBAT", guid) + local func = m[mobId] + self[func](self, guid, mobId) + end + end + end + elseif engaged and not UnitAffectingCombat(unit) then + engagedGUIDs[guid] = nil + end + end + end) + --- Register a callback for a unit nameplate entering combat. + -- @param func callback function, passed (guid, mobId) + -- @number ... any number of mob ids + function boss:RegisterEngageMob(func, ...) + if not func then core:Print(format(missingArgument, self.moduleName)) return end + if type(func) ~= "function" and not self[func] then core:Print(format(missingFunction, self.moduleName, func)) return end + if not eventMap[self].UNIT_ENTERING_COMBAT then eventMap[self].UNIT_ENTERING_COMBAT = {} end + for i = 1, select("#", ...) do + eventMap[self]["UNIT_ENTERING_COMBAT"][select(i, ...)] = func + end + activeNameplateUtilityFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED") + inactiveNameplateUtilityFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED") + nameplateWatcher:Play() + end + end + --- Checks if a mob is engaged. + -- @param guid a mob to check + -- @return boolean + function boss:IsMobEngaged(guid) + return engagedGUIDs[guid] and true or false + end end ------------------------------------------------------------------------------- @@ -3138,6 +3203,10 @@ do -- @string guid Anchor to a unit's nameplate by GUID -- @param[opt] customIconOrText a custom icon (File ID as a number) or text to show text instead function boss:Nameplate(key, length, guid, customIconOrText) + if not engagedGUIDs[guid] then + -- in rare cases a NPC can start casting before being engaged, make sure this timer isn't overwritten + engagedGUIDs[guid] = true + end self:SendMessage("BigWigs_StartNameplate", self, guid, key, length, customIconOrText) end diff --git a/Core/BossPrototype_Classic.lua b/Core/BossPrototype_Classic.lua index a5f44a9785..d554f72912 100644 --- a/Core/BossPrototype_Classic.lua +++ b/Core/BossPrototype_Classic.lua @@ -57,6 +57,8 @@ local myLocale = GetLocale() local hasVoice = BigWigsAPI:HasVoicePack() local bossUtilityFrame = CreateFrame("Frame") local petUtilityFrame = CreateFrame("Frame") +local activeNameplateUtilityFrame, inactiveNameplateUtilityFrame = CreateFrame("Frame"), CreateFrame("Frame") +local engagedGUIDs, activeNameplates = {}, {} local enabledModules, unitTargetScans = {}, {} local allowedEvents = {} local difficulty, maxPlayers @@ -528,6 +530,11 @@ function boss:Disable(isWipe) if #enabledModules == 0 then bossUtilityFrame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED") petUtilityFrame:UnregisterEvent("UNIT_PET") + activeNameplateUtilityFrame:UnregisterEvent("NAME_PLATE_UNIT_ADDED") + inactiveNameplateUtilityFrame:UnregisterEvent("NAME_PLATE_UNIT_REMOVED") + activeNameplateUtilityFrame.nameplateWatcher:Stop() + engagedGUIDs = {} + activeNameplates = {} unitTargetScans = {} else for i = #unitTargetScans, 1, -1 do @@ -866,6 +873,64 @@ do allowedEvents.UNIT_DIED = true bossUtilityFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") end + do + local UnitAffectingCombat = UnitAffectingCombat + activeNameplateUtilityFrame:SetScript("OnEvent", function(_, _, unit) + activeNameplates[unit] = true + end) + inactiveNameplateUtilityFrame:SetScript("OnEvent", function(_, _, unit) + activeNameplates[unit] = nil + end) + local nameplateWatcher = activeNameplateUtilityFrame:CreateAnimationGroup() + nameplateWatcher:SetLooping("REPEAT") + local anim = nameplateWatcher:CreateAnimation() + anim:SetDuration(0.5) + activeNameplateUtilityFrame.nameplateWatcher = nameplateWatcher + nameplateWatcher:SetScript("OnLoop", function() + for unit in next, activeNameplates do + local guid = UnitGUID(unit) + local engaged = engagedGUIDs[guid] + if not engaged and UnitAffectingCombat(unit) then + engagedGUIDs[guid] = true + local _, _, _, _, _, id = strsplit("-", guid) + local mobId = tonumber(id) + if mobId then + for i = #enabledModules, 1, -1 do + local self = enabledModules[i] + local m = eventMap[self]["UNIT_ENTERING_COMBAT"] + if m and m[mobId] then + self:Debug("UNIT_ENTERING_COMBAT", guid) + local func = m[mobId] + self[func](self, guid, mobId) + end + end + end + elseif engaged and not UnitAffectingCombat(unit) then + engagedGUIDs[guid] = nil + end + end + end) + --- Register a callback for a unit nameplate entering combat. + -- @param func callback function, passed (guid, mobId) + -- @number ... any number of mob ids + function boss:RegisterEngageMob(func, ...) + if not func then core:Print(format(missingArgument, self.moduleName)) return end + if type(func) ~= "function" and not self[func] then core:Print(format(missingFunction, self.moduleName, func)) return end + if not eventMap[self].UNIT_ENTERING_COMBAT then eventMap[self].UNIT_ENTERING_COMBAT = {} end + for i = 1, select("#", ...) do + eventMap[self]["UNIT_ENTERING_COMBAT"][select(i, ...)] = func + end + activeNameplateUtilityFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED") + inactiveNameplateUtilityFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED") + nameplateWatcher:Play() + end + end + --- Checks if a mob is engaged. + -- @param guid a mob to check + -- @return boolean + function boss:IsMobEngaged(guid) + return engagedGUIDs[guid] and true or false + end end ------------------------------------------------------------------------------- @@ -3132,6 +3197,10 @@ do -- @string guid Anchor to a unit's nameplate by GUID -- @param[opt] customIconOrText a custom icon (File ID as a number) or text to show text instead function boss:Nameplate(key, length, guid, customIconOrText) + if not engagedGUIDs[guid] then + -- in rare cases a NPC can start casting before being engaged, make sure this timer isn't overwritten + engagedGUIDs[guid] = true + end self:SendMessage("BigWigs_StartNameplate", self, guid, key, length, customIconOrText) end diff --git a/gen_option_values.lua b/gen_option_values.lua index b045fcbb45..d10e2d5d4b 100644 --- a/gen_option_values.lua +++ b/gen_option_values.lua @@ -601,7 +601,7 @@ local function parseLua(file) local options, option_keys, option_key_used, bitflag_used = {}, {}, {}, {} local options_block_start = 0 local special_options = {} - local methods, registered_methods, unit_died_methods = {Win=true,Disable=true}, {}, {} + local methods, registered_methods, unit_died_methods, mob_engaged_methods = {Win=true,Disable=true}, {}, {}, {} local event_callbacks = {} local current_func = nil local args_keys = standard_args_keys @@ -807,6 +807,12 @@ local function parseLua(file) registered_methods[callback] = n unit_died_methods[callback] = true end + event = line:match(":MobEngaged%(\"(.-)\"%s*,.*") + if event then + callback = event + registered_methods[callback] = n + mob_engaged_methods[callback] = true + end --- Set spellId replacement values. -- Record the function that was declared and use the callback map that was @@ -822,6 +828,8 @@ local function parseLua(file) args_keys = {} elseif unit_died_methods[name] then args_keys = unit_died_args_keys + elseif mob_engaged_methods[name] then + args_keys = {} else args_keys = standard_args_keys end