-- Modules/BuffEndingAnnouncer/BuffEndingAnnouncerOptions.lua local ADDON_NAME = "HailMaryGuildTools" local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME) if not HMGT then return end local BEA = HMGT.AuraExpiry or HMGT.BuffEndingAnnouncer if not BEA then return end if not HMGT_Config or not HMGT_Config.RegisterOptionsProvider then return end local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME, true) or {} local AceConfigRegistry = LibStub("AceConfigRegistry-3.0") local beaOptionsGroup local function GetSpellName(spellId) local sid = tonumber(spellId) if not sid or sid <= 0 then return nil end if C_Spell and type(C_Spell.GetSpellName) == "function" then local name = C_Spell.GetSpellName(sid) if type(name) == "string" and name ~= "" then return name end end if type(GetSpellInfo) == "function" then local name = GetSpellInfo(sid) if type(name) == "string" and name ~= "" then return name end end return nil end local function GetSpellIcon(spellId) local sid = tonumber(spellId) if not sid or sid <= 0 then return nil end if HMGT_SpellData and type(HMGT_SpellData.GetSpellIcon) == "function" then return HMGT_SpellData.GetSpellIcon(sid) end if C_Spell and type(C_Spell.GetSpellTexture) == "function" then return C_Spell.GetSpellTexture(sid) end local _, _, icon = GetSpellInfo(sid) return icon end local function RefreshTrackedGroup() if not beaOptionsGroup or type(beaOptionsGroup.args) ~= "table" then return end local fresh = BEA:BuildOptionsGroup() local currentTracked = beaOptionsGroup.args.tracked local freshTracked = fresh and fresh.args and fresh.args.tracked if type(currentTracked) == "table" and type(freshTracked) == "table" then currentTracked.name = freshTracked.name currentTracked.order = freshTracked.order currentTracked.inline = freshTracked.inline currentTracked.childGroups = freshTracked.childGroups currentTracked.args = freshTracked.args end end local function NotifyOptionsChanged(rebuild) if rebuild ~= false then RefreshTrackedGroup() end AceConfigRegistry:NotifyChange(ADDON_NAME) end local function GetFilterState() HMGT._beaSpellFilter = HMGT._beaSpellFilter or { search = "", } return HMGT._beaSpellFilter end local function NormalizeSearchText(value) local text = tostring(value or ""):lower() text = text:gsub("^%s+", "") text = text:gsub("%s+$", "") return text end local function GetDraft() HMGT._beaDraft = HMGT._beaDraft or {} return HMGT._beaDraft end local function GetTrackedBuffLabel(entry) local spellId = tonumber(entry and entry.spellId) or 0 local spellName = tostring(entry and entry.name or GetSpellName(spellId) or ("Spell " .. spellId)) local icon = GetSpellIcon(spellId) local threshold = tonumber(entry and entry.threshold) or tonumber(BEA:GetDefaultThreshold()) or 0 if icon and icon ~= "" then return string.format("|T%s:16:16:0:0|t %s (%ss)", tostring(icon), spellName, threshold) end return string.format("%s (%ss)", spellName, threshold) end local function BuildTrackedBuffLeaf(entry) local spellId = tonumber(entry and entry.spellId) or 0 return { type = "group", name = GetTrackedBuffLabel(entry), args = { header = { type = "header", order = 1, name = GetTrackedBuffLabel(entry), }, spellInfo = { type = "description", order = 2, width = "full", name = string.format( "|cffffd100Spell ID|r: %d\n|cffffd100%s|r: %s", spellId, L["OPT_BEA_COL_SPELL"] or "Spellname", tostring(entry and entry.name or GetSpellName(spellId) or ("Spell " .. spellId)) ), }, threshold = { type = "range", order = 3, min = 1, max = 60, step = 1, width = "full", name = L["OPT_BEA_COL_THRESHOLD"] or "Threshold", get = function() local current = BEA:GetTrackedBuffEntries() for _, candidate in ipairs(current) do if tonumber(candidate.spellId) == spellId then return tonumber(candidate.threshold) or BEA:GetDefaultThreshold() end end return tonumber(BEA:GetDefaultThreshold()) or 5 end, set = function(_, val) BEA:SetTrackedBuffThreshold(spellId, val) NotifyOptionsChanged() end, }, remove = { type = "execute", order = 4, width = "full", name = REMOVE or (L["OPT_BEA_REMOVE"] or "Remove buff"), confirm = function() return string.format("%s?", tostring(entry and entry.name or GetSpellName(spellId) or ("Spell " .. spellId))) end, func = function() local ok, info = BEA:RemoveTrackedBuff(spellId) if ok then HMGT:Print(string.format(L["OPT_BEA_MSG_REMOVED"] or "HMGT: buff removed: %s", tostring(info or spellId))) else HMGT:Print(L["OPT_BEA_MSG_NOT_FOUND"] or "HMGT: buff not found") end NotifyOptionsChanged() end, }, }, } end local function IsTrackedBuffVisible(entry) local spellId = tonumber(entry and entry.spellId) or 0 if spellId <= 0 then return false end local search = NormalizeSearchText(GetFilterState().search) if search == "" then return true end local haystack = table.concat({ tostring(entry and entry.name or GetSpellName(spellId) or ""), tostring(spellId), }, " "):lower() return haystack:find(search, 1, true) ~= nil end local function CountVisibleEntries(entries) local count = 0 for _, entry in ipairs(entries or {}) do if IsTrackedBuffVisible(entry) then count = count + 1 end end return count end function BEA:BuildOptionsGroup() local draft = GetDraft() local entries = self:GetTrackedBuffEntries() local group = { type = "group", name = L["AE_NAME"] or L["BEA_NAME"] or "Aura Expiry", order = 3, childGroups = "tree", args = { general = { type = "group", order = 1, name = L["OPT_BEA_SECTION_GENERAL"] or "General", args = { enabled = { type = "toggle", order = 1, width = "full", name = L["OPT_BEA_ENABLED"] or "Enable buff ending announcer", desc = L["OPT_BEA_ENABLED_DESC"] or "Announce tracked buff countdowns in /say", get = function() return self:GetSettings().enabled == true end, set = function(_, val) self:GetSettings().enabled = val and true or false if val then self:Enable() else self:Disable() end end, }, defaultThreshold = { type = "range", order = 2, min = 1, max = 60, step = 1, width = "full", name = L["OPT_BEA_DEFAULT_THRESHOLD"] or "Default threshold (sec)", desc = L["OPT_BEA_DEFAULT_THRESHOLD_DESC"] or "Used when you add a new tracked buff", get = function() return tonumber(self:GetDefaultThreshold()) or 5 end, set = function(_, val) self:GetSettings().announceAtSec = math.floor((tonumber(val) or 5) + 0.5) self:Refresh() end, }, }, }, tracked = { type = "group", order = 2, name = string.format( "%s (%d)", L["OPT_BEA_SECTION_BUFFS"] or "Tracked buffs", #entries ), args = { header = { type = "header", order = 1, name = L["OPT_SPELL_BROWSER"] or "Spell Browser", }, summary = { type = "description", order = 2, width = "full", name = function() local currentEntries = BEA:GetTrackedBuffEntries() return string.format( "%s\n\n|cffffd100%s|r: %d\n|cffffd100%s|r: %d", L["OPT_SPELL_BROWSER_DESC"] or "Filter tracked spells by name or Spell ID and apply quick actions to the visible results.", L["OPT_SPELLS_VISIBLE"] or "Visible spells", CountVisibleEntries(currentEntries), L["OPT_BEA_SECTION_BUFFS"] or "Tracked buffs", #currentEntries ) end, }, search = { type = "input", order = 3, width = "full", name = L["OPT_FILTER_SEARCH"] or "Search", desc = L["OPT_FILTER_SEARCH_DESC"] or "Search by spell name or Spell ID", get = function() return GetFilterState().search or "" end, set = function(_, val) GetFilterState().search = val or "" NotifyOptionsChanged(false) end, }, resetFilters = { type = "execute", order = 4, width = "full", name = L["OPT_FILTER_RESET"] or "Reset filters", func = function() GetFilterState().search = "" NotifyOptionsChanged(false) end, }, addGroup = { type = "group", order = 5, inline = true, name = L["OPT_BEA_CURRENT"] or "Current tracked buffs", args = { addSpellId = { type = "input", order = 1, width = 0.9, name = L["OPT_BEA_ADD_ID"] or "Add Spell ID", get = function() return tostring(draft.addSpellId or "") end, set = function(_, val) draft.addSpellId = val end, }, addThreshold = { type = "range", order = 2, min = 1, max = 60, step = 1, width = 1.1, name = L["OPT_BEA_ADD_THRESHOLD"] or "Threshold", desc = L["OPT_BEA_ADD_THRESHOLD_DESC"] or "Countdown start in seconds for this buff", get = function() return tonumber(draft.addThreshold) or tonumber(self:GetDefaultThreshold()) or 5 end, set = function(_, val) draft.addThreshold = tonumber(val) or self:GetDefaultThreshold() end, }, addSpell = { type = "execute", order = 3, width = "full", name = L["OPT_BEA_ADD"] or "Add buff", func = function() local sid = tonumber(draft.addSpellId) local threshold = tonumber(draft.addThreshold) or self:GetDefaultThreshold() local ok, info = self:AddTrackedBuff(sid, threshold) if ok then draft.addSpellId = "" draft.addThreshold = tonumber(self:GetDefaultThreshold()) or 5 HMGT:Print(string.format(L["OPT_BEA_MSG_ADDED"] or "HMGT: buff added: %s", tostring(info or sid))) else HMGT:Print(L["OPT_BEA_MSG_INVALID"] or "HMGT: invalid buff spell ID") end NotifyOptionsChanged() end, }, }, }, }, }, }, } if #entries == 0 then group.args.tracked.args.empty = { type = "description", order = 10, width = "full", name = L["OPT_BEA_EMPTY"] or "No buffs configured.", } else for index, entry in ipairs(entries) do local key = "buff_" .. tostring(tonumber(entry.spellId) or index) local leaf = BuildTrackedBuffLeaf(entry) leaf.order = 10 + index leaf.inline = true leaf.name = " " leaf.hidden = function() return not IsTrackedBuffVisible(entry) end group.args.tracked.args[key] = leaf end group.args.tracked.args.noVisible = { type = "description", order = 1000, width = "full", hidden = function() return CountVisibleEntries(BEA:GetTrackedBuffEntries()) > 0 end, name = L["OPT_BEA_EMPTY"] or "No buffs configured.", } end return group end function BEA:GetOptionsGroup() if not beaOptionsGroup then beaOptionsGroup = self:BuildOptionsGroup() else RefreshTrackedGroup() end return beaOptionsGroup end HMGT_Config:RegisterOptionsProvider("announcer.auraExpiry", function() return { path = "announcer", order = 3, group = BEA:GetOptionsGroup(), } end)