- Deleted LibGroupInSpecT-1.1.toc, LibStub.lua, and lib.xml files as they are no longer needed. - Updated localization files (deDE.lua, enUS.lua) to reflect the change from "Buff Ending" to "Aura Expiry". - Renamed BuffEndingAnnouncer module to AuraExpiry and updated references throughout the codebase. - Adjusted options and configuration to align with the new module name. - Updated readme to describe the new Aura Expiry feature instead of Buff Ending Announcer.
2064 lines
96 KiB
Lua
2064 lines
96 KiB
Lua
-- Config.lua
|
|
-- Hail Mary Guild Tools – AceConfig Optionentabellen
|
|
-- Spell-Liste: Icon-Anzeige, gruppiert nach Klasse → Kategorie
|
|
|
|
local ADDON_NAME = "HailMaryGuildTools"
|
|
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
|
|
local LSM = LibStub("LibSharedMedia-3.0")
|
|
local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
|
|
local AceConfigRegistry = LibStub("AceConfigRegistry-3.0", true)
|
|
|
|
HMGT_Config = {}
|
|
HMGT_Config._optionProviders = HMGT_Config._optionProviders or {}
|
|
HMGT_Config._settingsCategoryId = HMGT_Config._settingsCategoryId or nil
|
|
HMGT_Config._settingsCategoryName = HMGT_Config._settingsCategoryName or nil
|
|
|
|
function HMGT_Config:RegisterOptionsProvider(id, provider)
|
|
if type(id) ~= "string" or id == "" then return false end
|
|
if type(provider) ~= "function" then return false end
|
|
self._optionProviders[id] = provider
|
|
return true
|
|
end
|
|
|
|
function HMGT_Config:UnregisterOptionsProvider(id)
|
|
if type(id) ~= "string" or id == "" then return false end
|
|
self._optionProviders[id] = nil
|
|
return true
|
|
end
|
|
|
|
function HMGT_Config:GetSettingsCategory()
|
|
return self._settingsCategoryId or self._settingsCategoryName
|
|
end
|
|
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
-- SPELL-TOGGLE-LISTE (Icon + Klassen-/Kategorie-Gruppen)
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
|
|
--- Erstellt das Icon+Name Label für einen Spell
|
|
local function SpellLabel(entry)
|
|
-- |T path:h:w:xOff:yOff|t → 20×20 Icon vor dem Namen
|
|
local icon = HMGT_SpellData.GetSpellIcon(entry.spellId)
|
|
return string.format("|T%s:20:20:0:0|t %s", icon, entry.name)
|
|
end
|
|
|
|
--- Erstellt den Tooltip-Text eines Spell-Toggles
|
|
local function SpellDesc(entry)
|
|
local cooldown = tonumber(HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(entry)) or tonumber(entry.cooldown) or 0
|
|
local cdStr = cooldown > 0
|
|
and string.format("%ds", cooldown)
|
|
or "–"
|
|
local specStr = entry.specs
|
|
and ("Spec " .. table.concat(entry.specs, "/"))
|
|
or "All Specs"
|
|
return string.format("SpellID: %d | CD: %s | %s", entry.spellId, cdStr, specStr)
|
|
end
|
|
|
|
--- Erstellt einen Hex-farbigen Klassen-Header
|
|
local function ClassHeader(classToken)
|
|
local hex = HMGT_SpellData.ClassColor[classToken] or "ffffffff"
|
|
-- Lokalisierter Klassenname (WoW-Globaltabelle)
|
|
local displayName = (LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[classToken])
|
|
or classToken
|
|
return string.format("|c%s%s|r", hex, displayName)
|
|
end
|
|
|
|
--- Baut die Spell-Toggle-Options eines EINZELNEN Trackers,
|
|
--- gruppiert nach Klasse → Kategorie, mit Icons und Select-All-Buttons.
|
|
---
|
|
--- @param database table HMGT_SpellData.Interrupts / .RaidCooldowns
|
|
--- @param dbKey string z.B. "interruptTracker"
|
|
--- @return table AceConfig-args-Tabelle
|
|
local function BuildSpellOptions(database, dbKey)
|
|
-- live helper – nie gecachte Referenz
|
|
local function es() return HMGT.db.profile[dbKey].enabledSpells end
|
|
if true then
|
|
local function filterState()
|
|
HMGT._cfgSpellFilters = HMGT._cfgSpellFilters or {}
|
|
HMGT._cfgSpellFilters[dbKey] = HMGT._cfgSpellFilters[dbKey] or {
|
|
search = "",
|
|
enabledOnly = false,
|
|
}
|
|
return HMGT._cfgSpellFilters[dbKey]
|
|
end
|
|
local function NormalizeText(value)
|
|
local text = tostring(value or ""):lower()
|
|
text = text:gsub("^%s+", "")
|
|
text = text:gsub("%s+$", "")
|
|
return text
|
|
end
|
|
local function LocalClassName(classToken)
|
|
return (LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[classToken])
|
|
or (LOCALIZED_CLASS_NAMES_FEMALE and LOCALIZED_CLASS_NAMES_FEMALE[classToken])
|
|
or classToken
|
|
end
|
|
local function CategoryLabel(category)
|
|
return L["CAT_" .. tostring(category)] or tostring(category or "utility")
|
|
end
|
|
local function SpellNameForId(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 ok, name = pcall(C_Spell.GetSpellName, sid)
|
|
if ok and 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 Notify()
|
|
if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then
|
|
AceConfigRegistry:NotifyChange(ADDON_NAME)
|
|
end
|
|
end
|
|
local browserClassSpells = {}
|
|
|
|
for _, entry in ipairs(database) do
|
|
for _, cls in ipairs(entry.classes) do
|
|
if not browserClassSpells[cls] then browserClassSpells[cls] = {} end
|
|
local cat = entry.category or "utility"
|
|
if not browserClassSpells[cls][cat] then browserClassSpells[cls][cat] = {} end
|
|
local already = false
|
|
for _, existing in ipairs(browserClassSpells[cls][cat]) do
|
|
if existing.spellId == entry.spellId then
|
|
already = true
|
|
break
|
|
end
|
|
end
|
|
if not already then
|
|
table.insert(browserClassSpells[cls][cat], entry)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function GetSearchTerm()
|
|
return NormalizeText(filterState().search)
|
|
end
|
|
|
|
local function IsEntryVisible(entry, classToken, category)
|
|
local spellId = tonumber(entry and entry.spellId)
|
|
if not spellId or spellId <= 0 then
|
|
return false
|
|
end
|
|
if filterState().enabledOnly and es()[spellId] == false then
|
|
return false
|
|
end
|
|
|
|
local search = GetSearchTerm()
|
|
if search == "" then
|
|
return true
|
|
end
|
|
|
|
local haystack = table.concat({
|
|
tostring(entry.name or SpellNameForId(spellId) or ""),
|
|
tostring(spellId),
|
|
tostring(LocalClassName(classToken)),
|
|
tostring(CategoryLabel(category)),
|
|
}, " "):lower()
|
|
return haystack:find(search, 1, true) ~= nil
|
|
end
|
|
|
|
local function CollectVisibleSpellIds(classToken, category)
|
|
local ids = {}
|
|
local categories = {}
|
|
if category then
|
|
categories[1] = category
|
|
else
|
|
categories = HMGT_SpellData.CategoryOrder or {}
|
|
end
|
|
|
|
for _, cat in ipairs(categories) do
|
|
for _, entry in ipairs((browserClassSpells[classToken] and browserClassSpells[classToken][cat]) or {}) do
|
|
if IsEntryVisible(entry, classToken, cat) then
|
|
ids[#ids + 1] = tonumber(entry.spellId)
|
|
end
|
|
end
|
|
end
|
|
|
|
return ids
|
|
end
|
|
|
|
local function CountVisible(classToken, category)
|
|
local total = 0
|
|
local enabled = 0
|
|
for _, spellId in ipairs(CollectVisibleSpellIds(classToken, category)) do
|
|
total = total + 1
|
|
if es()[spellId] ~= false then
|
|
enabled = enabled + 1
|
|
end
|
|
end
|
|
return total, enabled
|
|
end
|
|
|
|
local function CountAllVisible()
|
|
local total = 0
|
|
local enabled = 0
|
|
for _, classToken in ipairs(HMGT_SpellData.ClassOrder or {}) do
|
|
local visible, active = CountVisible(classToken)
|
|
total = total + visible
|
|
enabled = enabled + active
|
|
end
|
|
return total, enabled
|
|
end
|
|
|
|
local function SetSpellIdsEnabled(spellIds, value)
|
|
for _, spellId in ipairs(spellIds) do
|
|
es()[spellId] = value
|
|
end
|
|
HMGT:TriggerTrackerUpdate()
|
|
Notify()
|
|
end
|
|
|
|
local browserArgs = {
|
|
overview = {
|
|
type = "group",
|
|
name = L["OPT_SPELL_BROWSER"] or "Spell Browser",
|
|
order = 1,
|
|
args = {
|
|
info = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = function()
|
|
local total, enabled = CountAllVisible()
|
|
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",
|
|
total,
|
|
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
|
|
enabled
|
|
)
|
|
end,
|
|
},
|
|
search = {
|
|
type = "input",
|
|
order = 2,
|
|
width = "full",
|
|
name = L["OPT_FILTER_SEARCH"] or "Search",
|
|
desc = L["OPT_FILTER_SEARCH_DESC"] or "Search by spell name, Spell ID, class, or category.",
|
|
get = function()
|
|
return filterState().search or ""
|
|
end,
|
|
set = function(_, val)
|
|
filterState().search = val or ""
|
|
Notify()
|
|
end,
|
|
},
|
|
enabledOnly = {
|
|
type = "toggle",
|
|
order = 3,
|
|
width = "full",
|
|
name = L["OPT_FILTER_ENABLED_ONLY"] or "Show enabled spells only",
|
|
get = function()
|
|
return filterState().enabledOnly == true
|
|
end,
|
|
set = function(_, val)
|
|
filterState().enabledOnly = val and true or false
|
|
Notify()
|
|
end,
|
|
},
|
|
selectVisible = {
|
|
type = "execute",
|
|
order = 4,
|
|
width = "half",
|
|
name = L["OPT_SELECT_VISIBLE"] or "Select visible",
|
|
func = function()
|
|
local allIds = {}
|
|
for _, classToken in ipairs(HMGT_SpellData.ClassOrder or {}) do
|
|
for _, spellId in ipairs(CollectVisibleSpellIds(classToken)) do
|
|
allIds[#allIds + 1] = spellId
|
|
end
|
|
end
|
|
SetSpellIdsEnabled(allIds, true)
|
|
end,
|
|
},
|
|
deselectVisible = {
|
|
type = "execute",
|
|
order = 5,
|
|
width = "half",
|
|
name = L["OPT_DESELECT_VISIBLE"] or "Deselect visible",
|
|
func = function()
|
|
local allIds = {}
|
|
for _, classToken in ipairs(HMGT_SpellData.ClassOrder or {}) do
|
|
for _, spellId in ipairs(CollectVisibleSpellIds(classToken)) do
|
|
allIds[#allIds + 1] = spellId
|
|
end
|
|
end
|
|
SetSpellIdsEnabled(allIds, false)
|
|
end,
|
|
},
|
|
resetFilters = {
|
|
type = "execute",
|
|
order = 6,
|
|
width = "full",
|
|
name = L["OPT_FILTER_RESET"] or "Reset filters",
|
|
func = function()
|
|
filterState().search = ""
|
|
filterState().enabledOnly = false
|
|
Notify()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
local classOrder = 2
|
|
|
|
for _, classToken in ipairs(HMGT_SpellData.ClassOrder or {}) do
|
|
if browserClassSpells[classToken] then
|
|
local classKey = "cls_" .. classToken
|
|
local classArgs = {
|
|
summary = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = function()
|
|
local total, enabled = CountVisible(classToken)
|
|
return string.format(
|
|
"|cffffd100%s|r: %d |cffffd100%s|r: %d",
|
|
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
|
|
total,
|
|
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
|
|
enabled
|
|
)
|
|
end,
|
|
},
|
|
selectAll = {
|
|
type = "execute",
|
|
order = 2,
|
|
width = "half",
|
|
name = L["OPT_SELECT_VISIBLE"] or "Select visible",
|
|
func = function()
|
|
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken), true)
|
|
end,
|
|
},
|
|
deselectAll = {
|
|
type = "execute",
|
|
order = 3,
|
|
width = "half",
|
|
name = L["OPT_DESELECT_VISIBLE"] or "Deselect visible",
|
|
func = function()
|
|
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken), false)
|
|
end,
|
|
},
|
|
}
|
|
local catOrder = 4
|
|
|
|
for _, cat in ipairs(HMGT_SpellData.CategoryOrder or {}) do
|
|
if browserClassSpells[classToken][cat] then
|
|
local catKey = "cat_" .. cat
|
|
local catArgs = {
|
|
summary = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = function()
|
|
local total, enabled = CountVisible(classToken, cat)
|
|
return string.format(
|
|
"|cffffd100%s|r: %d |cffffd100%s|r: %d",
|
|
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
|
|
total,
|
|
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
|
|
enabled
|
|
)
|
|
end,
|
|
},
|
|
selectVisible = {
|
|
type = "execute",
|
|
order = 2,
|
|
width = "half",
|
|
name = L["OPT_SELECT_VISIBLE"] or "Select visible",
|
|
func = function()
|
|
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken, cat), true)
|
|
end,
|
|
},
|
|
deselectVisible = {
|
|
type = "execute",
|
|
order = 3,
|
|
width = "half",
|
|
name = L["OPT_DESELECT_VISIBLE"] or "Deselect visible",
|
|
func = function()
|
|
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken, cat), false)
|
|
end,
|
|
},
|
|
}
|
|
local spOrder = 4
|
|
|
|
for _, entry in ipairs(browserClassSpells[classToken][cat]) do
|
|
local spellId = tonumber(entry.spellId)
|
|
catArgs["sp_" .. spellId] = {
|
|
type = "toggle",
|
|
name = SpellLabel(entry),
|
|
desc = SpellDesc(entry),
|
|
order = spOrder,
|
|
hidden = function()
|
|
return not IsEntryVisible(entry, classToken, cat)
|
|
end,
|
|
get = function()
|
|
return es()[spellId] ~= false
|
|
end,
|
|
set = function(_, val)
|
|
es()[spellId] = val
|
|
HMGT:TriggerTrackerUpdate()
|
|
end,
|
|
}
|
|
spOrder = spOrder + 1
|
|
end
|
|
|
|
classArgs[catKey] = {
|
|
type = "group",
|
|
name = function()
|
|
local total = CountVisible(classToken, cat)
|
|
return string.format("%s |cff808080(%d)|r", CategoryLabel(cat), total)
|
|
end,
|
|
order = catOrder,
|
|
inline = true,
|
|
hidden = function()
|
|
local total = CountVisible(classToken, cat)
|
|
return total <= 0
|
|
end,
|
|
args = catArgs,
|
|
}
|
|
catOrder = catOrder + 1
|
|
end
|
|
end
|
|
|
|
browserArgs[classKey] = {
|
|
type = "group",
|
|
name = function()
|
|
local total = CountVisible(classToken)
|
|
return string.format("%s |cff808080(%d)|r", ClassHeader(classToken), total)
|
|
end,
|
|
order = classOrder,
|
|
hidden = function()
|
|
local total = CountVisible(classToken)
|
|
return total <= 0
|
|
end,
|
|
args = classArgs,
|
|
}
|
|
classOrder = classOrder + 1
|
|
end
|
|
end
|
|
|
|
return browserArgs
|
|
end
|
|
-- 1. Spells nach Klasse und Kategorie einteilen
|
|
-- classSpells[classToken][category] = { entry, ... }
|
|
local classSpells = {}
|
|
for _, entry in ipairs(database) do
|
|
for _, cls in ipairs(entry.classes) do
|
|
if not classSpells[cls] then classSpells[cls] = {} end
|
|
local cat = entry.category or "utility"
|
|
if not classSpells[cls][cat] then classSpells[cls][cat] = {} end
|
|
-- Duplikate vermeiden (ein Spell kann für mehrere Klassen gelten)
|
|
local already = false
|
|
for _, e in ipairs(classSpells[cls][cat]) do
|
|
if e.spellId == entry.spellId then already = true; break end
|
|
end
|
|
if not already then
|
|
table.insert(classSpells[cls][cat], entry)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- 2. Args-Tabelle aufbauen
|
|
local args = {}
|
|
local classOrder = 1
|
|
|
|
for _, classToken in ipairs(HMGT_SpellData.ClassOrder) do
|
|
repeat -- repeat/until true = continue-Ersatz für Lua 5.1
|
|
if not classSpells[classToken] then break end -- skip this class
|
|
|
|
-- ── Klassen-Gruppe ────────────────────────────────
|
|
local classKey = "cls_" .. classToken
|
|
local classArgs = {}
|
|
local catOrder = 1
|
|
|
|
-- Alle Spells dieser Klasse für Select/Deselect All sammeln
|
|
local allSpellIds = {}
|
|
for _, cat in ipairs(HMGT_SpellData.CategoryOrder) do
|
|
if classSpells[classToken][cat] then
|
|
for _, entry in ipairs(classSpells[classToken][cat]) do
|
|
table.insert(allSpellIds, entry.spellId)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Select All / Deselect All Buttons
|
|
classArgs["selectAll"] = {
|
|
type = "execute",
|
|
name = L["OPT_SELECT_ALL"],
|
|
order = 1,
|
|
width = "half",
|
|
func = function()
|
|
for _, sid in ipairs(allSpellIds) do
|
|
es()[sid] = true
|
|
end
|
|
HMGT:TriggerTrackerUpdate()
|
|
end,
|
|
}
|
|
classArgs["deselectAll"] = {
|
|
type = "execute",
|
|
name = L["OPT_DESELECT_ALL"],
|
|
order = 2,
|
|
width = "half",
|
|
func = function()
|
|
for _, sid in ipairs(allSpellIds) do
|
|
es()[sid] = false
|
|
end
|
|
HMGT:TriggerTrackerUpdate()
|
|
end,
|
|
}
|
|
catOrder = 3
|
|
|
|
-- ── Kategorie-Gruppen innerhalb der Klasse ────────
|
|
for _, cat in ipairs(HMGT_SpellData.CategoryOrder) do
|
|
repeat -- repeat/until true = continue-Ersatz für Lua 5.1
|
|
if not classSpells[classToken][cat] then break end -- skip empty category
|
|
|
|
local catKey = "cat_" .. cat
|
|
local catArgs = {}
|
|
local spOrder = 1
|
|
|
|
for _, entry in ipairs(classSpells[classToken][cat]) do
|
|
local spellId = entry.spellId
|
|
catArgs["sp_" .. spellId] = {
|
|
type = "toggle",
|
|
name = SpellLabel(entry),
|
|
desc = SpellDesc(entry),
|
|
order = spOrder,
|
|
get = function()
|
|
return es()[spellId] ~= false
|
|
end,
|
|
set = function(_, val)
|
|
es()[spellId] = val
|
|
HMGT:TriggerTrackerUpdate()
|
|
end,
|
|
}
|
|
spOrder = spOrder + 1
|
|
end
|
|
|
|
classArgs[catKey] = {
|
|
type = "group",
|
|
name = L["CAT_" .. cat] or cat,
|
|
order = catOrder,
|
|
inline = true,
|
|
args = catArgs,
|
|
}
|
|
catOrder = catOrder + 1
|
|
until true -- end of category repeat block
|
|
end
|
|
|
|
args[classKey] = {
|
|
type = "group",
|
|
name = ClassHeader(classToken),
|
|
order = classOrder,
|
|
args = classArgs,
|
|
}
|
|
classOrder = classOrder + 1
|
|
until true -- end of class repeat block
|
|
end
|
|
|
|
return args
|
|
end
|
|
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
-- TRACKER-EINSTELLUNGEN (gemeinsamer Builder)
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
|
|
local function LocalizedClassName(classToken)
|
|
return (LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[classToken])
|
|
or (LOCALIZED_CLASS_NAMES_FEMALE and LOCALIZED_CLASS_NAMES_FEMALE[classToken])
|
|
or classToken
|
|
end
|
|
|
|
local function BuildClassDropdownValues()
|
|
local out = {}
|
|
for _, token in ipairs(HMGT_SpellData.ClassOrder or {}) do
|
|
out[token] = LocalizedClassName(token)
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function NotifyOptionsChanged()
|
|
if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then
|
|
AceConfigRegistry:NotifyChange(ADDON_NAME)
|
|
end
|
|
end
|
|
|
|
local function GetSpellNameById(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 ok, name = pcall(C_Spell.GetSpellName, sid)
|
|
if ok and 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 GetSpellIconById(spellId)
|
|
local sid = tonumber(spellId)
|
|
if sid and HMGT_SpellData and type(HMGT_SpellData.GetSpellIcon) == "function" then
|
|
local ok, icon = pcall(HMGT_SpellData.GetSpellIcon, sid)
|
|
if ok and type(icon) == "string" and icon ~= "" then
|
|
return icon
|
|
end
|
|
end
|
|
return "Interface\\Icons\\INV_Misc_QuestionMark"
|
|
end
|
|
|
|
local function BuildSpellIconMarkup(spellId, size)
|
|
local iconSize = tonumber(size) or 18
|
|
return string.format("|T%s:%d:%d:0:0|t", GetSpellIconById(spellId), iconSize, iconSize)
|
|
end
|
|
|
|
local function GetCategoryLabel(category)
|
|
return L["CAT_" .. tostring(category)] or tostring(category or "utility")
|
|
end
|
|
|
|
local function NormalizeSearchText(value)
|
|
local text = tostring(value or ""):lower()
|
|
text = text:gsub("^%s+", "")
|
|
text = text:gsub("%s+$", "")
|
|
return text
|
|
end
|
|
|
|
local function GetSpellFilterState(dbKey)
|
|
HMGT._cfgSpellFilters = HMGT._cfgSpellFilters or {}
|
|
HMGT._cfgSpellFilters[dbKey] = HMGT._cfgSpellFilters[dbKey] or {
|
|
search = "",
|
|
enabledOnly = false,
|
|
}
|
|
return HMGT._cfgSpellFilters[dbKey]
|
|
end
|
|
|
|
local function GetCustomSpellDraft(dbKey)
|
|
HMGT._cfgCustomDraft = HMGT._cfgCustomDraft or {}
|
|
HMGT._cfgCustomDraft[dbKey] = HMGT._cfgCustomDraft[dbKey] or {}
|
|
return HMGT._cfgCustomDraft[dbKey]
|
|
end
|
|
|
|
local function ParseSpecText(specText)
|
|
local specs = {}
|
|
if type(specText) ~= "string" then
|
|
return specs
|
|
end
|
|
for spec in specText:gmatch("(%d+)") do
|
|
specs[#specs + 1] = tostring(tonumber(spec))
|
|
end
|
|
return specs
|
|
end
|
|
|
|
local function FormatSpecsLabel(specs)
|
|
if type(specs) ~= "table" or #specs == 0 then
|
|
return L["OPT_ALL_SPECS"] or "All specs"
|
|
end
|
|
return table.concat(specs, ", ")
|
|
end
|
|
|
|
local function GetCustomSpellList(dbKey)
|
|
local profile = HMGT.db and HMGT.db.profile
|
|
local list = profile and profile.customSpells and profile.customSpells[dbKey]
|
|
if type(list) ~= "table" then
|
|
return {}
|
|
end
|
|
return list
|
|
end
|
|
|
|
local function GetTrackerModeSummary(settings)
|
|
if settings.testMode then
|
|
return L["OPT_TEST_MODE"]
|
|
end
|
|
if settings.demoMode then
|
|
return L["OPT_DEMO_MODE"]
|
|
end
|
|
if settings.enabled then
|
|
return L["OPT_ENABLED"]
|
|
end
|
|
return L["OPT_DISABLED"] or "Disabled"
|
|
end
|
|
|
|
local function GetTrackerVisibilitySummary(settings)
|
|
local parts = {}
|
|
if settings.showInSolo ~= false then parts[#parts + 1] = L["OPT_SHOW_SOLO"] or "Solo" end
|
|
if settings.showInGroup ~= false then parts[#parts + 1] = L["OPT_SHOW_GROUP"] or "Group" end
|
|
if settings.showInRaid ~= false then parts[#parts + 1] = L["OPT_SHOW_RAID"] or "Raid" end
|
|
if #parts == 0 then
|
|
return L["OPT_VISIBILITY_NONE"] or "Hidden everywhere"
|
|
end
|
|
return table.concat(parts, ", ")
|
|
end
|
|
|
|
local function BuildTrackerStatusText(moduleName, settings)
|
|
local display = settings.showBar and (L["OPT_DISPLAY_BAR"] or "Progress bars") or (L["OPT_DISPLAY_ICON"] or "Icons")
|
|
local grow = settings.growDirection or "DOWN"
|
|
local visibility = GetTrackerVisibilitySummary(settings)
|
|
local lines = {
|
|
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_MODE"] or "Mode", tostring(GetTrackerModeSummary(settings))),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_DISPLAY"] or "Display", tostring(display)),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_VISIBILITY"] or "Visibility", tostring(visibility)),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_GROWTH"] or "Growth", tostring(grow)),
|
|
}
|
|
if moduleName == "GroupCooldownTracker" and settings.attachToPartyFrame == true then
|
|
lines[#lines + 1] = string.format("|cffffd100%s|r: %s", L["OPT_STATUS_ATTACH"] or "Attach", L["OPT_ATTACH_PARTY_FRAME"] or "Attach to Party Frame")
|
|
end
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
local function BuildTrackerOptions(moduleName, dbKey, spellDatabase, order)
|
|
-- Immer LIVE aus dem aktuellen Profil lesen – nie eine stale Closure-Referenz nutzen
|
|
local function s() return HMGT.db.profile[dbKey] end
|
|
local labelByModule = {
|
|
InterruptTracker = L["IT_NAME"],
|
|
RaidCooldownTracker = L["RCD_NAME"],
|
|
GroupCooldownTracker= L["GCD_NAME"],
|
|
}
|
|
local label = labelByModule[moduleName] or moduleName
|
|
local categoryValues = {
|
|
interrupt = L["CAT_interrupt"] or "Interrupt",
|
|
offensive = L["CAT_offensive"] or "Offensive",
|
|
defensive = L["CAT_defensive"] or "Defensive",
|
|
tank = L["CAT_tank"] or "Tank",
|
|
healing = L["CAT_healing"] or "Healing",
|
|
utility = L["CAT_utility"] or "Utility",
|
|
cc = L["CAT_cc"] or "CC",
|
|
lust = L["CAT_lust"] or "Lust",
|
|
}
|
|
local currentListGroup
|
|
|
|
local function ValueLabel(baseLabel, value)
|
|
return string.format("%s: %s", tostring(baseLabel or ""), tostring(value or ""))
|
|
end
|
|
|
|
local function TriggerUpdate()
|
|
HMGT:TriggerTrackerUpdate()
|
|
end
|
|
|
|
local function RefreshAnchors()
|
|
HMGT:RefreshFrameAnchors()
|
|
end
|
|
|
|
local function GetDraft()
|
|
return GetCustomSpellDraft(dbKey)
|
|
end
|
|
|
|
local function SpecsToText(specs)
|
|
local values = {}
|
|
if type(specs) == "table" then
|
|
for _, spec in ipairs(specs) do
|
|
values[#values + 1] = tostring(spec)
|
|
end
|
|
end
|
|
return table.concat(values, ",")
|
|
end
|
|
|
|
local function LoadCustomSpellIntoDraft(entry)
|
|
local draft = GetDraft()
|
|
local cooldown = tonumber((HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(entry)) or entry.cooldown)
|
|
draft.spellId = tostring(tonumber(entry.spellId) or "")
|
|
draft.cooldown = cooldown and tostring(cooldown) or ""
|
|
draft.classToken = (type(entry.classes) == "table" and entry.classes[1]) or draft.classToken or "WARRIOR"
|
|
draft.specs = SpecsToText(entry.specs)
|
|
draft.category = entry.category or "utility"
|
|
end
|
|
|
|
local function GetCustomSpellPreviewText()
|
|
local draft = GetDraft()
|
|
local spellId = tonumber(draft.spellId)
|
|
if not spellId or spellId <= 0 then
|
|
return L["OPT_CUSTOM_SPELLS_PREVIEW_EMPTY"] or "Enter a spell ID to preview the custom spell."
|
|
end
|
|
|
|
local spellName = GetSpellNameById(spellId) or ("Spell " .. spellId)
|
|
local classLabel = LocalizedClassName(tostring(draft.classToken or "WARRIOR"))
|
|
local cooldown = tonumber(draft.cooldown) or 0
|
|
local specs = ParseSpecText(draft.specs or "")
|
|
local category = GetCategoryLabel(draft.category or "utility")
|
|
|
|
return table.concat({
|
|
string.format("%s %s", BuildSpellIconMarkup(spellId, 18), spellName),
|
|
string.format("|cffffd100%s|r: %d", L["OPT_CUSTOM_SPELLS_ID"] or "Spell ID", spellId),
|
|
string.format("|cffffd100%s|r: %ss", L["OPT_CUSTOM_SPELLS_CD"] or "Cooldown (sec)", cooldown),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_CUSTOM_SPELLS_CLASS"] or "Class", classLabel),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_CUSTOM_SPELLS_CATEGORY"] or "Category", category),
|
|
string.format("|cffffd100%s|r: %s", L["OPT_CUSTOM_SPELLS_SPECS"] or "Specs", FormatSpecsLabel(specs)),
|
|
}, "\n")
|
|
end
|
|
|
|
local function RefreshCustomSpellRows()
|
|
if not currentListGroup then
|
|
return
|
|
end
|
|
|
|
currentListGroup.args = {}
|
|
local list = GetCustomSpellList(dbKey)
|
|
if type(list) ~= "table" or #list == 0 then
|
|
currentListGroup.args.empty = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = L["OPT_CUSTOM_SPELLS_EMPTY"] or "No custom spells for this tracker.",
|
|
}
|
|
return
|
|
end
|
|
|
|
for index, entry in ipairs(list) do
|
|
local spellId = tonumber(entry.spellId) or 0
|
|
local spellName = tostring(entry.name or GetSpellNameById(spellId) or ("Spell " .. spellId))
|
|
local classToken = (type(entry.classes) == "table" and entry.classes[1]) or "WARRIOR"
|
|
local specs = {}
|
|
if type(entry.specs) == "table" then
|
|
for _, spec in ipairs(entry.specs) do
|
|
specs[#specs + 1] = tostring(spec)
|
|
end
|
|
end
|
|
local cooldown = tonumber((HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(entry)) or entry.cooldown) or 0
|
|
local rowKey = "entry_" .. index
|
|
|
|
currentListGroup.args[rowKey] = {
|
|
type = "group",
|
|
inline = true,
|
|
order = index,
|
|
name = string.format("%s %s |cff808080(#%d)|r", BuildSpellIconMarkup(spellId, 16), spellName, spellId),
|
|
args = {
|
|
details = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = string.format(
|
|
"|cffffd100%s|r: %s |cffffd100%s|r: %s |cffffd100%s|r: %ss\n|cffffd100%s|r: %s",
|
|
L["OPT_CUSTOM_SPELLS_CLASS"] or "Class",
|
|
LocalizedClassName(classToken),
|
|
L["OPT_CUSTOM_SPELLS_CATEGORY"] or "Category",
|
|
GetCategoryLabel(entry.category or "utility"),
|
|
L["OPT_CUSTOM_SPELLS_CD"] or "Cooldown (sec)",
|
|
cooldown,
|
|
L["OPT_CUSTOM_SPELLS_SPECS"] or "Specs",
|
|
FormatSpecsLabel(specs)
|
|
),
|
|
},
|
|
load = {
|
|
type = "execute",
|
|
order = 2,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_LOAD"] or "Load into editor",
|
|
func = function()
|
|
LoadCustomSpellIntoDraft(entry)
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
remove = {
|
|
type = "execute",
|
|
order = 3,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_REMOVE"] or "Remove SpellID",
|
|
func = function()
|
|
local ok = HMGT.RemoveCustomSpell and HMGT:RemoveCustomSpell(dbKey, spellId)
|
|
if ok then
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_REMOVED"] or "HMGT: custom spell removed")
|
|
RefreshCustomSpellRows()
|
|
NotifyOptionsChanged()
|
|
else
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_NOT_FOUND"] or "HMGT: spell not found")
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
}
|
|
end
|
|
end
|
|
|
|
local function AddDraftSpell()
|
|
local draft = GetDraft()
|
|
local ok = HMGT.AddCustomSpell and HMGT:AddCustomSpell(
|
|
dbKey,
|
|
draft.spellId,
|
|
draft.cooldown,
|
|
draft.classToken,
|
|
draft.specs,
|
|
draft.category
|
|
)
|
|
if ok then
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_ADDED"] or "HMGT: custom spell added")
|
|
RefreshCustomSpellRows()
|
|
NotifyOptionsChanged()
|
|
else
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_INVALID"] or "HMGT: invalid custom spell input")
|
|
end
|
|
end
|
|
|
|
local function RemoveDraftSpell()
|
|
local draft = GetDraft()
|
|
local ok = HMGT.RemoveCustomSpell and HMGT:RemoveCustomSpell(dbKey, draft.spellId)
|
|
if ok then
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_REMOVED"] or "HMGT: custom spell removed")
|
|
RefreshCustomSpellRows()
|
|
NotifyOptionsChanged()
|
|
else
|
|
HMGT:Print(L["OPT_CUSTOM_SPELLS_MSG_NOT_FOUND"] or "HMGT: spell not found")
|
|
end
|
|
end
|
|
|
|
currentListGroup = {
|
|
type = "group",
|
|
name = L["OPT_CUSTOM_SPELLS_CURRENT"] or "Current custom spells",
|
|
order = 4,
|
|
args = {},
|
|
}
|
|
RefreshCustomSpellRows()
|
|
|
|
return {
|
|
type = "group",
|
|
name = label,
|
|
order = order,
|
|
childGroups = "tab",
|
|
args = {
|
|
general = {
|
|
type = "group",
|
|
name = L["OPT_GENERAL"],
|
|
order = 1,
|
|
args = {
|
|
status = {
|
|
type = "group",
|
|
inline = true,
|
|
name = label,
|
|
order = 1,
|
|
args = {
|
|
summary = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = function()
|
|
return BuildTrackerStatusText(moduleName, s())
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
modeGroup = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_UI_GROUP_MODE"] or "Mode",
|
|
order = 10,
|
|
args = {
|
|
enabled = {
|
|
type = "toggle", order = 1, width = "full",
|
|
name = L["OPT_ENABLED"], desc = L["OPT_ENABLED_DESC"],
|
|
get = function() return s().enabled end,
|
|
set = function(_, val)
|
|
s().enabled = val
|
|
if val then
|
|
if HMGT[moduleName] then HMGT[moduleName]:Enable() end
|
|
RefreshAnchors()
|
|
else
|
|
if not s().demoMode and not s().testMode and HMGT[moduleName] then
|
|
HMGT[moduleName]:Disable()
|
|
end
|
|
end
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
demoMode = {
|
|
type = "toggle", order = 2, width = "full",
|
|
name = L["OPT_DEMO_MODE"], desc = L["OPT_DEMO_MODE_DESC"],
|
|
get = function() return s().demoMode == true end,
|
|
set = function(_, val)
|
|
s().demoMode = val
|
|
if val then
|
|
s().testMode = false
|
|
end
|
|
if val then
|
|
if HMGT[moduleName] then HMGT[moduleName]:Enable() end
|
|
else
|
|
if not s().enabled and not s().testMode and HMGT[moduleName] then
|
|
HMGT[moduleName]:Disable()
|
|
end
|
|
end
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
testMode = {
|
|
type = "toggle", order = 3, width = "full",
|
|
name = L["OPT_TEST_MODE"], desc = L["OPT_TEST_MODE_DESC"],
|
|
get = function() return s().testMode == true end,
|
|
set = function(_, val)
|
|
s().testMode = val
|
|
if val then
|
|
s().demoMode = false
|
|
end
|
|
if val then
|
|
if HMGT[moduleName] then HMGT[moduleName]:Enable() end
|
|
else
|
|
if not s().enabled and not s().demoMode and HMGT[moduleName] then
|
|
HMGT[moduleName]:Disable()
|
|
end
|
|
end
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
showBar = {
|
|
type = "select", order = 4, width = "full",
|
|
name = L["OPT_DISPLAY_MODE"], desc = L["OPT_DISPLAY_MODE_DESC"],
|
|
values = { bar = L["OPT_DISPLAY_BAR"], icon = L["OPT_DISPLAY_ICON"] },
|
|
get = function() return s().showBar and "bar" or "icon" end,
|
|
set = function(_, val)
|
|
s().showBar = (val == "bar")
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
placementGroup = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_UI_GROUP_PLACEMENT"] or "Placement",
|
|
order = 20,
|
|
args = {
|
|
locked = {
|
|
type = "toggle", order = 1,
|
|
name = L["OPT_LOCKED"], desc = L["OPT_LOCKED_DESC"],
|
|
get = function() return s().locked end,
|
|
set = function(_, val)
|
|
s().locked = val
|
|
if moduleName == "GroupCooldownTracker" and HMGT.GroupCooldownTracker then
|
|
if HMGT.GroupCooldownTracker.SetLockedAll then
|
|
HMGT.GroupCooldownTracker:SetLockedAll(val)
|
|
end
|
|
if HMGT.GroupCooldownTracker.RefreshAnchors then
|
|
HMGT.GroupCooldownTracker:RefreshAnchors()
|
|
end
|
|
elseif HMGT[moduleName] and HMGT[moduleName].frame then
|
|
HMGT.TrackerFrame:SetLocked(HMGT[moduleName].frame, val)
|
|
end
|
|
end,
|
|
},
|
|
growDirection = {
|
|
type = "select", order = 2,
|
|
name = L["OPT_GROW_DIR"],
|
|
values = function()
|
|
if s().showBar then
|
|
return { DOWN = L["OPT_GROW_DOWN"], UP = L["OPT_GROW_UP"] }
|
|
end
|
|
return {
|
|
DOWN = L["OPT_GROW_DOWN"],
|
|
UP = L["OPT_GROW_UP"],
|
|
LEFT = L["OPT_GROW_LEFT"],
|
|
RIGHT = L["OPT_GROW_RIGHT"],
|
|
}
|
|
end,
|
|
get = function()
|
|
local dir = s().growDirection or "DOWN"
|
|
if s().showBar and (dir == "LEFT" or dir == "RIGHT") then
|
|
return "DOWN"
|
|
end
|
|
return dir
|
|
end,
|
|
set = function(_, val)
|
|
s().growDirection = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
attachToPartyFrame = {
|
|
type = "toggle", order = 3, width = "full",
|
|
name = L["OPT_ATTACH_PARTY_FRAME"],
|
|
desc = L["OPT_ATTACH_PARTY_FRAME_DESC"],
|
|
hidden = function() return moduleName ~= "GroupCooldownTracker" end,
|
|
get = function() return s().attachToPartyFrame == true end,
|
|
set = function(_, val)
|
|
s().attachToPartyFrame = val
|
|
RefreshAnchors()
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
partyAttachSide = {
|
|
type = "select", order = 4,
|
|
name = L["OPT_ATTACH_PARTY_SIDE"],
|
|
values = {
|
|
LEFT = L["OPT_ATTACH_LEFT"],
|
|
RIGHT = L["OPT_ATTACH_RIGHT"],
|
|
},
|
|
hidden = function()
|
|
return moduleName ~= "GroupCooldownTracker" or s().attachToPartyFrame ~= true
|
|
end,
|
|
get = function() return s().partyAttachSide or "RIGHT" end,
|
|
set = function(_, val)
|
|
s().partyAttachSide = val
|
|
RefreshAnchors()
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
partyAttachOffsetX = {
|
|
type = "range", order = 5, min = -200, max = 200, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_ATTACH_PARTY_OFFSET_X"] or "Attach X offset", s().partyAttachOffsetX or 8)
|
|
end,
|
|
hidden = function()
|
|
return moduleName ~= "GroupCooldownTracker" or s().attachToPartyFrame ~= true
|
|
end,
|
|
get = function() return s().partyAttachOffsetX or 8 end,
|
|
set = function(_, val)
|
|
s().partyAttachOffsetX = val
|
|
RefreshAnchors()
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
partyAttachOffsetY = {
|
|
type = "range", order = 6, min = -200, max = 200, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_ATTACH_PARTY_OFFSET_Y"] or "Attach Y offset", s().partyAttachOffsetY or 0)
|
|
end,
|
|
hidden = function()
|
|
return moduleName ~= "GroupCooldownTracker" or s().attachToPartyFrame ~= true
|
|
end,
|
|
get = function() return s().partyAttachOffsetY or 0 end,
|
|
set = function(_, val)
|
|
s().partyAttachOffsetY = val
|
|
RefreshAnchors()
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
anchorTo = {
|
|
type = "select", order = 10,
|
|
name = L["OPT_ANCHOR_TO"], desc = L["OPT_ANCHOR_TO_DESC"],
|
|
hidden = function() return moduleName == "GroupCooldownTracker" end,
|
|
values = function()
|
|
return HMGT:GetAnchorTargetOptions(moduleName, s().anchorTo)
|
|
end,
|
|
get = function()
|
|
local current = s().anchorTo or "UIParent"
|
|
local values = HMGT:GetAnchorTargetOptions(moduleName, current)
|
|
if values[current] then
|
|
return current
|
|
end
|
|
return "CUSTOM"
|
|
end,
|
|
set = function(_, val)
|
|
if val == moduleName then
|
|
val = "UIParent"
|
|
end
|
|
if val ~= "CUSTOM" and type(val) == "string" then
|
|
s().anchorCustom = s().anchorCustom or ""
|
|
end
|
|
s().anchorTo = val
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
anchorCustom = {
|
|
type = "input", order = 11, width = "full",
|
|
name = L["OPT_ANCHOR_CUSTOM_NAME"],
|
|
desc = L["OPT_ANCHOR_CUSTOM_NAME_DESC"],
|
|
hidden = function()
|
|
if moduleName == "GroupCooldownTracker" then
|
|
return true
|
|
end
|
|
return (s().anchorTo or "UIParent") ~= "CUSTOM"
|
|
end,
|
|
get = function() return s().anchorCustom or "" end,
|
|
set = function(_, val)
|
|
s().anchorCustom = (val or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
anchorPoint = {
|
|
type = "select", order = 12,
|
|
name = L["OPT_ANCHOR_POINT"], desc = L["OPT_ANCHOR_POINT_DESC"],
|
|
hidden = function() return moduleName == "GroupCooldownTracker" end,
|
|
values = {
|
|
TOPLEFT = "TOPLEFT", TOP = "TOP", TOPRIGHT = "TOPRIGHT",
|
|
LEFT = "LEFT", CENTER = "CENTER", RIGHT = "RIGHT",
|
|
BOTTOMLEFT = "BOTTOMLEFT", BOTTOM = "BOTTOM", BOTTOMRIGHT = "BOTTOMRIGHT",
|
|
},
|
|
get = function() return s().anchorPoint or "TOPLEFT" end,
|
|
set = function(_, val)
|
|
s().anchorPoint = val
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
anchorRelativePoint = {
|
|
type = "select", order = 13,
|
|
name = L["OPT_ANCHOR_REL_POINT"], desc = L["OPT_ANCHOR_REL_POINT_DESC"],
|
|
hidden = function() return moduleName == "GroupCooldownTracker" end,
|
|
values = {
|
|
TOPLEFT = "TOPLEFT", TOP = "TOP", TOPRIGHT = "TOPRIGHT",
|
|
LEFT = "LEFT", CENTER = "CENTER", RIGHT = "RIGHT",
|
|
BOTTOMLEFT = "BOTTOMLEFT", BOTTOM = "BOTTOM", BOTTOMRIGHT = "BOTTOMRIGHT",
|
|
},
|
|
get = function() return s().anchorRelPoint or "TOPLEFT" end,
|
|
set = function(_, val)
|
|
s().anchorRelPoint = val
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
anchorX = {
|
|
type = "range", order = 14, min = -2000, max = 2000, step = 1,
|
|
name = function()
|
|
local value = s().anchorX
|
|
if value == nil then value = s().posX or 0 end
|
|
return ValueLabel(L["OPT_ANCHOR_X"], value)
|
|
end,
|
|
hidden = function() return moduleName == "GroupCooldownTracker" end,
|
|
get = function()
|
|
if s().anchorX ~= nil then return s().anchorX end
|
|
return s().posX or 0
|
|
end,
|
|
set = function(_, val)
|
|
s().anchorX = val
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
anchorY = {
|
|
type = "range", order = 15, min = -2000, max = 2000, step = 1,
|
|
name = function()
|
|
local value = s().anchorY
|
|
if value == nil then value = s().posY or 0 end
|
|
return ValueLabel(L["OPT_ANCHOR_Y"], value)
|
|
end,
|
|
hidden = function() return moduleName == "GroupCooldownTracker" end,
|
|
get = function()
|
|
if s().anchorY ~= nil then return s().anchorY end
|
|
return s().posY or 0
|
|
end,
|
|
set = function(_, val)
|
|
s().anchorY = val
|
|
RefreshAnchors()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
visibilityGroup = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_UI_GROUP_VISIBILITY"] or "Visibility",
|
|
order = 30,
|
|
args = {
|
|
showInSolo = {
|
|
type = "toggle", order = 1,
|
|
name = L["OPT_SHOW_SOLO"], desc = L["OPT_SHOW_SOLO_DESC"],
|
|
get = function() return s().showInSolo ~= false end,
|
|
set = function(_, val)
|
|
s().showInSolo = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
showInGroup = {
|
|
type = "toggle", order = 2,
|
|
name = L["OPT_SHOW_GROUP"], desc = L["OPT_SHOW_GROUP_DESC"],
|
|
get = function() return s().showInGroup ~= false end,
|
|
set = function(_, val)
|
|
s().showInGroup = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
showInRaid = {
|
|
type = "toggle", order = 3,
|
|
name = L["OPT_SHOW_RAID"], desc = L["OPT_SHOW_RAID_DESC"],
|
|
get = function() return s().showInRaid ~= false end,
|
|
set = function(_, val)
|
|
s().showInRaid = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
showOnlyReady = {
|
|
type = "toggle", order = 4, width = "full",
|
|
name = L["OPT_SHOW_ONLY_READY"],
|
|
desc = L["OPT_SHOW_ONLY_READY_DESC"],
|
|
get = function() return s().showOnlyReady == true end,
|
|
set = function(_, val)
|
|
s().showOnlyReady = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
readySoonSec = {
|
|
type = "range", order = 5, min = 0, max = 300, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_READY_SOON_SEC"], s().readySoonSec or 0)
|
|
end,
|
|
desc = L["OPT_READY_SOON_SEC_DESC"],
|
|
get = function() return s().readySoonSec or 0 end,
|
|
set = function(_, val)
|
|
s().readySoonSec = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
roleFilter = {
|
|
type = "select", order = 6,
|
|
name = L["OPT_ROLE_FILTER"] or "Role filter",
|
|
values = {
|
|
ALL = L["OPT_ROLE_FILTER_ALL"] or "All",
|
|
TANK = L["OPT_ROLE_FILTER_TANK"] or "Tank",
|
|
HEALER = L["OPT_ROLE_FILTER_HEALER"] or "Healer",
|
|
DAMAGER = L["OPT_ROLE_FILTER_DAMAGER"] or "Damage",
|
|
},
|
|
get = function() return s().roleFilter or "ALL" end,
|
|
set = function(_, val)
|
|
s().roleFilter = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
rangeCheck = {
|
|
type = "toggle", order = 7,
|
|
name = L["OPT_RANGE_CHECK"] or "Range check",
|
|
get = function() return s().rangeCheck == true end,
|
|
set = function(_, val)
|
|
s().rangeCheck = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
hideOutOfRange = {
|
|
type = "toggle", order = 8,
|
|
name = L["OPT_HIDE_OOR"] or "Hide out of range",
|
|
disabled = function() return s().rangeCheck ~= true end,
|
|
get = function() return s().hideOutOfRange == true end,
|
|
set = function(_, val)
|
|
s().hideOutOfRange = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
outOfRangeAlpha = {
|
|
type = "range", order = 9, min = 0.1, max = 1, step = 0.05,
|
|
name = function()
|
|
return ValueLabel(L["OPT_OOR_ALPHA"] or "Out of range alpha", s().outOfRangeAlpha or 0.4)
|
|
end,
|
|
disabled = function() return s().rangeCheck ~= true end,
|
|
get = function() return s().outOfRangeAlpha or 0.4 end,
|
|
set = function(_, val)
|
|
s().outOfRangeAlpha = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
layoutGroup = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_UI_GROUP_LAYOUT"] or "Layout",
|
|
order = 40,
|
|
args = {
|
|
width = {
|
|
type = "range", order = 1, min = 100, max = 600, step = 5,
|
|
name = function()
|
|
return ValueLabel(L["OPT_WIDTH"], s().width or 250)
|
|
end,
|
|
hidden = function() return not s().showBar end,
|
|
get = function() return s().width or 250 end,
|
|
set = function(_, val)
|
|
s().width = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
barHeight = {
|
|
type = "range", order = 2, min = 10, max = 60, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_BAR_HEIGHT"], s().barHeight or 20)
|
|
end,
|
|
hidden = function() return not s().showBar end,
|
|
get = function() return s().barHeight or 20 end,
|
|
set = function(_, val)
|
|
s().barHeight = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
barSpacing = {
|
|
type = "range", order = 3, min = 0, max = 20, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_BAR_SPACING"], s().barSpacing or 2)
|
|
end,
|
|
hidden = function() return not s().showBar end,
|
|
get = function() return s().barSpacing or 2 end,
|
|
set = function(_, val)
|
|
s().barSpacing = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
barTexture = {
|
|
type = "select", order = 4,
|
|
name = L["OPT_BAR_TEXTURE"], desc = L["OPT_BAR_TEXTURE_DESC"],
|
|
hidden = function() return not s().showBar end,
|
|
dialogControl = "LSM30_Statusbar",
|
|
values = AceGUIWidgetLSMlists and AceGUIWidgetLSMlists.statusbar or LSM:HashTable("statusbar"),
|
|
get = function() return s().barTexture or "Blizzard" end,
|
|
set = function(_, val)
|
|
s().barTexture = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
borderEnabled = {
|
|
type = "toggle", order = 5, width = "full",
|
|
name = L["OPT_BORDER_ENABLED"], desc = L["OPT_BORDER_ENABLED_DESC"],
|
|
get = function() return s().borderEnabled == true end,
|
|
set = function(_, val)
|
|
s().borderEnabled = val
|
|
TriggerUpdate()
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
borderColor = {
|
|
type = "color", order = 6, hasAlpha = false,
|
|
name = L["OPT_BORDER_COLOR"], desc = L["OPT_BORDER_COLOR_DESC"],
|
|
disabled = function() return s().borderEnabled ~= true end,
|
|
get = function()
|
|
local c = s().borderColor or {}
|
|
return c.r or c[1] or 1, c.g or c[2] or 1, c.b or c[3] or 1
|
|
end,
|
|
set = function(_, r, g, b)
|
|
s().borderColor = { r = r, g = g, b = b, a = 1 }
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
iconSize = {
|
|
type = "range", order = 10, min = 16, max = 80, step = 2,
|
|
name = function()
|
|
return ValueLabel(L["OPT_ICON_SIZE"], s().iconSize or 32)
|
|
end,
|
|
hidden = function() return s().showBar end,
|
|
get = function() return s().iconSize or 32 end,
|
|
set = function(_, val)
|
|
s().iconSize = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
iconSpacing = {
|
|
type = "range", order = 11, min = 0, max = 20, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_ICON_SPACING"], s().iconSpacing or 2)
|
|
end,
|
|
hidden = function() return s().showBar end,
|
|
get = function() return s().iconSpacing or 2 end,
|
|
set = function(_, val)
|
|
s().iconSpacing = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
iconCols = {
|
|
type = "range", order = 12, min = 1, max = 12, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_ICON_COLS"], s().iconCols or 6)
|
|
end,
|
|
desc = L["OPT_ICON_COLS_DESC"],
|
|
hidden = function() return s().showBar end,
|
|
get = function() return s().iconCols or 6 end,
|
|
set = function(_, val)
|
|
s().iconCols = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
textAnchor = {
|
|
type = "select", order = 14,
|
|
name = L["OPT_TEXT_ANCHOR"], desc = L["OPT_TEXT_ANCHOR_DESC"],
|
|
hidden = function()
|
|
return s().showBar or (s().iconOverlay or "sweep") == "sweep"
|
|
end,
|
|
values = {
|
|
onIcon = L["OPT_ANCHOR_ON_ICON"],
|
|
above = L["OPT_ANCHOR_ABOVE"],
|
|
below = L["OPT_ANCHOR_BELOW"],
|
|
},
|
|
get = function() return s().textAnchor or "below" end,
|
|
set = function(_, val)
|
|
s().textAnchor = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
appearanceGroup = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_UI_GROUP_APPEARANCE"] or "Appearance",
|
|
order = 50,
|
|
args = {
|
|
showPlayerName = {
|
|
type = "toggle", order = 1,
|
|
name = L["OPT_SHOW_NAME"],
|
|
get = function() return s().showPlayerName end,
|
|
set = function(_, val)
|
|
s().showPlayerName = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
colorByClass = {
|
|
type = "toggle", order = 2,
|
|
name = L["OPT_CLASS_COLOR"],
|
|
get = function() return s().colorByClass end,
|
|
set = function(_, val)
|
|
s().colorByClass = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
font = {
|
|
type = "select", order = 3,
|
|
name = L["OPT_FONT"],
|
|
dialogControl = "LSM30_Font",
|
|
values = AceGUIWidgetLSMlists and AceGUIWidgetLSMlists.font or LSM:HashTable("font"),
|
|
get = function() return s().font or "Friz Quadrata TT" end,
|
|
set = function(_, val)
|
|
s().font = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
fontSize = {
|
|
type = "range", order = 4, min = 6, max = 24, step = 1,
|
|
name = function()
|
|
return ValueLabel(L["OPT_FONT_SIZE"], s().fontSize or 12)
|
|
end,
|
|
get = function() return s().fontSize or 12 end,
|
|
set = function(_, val)
|
|
s().fontSize = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
fontOutline = {
|
|
type = "select", order = 5,
|
|
name = L["OPT_FONT_OUTLINE"],
|
|
values = {
|
|
[""] = L["OPT_OUTLINE_NONE"],
|
|
["OUTLINE"] = L["OPT_OUTLINE_NORMAL"],
|
|
["THICKOUTLINE"] = L["OPT_OUTLINE_THICK"],
|
|
["MONOCHROME"] = L["OPT_OUTLINE_MONO"],
|
|
},
|
|
get = function() return s().fontOutline or "OUTLINE" end,
|
|
set = function(_, val)
|
|
s().fontOutline = val
|
|
TriggerUpdate()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
spells = {
|
|
type = "group",
|
|
name = L["OPT_SECTION_SPELLS"],
|
|
order = 2,
|
|
childGroups = "tree",
|
|
args = BuildSpellOptions(spellDatabase, dbKey),
|
|
},
|
|
customSpells = {
|
|
type = "group",
|
|
name = L["OPT_SECTION_CUSTOM_SPELLS"] or "Custom Spells",
|
|
order = 3,
|
|
args = {
|
|
editor = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_CUSTOM_SPELLS_EDITOR"] or "Spell Editor",
|
|
order = 1,
|
|
args = {
|
|
spellId = {
|
|
type = "input",
|
|
order = 1,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_ID"] or "Spell ID",
|
|
get = function() return GetDraft().spellId or "" end,
|
|
set = function(_, val)
|
|
GetDraft().spellId = val
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
cooldown = {
|
|
type = "input",
|
|
order = 2,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_CD"] or "Cooldown (sec)",
|
|
get = function() return GetDraft().cooldown or "" end,
|
|
set = function(_, val)
|
|
GetDraft().cooldown = val
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
classToken = {
|
|
type = "select",
|
|
order = 3,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_CLASS"] or "Class",
|
|
values = BuildClassDropdownValues,
|
|
get = function() return GetDraft().classToken or "WARRIOR" end,
|
|
set = function(_, val)
|
|
GetDraft().classToken = val
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
category = {
|
|
type = "select",
|
|
order = 4,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_CATEGORY"] or "Category",
|
|
values = categoryValues,
|
|
get = function() return GetDraft().category or "utility" end,
|
|
set = function(_, val)
|
|
GetDraft().category = val
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
specs = {
|
|
type = "input",
|
|
order = 5,
|
|
width = "full",
|
|
name = L["OPT_CUSTOM_SPELLS_SPECS"] or "Specs (optional, e.g. 1,3)",
|
|
get = function() return GetDraft().specs or "" end,
|
|
set = function(_, val)
|
|
GetDraft().specs = val
|
|
NotifyOptionsChanged()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
preview = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_CUSTOM_SPELLS_PREVIEW"] or "Preview",
|
|
order = 2,
|
|
args = {
|
|
previewText = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = function()
|
|
return GetCustomSpellPreviewText()
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
actions = {
|
|
type = "group",
|
|
inline = true,
|
|
name = L["OPT_TRACKER_ACTIONS"] or "Actions",
|
|
order = 3,
|
|
args = {
|
|
add = {
|
|
type = "execute",
|
|
order = 1,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_ADD"] or "Add Spell",
|
|
func = AddDraftSpell,
|
|
},
|
|
remove = {
|
|
type = "execute",
|
|
order = 2,
|
|
width = "half",
|
|
name = L["OPT_CUSTOM_SPELLS_REMOVE"] or "Remove SpellID",
|
|
func = RemoveDraftSpell,
|
|
},
|
|
},
|
|
},
|
|
currentList = currentListGroup,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
end
|
|
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
-- INITIALISIERUNG
|
|
-- â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
|
|
|
|
local function GetAddonVersion()
|
|
if HMGT and HMGT.ADDON_VERSION then
|
|
return HMGT.ADDON_VERSION
|
|
end
|
|
if C_AddOns and type(C_AddOns.GetAddOnMetadata) == "function" then
|
|
return C_AddOns.GetAddOnMetadata(ADDON_NAME, "Version")
|
|
end
|
|
if type(GetAddOnMetadata) == "function" then
|
|
return GetAddOnMetadata(ADDON_NAME, "Version")
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function GetChangelogReleases()
|
|
local data = HMGT_Changelog and HMGT_Changelog.releases
|
|
if type(data) ~= "table" then
|
|
return nil
|
|
end
|
|
|
|
local locale = type(GetLocale) == "function" and GetLocale() or "enUS"
|
|
local releases = data[locale]
|
|
if type(releases) == "table" and #releases > 0 then
|
|
return releases
|
|
end
|
|
|
|
releases = data.enUS
|
|
if type(releases) == "table" and #releases > 0 then
|
|
return releases
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
local function GetChangelogText()
|
|
local releases = GetChangelogReleases()
|
|
if type(releases) ~= "table" or #releases == 0 then
|
|
local version = tostring(GetAddonVersion() or "dev")
|
|
local header = string.format("%s: %s", L["OPT_CHANGELOG_VERSION"] or "Version", version)
|
|
local body = L["OPT_CHANGELOG_EMPTY"] or "No changelog entries available."
|
|
return table.concat({ header, "", body }, "\n")
|
|
end
|
|
|
|
local lines = {}
|
|
for index, release in ipairs(releases) do
|
|
local version = tostring(release.version or GetAddonVersion() or "dev")
|
|
lines[#lines + 1] = string.format("%s: %s", L["OPT_CHANGELOG_VERSION"] or "Version", version)
|
|
|
|
local entries = release.entries
|
|
if type(entries) == "table" and #entries > 0 then
|
|
for _, entry in ipairs(entries) do
|
|
lines[#lines + 1] = "- " .. tostring(entry)
|
|
end
|
|
else
|
|
lines[#lines + 1] = "- " .. (L["OPT_CHANGELOG_EMPTY"] or "No changelog entries available.")
|
|
end
|
|
|
|
if index < #releases then
|
|
lines[#lines + 1] = ""
|
|
end
|
|
end
|
|
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
local function NormalizeExecuteButtonWidths(option)
|
|
if type(option) ~= "table" then
|
|
return
|
|
end
|
|
|
|
if option.type == "execute" and option.width == nil then
|
|
option.width = "double"
|
|
end
|
|
|
|
if type(option.args) == "table" then
|
|
for _, child in pairs(option.args) do
|
|
NormalizeExecuteButtonWidths(child)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function SetAllTrackerFramesLocked(locked)
|
|
local profile = HMGT.db and HMGT.db.profile
|
|
if not profile then
|
|
return
|
|
end
|
|
|
|
if type(profile.trackers) == "table" then
|
|
for _, tracker in ipairs(profile.trackers) do
|
|
tracker.locked = locked
|
|
end
|
|
end
|
|
|
|
if HMGT.TrackerManager and HMGT.TrackerManager.SetAllLocked then
|
|
HMGT.TrackerManager:SetAllLocked(locked)
|
|
end
|
|
HMGT:RefreshFrameAnchors(true)
|
|
HMGT:TriggerTrackerUpdate()
|
|
end
|
|
|
|
HMGT_Config.SetAllTrackerFramesLocked = SetAllTrackerFramesLocked
|
|
|
|
function HMGT_Config:Initialize()
|
|
-- Profiles: GetOptionsTable() direkt verwenden – NICHT einwickeln!
|
|
local profilesOpts = LibStub("AceDBOptions-3.0"):GetOptionsTable(HMGT.db)
|
|
profilesOpts.order = 99
|
|
profilesOpts.name = L["OPT_PROFILES"] or profilesOpts.name
|
|
|
|
|
|
local function NormalizeProviderResult(result)
|
|
if type(result) ~= "table" then return {} end
|
|
if type(result.path) == "string" and type(result.group) == "table" then
|
|
return { result }
|
|
end
|
|
local out = {}
|
|
for _, entry in ipairs(result) do
|
|
if type(entry) == "table" and type(entry.path) == "string" and type(entry.group) == "table" then
|
|
out[#out + 1] = entry
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function BuildGeneralSettingsGroup()
|
|
return {
|
|
type = "group",
|
|
order = 10,
|
|
name = L["OPT_GENERAL_SETTINGS"] or "General Settings",
|
|
args = {
|
|
generalSettings = {
|
|
type = "group",
|
|
order = 1,
|
|
inline = true,
|
|
name = L["OPT_GENERAL_SETTINGS"] or "General Settings",
|
|
args = {
|
|
showMinimapIcon = {
|
|
type = "toggle",
|
|
order = 1,
|
|
width = "full",
|
|
name = L["OPT_SHOW_MINIMAP_ICON"] or "Show Minimap Icon",
|
|
get = function()
|
|
local profile = HMGT.db and HMGT.db.profile
|
|
if not profile then
|
|
return true
|
|
end
|
|
profile.minimap = profile.minimap or {}
|
|
return profile.minimap.hide ~= true
|
|
end,
|
|
set = function(_, value)
|
|
local profile = HMGT.db and HMGT.db.profile
|
|
if not profile then
|
|
return
|
|
end
|
|
profile.minimap = profile.minimap or {}
|
|
profile.minimap.hide = not (value == true)
|
|
if HMGT.CreateMinimapButton then
|
|
HMGT:CreateMinimapButton()
|
|
end
|
|
if HMGT.UpdateMinimapButtonPosition then
|
|
HMGT:UpdateMinimapButtonPosition()
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
commands = {
|
|
type = "group",
|
|
order = 2,
|
|
inline = true,
|
|
name = L["OPT_COMMANDS"] or "Commands",
|
|
args = {
|
|
list = {
|
|
type = "description",
|
|
order = 1,
|
|
width = "full",
|
|
name = table.concat({
|
|
"|cffffd100/hmgt|r",
|
|
"|cffffd100/hmgt debug|r",
|
|
"|cffffd100/hmgt version|r",
|
|
}, "\n"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
end
|
|
|
|
local function GetProviderEntries(providerId)
|
|
local provider = HMGT_Config._optionProviders and HMGT_Config._optionProviders[providerId]
|
|
if type(provider) ~= "function" then
|
|
return {}
|
|
end
|
|
|
|
local ok, result = pcall(provider, HMGT_Config)
|
|
if not ok then
|
|
if HMGT and HMGT.DevError then
|
|
HMGT:DevError("Options", "provider_failed", {
|
|
provider = tostring(providerId),
|
|
error = tostring(result),
|
|
})
|
|
end
|
|
return {}
|
|
end
|
|
|
|
return NormalizeProviderResult(result)
|
|
end
|
|
|
|
local function GetSingleProviderGroup(providerId)
|
|
local entries = GetProviderEntries(providerId)
|
|
if type(entries[1]) == "table" then
|
|
return entries[1].group
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function CopyGroup(group, overrides)
|
|
if type(group) ~= "table" then
|
|
return nil
|
|
end
|
|
|
|
local out = {}
|
|
for key, value in pairs(group) do
|
|
out[key] = value
|
|
end
|
|
for key, value in pairs(overrides or {}) do
|
|
out[key] = value
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function FlattenGeneralSubgroup(group)
|
|
if type(group) ~= "table" or type(group.args) ~= "table" then
|
|
return group
|
|
end
|
|
|
|
local generalGroup = group.args.general
|
|
if type(generalGroup) ~= "table" or generalGroup.type ~= "group" or type(generalGroup.args) ~= "table" then
|
|
return group
|
|
end
|
|
|
|
local merged = CopyGroup(group)
|
|
merged.args = {}
|
|
|
|
for key, value in pairs(group.args) do
|
|
if key ~= "general" then
|
|
merged.args[key] = value
|
|
end
|
|
end
|
|
|
|
for key, value in pairs(generalGroup.args) do
|
|
if merged.args[key] == nil then
|
|
merged.args[key] = value
|
|
else
|
|
merged.args["general_" .. key] = value
|
|
end
|
|
end
|
|
|
|
local hasChildGroups = false
|
|
for _, value in pairs(merged.args) do
|
|
if type(value) == "table" and value.type == "group" then
|
|
hasChildGroups = true
|
|
break
|
|
end
|
|
end
|
|
if not hasChildGroups then
|
|
merged.childGroups = nil
|
|
end
|
|
|
|
return merged
|
|
end
|
|
|
|
local function BuildNamedModuleGroup(providerId, displayName, order)
|
|
local group = GetSingleProviderGroup(providerId)
|
|
if not group then
|
|
return nil
|
|
end
|
|
|
|
return FlattenGeneralSubgroup(CopyGroup(group, {
|
|
name = displayName or group.name,
|
|
order = order or group.order,
|
|
}))
|
|
end
|
|
|
|
local function BuildModulesGroup()
|
|
local modulesGroup = {
|
|
type = "group",
|
|
name = L["OPT_MODULES"] or "Modules",
|
|
order = 20,
|
|
childGroups = "tree",
|
|
args = {},
|
|
}
|
|
|
|
local trackerGroup = BuildNamedModuleGroup(
|
|
"tracker",
|
|
L["OPT_MODULE_TRACKER"] or "Tracker",
|
|
10
|
|
)
|
|
if trackerGroup then
|
|
modulesGroup.args.tracker = trackerGroup
|
|
end
|
|
|
|
local buffEndingGroup = BuildNamedModuleGroup(
|
|
"announcer.auraExpiry",
|
|
L["OPT_MODULE_AURA_EXPIRY"] or L["OPT_MODULE_BUFF_ENDING"] or "Aura Expiry",
|
|
20
|
|
)
|
|
if buffEndingGroup then
|
|
modulesGroup.args.buffEnding = buffEndingGroup
|
|
end
|
|
|
|
local mapOverlayGroup = BuildNamedModuleGroup(
|
|
"map.overlay",
|
|
L["OPT_MODULE_MAP_OVERLAY"] or "Map Overlay",
|
|
30
|
|
)
|
|
if mapOverlayGroup then
|
|
modulesGroup.args.mapOverlay = mapOverlayGroup
|
|
end
|
|
|
|
local raidTimelineGroup = BuildNamedModuleGroup(
|
|
"raidTimeline",
|
|
L["OPT_RT_NAME"] or "Raid Timeline",
|
|
40
|
|
)
|
|
if raidTimelineGroup then
|
|
modulesGroup.args.raidTimeline = raidTimelineGroup
|
|
end
|
|
|
|
if next(modulesGroup.args) == nil then
|
|
return nil
|
|
end
|
|
|
|
return modulesGroup
|
|
end
|
|
|
|
local function BuildChangelogOptions()
|
|
return {
|
|
type = "group",
|
|
name = L["OPT_CHANGELOG"] or "Changelog",
|
|
args = {
|
|
changelog = {
|
|
type = "input",
|
|
order = 1,
|
|
width = "full",
|
|
multiline = 24,
|
|
name = L["OPT_CHANGELOG_DESC"] or "Recent addon changes",
|
|
get = function()
|
|
return GetChangelogText()
|
|
end,
|
|
set = function() end,
|
|
},
|
|
},
|
|
}
|
|
end
|
|
|
|
local rootOptions = {
|
|
type = "group",
|
|
name = L["ADDON_TITLE"],
|
|
childGroups = "tree",
|
|
args = {
|
|
generalSettings = BuildGeneralSettingsGroup(),
|
|
profiles = profilesOpts,
|
|
},
|
|
}
|
|
|
|
local modulesGroup = BuildModulesGroup()
|
|
if modulesGroup then
|
|
rootOptions.args.modules = modulesGroup
|
|
end
|
|
|
|
NormalizeExecuteButtonWidths(rootOptions)
|
|
|
|
local aceConfig = LibStub("AceConfig-3.0")
|
|
local aceConfigDialog = LibStub("AceConfigDialog-3.0")
|
|
local rootCategoryName = L["ADDON_TITLE"]
|
|
|
|
aceConfig:RegisterOptionsTable(ADDON_NAME, rootOptions)
|
|
local _, rootCategoryId = aceConfigDialog:AddToBlizOptions(ADDON_NAME, rootCategoryName)
|
|
HMGT_Config._settingsCategoryId = rootCategoryId
|
|
HMGT_Config._settingsCategoryName = rootCategoryName
|
|
|
|
local changelogTableName = ADDON_NAME .. "_Changelog"
|
|
aceConfig:RegisterOptionsTable(changelogTableName, BuildChangelogOptions())
|
|
aceConfigDialog:AddToBlizOptions(changelogTableName, L["OPT_CHANGELOG"] or "Changelog", rootCategoryName)
|
|
end
|
|
|
|
HMGT_Config.BuildTrackerOptions = BuildTrackerOptions
|
|
|
|
|