diff --git a/Modules/Commander.lua b/Modules/Commander.lua index c107566..36b2554 100644 --- a/Modules/Commander.lua +++ b/Modules/Commander.lua @@ -352,7 +352,7 @@ shared.Commander = { enabled and ( not command.commanderOnly - -- if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring command, sender %s not commander %s", ModuleName, sender, Heimdall_Data.config.commander.commander)) end + -- if Heimdall_Data.config.commander.debug then print(string.format("[%s] Ignoring command, sender %s not commander %s", ModuleName, sender, Heimdall_Data.config.commander.commander)) end or (command.commanderOnly and sender == Heimdall_Data.config.commander.commander) ) then diff --git a/Modules/Inviter.lua b/Modules/Inviter.lua index 763c181..d11799f 100644 --- a/Modules/Inviter.lua +++ b/Modules/Inviter.lua @@ -1,5 +1,6 @@ local _, shared = ... ---@cast shared HeimdallShared +local ModuleName = "Inviter" ---@class HeimdallInviterConfig ---@field enabled boolean @@ -14,8 +15,6 @@ local _, shared = ... ---@field afkThreshold number ---@field listeningChannel table -local ModuleName = "Inviter" - ---@class Inviter shared.Inviter = { Init = function() diff --git a/Modules/Macroer.lua b/Modules/Macroer.lua index 3347ca2..4c91a92 100644 --- a/Modules/Macroer.lua +++ b/Modules/Macroer.lua @@ -1,13 +1,12 @@ local _, shared = ... ---@cast shared HeimdallShared +local ModuleName = "Macroer" ---@class HeimdallMacroerConfig ---@field enabled boolean ---@field debug boolean ---@field priority string[] -local ModuleName = "Macroer" - ---@class Macroer shared.Macroer = { Init = function() diff --git a/Modules/Messenger.lua b/Modules/Messenger.lua index 83b389b..cfe7824 100644 --- a/Modules/Messenger.lua +++ b/Modules/Messenger.lua @@ -2,175 +2,184 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "Messenger" ----@diagnostic disable-next-line: missing-fields -shared.Messenger = {} -function shared.Messenger.Init() - ---@class Message - ---@field message string - ---@field channel string - ---@field data string +---@class Message +---@field message string +---@field channel string +---@field data string - local function FindOrJoinChannel(channelName, password) - local channelId = GetChannelName(channelName) - if channelId == 0 then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName)) - end - if password then - JoinPermanentChannel(channelName, password) - else - JoinPermanentChannel(channelName) - end - end - channelId = GetChannelName(channelName) - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName)) - end - return channelId - end - - ---@diagnostic disable-next-line: missing-fields - if not shared.messenger then shared.messenger = {} end - if not shared.messenger.queue then shared.messenger.queue = {} end - if not shared.messenger.ticker then - local function DoMessage() - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Processing message queue - Size: %d", ModuleName, #shared.messenger.queue)) - end - - if not Heimdall_Data.config.messenger.enabled then +---@class Messenger +shared.Messenger = { + Init = function() + local function FindOrJoinChannel(channelName, password) + local channelId = GetChannelName(channelName) + if channelId == 0 then if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Module disabled, skipping message processing", ModuleName)) + print(string.format("[%s] Channel not found, joining: %s", ModuleName, channelName)) end - return - end - - ---@type Message - local message = shared.messenger.queue[1] - if not message then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Message queue empty", ModuleName)) + if password then + JoinPermanentChannel(channelName, password) + else + JoinPermanentChannel(channelName) end - return end - + channelId = GetChannelName(channelName) if Heimdall_Data.config.messenger.debug then - print( - string.format( - "[%s] Processing message - Channel: %s, Data: %s", - ModuleName, - message.channel or "nil", - message.data or "nil" + print(string.format("[%s] Channel found with ID: %s (%s)", ModuleName, channelId, channelName)) + end + return channelId + end + + ---@diagnostic disable-next-line: missing-fields + if not shared.messenger then shared.messenger = {} end + if not shared.messenger.queue then shared.messenger.queue = {} end + if not shared.messenger.ticker then + local function DoMessage() + if Heimdall_Data.config.messenger.debug then + print( + string.format("[%s] Processing message queue - Size: %d", ModuleName, #shared.messenger.queue) ) - ) - print(string.format("[%s] Message content: %s", ModuleName, message.message or "nil")) - end - - if not message.message or message.message == "" then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Invalid message: empty content", ModuleName)) end - return - end - if not message.channel or message.channel == "" then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Invalid message: no channel specified", ModuleName)) + if not Heimdall_Data.config.messenger.enabled then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Module disabled, skipping message processing", ModuleName)) + end + return end - return - end - if string.find(message.channel, "^C") then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Converting channel type from C to CHANNEL", ModuleName)) + ---@type Message + local message = shared.messenger.queue[1] + if not message then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Message queue empty", ModuleName)) + end + return end - message.channel = "CHANNEL" - elseif string.find(message.channel, "^W") then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Converting channel type from W to WHISPER", ModuleName)) - end - message.channel = "WHISPER" - end - if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then if Heimdall_Data.config.messenger.debug then print( string.format( - "[%s] Processing channel message: '%s' to '%s'", + "[%s] Processing message - Channel: %s, Data: %s", + ModuleName, + message.channel or "nil", + message.data or "nil" + ) + ) + print(string.format("[%s] Message content: %s", ModuleName, message.message or "nil")) + end + + if not message.message or message.message == "" then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Invalid message: empty content", ModuleName)) + end + return + end + + if not message.channel or message.channel == "" then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Invalid message: no channel specified", ModuleName)) + end + return + end + + if string.find(message.channel, "^C") then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Converting channel type from C to CHANNEL", ModuleName)) + end + message.channel = "CHANNEL" + elseif string.find(message.channel, "^W") then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Converting channel type from W to WHISPER", ModuleName)) + end + message.channel = "WHISPER" + end + + if message.channel == "CHANNEL" and message.data and string.match(message.data, "%D") then + if Heimdall_Data.config.messenger.debug then + print( + string.format( + "[%s] Processing channel message: '%s' to '%s'", + ModuleName, + message.message, + message.data + ) + ) + end + local channelId = GetChannelName(message.data) + if channelId == 0 then + if Heimdall_Data.config.messenger.debug then + print( + string.format( + "[%s] Channel not found, attempting to join: %s", + ModuleName, + message.data + ) + ) + end + channelId = FindOrJoinChannel(message.data) + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId)) + end + end + message.data = tostring(channelId) + end + + table.remove(shared.messenger.queue, 1) + if not message.message or message.message == "" then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Skipping empty message", ModuleName)) + end + return + end + if not message.channel or message.channel == "" then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Skipping message with no channel", ModuleName)) + end + return + end + if not message.data or message.data == "" then + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Skipping message with no data", ModuleName)) + end + return + end + + if Heimdall_Data.config.messenger.debug then + print( + string.format( + "[%s] Sending message: '%s' to %s:%s", ModuleName, message.message, + message.channel, message.data ) ) end - local channelId = GetChannelName(message.data) - if channelId == 0 then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Channel not found, attempting to join: %s", ModuleName, message.data)) - end - channelId = FindOrJoinChannel(message.data) - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Channel join result - ID: %s", ModuleName, channelId)) - end + if string.len(message.message) > 255 then + print(string.format("[%s] Message too long!!!!: %s", ModuleName, message.message)) + return end - message.data = tostring(channelId) + SendChatMessage(message.message, message.channel, nil, message.data) end + local function Tick() + if Heimdall_Data.config.messenger.debug then + print(string.format("[%s] Tick - Queue size: %d", ModuleName, #shared.messenger.queue)) + end + DoMessage() + shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1) + end + Tick() + end - table.remove(shared.messenger.queue, 1) - if not message.message or message.message == "" then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Skipping empty message", ModuleName)) - end - return - end - if not message.channel or message.channel == "" then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Skipping message with no channel", ModuleName)) - end - return - end - if not message.data or message.data == "" then - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Skipping message with no data", ModuleName)) - end - return - end - - if Heimdall_Data.config.messenger.debug then - print( - string.format( - "[%s] Sending message: '%s' to %s:%s", - ModuleName, - message.message, - message.channel, - message.data - ) + if Heimdall_Data.config.messenger.debug then + print( + string.format( + "[%s] Module initialized with interval: %s", + ModuleName, + Heimdall_Data.config.messenger.interval ) - end - if string.len(message.message) > 255 then - print(string.format("[%s] Message too long!!!!: %s", ModuleName, message.message)) - return - end - SendChatMessage(message.message, message.channel, nil, message.data) - end - local function Tick() - if Heimdall_Data.config.messenger.debug then - print(string.format("[%s] Tick - Queue size: %d", ModuleName, #shared.messenger.queue)) - end - DoMessage() - shared.messenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.messenger.interval, Tick, 1) - end - Tick() - end - - if Heimdall_Data.config.messenger.debug then - print( - string.format( - "[%s] Module initialized with interval: %s", - ModuleName, - Heimdall_Data.config.messenger.interval ) - ) - end - print("[Heimdall] Messenger loaded") -end + end + print("[Heimdall] Messenger loaded") + end, +} diff --git a/Modules/MinimapTagger.lua b/Modules/MinimapTagger.lua index 7555f59..0c0ed90 100644 --- a/Modules/MinimapTagger.lua +++ b/Modules/MinimapTagger.lua @@ -1,571 +1,570 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "MinimapTagger" + local HeimdallRoot = "Interface\\AddOns\\Heimdall\\" local SoundRoot = HeimdallRoot .. "Sounds\\" local TextureRoot = HeimdallRoot .. "Texture\\" --/run local a=GetChannelName("Agent")local b,c=GetPlayerMapPosition("player")b,c=b*100,c*100;local d=string.format("I need help at %s (%s) [%s](%2.2f, %2.2f)",GetZoneText(),GetSubZoneText(),GetCurrentMapAreaID(),b,c)SendChatMessage(d,"CHANNEL",nil,a) ----@diagnostic disable-next-line: missing-fields -shared.MinimapTagger = {} -function shared.MinimapTagger.Init() - ---@param x number - ---@param y number - ---@param frame Frame - ---@param scale number? - ---@param ttl number? - local function PlantFrame(x, y, frame, scale, ttl) - if not BattlefieldMinimap then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] BattlefieldMinimap not found", ModuleName)) - end - return - end - scale = scale or 1 - ttl = ttl or 1 - local w, h = BattlefieldMinimap:GetSize() - w, h = w * BattlefieldMinimap:GetEffectiveScale(), h * BattlefieldMinimap:GetEffectiveScale() - local maxSize = w > h and w or h - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Minimap size: %d", ModuleName, maxSize)) - print(string.format("[%s] Scale: %d", ModuleName, scale)) - print(string.format("[%s] TTL: %d", ModuleName, ttl)) - end - local iconSize = maxSize * 0.05 - iconSize = iconSize * scale - - x, y = x / 100, y / 100 - -- Could do with how... I have no idea, but this seems more accurate than without - --x, y = x - 0.01, y - 0.01 - local offsetx, offsety = w * x, h * y - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert position: %d, %d", ModuleName, x, y)) - print(string.format("[%s] Alert offset: %d, %d", ModuleName, offsetx, offsety)) - end - - frame:Hide() - frame:SetSize(iconSize, iconSize) - frame:SetFrameStrata("HIGH") - frame:SetFrameLevel(100) - frame:SetPoint("CENTER", BattlefieldMinimap, "TOPLEFT", offsetx, -offsety) - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert frame created, OnUpdate hooked", ModuleName)) - end - frame:SetScript("OnShow", function(self) - self:SetAlpha(1) - self.custom.busy = true - self.custom.progress = 0 - self:SetScript("OnUpdate", function(selff, elapsed) - self.custom.progress = self.custom.progress + elapsed - local progress = self.custom.progress / ttl - -- if Heimdall_Data.config.minimapTagger.debug then - -- print(string.format("[%s] Alert progress%%: %f", ModuleName, progress)) - -- print(string.format("[%s] Alert progress: %f", ModuleName, self.custom.progress)) - -- print(string.format("[%s] Alert ttl: %d", ModuleName, Heimdall_Data.config.minimapTagger.ttl)) - -- end - self:SetAlpha(1 - progress) - - if progress >= 1 then - self:Hide() - self.custom.busy = false - self:SetScript("OnUpdate", nil) - end - end) - end) - frame:Show() - end - - --region Alert - ---@type Frame[] - local alertFramePool = {} - local alertFramePoolMaxSize = 20 - for i = 1, alertFramePoolMaxSize do - local frame = CreateFrame("Frame") - frame.custom = { busy = false } - local texture = frame:CreateTexture(nil, "ARTWORK") - texture:SetAllPoints(frame) - texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.alertTextureFile) - table.insert(alertFramePool, frame) - end - local muteAlertUntil = 0 - ---@param x number|nil - ---@param y number|nil - ---@param scale number? - ---@param doTag boolean? - local function PlantAlert(x, y, scale, doTag) - if x == nil or y == nil then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert position is nil, ignoring", ModuleName)) - end - return - end - if doTag == nil then doTag = true end - local frame = nil - for _, alertFrame in ipairs(alertFramePool) do - ---@diagnostic disable-next-line: undefined-field - if not alertFrame.custom.busy then - frame = alertFrame - break - end - end - if not frame then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert frame pool is full and could not get frame", ModuleName)) - end - return - end - if Heimdall_Data.config.minimapTagger.alertSound then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Playing alert sound: %s", - ModuleName, - Heimdall_Data.config.minimapTagger.alertSoundFile - ) - ) - end - if muteAlertUntil > GetTime() then +---@class MinimapTagger +shared.MinimapTagger = { + Init = function() + ---@param x number + ---@param y number + ---@param frame Frame + ---@param scale number? + ---@param ttl number? + local function PlantFrame(x, y, frame, scale, ttl) + if not BattlefieldMinimap then if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert sound is muted until %d", ModuleName, muteAlertUntil)) + print(string.format("[%s] BattlefieldMinimap not found", ModuleName)) end - else - muteAlertUntil = GetTime() + Heimdall_Data.config.minimapTagger.alertSoundThrottle - local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.alertSoundFile, "Master") - if not ok and Heimdall_Data.config.minimapTagger.debug then + return + end + scale = scale or 1 + ttl = ttl or 1 + local w, h = BattlefieldMinimap:GetSize() + w, h = w * BattlefieldMinimap:GetEffectiveScale(), h * BattlefieldMinimap:GetEffectiveScale() + local maxSize = w > h and w or h + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Minimap size: %d", ModuleName, maxSize)) + print(string.format("[%s] Scale: %d", ModuleName, scale)) + print(string.format("[%s] TTL: %d", ModuleName, ttl)) + end + local iconSize = maxSize * 0.05 + iconSize = iconSize * scale + + x, y = x / 100, y / 100 + -- Could do with how... I have no idea, but this seems more accurate than without + --x, y = x - 0.01, y - 0.01 + local offsetx, offsety = w * x, h * y + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert position: %d, %d", ModuleName, x, y)) + print(string.format("[%s] Alert offset: %d, %d", ModuleName, offsetx, offsety)) + end + + frame:Hide() + frame:SetSize(iconSize, iconSize) + frame:SetFrameStrata("HIGH") + frame:SetFrameLevel(100) + frame:SetPoint("CENTER", BattlefieldMinimap, "TOPLEFT", offsetx, -offsety) + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert frame created, OnUpdate hooked", ModuleName)) + end + frame:SetScript("OnShow", function(self) + self:SetAlpha(1) + self.custom.busy = true + self.custom.progress = 0 + self:SetScript("OnUpdate", function(selff, elapsed) + self.custom.progress = self.custom.progress + elapsed + local progress = self.custom.progress / ttl + -- if Heimdall_Data.config.minimapTagger.debug then + -- print(string.format("[%s] Alert progress%%: %f", ModuleName, progress)) + -- print(string.format("[%s] Alert progress: %f", ModuleName, self.custom.progress)) + -- print(string.format("[%s] Alert ttl: %d", ModuleName, Heimdall_Data.config.minimapTagger.ttl)) + -- end + self:SetAlpha(1 - progress) + + if progress >= 1 then + self:Hide() + self.custom.busy = false + self:SetScript("OnUpdate", nil) + end + end) + end) + frame:Show() + end + + --region Alert + ---@type Frame[] + local alertFramePool = {} + local alertFramePoolMaxSize = 20 + for i = 1, alertFramePoolMaxSize do + local frame = CreateFrame("Frame") + frame.custom = { busy = false } + local texture = frame:CreateTexture(nil, "ARTWORK") + texture:SetAllPoints(frame) + texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.alertTextureFile) + table.insert(alertFramePool, frame) + end + local muteAlertUntil = 0 + ---@param x number|nil + ---@param y number|nil + ---@param scale number? + ---@param doTag boolean? + local function PlantAlert(x, y, scale, doTag) + if x == nil or y == nil then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert position is nil, ignoring", ModuleName)) + end + return + end + if doTag == nil then doTag = true end + local frame = nil + for _, alertFrame in ipairs(alertFramePool) do + ---@diagnostic disable-next-line: undefined-field + if not alertFrame.custom.busy then + frame = alertFrame + break + end + end + if not frame then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert frame pool is full and could not get frame", ModuleName)) + end + return + end + if Heimdall_Data.config.minimapTagger.alertSound then + if Heimdall_Data.config.minimapTagger.debug then print( string.format( - "[%s] Failed to play alert sound: %s", + "[%s] Playing alert sound: %s", ModuleName, Heimdall_Data.config.minimapTagger.alertSoundFile ) ) end - end - end - if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.alertTTL) end - end - --endregion - - --region Tag - ---@type Frame[] - local tagFramePool = {} - local tagFramePoolMaxSize = 20 - for i = 1, tagFramePoolMaxSize do - local frame = CreateFrame("Frame") - frame.custom = { busy = false } - local texture = frame:CreateTexture(nil, "ARTWORK") - texture:SetAllPoints(frame) - texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.tagTextureFile) - table.insert(tagFramePool, frame) - end - local muteTagUntil = 0 - ---@param x number|nil - ---@param y number|nil - ---@param scale number? - ---@param doTag boolean? - local function PlantTag(x, y, scale, doTag) - if x == nil or y == nil then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Tag position is nil, ignoring", ModuleName)) - end - return - end - if doTag == nil then doTag = true end - local frame = nil - for _, tagFrame in ipairs(tagFramePool) do - ---@diagnostic disable-next-line: undefined-field - if not tagFrame.custom.busy then - frame = tagFrame - break - end - end - if not frame then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Tag frame pool is full and could not get frame", ModuleName)) - end - return - end - if Heimdall_Data.config.minimapTagger.tagSound then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Playing tag sound: %s", - ModuleName, - Heimdall_Data.config.minimapTagger.tagSoundFile - ) - ) - end - if muteTagUntil > GetTime() then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Tag sound is muted until %d", ModuleName, muteTagUntil)) + if muteAlertUntil > GetTime() then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert sound is muted until %d", ModuleName, muteAlertUntil)) + end + else + muteAlertUntil = GetTime() + Heimdall_Data.config.minimapTagger.alertSoundThrottle + local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.alertSoundFile, "Master") + if not ok and Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Failed to play alert sound: %s", + ModuleName, + Heimdall_Data.config.minimapTagger.alertSoundFile + ) + ) + end end - else - muteTagUntil = GetTime() + Heimdall_Data.config.minimapTagger.tagSoundThrottle - local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.tagSoundFile, "Master") - if not ok and Heimdall_Data.config.minimapTagger.debug then + end + if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.alertTTL) end + end + --endregion + + --region Tag + ---@type Frame[] + local tagFramePool = {} + local tagFramePoolMaxSize = 20 + for i = 1, tagFramePoolMaxSize do + local frame = CreateFrame("Frame") + frame.custom = { busy = false } + local texture = frame:CreateTexture(nil, "ARTWORK") + texture:SetAllPoints(frame) + texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.tagTextureFile) + table.insert(tagFramePool, frame) + end + local muteTagUntil = 0 + ---@param x number|nil + ---@param y number|nil + ---@param scale number? + ---@param doTag boolean? + local function PlantTag(x, y, scale, doTag) + if x == nil or y == nil then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Tag position is nil, ignoring", ModuleName)) + end + return + end + if doTag == nil then doTag = true end + local frame = nil + for _, tagFrame in ipairs(tagFramePool) do + ---@diagnostic disable-next-line: undefined-field + if not tagFrame.custom.busy then + frame = tagFrame + break + end + end + if not frame then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Tag frame pool is full and could not get frame", ModuleName)) + end + return + end + if Heimdall_Data.config.minimapTagger.tagSound then + if Heimdall_Data.config.minimapTagger.debug then print( string.format( - "[%s] Failed to play tag sound: %s", + "[%s] Playing tag sound: %s", ModuleName, Heimdall_Data.config.minimapTagger.tagSoundFile ) ) end - end - end - if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.tagTTL) end - end - --endregion - - --region Combat - ---@type Frame[] - local combatFramePool = {} - local combatFramePoolMaxSize = 20 - for i = 1, combatFramePoolMaxSize do - local frame = CreateFrame("Frame") - frame.custom = { busy = false } - local texture = frame:CreateTexture(nil, "ARTWORK") - texture:SetAllPoints(frame) - texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.combatTextureFile) - table.insert(combatFramePool, frame) - end - local muteCombatUntil = 0 - ---@param x number|nil - ---@param y number|nil - ---@param scale number? - ---@param doTag boolean? - local function PlantCombat(x, y, scale, doTag) - if x == nil or y == nil then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Combat position is nil, ignoring", ModuleName)) - end - return - end - if doTag == nil then doTag = true end - local frame = nil - for _, combatFrame in ipairs(combatFramePool) do - ---@diagnostic disable-next-line: undefined-field - if not combatFrame.custom.busy then - frame = combatFrame - break - end - end - if not frame then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Battle frame pool is full and could not get frame", ModuleName)) - end - return - end - if Heimdall_Data.config.minimapTagger.combatSound then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Playing combat sound: %s", - ModuleName, - Heimdall_Data.config.minimapTagger.combatSoundFile - ) - ) - end - if muteCombatUntil > GetTime() then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Combat sound is muted until %d", ModuleName, muteCombatUntil)) + if muteTagUntil > GetTime() then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Tag sound is muted until %d", ModuleName, muteTagUntil)) + end + else + muteTagUntil = GetTime() + Heimdall_Data.config.minimapTagger.tagSoundThrottle + local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.tagSoundFile, "Master") + if not ok and Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Failed to play tag sound: %s", + ModuleName, + Heimdall_Data.config.minimapTagger.tagSoundFile + ) + ) + end end - else - muteCombatUntil = GetTime() + Heimdall_Data.config.minimapTagger.combatSoundThrottle - local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.combatSoundFile, "Master") - if not ok and Heimdall_Data.config.minimapTagger.debug then + end + if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.tagTTL) end + end + --endregion + + --region Combat + ---@type Frame[] + local combatFramePool = {} + local combatFramePoolMaxSize = 20 + for i = 1, combatFramePoolMaxSize do + local frame = CreateFrame("Frame") + frame.custom = { busy = false } + local texture = frame:CreateTexture(nil, "ARTWORK") + texture:SetAllPoints(frame) + texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.combatTextureFile) + table.insert(combatFramePool, frame) + end + local muteCombatUntil = 0 + ---@param x number|nil + ---@param y number|nil + ---@param scale number? + ---@param doTag boolean? + local function PlantCombat(x, y, scale, doTag) + if x == nil or y == nil then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Combat position is nil, ignoring", ModuleName)) + end + return + end + if doTag == nil then doTag = true end + local frame = nil + for _, combatFrame in ipairs(combatFramePool) do + ---@diagnostic disable-next-line: undefined-field + if not combatFrame.custom.busy then + frame = combatFrame + break + end + end + if not frame then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Battle frame pool is full and could not get frame", ModuleName)) + end + return + end + if Heimdall_Data.config.minimapTagger.combatSound then + if Heimdall_Data.config.minimapTagger.debug then print( string.format( - "[%s] Failed to play combat sound: %s", + "[%s] Playing combat sound: %s", ModuleName, Heimdall_Data.config.minimapTagger.combatSoundFile ) ) end - end - end - if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.combatTTL) end - end - --endregion - - --region Help - ---@type Frame[] - local helpFramePool = {} - local helpFramePoolMaxSize = 20 - for i = 1, helpFramePoolMaxSize do - local frame = CreateFrame("Frame") - frame.custom = { busy = false } - local texture = frame:CreateTexture(nil, "ARTWORK") - texture:SetAllPoints(frame) - texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.helpTextureFile) - table.insert(helpFramePool, frame) - end - local muteHelpUntil = 0 - ---@param x number|nil - ---@param y number|nil - ---@param scale number? - ---@param doTag boolean? - local function PlantHelp(x, y, scale, doTag) - if x == nil or y == nil then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Help position is nil, ignoring", ModuleName)) - end - return - end - if doTag == nil then doTag = true end - local frame = nil - for _, helpFrame in ipairs(helpFramePool) do - ---@diagnostic disable-next-line: undefined-field - if not helpFrame.custom.busy then - frame = helpFrame - break - end - end - if not frame then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Help frame pool is full and could not get frame", ModuleName)) - end - return - end - if Heimdall_Data.config.minimapTagger.helpSound then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Playing help sound: %s", - ModuleName, - Heimdall_Data.config.minimapTagger.helpSoundFile - ) - ) - end - if muteHelpUntil > GetTime() then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Help sound is muted until %d", ModuleName, muteHelpUntil)) + if muteCombatUntil > GetTime() then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Combat sound is muted until %d", ModuleName, muteCombatUntil)) + end + else + muteCombatUntil = GetTime() + Heimdall_Data.config.minimapTagger.combatSoundThrottle + local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.combatSoundFile, "Master") + if not ok and Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Failed to play combat sound: %s", + ModuleName, + Heimdall_Data.config.minimapTagger.combatSoundFile + ) + ) + end end - else - muteHelpUntil = GetTime() + Heimdall_Data.config.minimapTagger.helpSoundThrottle - local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.helpSoundFile, "Master") - if not ok and Heimdall_Data.config.minimapTagger.debug then + end + if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.combatTTL) end + end + --endregion + + --region Help + ---@type Frame[] + local helpFramePool = {} + local helpFramePoolMaxSize = 20 + for i = 1, helpFramePoolMaxSize do + local frame = CreateFrame("Frame") + frame.custom = { busy = false } + local texture = frame:CreateTexture(nil, "ARTWORK") + texture:SetAllPoints(frame) + texture:SetTexture(TextureRoot .. Heimdall_Data.config.minimapTagger.helpTextureFile) + table.insert(helpFramePool, frame) + end + local muteHelpUntil = 0 + ---@param x number|nil + ---@param y number|nil + ---@param scale number? + ---@param doTag boolean? + local function PlantHelp(x, y, scale, doTag) + if x == nil or y == nil then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Help position is nil, ignoring", ModuleName)) + end + return + end + if doTag == nil then doTag = true end + local frame = nil + for _, helpFrame in ipairs(helpFramePool) do + ---@diagnostic disable-next-line: undefined-field + if not helpFrame.custom.busy then + frame = helpFrame + break + end + end + if not frame then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Help frame pool is full and could not get frame", ModuleName)) + end + return + end + if Heimdall_Data.config.minimapTagger.helpSound then + if Heimdall_Data.config.minimapTagger.debug then print( string.format( - "[%s] Failed to play help sound: %s", + "[%s] Playing help sound: %s", ModuleName, Heimdall_Data.config.minimapTagger.helpSoundFile ) ) end - end - end - if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.helpTTL) end - end - --endregion - - local pauseUntil = 0 - local frame = CreateFrame("Frame") - frame:RegisterEvent("WORLD_MAP_UPDATE") - frame:SetScript("OnEvent", function(self, event, addon) - if pauseUntil > GetTime() then return end - pauseUntil = GetTime() + 1 - if not BattlefieldMinimap then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] BattlefieldMinimap not found", ModuleName)) - end - return - end - if not Heimdall_Data.config.minimapTagger.enabled then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] MinimapTagger is disabled", ModuleName)) - end - return - end - local scale = Heimdall_Data.config.minimapTagger.scale - BattlefieldMinimap:SetScale(scale) - BattlefieldMinimap:SetMovable(true) - BattlefieldMinimap:EnableMouse(true) - BattlefieldMinimap:RegisterForDrag("LeftButton") - BattlefieldMinimap:SetScript("OnDragStart", function(selff) selff:StartMoving() end) - BattlefieldMinimap:SetScript("OnDragStop", function(selff) selff:StopMovingOrSizing() end) - BattlefieldMinimapBackground:Hide() - BattlefieldMinimapCloseButton:Hide() - BattlefieldMinimapCorner:Hide() - BattlefieldMinimap:HookScript("OnHide", function(selff) - for _, alertFrame in ipairs(alertFramePool) do - alertFrame:Hide() - ---@diagnostic disable-next-line: undefined-field - alertFrame.custom.busy = false - end - for _, tagFrame in ipairs(tagFramePool) do - tagFrame:Hide() - ---@diagnostic disable-next-line: undefined-field - tagFrame.custom.busy = false - end - -- What the fuck is this global? - for _, battleFrame in ipairs(battleFramePool) do - battleFrame:Hide() - battleFrame.custom.busy = false - end - end) - end) - - local chatFrame = CreateFrame("Frame") - chatFrame:RegisterEvent("CHAT_MSG_CHANNEL") - chatFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) - --if Heimdall_Data.config.echoer.debug then - -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender)) - --end - - if not Heimdall_Data.config.minimapTagger.enabled then - --if Heimdall_Data.config.echoer.debug then - -- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) - --end - return - end - - local channelId = select(6, ...) - local _, channelname = GetChannelName(channelId) - local ok = false - for _, channel in pairs(Heimdall_Data.config.minimapTagger.channels) do - if channelname == channel then - ok = true - break - end - end - - if not ok then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Ignoring message from non-master channel: %s, need %s", - ModuleName, - channelname, - Heimdall_Data.config.minimapTagger.masterChannel - ) - ) - end - return - end - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) - shared.dumpTable(Heimdall_Data.config.minimapTagger) - end - - local doTag = true - local messageMapId = string.match(msg, "%[(%d+)%]") or 0 - if messageMapId then messageMapId = tonumber(messageMapId) end - - local currentMapId = GetCurrentMapAreaID() - if currentMapId ~= messageMapId then - if Heimdall_Data.config.minimapTagger.debug then - print( - string.format( - "[%s] Current map ID (%d) does not match message map ID (%d), ignoring message", - ModuleName, - currentMapId, - messageMapId - ) - ) - end - doTag = false - end - - --region Tag - if string.find(msg, "^I see") then - if Heimdall_Data.config.minimapTagger.tagTTL == 0 then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Tag TTL is 0, ignoring message: %s", ModuleName, msg)) - end - return - end - local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found alert position: %s, %s", ModuleName, tostring(x), tostring(y))) - end - if x and y then PlantTag(tonumber(x), tonumber(y), 2, doTag) end - end - --endregion - --region Combat - if string.find(msg, "^I am in combat with") then - if Heimdall_Data.config.minimapTagger.combatTTL == 0 then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Combat TTL is 0, ignoring message: %s", ModuleName, msg)) - end - return - end - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found combat alert in message: %s", ModuleName, msg)) - end - local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found combat position: %s, %s", ModuleName, tostring(x), tostring(y))) - end - if x and y then PlantCombat(tonumber(x), tonumber(y), 2, doTag) end - end - --endregion - --region Death - if string.find(msg, " killed ") then - if Heimdall_Data.config.minimapTagger.alertTTL == 0 then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Alert TTL is 0, ignoring message: %s", ModuleName, msg)) - end - return - end - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found death alert in message: %s", ModuleName, msg)) - end - local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found death position: %s, %s", ModuleName, tostring(x), tostring(y))) - end - if x and y then PlantAlert(tonumber(x), tonumber(y), 2, doTag) end - end - --endregion - --region Help - if string.find(msg, "I need help") then - if Heimdall_Data.config.minimapTagger.helpTTL == 0 then - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Help TTL is 0, ignoring message: %s", ModuleName, msg)) - end - return - end - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found help alert in message: %s", ModuleName, msg)) - end - local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Found help position: %s, %s", ModuleName, tostring(x), tostring(y))) - end - if x and y then - x, y = tonumber(x), tonumber(y) - PlantHelp(x, y, 1, doTag) - ---@diagnostic disable-next-line: undefined-global - if TomTom then + if muteHelpUntil > GetTime() then if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Adding help waypoint to TomTom", ModuleName)) + print(string.format("[%s] Help sound is muted until %d", ModuleName, muteHelpUntil)) end - local areaId = string.match(msg, "%[(%d+)%]") or 0 - if areaId then areaId = tonumber(areaId) end - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] Area ID: %s", ModuleName, tostring(areaId))) - end - - ---@diagnostic disable-next-line: undefined-global - TomTom:AddMFWaypoint(areaId, nil, x / 100, y / 100, { - title = "Help " .. sender, - world = true, - from = "Heimdall", - crazy = true, - }) else - if Heimdall_Data.config.minimapTagger.debug then - print(string.format("[%s] No tomtom no waypoint", ModuleName)) + muteHelpUntil = GetTime() + Heimdall_Data.config.minimapTagger.helpSoundThrottle + local ok = PlaySoundFile(SoundRoot .. Heimdall_Data.config.minimapTagger.helpSoundFile, "Master") + if not ok and Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Failed to play help sound: %s", + ModuleName, + Heimdall_Data.config.minimapTagger.helpSoundFile + ) + ) end end end + if doTag then PlantFrame(x, y, frame, scale, Heimdall_Data.config.minimapTagger.helpTTL) end end --endregion - end) - print("[Heimdall] MinimapTagger loaded") -end + local pauseUntil = 0 + local frame = CreateFrame("Frame") + frame:RegisterEvent("WORLD_MAP_UPDATE") + frame:SetScript("OnEvent", function(self, event, addon) + if pauseUntil > GetTime() then return end + pauseUntil = GetTime() + 1 + if not BattlefieldMinimap then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] BattlefieldMinimap not found", ModuleName)) + end + return + end + if not Heimdall_Data.config.minimapTagger.enabled then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] MinimapTagger is disabled", ModuleName)) + end + return + end + local scale = Heimdall_Data.config.minimapTagger.scale + BattlefieldMinimap:SetScale(scale) + BattlefieldMinimap:SetMovable(true) + BattlefieldMinimap:EnableMouse(true) + BattlefieldMinimap:RegisterForDrag("LeftButton") + BattlefieldMinimap:SetScript("OnDragStart", function(selff) selff:StartMoving() end) + BattlefieldMinimap:SetScript("OnDragStop", function(selff) selff:StopMovingOrSizing() end) + BattlefieldMinimapBackground:Hide() + BattlefieldMinimapCloseButton:Hide() + BattlefieldMinimapCorner:Hide() + BattlefieldMinimap:HookScript("OnHide", function(selff) + for _, alertFrame in ipairs(alertFramePool) do + alertFrame:Hide() + ---@diagnostic disable-next-line: undefined-field + alertFrame.custom.busy = false + end + for _, tagFrame in ipairs(tagFramePool) do + tagFrame:Hide() + ---@diagnostic disable-next-line: undefined-field + tagFrame.custom.busy = false + end + -- What the fuck is this global? + for _, battleFrame in ipairs(battleFramePool) do + battleFrame:Hide() + battleFrame.custom.busy = false + end + end) + end) -SlashCmdList["HEIMDALL_MINIMAPTAGGER"] = function(args) shared.MinimapTagger.Init() end -SLASH_HEIMDALL_MINIMAPTAGGER1 = "/mf" + local chatFrame = CreateFrame("Frame") + chatFrame:RegisterEvent("CHAT_MSG_CHANNEL") + chatFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) + --if Heimdall_Data.config.echoer.debug then + -- print(string.format("[%s] Channel message received from: %s", ModuleName, sender)) + --end + + if not Heimdall_Data.config.minimapTagger.enabled then + --if Heimdall_Data.config.echoer.debug then + -- print(string.format("[%s] Module disabled, ignoring message", ModuleName)) + --end + return + end + + local channelId = select(6, ...) + local _, channelname = GetChannelName(channelId) + local ok = false + for _, channel in pairs(Heimdall_Data.config.minimapTagger.channels) do + if channelname == channel then + ok = true + break + end + end + + if not ok then + if Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Ignoring message from non-master channel: %s, need %s", + ModuleName, + channelname, + Heimdall_Data.config.minimapTagger.masterChannel + ) + ) + end + return + end + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Processing message from master channel: %s", ModuleName, sender)) + shared.dumpTable(Heimdall_Data.config.minimapTagger) + end + + local doTag = true + local messageMapId = string.match(msg, "%[(%d+)%]") or 0 + if messageMapId then messageMapId = tonumber(messageMapId) end + + local currentMapId = GetCurrentMapAreaID() + if currentMapId ~= messageMapId then + if Heimdall_Data.config.minimapTagger.debug then + print( + string.format( + "[%s] Current map ID (%d) does not match message map ID (%d), ignoring message", + ModuleName, + currentMapId, + messageMapId + ) + ) + end + doTag = false + end + + --region Tag + if string.find(msg, "^I see") then + if Heimdall_Data.config.minimapTagger.tagTTL == 0 then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Tag TTL is 0, ignoring message: %s", ModuleName, msg)) + end + return + end + local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found alert position: %s, %s", ModuleName, tostring(x), tostring(y))) + end + if x and y then PlantTag(tonumber(x), tonumber(y), 2, doTag) end + end + --endregion + --region Combat + if string.find(msg, "^I am in combat with") then + if Heimdall_Data.config.minimapTagger.combatTTL == 0 then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Combat TTL is 0, ignoring message: %s", ModuleName, msg)) + end + return + end + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found combat alert in message: %s", ModuleName, msg)) + end + local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found combat position: %s, %s", ModuleName, tostring(x), tostring(y))) + end + if x and y then PlantCombat(tonumber(x), tonumber(y), 2, doTag) end + end + --endregion + --region Death + if string.find(msg, " killed ") then + if Heimdall_Data.config.minimapTagger.alertTTL == 0 then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Alert TTL is 0, ignoring message: %s", ModuleName, msg)) + end + return + end + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found death alert in message: %s", ModuleName, msg)) + end + local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found death position: %s, %s", ModuleName, tostring(x), tostring(y))) + end + if x and y then PlantAlert(tonumber(x), tonumber(y), 2, doTag) end + end + --endregion + --region Help + if string.find(msg, "I need help") then + if Heimdall_Data.config.minimapTagger.helpTTL == 0 then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Help TTL is 0, ignoring message: %s", ModuleName, msg)) + end + return + end + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found help alert in message: %s", ModuleName, msg)) + end + local x, y = string.match(msg, "%((%d+%.%d+)%s*,%s*(%d+%.%d+)%)") + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Found help position: %s, %s", ModuleName, tostring(x), tostring(y))) + end + if x and y then + x, y = tonumber(x), tonumber(y) + PlantHelp(x, y, 1, doTag) + ---@diagnostic disable-next-line: undefined-global + if TomTom then + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Adding help waypoint to TomTom", ModuleName)) + end + local areaId = string.match(msg, "%[(%d+)%]") or 0 + if areaId then areaId = tonumber(areaId) end + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] Area ID: %s", ModuleName, tostring(areaId))) + end + + ---@diagnostic disable-next-line: undefined-global + TomTom:AddMFWaypoint(areaId, nil, x / 100, y / 100, { + title = "Help " .. sender, + world = true, + from = "Heimdall", + crazy = true, + }) + else + if Heimdall_Data.config.minimapTagger.debug then + print(string.format("[%s] No tomtom no waypoint", ModuleName)) + end + end + end + end + --endregion + end) + + print("[Heimdall] MinimapTagger loaded") + end, +} diff --git a/Modules/Network.lua b/Modules/Network.lua index 7f385a0..7d12967 100644 --- a/Modules/Network.lua +++ b/Modules/Network.lua @@ -5,6 +5,7 @@ local ModuleName = "Network" ---@class HeimdallNetworkData ---@field ticker Timer? +---@class Network shared.Network = { Init = function() if not shared.network then shared.network = {} end diff --git a/Modules/NetworkMessenger.lua b/Modules/NetworkMessenger.lua index 448ab75..b0f1748 100644 --- a/Modules/NetworkMessenger.lua +++ b/Modules/NetworkMessenger.lua @@ -2,182 +2,190 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "NetworkMessenger" ----@diagnostic disable-next-line: missing-fields -shared.NetworkMessenger = {} -function shared.NetworkMessenger.Init() - RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix) +---@class NetworkMessenger +shared.NetworkMessenger = { + Init = function() + RegisterAddonMessagePrefix(Heimdall_Data.config.addonPrefix) - if not shared.networkMessenger then shared.networkMessenger = {} end - if not shared.networkMessenger.queue then shared.networkMessenger.queue = {} end - if not shared.networkMessenger.ticker then - local function DoMessage() - --if Heimdall_Data.config.networkMessenger.debug then - -- print(string.format("[%s] Processing network message queue", ModuleName)) - --end - if not Heimdall_Data.config.networkMessenger.enabled then + if not shared.networkMessenger then shared.networkMessenger = {} end + if not shared.networkMessenger.queue then shared.networkMessenger.queue = {} end + if not shared.networkMessenger.ticker then + local function DoMessage() --if Heimdall_Data.config.networkMessenger.debug then - -- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName)) + -- print(string.format("[%s] Processing network message queue", ModuleName)) --end - return - end - ---@type Message - local message = shared.networkMessenger.queue[1] - if not message then - --if Heimdall_Data.config.networkMessenger.debug then - -- print(string.format("[%s] Network message queue empty", ModuleName)) - --end - return - end - if not message.message or message.message == "" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Invalid network message: empty content", ModuleName)) + if not Heimdall_Data.config.networkMessenger.enabled then + --if Heimdall_Data.config.networkMessenger.debug then + -- print(string.format("[%s] Module disabled, skipping network message processing", ModuleName)) + --end + return end - return - end - if not message.channel or message.channel == "" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Invalid network message: no channel specified", ModuleName)) + ---@type Message + local message = shared.networkMessenger.queue[1] + if not message then + --if Heimdall_Data.config.networkMessenger.debug then + -- print(string.format("[%s] Network message queue empty", ModuleName)) + --end + return + end + if not message.message or message.message == "" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Invalid network message: empty content", ModuleName)) + end + return + end + if not message.channel or message.channel == "" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Invalid network message: no channel specified", ModuleName)) + end + return end - return - end - table.remove(shared.networkMessenger.queue, 1) - if not message.message or message.message == "" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Skipping empty network message", ModuleName)) + table.remove(shared.networkMessenger.queue, 1) + if not message.message or message.message == "" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Skipping empty network message", ModuleName)) + end + return end - return - end - if not message.channel or message.channel == "" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Skipping network message with no channel", ModuleName)) + if not message.channel or message.channel == "" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Skipping network message with no channel", ModuleName)) + end + return end - return - end - if not message.data or message.data == "" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Skipping network message with no data", ModuleName)) + if not message.data or message.data == "" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Skipping network message with no data", ModuleName)) + end + return end - return - end - if Heimdall_Data.config.networkMessenger.debug then - print( - string.format( - "[%s] Sending network message: '%s' to %s:%s", - ModuleName, - message.message, - message.channel, - message.data + if Heimdall_Data.config.networkMessenger.debug then + print( + string.format( + "[%s] Sending network message: '%s' to %s:%s", + ModuleName, + message.message, + message.channel, + message.data + ) ) - ) + end + local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data) + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Payload: %s", ModuleName, payload)) + end + if not shared.networkNodes or #shared.networkNodes == 0 then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] No network nodes found, wtf????", ModuleName)) + end + return + end + local target = shared.networkNodes[1] + SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target) end - local payload = string.format("dmessage|%s|%s|%s", message.message, message.channel, message.data) + local function Tick() + --if Heimdall_Data.config.networkMessenger.debug then + -- local queueSize = #shared.networkMessenger.queue + -- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize)) + --end + DoMessage() + shared.networkMessenger.ticker = + C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1) + end + Tick() + end + + -- If we are the leader then we delegate messages (dmessage) + -- If we get a "message" command from leader then we send the message + + local nextIdx = 1 + local addonMsgFrame = CreateFrame("Frame") + addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON") + addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source) + if not Heimdall_Data.config.networkMessenger.enabled then return end + if prefix ~= Heimdall_Data.config.addonPrefix then return end + source = string.match(source, "[^%-]+") + if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Payload: %s", ModuleName, payload)) + print(string.format("[%s] Received message from %s: %s", ModuleName, source, message)) end - if not shared.networkNodes or #shared.networkNodes == 0 then + if #shared.networkNodes == 0 then if Heimdall_Data.config.networkMessenger.debug then print(string.format("[%s] No network nodes found, wtf????", ModuleName)) end return end - local target = shared.networkNodes[1] - SendAddonMessage(Heimdall_Data.config.addonPrefix, payload, "WHISPER", target) - end - local function Tick() - --if Heimdall_Data.config.networkMessenger.debug then - -- local queueSize = #shared.networkMessenger.queue - -- print(string.format("[%s] Queue check - Network messages pending: %d", ModuleName, queueSize)) + + -- There should always be at least one network node ergo should always exist a leader + -- Because the us, the player, is also a node + --local networkLeader = shared.networkNodes[1] + --if source ~= networkLeader then + -- if Heimdall_Data.config.networkMessenger.debug then + -- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source, + -- networkLeader)) + -- end + -- return --end - DoMessage() - shared.networkMessenger.ticker = C_Timer.NewTimer(Heimdall_Data.config.networkMessenger.interval, Tick, 1) - end - Tick() - end - -- If we are the leader then we delegate messages (dmessage) - -- If we get a "message" command from leader then we send the message - - local nextIdx = 1 - local addonMsgFrame = CreateFrame("Frame") - addonMsgFrame:RegisterEvent("CHAT_MSG_ADDON") - addonMsgFrame:SetScript("OnEvent", function(self, event, prefix, message, channel, source) - if not Heimdall_Data.config.networkMessenger.enabled then return end - if prefix ~= Heimdall_Data.config.addonPrefix then return end - source = string.match(source, "[^%-]+") - - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Received message from %s: %s", ModuleName, source, message)) - end - if #shared.networkNodes == 0 then + local parts = shared.Split(message, "|") if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] No network nodes found, wtf????", ModuleName)) + print(string.format("[%s] Received message parts:", ModuleName)) + shared.dumpTable(parts) end - return - end + local command = strtrim(parts[1]) + if command == "message" then + local content = strtrim(tostring(parts[2])) + local targetchannel = strtrim(tostring(parts[3])) + local target = strtrim(tostring(parts[4])) + if Heimdall_Data.config.networkMessenger.debug then + print( + string.format( + "[%s] Received message command: %s %s %s", + ModuleName, + content, + targetchannel, + target + ) + ) + end + ---@type Message + local msg = { + channel = targetchannel, + message = content, + data = target, + } + table.insert(shared.messenger.queue, msg) + elseif command == "dmessage" then + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Received dmessage command", ModuleName)) + end + parts[1] = "message" + local content = table.concat(parts, "|") - -- There should always be at least one network node ergo should always exist a leader - -- Because the us, the player, is also a node - --local networkLeader = shared.networkNodes[1] - --if source ~= networkLeader then - -- if Heimdall_Data.config.networkMessenger.debug then - -- print(string.format("[%s] Message from %s is not from the network leader (%s)", ModuleName, source, - -- networkLeader)) - -- end - -- return - --end - - local parts = shared.Split(message, "|") - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Received message parts:", ModuleName)) - shared.dumpTable(parts) - end - local command = strtrim(parts[1]) - if command == "message" then - local content = strtrim(tostring(parts[2])) - local targetchannel = strtrim(tostring(parts[3])) - local target = strtrim(tostring(parts[4])) - if Heimdall_Data.config.networkMessenger.debug then - print( - string.format("[%s] Received message command: %s %s %s", ModuleName, content, targetchannel, target) - ) + if nextIdx > #shared.networkNodes then nextIdx = 1 end + local recipient = shared.networkNodes[nextIdx] + nextIdx = nextIdx + 1 + if Heimdall_Data.config.networkMessenger.debug then + print(string.format("[%s] Sending message %s to %s", ModuleName, content, recipient)) + end + SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient) end - ---@type Message - local msg = { - channel = targetchannel, - message = content, - data = target, + end) + + --/run Heimdall_Data.Test() + Heimdall_Data.Test = function() + local testmsg = { + channel = "W", + message = "Hi, mom!", + data = "Secundus", } - table.insert(shared.messenger.queue, msg) - elseif command == "dmessage" then - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Received dmessage command", ModuleName)) + for i = 1, 36 do + table.insert(shared.networkMessenger.queue, testmsg) end - parts[1] = "message" - local content = table.concat(parts, "|") - - if nextIdx > #shared.networkNodes then nextIdx = 1 end - local recipient = shared.networkNodes[nextIdx] - nextIdx = nextIdx + 1 - if Heimdall_Data.config.networkMessenger.debug then - print(string.format("[%s] Sending message %s to %s", ModuleName, content, recipient)) - end - SendAddonMessage(Heimdall_Data.config.addonPrefix, content, "WHISPER", recipient) end - end) - --/run Heimdall_Data.Test() - Heimdall_Data.Test = function() - local testmsg = { - channel = "W", - message = "Hi, mom!", - data = "Secundus", - } - for i = 1, 36 do - table.insert(shared.networkMessenger.queue, testmsg) - end - end - - print("[Heimdall] NetworkMessenger module loaded") -end + print("[Heimdall] NetworkMessenger module loaded") + end, +} diff --git a/Modules/Noter.lua b/Modules/Noter.lua index 087845b..bbd5bf5 100644 --- a/Modules/Noter.lua +++ b/Modules/Noter.lua @@ -8,288 +8,291 @@ local ModuleName = "Noter" ---@field date string ---@field note string ----@diagnostic disable-next-line: missing-fields -shared.Noter = {} -function shared.Noter.Init() - -- ---Hopefully this will not be necessary - -- ---@param text string - -- ---@param size number - -- ---@return string[] - -- local function Partition(text, size) - -- local words = {} - -- for word in text:gmatch("[^,]+") do - -- words[#words + 1] = word - -- end +---@class Noter +shared.Noter = { + Init = function() + -- ---Hopefully this will not be necessary + -- ---@param text string + -- ---@param size number + -- ---@return string[] + -- local function Partition(text, size) + -- local words = {} + -- for word in text:gmatch("[^,]+") do + -- words[#words + 1] = word + -- end - -- local ret = {} - -- local currentChunk = "" + -- local ret = {} + -- local currentChunk = "" - -- for _, word in ipairs(words) do - -- if #currentChunk + #word + 1 <= size then - -- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word) - -- else - -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end - -- currentChunk = word - -- end - -- end + -- for _, word in ipairs(words) do + -- if #currentChunk + #word + 1 <= size then + -- currentChunk = currentChunk .. (currentChunk == "" and word or " " .. word) + -- else + -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end + -- currentChunk = word + -- end + -- end - -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end + -- if #currentChunk > 0 then ret[#ret + 1] = currentChunk end - -- return ret - -- end - ---@param array any[] - ---@return any[] - local function Compact(array) - local compacted = {} - for _, v in pairs(array) do - compacted[#compacted + 1] = v - end - return compacted - end - - ---@param name string - ---@param args string[] - local function DeleteNotes(name, args) - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Delete note command received for: %s", ModuleName, name)) - end - local range = args[4] - if range then - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Range received for delete note: %s", ModuleName, range)) + -- return ret + -- end + ---@param array any[] + ---@return any[] + local function Compact(array) + local compacted = {} + for _, v in pairs(array) do + compacted[#compacted + 1] = v end - local indices = shared.Split(range, "..") - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Indices for range deletion: %s", ModuleName, table.concat(indices, ", "))) - shared.dumpTable(indices) - end - local start = tonumber(indices[1]) - local finish = tonumber(indices[2]) + return compacted + end - if not start then + ---@param name string + ---@param args string[] + local function DeleteNotes(name, args) + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Delete note command received for: %s", ModuleName, name)) + end + local range = args[4] + if range then if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Invalid start range for delete note: %s", ModuleName, tostring(start))) + print(string.format("[%s] Range received for delete note: %s", ModuleName, range)) end - return - end - - if not finish then finish = start end - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name)) - end - - -- Here, because we are deleting random notes, we lose the "iterative" index property - -- Ie it's not longer 1..100, it might be 1..47, 50, 68..100 - -- Which means that we cannot use ipairs, bad! - for i = start, finish do - if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end - if not Heimdall_Data.config.notes[name][i] then - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Note at index %s does not exist", ModuleName, i)) - end - else - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i)) - shared.dumpTable(Heimdall_Data.config.notes[name][i]) - end - Heimdall_Data.config.notes[name][i] = nil + local indices = shared.Split(range, "..") + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Indices for range deletion: %s", ModuleName, table.concat(indices, ", "))) + shared.dumpTable(indices) end - end - Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name]) - end - end + local start = tonumber(indices[1]) + local finish = tonumber(indices[2]) - ---@param channel string - ---@param index number - ---@param note Note - local function PrintNote(channel, index, note) - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source)) - print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note)) + if not start then + if Heimdall_Data.config.noter.debug then + print( + string.format("[%s] Invalid start range for delete note: %s", ModuleName, tostring(start)) + ) + end + return + end + + if not finish then finish = start end + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Deleting note range %s to %s for: %s", ModuleName, start, finish, name)) + end + + -- Here, because we are deleting random notes, we lose the "iterative" index property + -- Ie it's not longer 1..100, it might be 1..47, 50, 68..100 + -- Which means that we cannot use ipairs, bad! + for i = start, finish do + if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end + if not Heimdall_Data.config.notes[name][i] then + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Note at index %s does not exist", ModuleName, i)) + end + else + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Deleting note %s at index %s", ModuleName, name, i)) + shared.dumpTable(Heimdall_Data.config.notes[name][i]) + end + Heimdall_Data.config.notes[name][i] = nil + end + end + Heimdall_Data.config.notes[name] = Compact(Heimdall_Data.config.notes[name]) + end end - ---@type Message - local msg = { - channel = "C", - data = channel, - message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note), - } - --table.insert(shared.messenger.queue, msg) - table.insert(shared.networkMessenger.queue, msg) - end - ---@param name string - ---@param args string[] - local function PrintNotes(channel, name, args) - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Print note command received for: %s", ModuleName, name)) - end - local range = args[3] - if not range then + + ---@param channel string + ---@param index number + ---@param note Note + local function PrintNote(channel, index, note) if Heimdall_Data.config.noter.debug then - print( - string.format( - "[%s] No range specified for print note, defaulting to last %d notes", - ModuleName, - Heimdall_Data.config.noter.lastNotes + print(string.format("[%s] Printing note at index %d for: %s", ModuleName, index, note.source)) + print(string.format("[%s] [%s][%d] %s: %s", ModuleName, note.source, index, note.date, note.note)) + end + ---@type Message + local msg = { + channel = "C", + data = channel, + message = string.format("[%s][%d] %s: %s", note.source, index, note.date, note.note), + } + --table.insert(shared.messenger.queue, msg) + table.insert(shared.networkMessenger.queue, msg) + end + ---@param name string + ---@param args string[] + local function PrintNotes(channel, name, args) + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Print note command received for: %s", ModuleName, name)) + end + local range = args[3] + if not range then + if Heimdall_Data.config.noter.debug then + print( + string.format( + "[%s] No range specified for print note, defaulting to last %d notes", + ModuleName, + Heimdall_Data.config.noter.lastNotes + ) ) - ) - end - local notes = Heimdall_Data.config.notes[name] or {} - local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1) - local finish = #notes - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name)) - end - for i = start, finish do - PrintNote(channel, i, notes[i]) - end - return - end - if range then - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Range received for print note: %s", ModuleName, range)) - end - local indices = shared.Split(range, "..") - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Indices for range printing: %s", ModuleName, table.concat(indices, ", "))) - shared.dumpTable(indices) - end - local start = tonumber(indices[1]) - local finish = tonumber(indices[2]) - - if not start then + end + local notes = Heimdall_Data.config.notes[name] or {} + local start = math.max(1, #notes - Heimdall_Data.config.noter.lastNotes + 1) + local finish = #notes if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Invalid start range for print note: %s", ModuleName, tostring(start))) + print(string.format("[%s] Printing notes from %d to %d for: %s", ModuleName, start, finish, name)) + end + for i = start, finish do + PrintNote(channel, i, notes[i]) + end + return + end + if range then + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Range received for print note: %s", ModuleName, range)) + end + local indices = shared.Split(range, "..") + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Indices for range printing: %s", ModuleName, table.concat(indices, ", "))) + shared.dumpTable(indices) + end + local start = tonumber(indices[1]) + local finish = tonumber(indices[2]) + + if not start then + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Invalid start range for print note: %s", ModuleName, tostring(start))) + end + return + end + + if not finish then finish = start end + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name)) + end + + for i = start, finish do + if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end + if not Heimdall_Data.config.notes[name][i] then + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Note at index %s does not exist", ModuleName, i)) + end + else + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i)) + shared.dumpTable(Heimdall_Data.config.notes[name][i]) + end + PrintNote(channel, i, Heimdall_Data.config.notes[name][i]) + end + end + end + end + + ---@param name string + ---@param sender string + ---@param args string[] + local function AddNote(name, sender, args) + if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender)) + shared.dumpTable(args) + end + local msgparts = {} + for i = 3, #args do + msgparts[#msgparts + 1] = args[i] + end + local msg = table.concat(msgparts, " ") + + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender)) + print(string.format("[%s] Note: %s", ModuleName, msg)) + end + + local note = { + source = sender, + date = date("%Y-%m-%dT%H:%M:%S"), + note = msg, + } + + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Adding note", ModuleName)) + shared.dumpTable(note) + end + table.insert(Heimdall_Data.config.notes[name], note) + end + + -- Here's the plan: + -- Implement a "note" command, that will do everything + -- Saying "note " will add a note to the list for the character + -- Saying "note " will list last N notes + -- Saying "note i" will list the i-th note + -- Saying "note i..j" will list notes from i to j + -- Saying "note delete i" will delete the i-th note + -- Saying "note delete i..j" will delete notes from i to j + local noterChannelFrame = CreateFrame("Frame") + noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") + noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) + --if Heimdall_Data.config.noter.debug then + -- print(string.format("[%s] Event received", ModuleName)) + -- shared.dumpTable(Heimdall_Data.config.noter) + --end + if not Heimdall_Data.config.noter.enabled then + --if Heimdall_Data.config.noter.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.noter.channels) do + if channelname == channel then + ok = true + break + end + end + if not ok then + --if Heimdall_Data.config.noter.debug then + -- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel)) + --end + return + end + + sender = string.match(sender, "^[^-]+") + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Message from: %s", ModuleName, sender)) + shared.dumpTable(Heimdall_Data.config.noter) + end + + if not msg or msg == "" then + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Empty message, ignoring", ModuleName)) end return end - if not finish then finish = start end + local args = { strsplit(" ", msg) } if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Printing note range %s to %s for: %s", ModuleName, start, finish, name)) + print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", "))) + shared.dumpTable(args) end - - for i = start, finish do - if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end - if not Heimdall_Data.config.notes[name][i] then - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Note at index %s does not exist", ModuleName, i)) - end + local command = args[1] + if command == "note" then + local name = strtrim(string.lower(args[2] or "")) + if Heimdall_Data.config.noter.debug then + print(string.format("[%s] Note command received for: %s", ModuleName, name)) + end + local note = strtrim(args[3] or "") + if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note: %s", ModuleName, note)) end + if note == "delete" then + DeleteNotes(name, args) + elseif string.find(note, "^[%d%.]*$") then + PrintNotes(channelname, name, args) else - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Printing note %s at index %s", ModuleName, name, i)) - shared.dumpTable(Heimdall_Data.config.notes[name][i]) - end - PrintNote(channel, i, Heimdall_Data.config.notes[name][i]) + AddNote(name, sender, args) end end - end - end + end) - ---@param name string - ---@param sender string - ---@param args string[] - local function AddNote(name, sender, args) - if not Heimdall_Data.config.notes[name] then Heimdall_Data.config.notes[name] = {} end - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender)) - shared.dumpTable(args) - end - local msgparts = {} - for i = 3, #args do - msgparts[#msgparts + 1] = args[i] - end - local msg = table.concat(msgparts, " ") - - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Adding note for: %s from: %s", ModuleName, name, sender)) - print(string.format("[%s] Note: %s", ModuleName, msg)) - end - - local note = { - source = sender, - date = date("%Y-%m-%dT%H:%M:%S"), - note = msg, - } - - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Adding note", ModuleName)) - shared.dumpTable(note) - end - table.insert(Heimdall_Data.config.notes[name], note) - end - - -- Here's the plan: - -- Implement a "note" command, that will do everything - -- Saying "note " will add a note to the list for the character - -- Saying "note " will list last N notes - -- Saying "note i" will list the i-th note - -- Saying "note i..j" will list notes from i to j - -- Saying "note delete i" will delete the i-th note - -- Saying "note delete i..j" will delete notes from i to j - local noterChannelFrame = CreateFrame("Frame") - noterChannelFrame:RegisterEvent("CHAT_MSG_CHANNEL") - noterChannelFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) - --if Heimdall_Data.config.noter.debug then - -- print(string.format("[%s] Event received", ModuleName)) - -- shared.dumpTable(Heimdall_Data.config.noter) - --end - if not Heimdall_Data.config.noter.enabled then - --if Heimdall_Data.config.noter.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.noter.channels) do - if channelname == channel then - ok = true - break - end - end - if not ok then - --if Heimdall_Data.config.noter.debug then - -- print(string.format("[%s] Channel %s does not match the master channel %s", ModuleName, channelname, Heimdall_Data.config.noter.masterChannel)) - --end - return - end - - sender = string.match(sender, "^[^-]+") - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Message from: %s", ModuleName, sender)) - shared.dumpTable(Heimdall_Data.config.noter) - end - - if not msg or msg == "" then - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Empty message, ignoring", ModuleName)) - end - return - end - - local args = { strsplit(" ", msg) } - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Arguments received: %s", ModuleName, table.concat(args, ", "))) - shared.dumpTable(args) - end - local command = args[1] - if command == "note" then - local name = strtrim(string.lower(args[2] or "")) - if Heimdall_Data.config.noter.debug then - print(string.format("[%s] Note command received for: %s", ModuleName, name)) - end - local note = strtrim(args[3] or "") - if Heimdall_Data.config.noter.debug then print(string.format("[%s] Note: %s", ModuleName, note)) end - if note == "delete" then - DeleteNotes(name, args) - elseif string.find(note, "^[%d%.]*$") then - PrintNotes(channelname, name, args) - else - AddNote(name, sender, args) - end - end - end) - - print("[Heimdall] Commander module loaded") -end + print("[Heimdall] Commander module loaded") + end, +} diff --git a/Modules/Spotter.lua b/Modules/Spotter.lua index e537f70..6897152 100644 --- a/Modules/Spotter.lua +++ b/Modules/Spotter.lua @@ -2,223 +2,226 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "Spotter" ----@diagnostic disable-next-line: missing-fields -shared.Spotter = {} -function shared.Spotter.Init() - local function FormatHP(hp) - if hp > 1e9 then - return string.format("%.1fB", hp / 1e9) - elseif hp > 1e6 then - return string.format("%.1fM", hp / 1e6) - elseif hp > 1e3 then - return string.format("%.1fK", hp / 1e3) - else - return hp - end - end - - ---@type table - local throttleTable = {} - - ---@param unit string - ---@param name string - ---@param faction string - ---@param hostile boolean - ---@return boolean - ---@return string? error - local function ShouldNotify(unit, name, faction, hostile) - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction)) +---@class Spotter +shared.Spotter = { + Init = function() + local function FormatHP(hp) + if hp > 1e9 then + return string.format("%.1fB", hp / 1e9) + elseif hp > 1e6 then + return string.format("%.1fM", hp / 1e6) + elseif hp > 1e3 then + return string.format("%.1fK", hp / 1e3) + else + return hp + end end - if shared.AgentTracker.IsAgent(name) then + ---@type table + local throttleTable = {} + + ---@param unit string + ---@param name string + ---@param faction string + ---@param hostile boolean + ---@return boolean + ---@return string? error + local function ShouldNotify(unit, name, faction, hostile) if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Skipping agent: %s", ModuleName, name)) + print(string.format("[%s] Checking notification criteria for %s (%s)", ModuleName, name, faction)) end - return false - end - if Heimdall_Data.config.spotter.stinky then - if shared.IsStinky(name) then + if shared.AgentTracker.IsAgent(name) then if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name)) + print(string.format("[%s] Skipping agent: %s", ModuleName, name)) end - return true + return false end - end - if Heimdall_Data.config.spotter.alliance then - if faction == "Alliance" then - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name)) + if Heimdall_Data.config.spotter.stinky then + if shared.IsStinky(name) then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Notifying - Found stinky: %s", ModuleName, name)) + end + return true end - return true end - end - if Heimdall_Data.config.spotter.hostile then - if hostile then - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name)) + if Heimdall_Data.config.spotter.alliance then + if faction == "Alliance" then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Notifying - Found Alliance player: %s", ModuleName, name)) + end + return true end - return true end - end - if Heimdall_Data.config.spotter.debug then - print( - string.format( - "[%s] Using everyone setting: %s", - ModuleName, - tostring(Heimdall_Data.config.spotter.everyone) + if Heimdall_Data.config.spotter.hostile then + if hostile then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Notifying - Found hostile player: %s", ModuleName, name)) + end + return true + end + end + + if Heimdall_Data.config.spotter.debug then + print( + string.format( + "[%s] Using everyone setting: %s", + ModuleName, + tostring(Heimdall_Data.config.spotter.everyone) + ) ) - ) - end - return Heimdall_Data.config.spotter.everyone - end - - ---@param unit string - ---@return string? - local function NotifySpotted(unit) - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit)) - end - - if not unit then return string.format("Could not find unit %s", tostring(unit)) end - if not UnitIsPlayer(unit) then - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit)) end - return nil + return Heimdall_Data.config.spotter.everyone end - local name = UnitName(unit) - if not name then return string.format("Could not find name for unit %s", tostring(unit)) end - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Processing player: %s", ModuleName, name)) - end - - local time = GetTime() - if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then + ---@param unit string + ---@return string? + local function NotifySpotted(unit) if Heimdall_Data.config.spotter.debug then - local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name]) - print(string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime)) + print(string.format("[%s] Processing spotted unit: %s", ModuleName, unit)) end - return string.format("Throttled %s", tostring(name)) - end - throttleTable[name] = time - local race = UnitRace(unit) - if not race then return string.format("Could not find race for unit %s", tostring(unit)) end - local faction = shared.raceMap[race] - if not faction then return string.format("Could not find faction for race %s", tostring(race)) end - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction)) - end - - local hostile = UnitCanAttack("player", unit) - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly")) - end - - local doNotify = ShouldNotify(unit, name, faction, hostile) - if not doNotify then - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Skipping notification for %s", ModuleName, name)) + if not unit then return string.format("Could not find unit %s", tostring(unit)) end + if not UnitIsPlayer(unit) then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Ignoring non-player unit: %s", ModuleName, unit)) + end + return nil end - return string.format("Not notifying for %s", tostring(name)) - end - local hp = UnitHealth(unit) - if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end - local maxHp = UnitHealthMax(unit) - if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp))) - end - - local class = UnitClass(unit) - if not class then return string.format("Could not find class for unit %s", tostring(unit)) end - - local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown" - if Heimdall_Data.config.spotter.zoneOverride then - zone = Heimdall_Data.config.spotter.zoneOverride or "" - subzone = "" - end - - local x, y = GetPlayerMapPosition("player") - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100)) - end - - local pvpOn = UnitIsPVP(unit) - local stinky = shared.IsStinky(name) or false - SetMapToCurrentZone() - SetMapByID(GetCurrentMapAreaID()) - local areaId = tostring(GetCurrentMapAreaID()) - - for _, channel in pairs(Heimdall_Data.config.spotter.channels) do + local name = UnitName(unit) + if not name then return string.format("Could not find name for unit %s", tostring(unit)) end if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Processing channel: %s", ModuleName, channel)) + print(string.format("[%s] Processing player: %s", ModuleName, name)) end - local locale = shared.GetLocaleForChannel(channel) - local text = string.format( - shared._L("spotterSpotted", locale), - hostile and shared._L("hostile", locale) or shared._L("friendly", locale), - name, - shared._L(class, locale), - stinky and string.format("(%s)", "!!!!") or "", - shared._L(race, locale), - shared._L(faction, locale), - pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale), - string.gsub(FormatHP(hp), "M", "kk"), - string.gsub(FormatHP(maxHp), "M", "kk"), - shared._L(zone, locale), - shared._L(subzone, locale), - areaId, - x * 100, - y * 100 - ) - ---@type Message - local msg = { - channel = "C", - data = channel, - message = text, - } - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Queuing spotter message", ModuleName)) - shared.dumpTable(msg) + local time = GetTime() + if throttleTable[name] and time - throttleTable[name] < Heimdall_Data.config.spotter.throttleTime then + if Heimdall_Data.config.spotter.debug then + local remainingTime = Heimdall_Data.config.spotter.throttleTime - (time - throttleTable[name]) + print( + string.format("[%s] Player %s throttled for %.1f more seconds", ModuleName, name, remainingTime) + ) + end + return string.format("Throttled %s", tostring(name)) end - table.insert(shared.messenger.queue, msg) - end - end + throttleTable[name] = time - local frame = CreateFrame("Frame") - frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") - frame:RegisterEvent("UNIT_TARGET") - frame:SetScript("OnEvent", function(self, event, unit) - if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) - end - - if not Heimdall_Data.config.spotter.enabled then + local race = UnitRace(unit) + if not race then return string.format("Could not find race for unit %s", tostring(unit)) end + local faction = shared.raceMap[race] + if not faction then return string.format("Could not find faction for race %s", tostring(race)) end if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Module disabled, ignoring event", ModuleName)) + print(string.format("[%s] Player %s is %s (%s)", ModuleName, name, race, faction)) end - return - end - if event == "UNIT_TARGET" then unit = "target" end - - local err = NotifySpotted(unit) - if err then + local hostile = UnitCanAttack("player", unit) if Heimdall_Data.config.spotter.debug then - print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err)) + print(string.format("[%s] Player %s is %s", ModuleName, name, hostile and "hostile" or "friendly")) + end + + local doNotify = ShouldNotify(unit, name, faction, hostile) + if not doNotify then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Skipping notification for %s", ModuleName, name)) + end + return string.format("Not notifying for %s", tostring(name)) + end + + local hp = UnitHealth(unit) + if not hp then return string.format("Could not find hp for unit %s", tostring(unit)) end + local maxHp = UnitHealthMax(unit) + if not maxHp then return string.format("Could not find maxHp for unit %s", tostring(unit)) end + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Player %s health: %s/%s", ModuleName, name, FormatHP(hp), FormatHP(maxHp))) + end + + local class = UnitClass(unit) + if not class then return string.format("Could not find class for unit %s", tostring(unit)) end + + local zone, subzone = GetZoneText() or "Unknown", GetSubZoneText() or "Unknown" + if Heimdall_Data.config.spotter.zoneOverride then + zone = Heimdall_Data.config.spotter.zoneOverride or "" + subzone = "" + end + + local x, y = GetPlayerMapPosition("player") + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Player %s coordinates: %.2f, %.2f", ModuleName, name, x * 100, y * 100)) + end + + local pvpOn = UnitIsPVP(unit) + local stinky = shared.IsStinky(name) or false + SetMapToCurrentZone() + SetMapByID(GetCurrentMapAreaID()) + local areaId = tostring(GetCurrentMapAreaID()) + + for _, channel in pairs(Heimdall_Data.config.spotter.channels) do + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Processing channel: %s", ModuleName, channel)) + end + local locale = shared.GetLocaleForChannel(channel) + local text = string.format( + shared._L("spotterSpotted", locale), + hostile and shared._L("hostile", locale) or shared._L("friendly", locale), + name, + shared._L(class, locale), + stinky and string.format("(%s)", "!!!!") or "", + shared._L(race, locale), + shared._L(faction, locale), + pvpOn and shared._L("pvpOn", locale) or shared._L("pvpOff", locale), + string.gsub(FormatHP(hp), "M", "kk"), + string.gsub(FormatHP(maxHp), "M", "kk"), + shared._L(zone, locale), + shared._L(subzone, locale), + areaId, + x * 100, + y * 100 + ) + + ---@type Message + local msg = { + channel = "C", + data = channel, + message = text, + } + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Queuing spotter message", ModuleName)) + shared.dumpTable(msg) + end + table.insert(shared.messenger.queue, msg) end end - end) - if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end - print("[Heimdall] Spotter loaded") -end + local frame = CreateFrame("Frame") + frame:RegisterEvent("NAME_PLATE_UNIT_ADDED") + frame:RegisterEvent("UNIT_TARGET") + frame:SetScript("OnEvent", function(self, event, unit) + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Event received: %s for unit: %s", ModuleName, event, unit or "target")) + end + + if not Heimdall_Data.config.spotter.enabled then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Module disabled, ignoring event", ModuleName)) + end + return + end + + if event == "UNIT_TARGET" then unit = "target" end + + local err = NotifySpotted(unit) + if err then + if Heimdall_Data.config.spotter.debug then + print(string.format("[%s] Error processing unit %s: %s", ModuleName, unit, err)) + end + end + end) + + if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Module initialized", ModuleName)) end + print("[Heimdall] Spotter loaded") + end, +} diff --git a/Modules/StinkyCache.lua b/Modules/StinkyCache.lua index ddc175d..24f0ae4 100644 --- a/Modules/StinkyCache.lua +++ b/Modules/StinkyCache.lua @@ -1,72 +1,74 @@ -local addonname, shared = ... +local _, shared = ... ---@cast shared HeimdallShared ----@cast addonname string local ModuleName = "StinkyCache" ----@diagnostic disable-next-line: missing-fields -shared.StinkyCache = {} -function shared.StinkyCache.Init() - shared.stinkyCache = { - stinkies = {}, - } +---@class StinkyCache +shared.StinkyCache = { + Init = function() + shared.stinkyCache = { + stinkies = {}, + } - ---@param name string - local function AskCommander(name) - if Heimdall_Data.config.stinkyCache.debug then - print( - string.format( - "[%s] Asking commander %s about %s", - ModuleName, - Heimdall_Data.config.stinkyCache.commander, - name - ) - ) - end - local messageParts = { "isstinky", name } - local message = table.concat(messageParts, "|") - SendAddonMessage( - Heimdall_Data.config.addonPrefix, - message, - "WHISPER", - Heimdall_Data.config.stinkyCache.commander - ) - return - end - - local addonMessageFrame = CreateFrame("Frame") - addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON") - addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) - if sender == Heimdall_Data.config.stinkyCache.commander then + ---@param name string + local function AskCommander(name) if Heimdall_Data.config.stinkyCache.debug then print( string.format( - "[%s] Received stinky from commander %s: %s", + "[%s] Asking commander %s about %s", ModuleName, Heimdall_Data.config.stinkyCache.commander, - msg + name ) ) end - local parts = { strsplit("|", msg) } - local name, value = parts[1], parts[2] - shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() } - else - if Heimdall_Data.config.stinkyCache.debug then - print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg)) - end - local parts = { strsplit("|", msg) } - local command, name = parts[1], parts[2] - if parts[1] == "isstinky" then local res = Heimdall_Data.config.stinkies[parts[2]] end + local messageParts = { "isstinky", name } + local message = table.concat(messageParts, "|") + SendAddonMessage( + Heimdall_Data.config.addonPrefix, + message, + "WHISPER", + Heimdall_Data.config.stinkyCache.commander + ) + return end - end) - setmetatable(shared.stinkyCache.stinkies, { - __index = function(self, key) - local value = rawget(self, key) - local now = GetTime() - if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then AskCommander(key) end - return rawget(self, key) - end, - }) - print("[Heimdall] StinkyCache module loaded") -end + local addonMessageFrame = CreateFrame("Frame") + addonMessageFrame:RegisterEvent("CHAT_MSG_ADDON") + addonMessageFrame:SetScript("OnEvent", function(self, event, msg, sender, ...) + if sender == Heimdall_Data.config.stinkyCache.commander then + if Heimdall_Data.config.stinkyCache.debug then + print( + string.format( + "[%s] Received stinky from commander %s: %s", + ModuleName, + Heimdall_Data.config.stinkyCache.commander, + msg + ) + ) + end + local parts = { strsplit("|", msg) } + local name, value = parts[1], parts[2] + shared.stinkyCache.stinkies[name] = { value = value, timestamp = time() } + else + if Heimdall_Data.config.stinkyCache.debug then + print(string.format("[%s] Received stinky from non-commander %s: %s", ModuleName, sender, msg)) + end + local parts = { strsplit("|", msg) } + local command, name = parts[1], parts[2] + if parts[1] == "isstinky" then local res = Heimdall_Data.config.stinkies[parts[2]] end + end + end) + + setmetatable(shared.stinkyCache.stinkies, { + __index = function(self, key) + local value = rawget(self, key) + local now = GetTime() + if value == nil or now - value.timestamp > Heimdall_Data.config.stinkyCache.ttl then + AskCommander(key) + end + return rawget(self, key) + end, + }) + print("[Heimdall] StinkyCache module loaded") + end, +} diff --git a/Modules/Whoer.lua b/Modules/Whoer.lua index a18fafc..411ff55 100644 --- a/Modules/Whoer.lua +++ b/Modules/Whoer.lua @@ -2,647 +2,699 @@ local _, shared = ... ---@cast shared HeimdallShared local ModuleName = "Whoer" ----@diagnostic disable-next-line: missing-fields -shared.Whoer = {} -function shared.Whoer.Init() - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end +---@class Whoer +shared.Whoer = { + Init = function() + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - ---@type table - HeimdallStinkies = {} + ---@type table + HeimdallStinkies = {} - ---@class Player - ---@field name string - ---@field guild string - ---@field race string - ---@field class string - ---@field zone string - ---@field lastSeenInternal number - ---@field lastSeen string - ---@field firstSeen string - ---@field seenCount number - ---@field stinky boolean? - Player = { - ---@param name string - ---@param guild string - ---@param race string - ---@param class string - ---@param zone string - ---@return Player - new = function(name, guild, race, class, zone) - local self = setmetatable({}, { - __index = Player, - }) - self.name = name - self.guild = guild - self.race = race - self.class = class - self.zone = zone - self.lastSeenInternal = GetTime() - self.lastSeen = "never" - self.firstSeen = "never" - self.seenCount = 0 - return self - end, - ---@return string - ToString = function(self) - local out = string.format( - "%s %s %s\nFirst: %s Last: %s Seen: %3d", - shared.padString(self.name, 16, true), - shared.padString(self.guild, 26, false), - shared.padString(self.zone, 26, false), - shared.padString(self.firstSeen, 10, true), - shared.padString(self.lastSeen, 10, true), - self.seenCount - ) - return string.format("|cFF%s%s|r", shared.classColors[self.class], out) - end, - } - - ---@class WHOQuery - ---@field query string - ---@field filters WHOFilter[] - WHOQuery = { - ---@param query string - ---@param filters WHOFilter[] - ---@return WHOQuery - new = function(query, filters) - local self = setmetatable({}, { - __index = WHOQuery, - }) - self.query = query - self.filters = filters - return self - end, - } - - ---@class WHOFilter - ---@field Run fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean - ---@field key string - ---@type WHOFilter - local NotSiegeOfOrgrimmarFilter = { - Run = function(name, guild, level, race, class, zone) - if not zone then return false end - return zone ~= "Siege of Orgrimmar" - end, - key = "notsoo", - } - ---@type WHOFilter - local AllianceFilter = { - Run = function(name, guild, level, race, class, zone) - if not race then return false end - if not shared.raceMap[race] then return false end - return shared.raceMap[race] == "Alliance" - end, - key = "ally", - } - - ---@class WhoQueryService - ---@field queries WHOQuery[] - ---@field filters WHOFilter[] - ---@field getFilter fun(key: string): WHOFilter? - ---@field WhoQueryToString fun(query: WHOQuery): string - ---@field WhoQueryFromString fun(query: string): WHOQuery - ---@field WhoQueriesToString fun(queries: WHOQuery[]): string - ---@field WhoQueriesFromString fun(queries: string): WHOQuery[] - shared.WhoQueryService = { - queries = {}, - filters = { - NotSiegeOfOrgrimmarFilter, - AllianceFilter, - }, - ---@param key string - ---@return WHOFilter? - getFilter = function(key) - for _, filter in pairs(shared.WhoQueryService.filters) do - if filter.key == key then return filter end - end - return nil - end, - ---@param query WHOQuery - ---@return string - WhoQueryToString = function(query) - local ret = "" - ret = ret .. query.query - ret = ret .. ";" - for _, filter in pairs(query.filters) do - ret = ret .. filter.key .. ";" - end - return ret - end, - ---@param queries WHOQuery[] - ---@return string - WhoQueriesToString = function(queries) - local ret = "" - for _, query in pairs(queries) do - ret = ret .. shared.WhoQueryService.WhoQueryToString(query) .. "\n" - end - return ret - end, - ---@param query string - ---@return WHOQuery - WhoQueryFromString = function(query) - local queryParts = shared.Split(query, ";") - local filters = {} - for _, filterKey in pairs(queryParts) do - local filter = shared.WhoQueryService.getFilter(filterKey) - if not filter then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Filter %s not found", ModuleName, filterKey)) - end - else - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Filter %s found", ModuleName, filterKey)) - end - - table.insert(filters, filter) - end - end - if Heimdall_Data.config.who.debug then - print(string.format("[%s] WHO query: %s with %d filters", ModuleName, queryParts[1], #filters)) - end - shared.dumpTable(filters) - return WHOQuery.new(queryParts[1], filters) - end, - ---@param queryStr string - ---@return WHOQuery[] - WhoQueriesFromString = function(queryStr) - local queries = shared.Split(queryStr, "\n") - local ret = {} - for _, query in pairs(queries) do - table.insert(ret, shared.WhoQueryService.WhoQueryFromString(query)) - end - return ret - end, - } - shared.WhoQueryService.queries = shared.WhoQueryService.WhoQueriesFromString(Heimdall_Data.config.who.queries) - - ---@param inputZone string - ---@return boolean - shared.Whoer.ShouldNotifyForZone = shared.Memoize(function(inputZone) - if not Heimdall_Data.config.who.debug then - print(string.format("[%s] ShouldNotifyForZone %s", ModuleName, inputZone)) - end - for zone, _ in pairs(Heimdall_Data.config.who.zoneNotifyFor) do - if Heimdall_Data.config.who.debug then print(string.format("[%s] Checking zone %s", ModuleName, zone)) end - if zone == "*" then return true end - if string.find(inputZone, zone) then - if not Heimdall_Data.config.who.debug then - print( - string.format("[%s] ShouldNotifyForZone %s is true thanks to %s", ModuleName, inputZone, zone) - ) - end - return true - end - end - if not Heimdall_Data.config.who.debug then - print(string.format("[%s] ShouldNotifyForZone %s is false", ModuleName, inputZone)) - end - return false - end) - - -----@type WHOQuery[] - --local whoQueries = { - -- WHOQuery.new("g-\"БеспредеЛ\"", {}), - -- WHOQuery.new("g-\"ЗАО бещёки\"", {}), - -- WHOQuery.new("g-\"КОНИЛИНГУСЫ\"", {}), - -- --WHOQuery.new("g-\"Dovahkin\"", {}), - -- WHOQuery.new( - -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Human\" r-\"Dwarf\" r-\"Night Elf\"", - -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - -- WHOQuery.new( - -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Gnome\" r-\"Draenei\" r-\"Worgen\"", - -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - -- WHOQuery.new( - -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Kul Tiran\" r-\"Dark Iron Dwarf\" r-\"Void Elf\"", - -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - -- WHOQuery.new( - -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Lightforged Draenei\" r-\"Mechagnome\"", - -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), - -- WHOQuery.new("Kekv Firobot Tomoki Mld Alltros", {}) - --} - local whoQueryIdx = 1 - ---@type WHOQuery? - local lastQuery = nil - - ---@param player Player - ---@return string? - local function Notify(player) - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Processing notification for player: %s", ModuleName, player.name)) - print( - string.format( - "[%s] Player details - Guild: %s, Race: %s, Class: %s, Zone: %s", - ModuleName, - player.guild, - player.race, - player.class, - player.zone + ---@class Player + ---@field name string + ---@field guild string + ---@field race string + ---@field class string + ---@field zone string + ---@field lastSeenInternal number + ---@field lastSeen string + ---@field firstSeen string + ---@field seenCount number + ---@field stinky boolean? + Player = { + ---@param name string + ---@param guild string + ---@param race string + ---@param class string + ---@param zone string + ---@return Player + new = function(name, guild, race, class, zone) + local self = setmetatable({}, { + __index = Player, + }) + self.name = name + self.guild = guild + self.race = race + self.class = class + self.zone = zone + self.lastSeenInternal = GetTime() + self.lastSeen = "never" + self.firstSeen = "never" + self.seenCount = 0 + return self + end, + ---@return string + ToString = function(self) + local out = string.format( + "%s %s %s\nFirst: %s Last: %s Seen: %3d", + shared.padString(self.name, 16, true), + shared.padString(self.guild, 26, false), + shared.padString(self.zone, 26, false), + shared.padString(self.firstSeen, 10, true), + shared.padString(self.lastSeen, 10, true), + self.seenCount ) - ) - print( - string.format( - "[%s] Player history - First seen: %s, Last seen: %s, Seen count: %d", - ModuleName, - player.firstSeen, - player.lastSeen, - player.seenCount - ) - ) - end + return string.format("|cFF%s%s|r", shared.classColors[self.class], out) + end, + } - if not Heimdall_Data.config.who.enabled then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Module disabled, skipping notification", ModuleName)) - end - return - end + ---@class WHOQuery + ---@field query string + ---@field filters WHOFilter[] + WHOQuery = { + ---@param query string + ---@param filters WHOFilter[] + ---@return WHOQuery + new = function(query, filters) + local self = setmetatable({}, { + __index = WHOQuery, + }) + self.query = query + self.filters = filters + return self + end, + } - if not player then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Error: Cannot notify for nil player", ModuleName)) - end - return string.format("Cannot notify for nil player %s", tostring(player)) - end + ---@class WHOFilter + ---@field Run fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean + ---@field key string + ---@type WHOFilter + local NotSiegeOfOrgrimmarFilter = { + Run = function(name, guild, level, race, class, zone) + if not zone then return false end + return zone ~= "Siege of Orgrimmar" + end, + key = "notsoo", + } + ---@type WHOFilter + local AllianceFilter = { + Run = function(name, guild, level, race, class, zone) + if not race then return false end + if not shared.raceMap[race] then return false end + return shared.raceMap[race] == "Alliance" + end, + key = "ally", + } - if not shared.Whoer.ShouldNotifyForZone(player.zone) then - --if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - if Heimdall_Data.config.who.debug then - print( - string.format("[%s] Skipping notification - Zone '%s' not in notify list", ModuleName, player.zone) - ) - end - return string.format("Not notifying for zone %s", tostring(player.zone)) - end - - for _, channel in pairs(Heimdall_Data.config.who.channels) do - local locale = shared.GetLocaleForChannel(channel) - local text = string.format( - shared._L("whoerNew", locale), - player.name, - player.stinky and "(!!!!)" or "", - shared._L(player.class, locale), - --shared._L(player.race, locale), - shared._L(shared.raceMap[player.race] or "unknown", locale), - player.guild, - shared._L(player.zone, locale) - ) - ---@type Message - local msg = { - channel = "C", - data = channel, - message = text, - } - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Queuing channel notification", ModuleName)) - shared.dumpTable(msg) - end - table.insert(shared.networkMessenger.queue, msg) - end - - --if Heimdall_Data.config.who.doWhisper then - -- if Heimdall_Data.config.who.debug then - -- print(string.format("[%s] Processing whisper notifications for %d recipients", ModuleName, - -- #Heimdall_Data.config.whisperNotify)) - -- end - -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do - -- ---@type Message - -- local msg = { - -- channel = "W", - -- data = name, - -- message = text - -- } - -- if Heimdall_Data.config.who.debug then - -- print(string.format("[%s] Queuing whisper to %s", ModuleName, name)) - -- end - -- --table.insert(shared.messenger.queue, msg) - -- table.insert(shared.networkMessenger.queue, msg) - -- end - --end - - return nil - end - ---@param player Player - ---@param zone string - ---@return string? - local function NotifyZoneChanged(player, zone) - if not Heimdall_Data.config.who.enabled then return end - if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end - --if not Heimdall_Data.config.who.zoneNotifyFor[zone] - -- and not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - if not shared.Whoer.ShouldNotifyForZone(zone) and not shared.Whoer.ShouldNotifyForZone(player.zone) then - return string.format("Not notifying for zones %s and %s", tostring(zone), tostring(player.zone)) - end - for _, channel in pairs(Heimdall_Data.config.who.channels) do - local locale = shared.GetLocaleForChannel(channel) - local text = string.format( - shared._L("whoerMoved", locale), - player.name, - player.stinky and "(!!!!)" or "", - shared._L(player.class, locale), - --shared._L(player.race, locale), - shared._L(shared.raceMap[player.race] or "unknown", locale), - player.guild, - shared._L(zone, locale) - ) - - ---@type Message - local msg = { - channel = "C", - data = channel, - message = text, - } - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Queuing channel notification", ModuleName)) - shared.dumpTable(msg) - end - table.insert(shared.networkMessenger.queue, msg) - end - - --if Heimdall_Data.config.who.doWhisper then - -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do - -- ---@type Message - -- local msg = { - -- channel = "W", - -- data = name, - -- message = text - -- } - -- --table.insert(shared.messenger.queue, msg) - -- table.insert(shared.networkMessenger.queue, msg) - -- end - --end - - return nil - end - ---@param player Player - ---@return string? - local function NotifyGone(player) - if not Heimdall_Data.config.who.enabled then return end - if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end - --if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then - if not shared.Whoer.ShouldNotifyForZone(player.zone) then - return string.format("Not notifying for zone %s", tostring(player.zone)) - end - - for _, channel in pairs(Heimdall_Data.config.who.channels) do - local locale = shared.GetLocaleForChannel(channel) - local text = string.format( - shared._L("whoerGone", locale), - player.name, - player.stinky and "(!!!!)" or "", - shared._L(player.class, locale), - --shared._L(player.race, locale), - shared._L(shared.raceMap[player.race] or "unknown", locale), - player.guild, - shared._L(player.zone, locale) - ) - - ---@type Message - local msg = { - channel = "C", - data = channel, - message = text, - } - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Queuing channel notification", ModuleName)) - shared.dumpTable(msg) - end - --table.insert(shared.messenger.queue, msg) - table.insert(shared.networkMessenger.queue, msg) - end - - --if Heimdall_Data.config.who.doWhisper then - -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do - -- ---@type Message - -- local msg = { - -- channel = "W", - -- data = name, - -- message = text - -- } - -- --table.insert(shared.messenger.queue, msg) - -- table.insert(shared.networkMessenger.queue, msg) - -- end - --end - - return nil - end - - local frame = CreateFrame("Frame") - frame:RegisterEvent("WHO_LIST_UPDATE") - frame:SetScript("OnEvent", function(self, event, ...) - if Heimdall_Data.config.who.debug then - print(string.format("[%s] WHO list update received", ModuleName)) - print(string.format("[%s] Query index: %d/%d", ModuleName, whoQueryIdx, #shared.WhoQueryService.queries)) - end - - if not Heimdall_Data.config.who.enabled then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Module disabled, ignoring WHO update", ModuleName)) - end - return - end - - ---@type WHOQuery? - local query = lastQuery - if not query then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Error: No active WHO query found", ModuleName)) - end - return - end - - local results = GetNumWhoResults() - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Processing %d WHO results for query: %s", ModuleName, results, query.query)) - end - - for i = 1, results do - local name, guild, level, race, class, zone = GetWhoInfo(i) - if Heimdall_Data.config.who.debug then - print( - string.format("[%s] Processing result %d/%d: %s/%s/%s", ModuleName, i, results, name, class, zone) - ) - end - - local continue = false - ---@type WHOFilter[] - local filters = query.filters - for _, filter in pairs(filters) do - if Heimdall_Data.config.who.debug then - print( - string.format("[%s] Running filter %s on %s/%s/%s", ModuleName, filter.key, name, class, zone) - ) + ---@class WhoQueryService + ---@field queries WHOQuery[] + ---@field filters WHOFilter[] + ---@field getFilter fun(key: string): WHOFilter? + ---@field WhoQueryToString fun(query: WHOQuery): string + ---@field WhoQueryFromString fun(query: string): WHOQuery + ---@field WhoQueriesToString fun(queries: WHOQuery[]): string + ---@field WhoQueriesFromString fun(queries: string): WHOQuery[] + shared.WhoQueryService = { + queries = {}, + filters = { + NotSiegeOfOrgrimmarFilter, + AllianceFilter, + }, + ---@param key string + ---@return WHOFilter? + getFilter = function(key) + for _, filter in pairs(shared.WhoQueryService.filters) do + if filter.key == key then return filter end end - if not filter.Run(name, guild, level, race, class, zone) then - if Heimdall_Data.config.who.debug then - print( - string.format("[%s] Player %s filtered out by WHO filter %s", ModuleName, name, filter.key) - ) - end - continue = true - break + return nil + end, + ---@param query WHOQuery + ---@return string + WhoQueryToString = function(query) + local ret = "" + ret = ret .. query.query + ret = ret .. ";" + for _, filter in pairs(query.filters) do + ret = ret .. filter.key .. ";" end - end - - if Heimdall_Data.config.who.ignored[name] then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Ignoring blacklisted player: %s", ModuleName, name)) + return ret + end, + ---@param queries WHOQuery[] + ---@return string + WhoQueriesToString = function(queries) + local ret = "" + for _, query in pairs(queries) do + ret = ret .. shared.WhoQueryService.WhoQueryToString(query) .. "\n" end - continue = true - end - - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Player %s is not blacklisted", ModuleName, name)) - end - if not continue then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Player %s is not filtered out", ModuleName, name)) - end - local timestamp = date("%Y-%m-%dT%H:%M:%S") - local player = HeimdallStinkies[name] - if not player then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] New player detected: %s (%s) in %s", ModuleName, name, class, zone)) - end - - player = Player.new(name, guild, race, class, zone) - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - local existing = Heimdall_Data.who.data[name] - - if existing then + return ret + end, + ---@param query string + ---@return WHOQuery + WhoQueryFromString = function(query) + local queryParts = shared.Split(query, ";") + local filters = {} + for _, filterKey in pairs(queryParts) do + local filter = shared.WhoQueryService.getFilter(filterKey) + if not filter then if Heimdall_Data.config.who.debug then - print( - string.format( - "[%s] Found existing data for %s - Last seen: %s, Count: %d", - ModuleName, - name, - existing.lastSeen or "never", - existing.seenCount or 0 - ) - ) + print(string.format("[%s] Filter %s not found", ModuleName, filterKey)) end - player.lastSeen = existing.lastSeen or "never" - player.firstSeen = existing.firstSeen or "never" - player.seenCount = existing.seenCount or 0 - end - - if player.firstSeen == "never" then - player.firstSeen = timestamp + else if Heimdall_Data.config.who.debug then - print(string.format("[%s] First time seeing player: %s at %s", ModuleName, name, timestamp)) + print(string.format("[%s] Filter %s found", ModuleName, filterKey)) end - end - local stinky = shared.IsStinky(name) - if stinky then - if Heimdall_Data.config.who.debug then - print(string.format("[%s] Player %s marked as stinky!", ModuleName, name)) - end - player.stinky = true - --PlaySoundFile("Interface\\Sounds\\Domination.ogg", "Master") - -- else - -- PlaySoundFile("Interface\\Sounds\\Cloak.ogg", "Master") + table.insert(filters, filter) end - - local err = Notify(player) - if err then - print( - string.format("[%s] Error notifying for %s: %s", ModuleName, tostring(name), tostring(err)) - ) - end - - player.lastSeen = timestamp - player.seenCount = player.seenCount + 1 - HeimdallStinkies[name] = player end + if Heimdall_Data.config.who.debug then + print(string.format("[%s] WHO query: %s with %d filters", ModuleName, queryParts[1], #filters)) + end + shared.dumpTable(filters) + return WHOQuery.new(queryParts[1], filters) + end, + ---@param queryStr string + ---@return WHOQuery[] + WhoQueriesFromString = function(queryStr) + local queries = shared.Split(queryStr, "\n") + local ret = {} + for _, query in pairs(queries) do + table.insert(ret, shared.WhoQueryService.WhoQueryFromString(query)) + end + return ret + end, + } + shared.WhoQueryService.queries = shared.WhoQueryService.WhoQueriesFromString(Heimdall_Data.config.who.queries) - player.lastSeenInternal = GetTime() - if player.zone ~= zone then - if Heimdall_Data.config.who.debug then + ---@param inputZone string + ---@return boolean + shared.Whoer.ShouldNotifyForZone = shared.Memoize(function(inputZone) + if not Heimdall_Data.config.who.debug then + print(string.format("[%s] ShouldNotifyForZone %s", ModuleName, inputZone)) + end + for zone, _ in pairs(Heimdall_Data.config.who.zoneNotifyFor) do + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Checking zone %s", ModuleName, zone)) + end + if zone == "*" then return true end + if string.find(inputZone, zone) then + if not Heimdall_Data.config.who.debug then print( string.format( - "[%s] Player %s zone changed from %s to %s", + "[%s] ShouldNotifyForZone %s is true thanks to %s", ModuleName, - name, - player.zone, + inputZone, zone ) ) end - local err = NotifyZoneChanged(player, zone) - if err then print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) end - end - player.zone = zone - player.lastSeen = timestamp - HeimdallStinkies[name] = player - if not Heimdall_Data.who then Heimdall_Data.who = {} end - if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end - Heimdall_Data.who.data[name] = player - end - end - -- Turns out WA cannot do this ( - -- aura_env.UpdateMacro() - _G["FriendsFrameCloseButton"]:Click() - end) - - do - local function UpdateStinkies() - for name, player in pairs(HeimdallStinkies) do - if player.lastSeenInternal + Heimdall_Data.config.who.ttl < GetTime() then - NotifyGone(player) - --PlaySoundFile("Interface\\Sounds\\Uncloak.ogg", "Master") - HeimdallStinkies[name] = nil + return true end end - end - local function Tick() - UpdateStinkies() - C_Timer.NewTimer(0.5, Tick, 1) - end - Tick() - end + if not Heimdall_Data.config.who.debug then + print(string.format("[%s] ShouldNotifyForZone %s is false", ModuleName, inputZone)) + end + return false + end) - do - local function DoQuery() - if not Heimdall_Data.config.who.enabled then return end + -----@type WHOQuery[] + --local whoQueries = { + -- WHOQuery.new("g-\"БеспредеЛ\"", {}), + -- WHOQuery.new("g-\"ЗАО бещёки\"", {}), + -- WHOQuery.new("g-\"КОНИЛИНГУСЫ\"", {}), + -- --WHOQuery.new("g-\"Dovahkin\"", {}), + -- WHOQuery.new( + -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Human\" r-\"Dwarf\" r-\"Night Elf\"", + -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + -- WHOQuery.new( + -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Gnome\" r-\"Draenei\" r-\"Worgen\"", + -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + -- WHOQuery.new( + -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Kul Tiran\" r-\"Dark Iron Dwarf\" r-\"Void Elf\"", + -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + -- WHOQuery.new( + -- "z-\"Orgrimmar\" z-\"Durotar\" z-\"Valley of Trials\" r-\"Lightforged Draenei\" r-\"Mechagnome\"", + -- { NotSiegeOfOrgrimmarFilter, AllianceFilter }), + -- WHOQuery.new("Kekv Firobot Tomoki Mld Alltros", {}) + --} + local whoQueryIdx = 1 + ---@type WHOQuery? + local lastQuery = nil - local query = shared.WhoQueryService.queries[whoQueryIdx] - if not query then + ---@param player Player + ---@return string? + local function Notify(player) + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Processing notification for player: %s", ModuleName, player.name)) + print( + string.format( + "[%s] Player details - Guild: %s, Race: %s, Class: %s, Zone: %s", + ModuleName, + player.guild, + player.race, + player.class, + player.zone + ) + ) + print( + string.format( + "[%s] Player history - First seen: %s, Last seen: %s, Seen count: %d", + ModuleName, + player.firstSeen, + player.lastSeen, + player.seenCount + ) + ) + end + + if not Heimdall_Data.config.who.enabled then if Heimdall_Data.config.who.debug then - print(string.format("[%s] Error: No WHO query found to run at index %d", ModuleName, whoQueryIdx)) + print(string.format("[%s] Module disabled, skipping notification", ModuleName)) end return end - if Heimdall_Data.config.who.debug then - print( - string.format( - "[%s] Running WHO query %d/%d: %s", - ModuleName, - whoQueryIdx, - #shared.WhoQueryService.queries, - query.query + if not player then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Error: Cannot notify for nil player", ModuleName)) + end + return string.format("Cannot notify for nil player %s", tostring(player)) + end + + if not shared.Whoer.ShouldNotifyForZone(player.zone) then + --if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Skipping notification - Zone '%s' not in notify list", + ModuleName, + player.zone + ) ) + end + return string.format("Not notifying for zone %s", tostring(player.zone)) + end + + for _, channel in pairs(Heimdall_Data.config.who.channels) do + local locale = shared.GetLocaleForChannel(channel) + local text = string.format( + shared._L("whoerNew", locale), + player.name, + player.stinky and "(!!!!)" or "", + shared._L(player.class, locale), + --shared._L(player.race, locale), + shared._L(shared.raceMap[player.race] or "unknown", locale), + player.guild, + shared._L(player.zone, locale) ) - print(string.format("[%s] Query has %d filters", ModuleName, #query.filters)) - for i, filter in ipairs(query.filters) do - print(string.format("[%s] Filter %d: %s", ModuleName, i, filter.key)) + ---@type Message + local msg = { + channel = "C", + data = channel, + message = text, + } + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Queuing channel notification", ModuleName)) + shared.dumpTable(msg) + end + table.insert(shared.networkMessenger.queue, msg) + end + + --if Heimdall_Data.config.who.doWhisper then + -- if Heimdall_Data.config.who.debug then + -- print(string.format("[%s] Processing whisper notifications for %d recipients", ModuleName, + -- #Heimdall_Data.config.whisperNotify)) + -- end + -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do + -- ---@type Message + -- local msg = { + -- channel = "W", + -- data = name, + -- message = text + -- } + -- if Heimdall_Data.config.who.debug then + -- print(string.format("[%s] Queuing whisper to %s", ModuleName, name)) + -- end + -- --table.insert(shared.messenger.queue, msg) + -- table.insert(shared.networkMessenger.queue, msg) + -- end + --end + + return nil + end + ---@param player Player + ---@param zone string + ---@return string? + local function NotifyZoneChanged(player, zone) + if not Heimdall_Data.config.who.enabled then return end + if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end + --if not Heimdall_Data.config.who.zoneNotifyFor[zone] + -- and not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + if not shared.Whoer.ShouldNotifyForZone(zone) and not shared.Whoer.ShouldNotifyForZone(player.zone) then + return string.format("Not notifying for zones %s and %s", tostring(zone), tostring(player.zone)) + end + for _, channel in pairs(Heimdall_Data.config.who.channels) do + local locale = shared.GetLocaleForChannel(channel) + local text = string.format( + shared._L("whoerMoved", locale), + player.name, + player.stinky and "(!!!!)" or "", + shared._L(player.class, locale), + --shared._L(player.race, locale), + shared._L(shared.raceMap[player.race] or "unknown", locale), + player.guild, + shared._L(zone, locale) + ) + + ---@type Message + local msg = { + channel = "C", + data = channel, + message = text, + } + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Queuing channel notification", ModuleName)) + shared.dumpTable(msg) + end + table.insert(shared.networkMessenger.queue, msg) + end + + --if Heimdall_Data.config.who.doWhisper then + -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do + -- ---@type Message + -- local msg = { + -- channel = "W", + -- data = name, + -- message = text + -- } + -- --table.insert(shared.messenger.queue, msg) + -- table.insert(shared.networkMessenger.queue, msg) + -- end + --end + + return nil + end + ---@param player Player + ---@return string? + local function NotifyGone(player) + if not Heimdall_Data.config.who.enabled then return end + if not player then return string.format("Cannot notify for nil player %s", tostring(player)) end + --if not Heimdall_Data.config.who.zoneNotifyFor[player.zone] then + if not shared.Whoer.ShouldNotifyForZone(player.zone) then + return string.format("Not notifying for zone %s", tostring(player.zone)) + end + + for _, channel in pairs(Heimdall_Data.config.who.channels) do + local locale = shared.GetLocaleForChannel(channel) + local text = string.format( + shared._L("whoerGone", locale), + player.name, + player.stinky and "(!!!!)" or "", + shared._L(player.class, locale), + --shared._L(player.race, locale), + shared._L(shared.raceMap[player.race] or "unknown", locale), + player.guild, + shared._L(player.zone, locale) + ) + + ---@type Message + local msg = { + channel = "C", + data = channel, + message = text, + } + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Queuing channel notification", ModuleName)) + shared.dumpTable(msg) + end + --table.insert(shared.messenger.queue, msg) + table.insert(shared.networkMessenger.queue, msg) + end + + --if Heimdall_Data.config.who.doWhisper then + -- for _, name in pairs(Heimdall_Data.config.whisperNotify) do + -- ---@type Message + -- local msg = { + -- channel = "W", + -- data = name, + -- message = text + -- } + -- --table.insert(shared.messenger.queue, msg) + -- table.insert(shared.networkMessenger.queue, msg) + -- end + --end + + return nil + end + + local frame = CreateFrame("Frame") + frame:RegisterEvent("WHO_LIST_UPDATE") + frame:SetScript("OnEvent", function(self, event, ...) + if Heimdall_Data.config.who.debug then + print(string.format("[%s] WHO list update received", ModuleName)) + print( + string.format("[%s] Query index: %d/%d", ModuleName, whoQueryIdx, #shared.WhoQueryService.queries) + ) + end + + if not Heimdall_Data.config.who.enabled then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Module disabled, ignoring WHO update", ModuleName)) + end + return + end + + ---@type WHOQuery? + local query = lastQuery + if not query then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Error: No active WHO query found", ModuleName)) + end + return + end + + local results = GetNumWhoResults() + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Processing %d WHO results for query: %s", ModuleName, results, query.query)) + end + + for i = 1, results do + local name, guild, level, race, class, zone = GetWhoInfo(i) + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Processing result %d/%d: %s/%s/%s", + ModuleName, + i, + results, + name, + class, + zone + ) + ) + end + + local continue = false + ---@type WHOFilter[] + local filters = query.filters + for _, filter in pairs(filters) do + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Running filter %s on %s/%s/%s", + ModuleName, + filter.key, + name, + class, + zone + ) + ) + end + if not filter.Run(name, guild, level, race, class, zone) then + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Player %s filtered out by WHO filter %s", + ModuleName, + name, + filter.key + ) + ) + end + continue = true + break + end + end + + if Heimdall_Data.config.who.ignored[name] then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Ignoring blacklisted player: %s", ModuleName, name)) + end + continue = true + end + + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Player %s is not blacklisted", ModuleName, name)) + end + if not continue then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Player %s is not filtered out", ModuleName, name)) + end + local timestamp = date("%Y-%m-%dT%H:%M:%S") + local player = HeimdallStinkies[name] + if not player then + if Heimdall_Data.config.who.debug then + print( + string.format("[%s] New player detected: %s (%s) in %s", ModuleName, name, class, zone) + ) + end + + player = Player.new(name, guild, race, class, zone) + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end + local existing = Heimdall_Data.who.data[name] + + if existing then + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Found existing data for %s - Last seen: %s, Count: %d", + ModuleName, + name, + existing.lastSeen or "never", + existing.seenCount or 0 + ) + ) + end + player.lastSeen = existing.lastSeen or "never" + player.firstSeen = existing.firstSeen or "never" + player.seenCount = existing.seenCount or 0 + end + + if player.firstSeen == "never" then + player.firstSeen = timestamp + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] First time seeing player: %s at %s", + ModuleName, + name, + timestamp + ) + ) + end + end + + local stinky = shared.IsStinky(name) + if stinky then + if Heimdall_Data.config.who.debug then + print(string.format("[%s] Player %s marked as stinky!", ModuleName, name)) + end + player.stinky = true + --PlaySoundFile("Interface\\Sounds\\Domination.ogg", "Master") + -- else + -- PlaySoundFile("Interface\\Sounds\\Cloak.ogg", "Master") + end + + local err = Notify(player) + if err then + print( + string.format( + "[%s] Error notifying for %s: %s", + ModuleName, + tostring(name), + tostring(err) + ) + ) + end + + player.lastSeen = timestamp + player.seenCount = player.seenCount + 1 + HeimdallStinkies[name] = player + end + + player.lastSeenInternal = GetTime() + if player.zone ~= zone then + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Player %s zone changed from %s to %s", + ModuleName, + name, + player.zone, + zone + ) + ) + end + local err = NotifyZoneChanged(player, zone) + if err then + print(string.format("Error notifying for %s: %s", tostring(name), tostring(err))) + end + end + player.zone = zone + player.lastSeen = timestamp + HeimdallStinkies[name] = player + if not Heimdall_Data.who then Heimdall_Data.who = {} end + if not Heimdall_Data.who.data then Heimdall_Data.who.data = {} end + Heimdall_Data.who.data[name] = player end end - whoQueryIdx = whoQueryIdx + 1 - if whoQueryIdx > #shared.WhoQueryService.queries then whoQueryIdx = 1 end - lastQuery = query - ---@diagnostic disable-next-line: param-type-mismatch - SetWhoToUI(1) - SendWho(query.query) - end - local function Tick() - DoQuery() - C_Timer.NewTimer(1, Tick, 1) - end - Tick() - end + -- Turns out WA cannot do this ( + -- aura_env.UpdateMacro() + _G["FriendsFrameCloseButton"]:Click() + end) - print("[Heimdall] Whoer loaded") -end + do + local function UpdateStinkies() + for name, player in pairs(HeimdallStinkies) do + if player.lastSeenInternal + Heimdall_Data.config.who.ttl < GetTime() then + NotifyGone(player) + --PlaySoundFile("Interface\\Sounds\\Uncloak.ogg", "Master") + HeimdallStinkies[name] = nil + end + end + end + local function Tick() + UpdateStinkies() + C_Timer.NewTimer(0.5, Tick, 1) + end + Tick() + end + + do + local function DoQuery() + if not Heimdall_Data.config.who.enabled then return end + + local query = shared.WhoQueryService.queries[whoQueryIdx] + if not query then + if Heimdall_Data.config.who.debug then + print( + string.format("[%s] Error: No WHO query found to run at index %d", ModuleName, whoQueryIdx) + ) + end + return + end + + if Heimdall_Data.config.who.debug then + print( + string.format( + "[%s] Running WHO query %d/%d: %s", + ModuleName, + whoQueryIdx, + #shared.WhoQueryService.queries, + query.query + ) + ) + print(string.format("[%s] Query has %d filters", ModuleName, #query.filters)) + for i, filter in ipairs(query.filters) do + print(string.format("[%s] Filter %d: %s", ModuleName, i, filter.key)) + end + end + whoQueryIdx = whoQueryIdx + 1 + if whoQueryIdx > #shared.WhoQueryService.queries then whoQueryIdx = 1 end + lastQuery = query + ---@diagnostic disable-next-line: param-type-mismatch + SetWhoToUI(1) + SendWho(query.query) + end + local function Tick() + DoQuery() + C_Timer.NewTimer(1, Tick, 1) + end + Tick() + end + + print("[Heimdall] Whoer loaded") + end, +}