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)) end if Heimdall_Data.config.agents[name] then if Heimdall_Data.config.spotter.debug then print(string.format("[%s] Skipping agent: %s", ModuleName, name)) end return false end 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 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)) 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)) 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 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 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 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)) 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.dump(msg) end table.insert(shared.messenger.queue, msg) end 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