Files
CritTracker/Config.lua
Sarah Kuhn edc52fdd86 Addon CritTracker implementiert (WoW Forever 12.x Beta, Interface 16001)
- Getrennte Erfassung für Schaden und Heilung
- Erkennung schwacher Crits mit einstellbarem Schwellenwert (Standard 30%)
- Audio-Feedback (crit1-4, heal1-4, low.mp3) für Deutsch und Englisch
- Einstellungsmenü mit Cooldown-Slider (1-30s), Sound-Auswahl und Test-Buttons
- Automatischer Reset der Rekorde bei Stufenaufstieg (PLAYER_LEVEL_UP)
- Lokale Chat-Benachrichtigungen (nur für Spieler sichtbar)
- Slash-Befehle /ctr und /crittracker
- Entwickler- & Agenten-Leitfaden (AGENT_GUIDE.md) hinzugefügt
- sounds/English/low.mp3 repariert
2026-09-30 09:41:35 +02:00

489 lines
19 KiB
Lua

--[[
CritTracker - Configuration & Options UI
Supports Modern WoW 12.x / Retail Settings API and Standalone UI
]]
local addonName, addonTable = ...
local CritTracker = addonTable.CritTracker
local panel = CreateFrame("Frame", "CritTrackerOptionsPanel", UIParent)
CritTracker.optionsPanel = panel
panel.name = "CritTracker"
-- Standalone Window Styling (Close Button & Dragging)
panel:SetSize(660, 560)
panel:SetMovable(true)
panel:EnableMouse(true)
panel:RegisterForDrag("LeftButton")
panel:SetScript("OnDragStart", panel.StartMoving)
panel:SetScript("OnDragStop", panel.StopMovingOrSizing)
-- Close Button for Standalone Mode
local closeBtn = CreateFrame("Button", nil, panel, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -6, -6)
closeBtn:SetScript("OnClick", function()
panel:Hide()
end)
-- UI Element References for Language Updating
local uiLabels = {}
local function RegisterLabel(fontString, locKey)
table.insert(uiLabels, { element = fontString, key = locKey, isText = true })
fontString:SetText(CritTracker.L[locKey] or locKey)
end
-- Controls State Update Helper
local function UpdateSegmentedButtons(buttons, selectedKey)
for key, btn in pairs(buttons) do
if key == selectedKey then
btn:LockHighlight()
btn:SetText("|cffffd100" .. (btn.displayLabel or key) .. "|r")
else
btn:UnlockHighlight()
btn:SetText(btn.displayLabel or key)
end
end
end
-- Refresh Panel Displays
function CritTracker:UpdateOptionsDisplay()
if not self.db then return end
-- Checkboxes
if self.ui_cbEnable then self.ui_cbEnable:SetChecked(self.db.enabled) end
if self.ui_cbChat then self.ui_cbChat:SetChecked(self.db.enableChat) end
if self.ui_cbSound then self.ui_cbSound:SetChecked(self.db.enableSound) end
if self.ui_cbLow then self.ui_cbLow:SetChecked(self.db.enableLowCrit) end
-- Sliders
if self.ui_sliderCooldown then
self.ui_sliderCooldown:SetValue(self.db.cooldown or 5)
self.ui_lblCooldown:SetText(string.format(self.L["OPT_COOLDOWN_TITLE"], self.db.cooldown or 5))
end
if self.ui_sliderThreshold then
self.ui_sliderThreshold:SetValue(self.db.lowThreshold or 30)
self.ui_lblThreshold:SetText(string.format(self.L["OPT_THRESHOLD_TITLE"], self.db.lowThreshold or 30))
end
-- Audio Buttons
if self.ui_damageButtons then
UpdateSegmentedButtons(self.ui_damageButtons, self.db.damageSound or "crit1")
end
if self.ui_healButtons then
UpdateSegmentedButtons(self.ui_healButtons, self.db.healSound or "heal1")
end
if self.ui_lowButtons then
UpdateSegmentedButtons(self.ui_lowButtons, self.db.lowSound or "low")
end
if self.ui_channelButtons then
UpdateSegmentedButtons(self.ui_channelButtons, self.db.soundChannel or "Master")
end
if self.ui_langButtons then
UpdateSegmentedButtons(self.ui_langButtons, self.db.language or "auto")
end
-- Character Stats
local charData = self:GetCharData()
local dmgNum = self:FormatNumber(charData.maxDamage)
local dmgSpell = (charData.maxDamageSpell ~= "") and charData.maxDamageSpell or self.L["OPT_STATS_NONE"]
local healNum = self:FormatNumber(charData.maxHeal)
local healSpell = (charData.maxHealSpell ~= "") and charData.maxHealSpell or self.L["OPT_STATS_NONE"]
if self.ui_lblDamageRecord then
self.ui_lblDamageRecord:SetText(string.format(self.L["OPT_STATS_DAMAGE"], "|cffff4444" .. dmgNum .. "|r", dmgSpell))
end
if self.ui_lblHealRecord then
self.ui_lblHealRecord:SetText(string.format(self.L["OPT_STATS_HEAL"], "|cff44ff44" .. healNum .. "|r", healSpell))
end
-- Dynamic Language Labels
for _, item in ipairs(uiLabels) do
if item.isText and item.element and item.key then
item.element:SetText(self.L[item.key] or item.key)
end
end
end
-- Initialize UI Components
function CritTracker:InitConfig()
-- Title & Header
local title = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
title:SetPoint("TOPLEFT", panel, "TOPLEFT", 20, -18)
title:SetText("|cff00e5ffCritTracker|r")
local subtitle = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
subtitle:SetText(self.L["ADDON_SUBTITLE"] .. " |cff888888v1.0.0 (TOC 16001)|r")
-------------------------------------------------------------------------
-- LEFT COLUMN: General Options, Cooldown/Threshold Sliders, Character Stats
-------------------------------------------------------------------------
local colLeftX = 20
local curY = -70
-- Section: General
local secGeneral = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal")
secGeneral:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
RegisterLabel(secGeneral, "OPT_SECTION_GENERAL")
curY = curY - 24
-- Checkbox: Enable Addon
local cbEnable = CreateFrame("CheckButton", "CritTrackerCB_Enable", panel, "UICheckButtonTemplate")
cbEnable:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
cbEnable.Text:SetText(self.L["OPT_ENABLE"])
RegisterLabel(cbEnable.Text, "OPT_ENABLE")
cbEnable:SetScript("OnClick", function(selfBtn)
CritTracker.db.enabled = selfBtn:GetChecked()
end)
self.ui_cbEnable = cbEnable
curY = curY - 28
-- Checkbox: Enable Chat
local cbChat = CreateFrame("CheckButton", "CritTrackerCB_Chat", panel, "UICheckButtonTemplate")
cbChat:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
cbChat.Text:SetText(self.L["OPT_CHAT"])
RegisterLabel(cbChat.Text, "OPT_CHAT")
cbChat:SetScript("OnClick", function(selfBtn)
CritTracker.db.enableChat = selfBtn:GetChecked()
end)
self.ui_cbChat = cbChat
curY = curY - 28
-- Checkbox: Enable Sound
local cbSound = CreateFrame("CheckButton", "CritTrackerCB_Sound", panel, "UICheckButtonTemplate")
cbSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
cbSound.Text:SetText(self.L["OPT_SOUND"])
RegisterLabel(cbSound.Text, "OPT_SOUND")
cbSound:SetScript("OnClick", function(selfBtn)
CritTracker.db.enableSound = selfBtn:GetChecked()
end)
self.ui_cbSound = cbSound
curY = curY - 28
-- Checkbox: Enable Low Crit
local cbLow = CreateFrame("CheckButton", "CritTrackerCB_Low", panel, "UICheckButtonTemplate")
cbLow:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
cbLow.Text:SetText(self.L["OPT_LOW"])
RegisterLabel(cbLow.Text, "OPT_LOW")
cbLow:SetScript("OnClick", function(selfBtn)
CritTracker.db.enableLowCrit = selfBtn:GetChecked()
end)
self.ui_cbLow = cbLow
curY = curY - 36
-- Section: Cooldown & Threshold
local secCooldown = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal")
secCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
RegisterLabel(secCooldown, "OPT_SECTION_COOLDOWN_THRESHOLD")
curY = curY - 22
-- Slider: Cooldown (1 - 30 seconds)
local lblCooldown = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 4, curY)
self.ui_lblCooldown = lblCooldown
curY = curY - 16
local sliderCooldown = CreateFrame("Slider", "CritTrackerSlider_Cooldown", panel, "OptionsSliderTemplate")
sliderCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 6, curY)
sliderCooldown:SetWidth(270)
sliderCooldown:SetMinMaxValues(1, 30)
sliderCooldown:SetValueStep(1)
sliderCooldown:SetObeyStepOnDrag(true)
_G[sliderCooldown:GetName() .. "Low"]:SetText("1s")
_G[sliderCooldown:GetName() .. "High"]:SetText("30s")
_G[sliderCooldown:GetName() .. "Text"]:SetText("")
sliderCooldown:SetScript("OnValueChanged", function(selfSlider, val)
val = math.floor(val + 0.5)
CritTracker.db.cooldown = val
lblCooldown:SetText(string.format(CritTracker.L["OPT_COOLDOWN_TITLE"], val))
end)
self.ui_sliderCooldown = sliderCooldown
curY = curY - 42
-- Slider: Low Threshold (5% - 100%, default 30%)
local lblThreshold = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblThreshold:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 4, curY)
self.ui_lblThreshold = lblThreshold
curY = curY - 16
local sliderThreshold = CreateFrame("Slider", "CritTrackerSlider_Threshold", panel, "OptionsSliderTemplate")
sliderThreshold:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 6, curY)
sliderThreshold:SetWidth(270)
sliderThreshold:SetMinMaxValues(5, 100)
sliderThreshold:SetValueStep(5)
sliderThreshold:SetObeyStepOnDrag(true)
_G[sliderThreshold:GetName() .. "Low"]:SetText("5%")
_G[sliderThreshold:GetName() .. "High"]:SetText("100%")
_G[sliderThreshold:GetName() .. "Text"]:SetText("")
sliderThreshold:SetScript("OnValueChanged", function(selfSlider, val)
val = math.floor(val + 0.5)
CritTracker.db.lowThreshold = val
lblThreshold:SetText(string.format(CritTracker.L["OPT_THRESHOLD_TITLE"], val))
end)
self.ui_sliderThreshold = sliderThreshold
curY = curY - 44
-- Section: Character Records
local secRecords = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal")
secRecords:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
RegisterLabel(secRecords, "OPT_SECTION_RECORDS")
curY = curY - 20
-- Box for Records
local recordBox = CreateFrame("Frame", nil, panel)
recordBox:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY)
recordBox:SetSize(280, 80)
local bgTex = recordBox:CreateTexture(nil, "BACKGROUND")
bgTex:SetAllPoints()
bgTex:SetColorTexture(0, 0, 0, 0.35)
local lblCharName = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblCharName:SetPoint("TOPLEFT", recordBox, "TOPLEFT", 8, -8)
lblCharName:SetText("|cff00e5ff" .. CritTracker:GetCharKey() .. "|r")
local lblDmg = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblDmg:SetPoint("TOPLEFT", lblCharName, "BOTTOMLEFT", 0, -6)
self.ui_lblDamageRecord = lblDmg
local lblHeal = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblHeal:SetPoint("TOPLEFT", lblDmg, "BOTTOMLEFT", 0, -4)
self.ui_lblHealRecord = lblHeal
-- Reset Button
local resetBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
resetBtn:SetPoint("TOPLEFT", recordBox, "BOTTOMLEFT", 0, -8)
resetBtn:SetSize(160, 24)
resetBtn:SetText(self.L["OPT_RESET_BUTTON"])
RegisterLabel(resetBtn:GetFontString(), "OPT_RESET_BUTTON")
resetBtn:SetScript("OnClick", function()
CritTracker:ResetRecords()
end)
-------------------------------------------------------------------------
-- RIGHT COLUMN: Audio Selection, Test Buttons, Language
-------------------------------------------------------------------------
local colRightX = 330
local curRightY = -70
-- Section: Audio
local secAudio = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal")
secAudio:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(secAudio, "OPT_SECTION_AUDIO")
curRightY = curRightY - 24
-- Damage Sound Row
local lblDmgSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblDmgSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(lblDmgSound, "OPT_DAMAGE_SOUND")
curRightY = curRightY - 18
self.ui_damageButtons = {}
local dmgOptions = { "crit1", "crit2", "crit3", "crit4", "none" }
local btnX = colRightX
for _, opt in ipairs(dmgOptions) do
local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY)
btn:SetSize((opt == "none") and 42 or 46, 22)
btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt
btn:SetText(btn.displayLabel)
btn:SetScript("OnClick", function()
CritTracker.db.damageSound = opt
UpdateSegmentedButtons(CritTracker.ui_damageButtons, opt)
end)
self.ui_damageButtons[opt] = btn
btnX = btnX + ((opt == "none") and 44 or 48)
end
local testDmgBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
testDmgBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY)
testDmgBtn:SetSize(52, 22)
testDmgBtn:SetText("►")
testDmgBtn:SetScript("OnClick", function()
CritTracker:PlaySound(CritTracker.db.damageSound or "crit1")
end)
curRightY = curRightY - 34
-- Heal Sound Row
local lblHealSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblHealSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(lblHealSound, "OPT_HEAL_SOUND")
curRightY = curRightY - 18
self.ui_healButtons = {}
local healOptions = { "heal1", "heal2", "heal3", "heal4", "none" }
btnX = colRightX
for _, opt in ipairs(healOptions) do
local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY)
btn:SetSize((opt == "none") and 42 or 46, 22)
btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt
btn:SetText(btn.displayLabel)
btn:SetScript("OnClick", function()
CritTracker.db.healSound = opt
UpdateSegmentedButtons(CritTracker.ui_healButtons, opt)
end)
self.ui_healButtons[opt] = btn
btnX = btnX + ((opt == "none") and 44 or 48)
end
local testHealBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
testHealBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY)
testHealBtn:SetSize(52, 22)
testHealBtn:SetText("►")
testHealBtn:SetScript("OnClick", function()
CritTracker:PlaySound(CritTracker.db.healSound or "heal1")
end)
curRightY = curRightY - 34
-- Low Sound Row
local lblLowSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblLowSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(lblLowSound, "OPT_LOW_SOUND")
curRightY = curRightY - 18
self.ui_lowButtons = {}
local lowOptions = { "low", "none" }
btnX = colRightX
for _, opt in ipairs(lowOptions) do
local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY)
btn:SetSize((opt == "none") and 42 or 52, 22)
btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt
btn:SetText(btn.displayLabel)
btn:SetScript("OnClick", function()
CritTracker.db.lowSound = opt
UpdateSegmentedButtons(CritTracker.ui_lowButtons, opt)
end)
self.ui_lowButtons[opt] = btn
btnX = btnX + ((opt == "none") and 44 or 54)
end
local testLowBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
testLowBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY)
testLowBtn:SetSize(52, 22)
testLowBtn:SetText("►")
testLowBtn:SetScript("OnClick", function()
CritTracker:PlaySound(CritTracker.db.lowSound or "low")
end)
curRightY = curRightY - 36
-- Sound Channel Row
local lblChannel = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblChannel:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(lblChannel, "OPT_SOUND_CHANNEL")
curRightY = curRightY - 18
self.ui_channelButtons = {}
local chanOptions = { "Master", "Dialog", "SFX" }
btnX = colRightX
for _, opt in ipairs(chanOptions) do
local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY)
btn:SetSize(62, 22)
btn.displayLabel = opt
btn:SetText(opt)
btn:SetScript("OnClick", function()
CritTracker.db.soundChannel = opt
UpdateSegmentedButtons(CritTracker.ui_channelButtons, opt)
end)
self.ui_channelButtons[opt] = btn
btnX = btnX + 66
end
curRightY = curRightY - 38
-- Section: Language
local secLang = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal")
secLang:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(secLang, "OPT_SECTION_LANGUAGE")
curRightY = curRightY - 22
local lblLang = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
lblLang:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY)
RegisterLabel(lblLang, "OPT_LANGUAGE")
curRightY = curRightY - 18
self.ui_langButtons = {}
local langOptions = {
{ id = "auto", label = "OPT_LANG_AUTO", width = 85 },
{ id = "German", label = "OPT_LANG_DE", width = 85 },
{ id = "English", label = "OPT_LANG_EN", width = 85 },
}
btnX = colRightX
for _, item in ipairs(langOptions) do
local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY)
btn:SetSize(item.width, 22)
btn.displayLabel = self.L[item.label] or item.id
btn:SetText(btn.displayLabel)
btn:SetScript("OnClick", function()
CritTracker.db.language = item.id
CritTracker:UpdateOptionsDisplay()
end)
self.ui_langButtons[item.id] = btn
btnX = btnX + item.width + 4
end
-- Initial display refresh
panel:SetScript("OnShow", function()
CritTracker:UpdateOptionsDisplay()
end)
-- Register with Modern Settings API if available
if Settings and Settings.RegisterCanvasLayoutCategory then
local category = Settings.RegisterCanvasLayoutCategory(panel, "CritTracker")
Settings.RegisterAddOnCategory(category)
CritTracker.settingsCategory = category
elseif InterfaceOptions_AddCategory then
InterfaceOptions_AddCategory(panel)
end
end
-- Open Options Method
function CritTracker:OpenOptions()
if InCombatLockdown and InCombatLockdown() then
self:Print("|cffff4444[CritTracker]|r Einstellungen können während des Kampfes nicht geöffnet werden.")
return
end
local opened = false
if Settings and Settings.OpenToCategory and self.settingsCategory then
local ok = pcall(function()
Settings.OpenToCategory(self.settingsCategory:GetID())
end)
if ok then
opened = true
end
end
if not opened and InterfaceOptionsFrame_OpenToCategory and panel then
local ok = pcall(function()
InterfaceOptionsFrame_OpenToCategory(panel)
end)
if ok then
opened = true
end
end
-- Fallback to Standalone Movable Dialog Window
if not opened and panel then
if panel:GetParent() ~= UIParent then
panel:SetParent(UIParent)
end
if not panel.hasBackdrop and BackdropTemplateMixin then
Mixin(panel, BackdropTemplateMixin)
panel:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true, tileSize = 32, edgeSize = 32,
insets = { left = 8, right = 8, top = 8, bottom = 8 },
})
panel.hasBackdrop = true
end
panel:ClearAllPoints()
panel:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
panel:Show()
self:UpdateOptionsDisplay()
end
end