diff --git a/Heimdall.lua b/Heimdall.lua index 925ee16..a8570e2 100644 --- a/Heimdall.lua +++ b/Heimdall.lua @@ -47,7 +47,7 @@ local function init() ---@field Echoer InitTable ---@field Macroer InitTable ---@field Commander InitTable - ---@field StinkyTracker InitTable + ---@field StinkyTracker StinkyTracker ---@field CombatAlerter InitTable ---@field Config InitTable ---@field Sniffer InitTable @@ -183,6 +183,7 @@ local function init() ---@class HeimdallStinkyTrackerConfig ---@field enabled boolean ---@field debug boolean + ---@field ignoredTimeout number ---@field channels string[] ---@class HeimdallCombatAlerterConfig @@ -283,7 +284,8 @@ local function init() ---@field ignored table ---@class HeimdallStinkyTrackerData - ---@field stinkies ReactiveValue + ---@field stinkies ReactiveValue> + ---@field ignored ReactiveValue> ---@class HeimdallNetworkData ---@field ticker number? diff --git a/Modules/CombatAlerter.lua b/Modules/CombatAlerter.lua index 6d12991..4417282 100644 --- a/Modules/CombatAlerter.lua +++ b/Modules/CombatAlerter.lua @@ -53,7 +53,7 @@ function shared.CombatAlerter.Init() print(string.format("[%s] Combat event source: %s", ModuleName, source)) end - if shared.stinkyTracker.stinkies and shared.stinkyTracker.stinkies[source] then + if shared.StinkyTracker.IsStinky(source) then if Heimdall_Data.config.combatAlerter.debug then print( string.format( diff --git a/Modules/Commander.lua b/Modules/Commander.lua index 2e7bcbe..3d26977 100644 --- a/Modules/Commander.lua +++ b/Modules/Commander.lua @@ -234,12 +234,40 @@ function shared.Commander.Init() if Heimdall_Data.config.commander.debug then print(string.format("[%s] Adding stinky: %s/%s", ModuleName, name, tostring(class))) end - shared.stinkyTracker.stinkies[name] = { + shared.StinkyTracker.Track({ name = name, class = class or "unknown", seenAt = GetTime(), hostile = true, - } + }) + if Heimdall_Data.config.commander.debug then + print(string.format("[%s] Added stinky: %s/%s", ModuleName, name, tostring(class))) + end + end + return {} + end + + ---@param args string[] + local function IgnoreMacroTarget(args) + if Heimdall_Data.config.commander.debug then + ---@diagnostic disable-next-line: param-type-mismatch something wrong with luals, it's picking up the "wrong" unpack + print(string.format("[%s] Macroing: %s", ModuleName, strjoin(" ", unpack(args)))) + end + if #args < 1 then + if Heimdall_Data.config.commander.debug then + print(string.format("[%s] Invalid number of arguments for IgnoreMacroTarget", ModuleName)) + end + return {} + end + table.remove(args, 1) + + for i = 1, #args do + local stinky = strtrim(args[i]) + local name = stinky:match("([^/]+)") + if Heimdall_Data.config.commander.debug then + print(string.format("[%s] Ignoring stinky: %s", ModuleName, name)) + end + shared.StinkyTracker.Ignore(name) end return {} end @@ -259,6 +287,7 @@ function shared.Commander.Init() { keywordRe = "^leavegroup$", commanderOnly = false, callback = LeaveGroup }, { keywordRe = "^follow$", commanderOnly = false, callback = FollowTarget }, { keywordRe = "^macro", commanderOnly = false, callback = MacroTarget }, + { keywordRe = "^ignore", commanderOnly = false, callback = IgnoreMacroTarget }, } local commanderChannelFrame = CreateFrame("Frame") diff --git a/Modules/StinkyTracker.lua b/Modules/StinkyTracker.lua index 3934f7d..2693e87 100644 --- a/Modules/StinkyTracker.lua +++ b/Modules/StinkyTracker.lua @@ -2,266 +2,337 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "StinkyTracker" ----@diagnostic disable-next-line: missing-fields -shared.StinkyTracker = {} -function shared.StinkyTracker.Init() - shared.stinkyTracker = { - stinkies = ReactiveValue.new({}), - } - local whoRegex = "([^ -/]+)-?%w*/(%w+)" - ---@param msg string - ---@return table - local function ParseWho(msg) +---@class Stinky +---@field name string +---@field class string +---@field seenAt number +---@field hostile boolean + +---@class StinkyTracker +shared.StinkyTracker = { + ---@param stinky Stinky + ---@return boolean + Track = function(stinky) if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg)) + print(string.format("[%s] Request to track stinky: %s (%s)", ModuleName, stinky.name, stinky.class)) end - local stinkies = {} - for name, class in string.gmatch(msg, whoRegex) do - stinkies[name] = { - name = name, - class = class, - seenAt = GetTime(), - hostile = true, - } + local ignored = shared.stinkyTracker.ignored[stinky.name] + -- TODO: Add a config option for the ignored timeout + if ignored and ignored > GetTime() - 60 then if Heimdall_Data.config.stinkyTracker.debug then print( string.format( - "[%s] Found hostile player: %s (%s) at %s", + "[%s] Stinky is ignored, not tracking: %s (%s)", + ModuleName, + stinky.name, + stinky.class + ) + ) + shared.dumpTable(shared.stinkyTracker.ignored) + shared.dumpTable(shared.stinkyTracker.stinkies) + end + return false + else + -- Timed out or was never ignored + shared.stinkyTracker.stinkies[stinky.name] = nil + end + + shared.stinkyTracker.stinkies[stinky.name] = stinky + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Stinky is now tracked: %s (%s)", ModuleName, stinky.name, stinky.class)) + shared.dumpTable(shared.stinkyTracker.stinkies) + shared.dumpTable(shared.stinkyTracker.ignored) + end + return true + end, + + ---@param name string + ---@return nil + Ignore = function(name) + shared.stinkyTracker.ignored[name] = GetTime() + shared.stinkyTracker.stinkies[name] = nil + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Stinky is now ignored: %s", ModuleName, name)) + shared.dumpTable(shared.stinkyTracker.ignored) + shared.dumpTable(shared.stinkyTracker.stinkies) + end + end, + + ---@param name string + ---@return boolean + IsStinky = function(name) + if not shared.stinkyTracker.stinkies then return false end + if not shared.stinkyTracker.stinkies[name] then return false end + if shared.stinkyTracker.ignored[name] then return false end + return true + end, + + Init = function() + shared.stinkyTracker = { + stinkies = ReactiveValue.new({}), + ignored = ReactiveValue.new({}), + } + + local whoRegex = "([^ -/]+)-?%w*/(%w+)" + ---@param msg string + ---@return table + local function ParseWho(msg) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Parsing WHO message: '%s'", ModuleName, msg)) + end + local stinkies = {} + for name, class in string.gmatch(msg, whoRegex) do + stinkies[name] = { + name = name, + class = class, + seenAt = GetTime(), + hostile = true, + } + if Heimdall_Data.config.stinkyTracker.debug then + print( + string.format( + "[%s] Found hostile player: %s (%s) at %s", + ModuleName, + name, + class, + date("%H:%M:%S", time()) + ) + ) + shared.dumpTable(stinkies) + end + end + return stinkies + end + + local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)" + ---@param msg string + ---@return table + local function ParseSee(msg) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg)) + end + local stinkies = {} + local aggression, name, class = string.match(msg, seeRegex) + if not name or not class then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Error: Invalid SEE message format", ModuleName)) + end + return stinkies + end + local stinky = { + name = name, + class = class, + seenAt = GetTime(), + hostile = aggression == "hostile", + } + stinkies[name] = stinky + if Heimdall_Data.config.stinkyTracker.debug then + print( + string.format( + "[%s] Found stinky in SEE: %s (%s) - %s at %s", ModuleName, name, class, + aggression, date("%H:%M:%S", time()) ) ) shared.dumpTable(stinkies) end - end - return stinkies - end - - local seeRegex = "I see %((%w+)%) ([^ -/]+)-?%w*/(%w+)" - ---@param msg string - ---@return table - local function ParseSee(msg) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Parsing SEE message: '%s'", ModuleName, msg)) - end - local stinkies = {} - local aggression, name, class = string.match(msg, seeRegex) - if not name or not class then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Error: Invalid SEE message format", ModuleName)) - end return stinkies end - local stinky = { - name = name, - class = class, - seenAt = GetTime(), - hostile = aggression == "hostile", - } - stinkies[name] = stinky - if Heimdall_Data.config.stinkyTracker.debug then - print( - string.format( - "[%s] Found stinky in SEE: %s (%s) - %s at %s", - ModuleName, - name, - class, - aggression, - date("%H:%M:%S", time()) - ) - ) - shared.dumpTable(stinkies) - end - return stinkies - end - local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)" - local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)" - ---@param msg string - ---@return table - local function ParseArrived(msg) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("%s: Parsing arrived message: %s", ModuleName, msg)) - end - local stinkies = {} - local name, class = string.match(msg, arrivedRegex) - if not name or not class then - name, class = string.match(msg, arrivedRegexAlt) - end - if not name or not class then + local arrivedRegex = "([^ -/]+)-?%w*; c:([^;]+)" + local arrivedRegexAlt = "([^ -/]+)-?%w*%(!!!!%); c:([^;]+)" + ---@param msg string + ---@return table + local function ParseArrived(msg) if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("%s: No valid stinky found in arrived message", ModuleName)) + print(string.format("%s: Parsing arrived message: %s", ModuleName, msg)) end - return stinkies - end - local stinky = { - name = name, - class = class, - seenAt = GetTime(), - hostile = true, - } - stinkies[name] = stinky - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class)) - shared.dumpTable(stinkies) - end - return stinkies - end - - local frame = CreateFrame("Frame") - frame:RegisterEvent("CHAT_MSG_CHANNEL") - frame:SetScript("OnEvent", function(self, event, msg, sender, ...) - --if Heimdall_Data.config.stinkyTracker.debug then - -- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender)) - --end - if not Heimdall_Data.config.stinkyTracker.enabled then - --if Heimdall_Data.config.stinkyTracker.debug then - -- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) - --end - return - end - local channelId = select(6, ...) - local _, channelname = GetChannelName(channelId) - local ok = false - for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do - if channel == channelname then - ok = true - break + local stinkies = {} + local name, class = string.match(msg, arrivedRegex) + if not name or not class then + name, class = string.match(msg, arrivedRegexAlt) end - end - if not ok then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname)) - end - return - end - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) - shared.dumpTable(Heimdall_Data.config.stinkyTracker) - end - - if string.find(msg, "^who:") then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Processing WHO message from %s", ModuleName, sender)) - end - local whoStinkies = ParseWho(msg) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Found stinkies in WHO message", ModuleName)) - end - for name, stinky in pairs(whoStinkies) do - if stinky.hostile then - shared.stinkyTracker.stinkies[name] = stinky - if Heimdall_Data.config.stinkyTracker.debug then - print( - string.format("[%s] Added hostile stinky from WHO: %s (%s)", ModuleName, name, stinky.class) - ) - end - end - end - end - if string.find(msg, "^I see") then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Processing SEE message from %s", ModuleName, sender)) - end - local seeStinkies = ParseSee(msg) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Found stinkies in SEE message", ModuleName)) - end - for name, stinky in pairs(seeStinkies) do - shared.stinkyTracker.stinkies[name] = stinky + if not name or not class then if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Added hostile stinky from SEE: %s (%s)", ModuleName, name, stinky.class)) + print(string.format("%s: No valid stinky found in arrived message", ModuleName)) end + return stinkies end - end - if string.find(msg, "arrived to") or string.find(msg, "moved to") then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender)) - end - local arrivedStinkies = ParseArrived(msg) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName)) - end - for name, stinky in pairs(arrivedStinkies) do - shared.stinkyTracker.stinkies[name] = stinky - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Added stinky from ARRIVED: %s (%s)", ModuleName, name, stinky.class)) - end - end - end - - -- Log total stinky count after processing - if Heimdall_Data.config.stinkyTracker.debug then - local count = 0 - for _ in pairs(shared.stinkyTracker.stinkies:get()) do - count = count + 1 - end - print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count)) - end - - for name, stinky in pairs(shared.stinkyTracker.stinkies) do - if Heimdall_Data.config.agents[name] then - shared.stinkyTracker.stinkies[name] = nil - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name)) - end - end - end - end) - - local targetFrame = CreateFrame("Frame") - targetFrame:RegisterEvent("UNIT_TARGET") - targetFrame:SetScript("OnEvent", function(self, event, unit) - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) - end - unit = "target" - - if not Heimdall_Data.config.stinkyTracker.enabled then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Module disabled, ignoring event", ModuleName)) - end - return - end - - local name = UnitName(unit) - if not UnitIsPlayer(unit) then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name)) - end - return - end - - local enemy = UnitCanAttack("player", unit) - if enemy then - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name)) - end - shared.stinkyTracker.stinkies[name] = { + local stinky = { name = name, - class = UnitClass(unit), + class = class, seenAt = GetTime(), hostile = true, } - return - end - - if not shared.stinkyTracker.stinkies[name] then + stinkies[name] = stinky if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name)) + print(string.format("%s: Found stinky in arrived: %s/%s", ModuleName, name, class)) + shared.dumpTable(stinkies) end - return + return stinkies end - if Heimdall_Data.config.stinkyTracker.debug then - print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name)) - end - shared.stinkyTracker.stinkies[name] = nil - end) + local frame = CreateFrame("Frame") + frame:RegisterEvent("CHAT_MSG_CHANNEL") + frame:SetScript("OnEvent", function(self, event, msg, sender, ...) + --if Heimdall_Data.config.stinkyTracker.debug then + -- print(string.format("[%s] Event received: %s from %s", ModuleName, event, sender)) + --end + if not Heimdall_Data.config.stinkyTracker.enabled then + --if Heimdall_Data.config.stinkyTracker.debug then + -- print(string.format("[%s] Module disabled, ignoring event", ModuleName)) + --end + return + end + local channelId = select(6, ...) + local _, channelname = GetChannelName(channelId) + local ok = false + for _, channel in pairs(Heimdall_Data.config.stinkyTracker.channels) do + if channel == channelname then + ok = true + break + end + end + if not ok then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Ignoring message from non-master channel: %s", ModuleName, channelname)) + end + return + end + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) + shared.dumpTable(Heimdall_Data.config.stinkyTracker) + end - if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Module initialized", ModuleName)) end - print("[Heimdall] StinkyTracker loaded") -end + local stinkies = {} + if string.find(msg, "^who:") then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Processing WHO message from %s", ModuleName, sender)) + end + local whoStinkies = ParseWho(msg) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Found stinkies in WHO message", ModuleName)) + shared.dumpTable(whoStinkies) + end + for name, stinky in pairs(whoStinkies) do + stinkies[name] = stinky + end + end + if string.find(msg, "^I see") then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Processing SEE message from %s", ModuleName, sender)) + end + local seeStinkies = ParseSee(msg) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Found stinkies in SEE message", ModuleName)) + shared.dumpTable(seeStinkies) + end + for name, stinky in pairs(seeStinkies) do + stinkies[name] = stinky + end + end + if string.find(msg, "arrived to") or string.find(msg, "moved to") then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Processing ARRIVED message from %s", ModuleName, sender)) + end + local arrivedStinkies = ParseArrived(msg) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Found stinkies in ARRIVED message", ModuleName)) + shared.dumpTable(arrivedStinkies) + end + for name, stinky in pairs(arrivedStinkies) do + stinkies[name] = stinky + end + end + + for name, stinky in pairs(stinkies) do + if shared.stinkyTracker.ignored[name] then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Ignoring stinky: %s (%s)", ModuleName, name, stinky.class)) + end + shared.stinkyTracker.ignored[name] = nil + else + shared.stinkyTracker.stinkies[name] = stinky + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Added stinky: %s (%s)", ModuleName, name, stinky.class)) + end + end + end + + -- Log total stinky count after processing + if Heimdall_Data.config.stinkyTracker.debug then + local count = 0 + for _ in pairs(shared.stinkyTracker.stinkies:get()) do + count = count + 1 + end + print(string.format("[%s] Current total stinkies tracked: %d", ModuleName, count)) + end + + for name, stinky in pairs(shared.stinkyTracker.stinkies) do + if Heimdall_Data.config.agents[name] then + shared.stinkyTracker.stinkies[name] = nil + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Removed agent from stinkies: %s", ModuleName, name)) + end + end + end + end) + + local targetFrame = CreateFrame("Frame") + targetFrame:RegisterEvent("UNIT_TARGET") + targetFrame:SetScript("OnEvent", function(self, event, unit) + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) + end + unit = "target" + + if not Heimdall_Data.config.stinkyTracker.enabled then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Module disabled, ignoring event", ModuleName)) + end + return + end + + local name = UnitName(unit) + if not UnitIsPlayer(unit) then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Target %s is not a player, nothing to do", ModuleName, name)) + end + return + end + + local enemy = UnitCanAttack("player", unit) + if enemy then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Target %s is enemy - tracking as stinky", ModuleName, name)) + end + shared.stinkyTracker.stinkies[name] = { + name = name, + class = UnitClass(unit), + seenAt = GetTime(), + hostile = true, + } + return + end + + if not shared.stinkyTracker.stinkies[name] then + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Target %s is friendly and not stinky, nothing to do", ModuleName, name)) + end + return + end + + if Heimdall_Data.config.stinkyTracker.debug then + print(string.format("[%s] Target %s is friendly and stinky - removing from stinkies", ModuleName, name)) + end + shared.stinkyTracker.stinkies[name] = nil + end) + + if Heimdall_Data.config.stinkyTracker.debug then print(string.format("[%s] Module initialized", ModuleName)) end + print("[Heimdall] StinkyTracker loaded") + end, +}