--## Version: 12.0.1-v3.2 ## SavedVariables: DCP_Saved ## SavedVariablesPerCharacter: DCP_SavedPerCharacter

local fadeInTime, fadeOutTime, maxAlpha, animScale, iconSize, holdTime, showSpellName
local watching, activeSpells, activeItems = {}, {}, {}
local secretItemsTracked = {}
local animFrames, shadowFrames = {}, {}
local trackerFramesPool = {}
local activeTrackers = {}
local GetTime = GetTime

-- =====================================================================
-- 🌍 多國語言 (Localization) 字典表 (完整 9 國語言支援)
-- =====================================================================
local L = {}

L.ENABLE_DCP = "Enable Central Flash"
L.ENABLE_TRACKER = "Enable Priority Tracking"
L.ICON_SIZE = "Flash Icon Size"
L.FADE_IN_TIME = "Fade In Time"
L.FADE_OUT_TIME = "Fade Out Time"
L.MAX_OPACITY = "Max Opacity"
L.HOLD_TIME = "Hold Time"
L.ANIM_SCALE = "Animation Scale"
L.BTN_CLOSE = "Close"
L.BTN_TEST = "Test"
L.BTN_UNLOCK = "Unlock"
L.BTN_LOCK = "Lock"
L.BTN_DEFAULTS = "Defaults"
L.PET_COLOR = "Pet CD Overlay:"
L.PET_COLOR_TIP = "Note: Use white if you don't want any overlay for pet cooldowns"
L.SHOW_SPELL_NAME = "Show Flash Name:"
L.FONT_SIZE = "Flash Font Size"
L.FILTER_MODE = "Central Flash Filter:"
L.BLACKLIST = "Blacklist"
L.WHITELIST = "Whitelist"
L.EDIT_BLACK = "Edit Blacklist"
L.EDIT_WHITE = "Edit Whitelist"
L.EDIT_PRIO = "Edit Priority"
L.FILTER_INPUT_TIP = "Enter Spell/Item ID or Name, or drag it here"
L.ADD = "Add"
L.TEST_SPELL_1 = "Test Spell 1"
L.TEST_SPELL_2 = "Test Spell 2"
L.TEST_SPELL_3 = "Test Spell 3"
L.TRACKER_SIZE = "Tracker Icon Size"
L.TRACKER_ANCHOR = "Priority Anchor"
L.TRACKER_SHOW_NAME = "Show Priority Name:"
L.TRACKER_NAME_POS = "Name Pos (1=L, 2=C, 3=R)"
L.TRACKER_NAME_FONT_SIZE = "Priority Font Size"
L.PRIORITY_LIST_TITLE = "Priority Tracking"
L.BLACKLIST_TITLE = "Blacklist"
L.WHITELIST_TITLE = "Whitelist"

local locale = GetLocale()

if locale == "zhTW" then
    L.ENABLE_DCP = "啟用中央快閃提示 (原始功能)"
    L.ENABLE_TRACKER = "啟用重點技能追蹤 (排隊功能)"
    L.ICON_SIZE = "閃爍圖示大小"
    L.FADE_IN_TIME = "淡入時間"
    L.FADE_OUT_TIME = "淡出時間"
    L.MAX_OPACITY = "最大不透明度"
    L.HOLD_TIME = "停留時間"
    L.ANIM_SCALE = "動畫縮放"
    L.BTN_CLOSE = "關閉"
    L.BTN_TEST = "測試"
    L.BTN_UNLOCK = "解鎖"
    L.BTN_LOCK = "鎖定"
    L.BTN_DEFAULTS = "預設值"
    L.PET_COLOR = "寵物冷卻遮罩顏色:"
    L.PET_COLOR_TIP = "注意：如果不想要寵物冷卻有顏色覆蓋，請設定為白色"
    L.SHOW_SPELL_NAME = "顯示閃爍法術名稱:"
    L.FONT_SIZE = "閃爍名稱大小"
    L.FILTER_MODE = "中央快閃過濾模式:"
    L.BLACKLIST = "黑名單"
    L.WHITELIST = "白名單"
    L.EDIT_BLACK = "編輯黑名單"
    L.EDIT_WHITE = "編輯白名單"
    L.EDIT_PRIO = "編輯重點追蹤"
    L.FILTER_INPUT_TIP = "輸入法術/物品 ID 或名稱，或直接從法術書/裝備欄拖曳至此"
    L.ADD = "新增"
    L.TEST_SPELL_1 = "測試技能 1"
    L.TEST_SPELL_2 = "測試技能 2"
    L.TEST_SPELL_3 = "重點測試"
    L.TRACKER_SIZE = "重點圖示大小"
    L.TRACKER_ANCHOR = "重點追蹤定位區"
    L.TRACKER_SHOW_NAME = "顯示重點技能名稱:"
    L.TRACKER_NAME_POS = "名稱位置 (1=左, 2=中, 3=右)"
    L.TRACKER_NAME_FONT_SIZE = "重點文字大小"
    L.PRIORITY_LIST_TITLE = "重點技能追蹤 (依專精)"
    L.BLACKLIST_TITLE = "黑名單 (全專精共用)"
    L.WHITELIST_TITLE = "白名單 (依專精)"

elseif locale == "zhCN" then
    L.ENABLE_DCP = "启用中央闪烁提示 (原始功能)"
    L.ENABLE_TRACKER = "启用重点技能追踪 (排队功能)"
    L.ICON_SIZE = "闪烁图标大小"
    L.FADE_IN_TIME = "淡入时间"
    L.FADE_OUT_TIME = "淡出时间"
    L.MAX_OPACITY = "最大不透明度"
    L.HOLD_TIME = "停留时间"
    L.ANIM_SCALE = "动画缩放"
    L.BTN_CLOSE = "关闭"
    L.BTN_TEST = "测试"
    L.BTN_UNLOCK = "解锁"
    L.BTN_LOCK = "锁定"
    L.BTN_DEFAULTS = "默认值"
    L.PET_COLOR = "宠物冷却遮罩颜色:"
    L.PET_COLOR_TIP = "注意：如果不想要宠物冷却有颜色覆盖，请设置为白色"
    L.SHOW_SPELL_NAME = "显示闪烁法术名称:"
    L.FONT_SIZE = "闪烁名称大小"
    L.FILTER_MODE = "中央闪烁过滤模式:"
    L.BLACKLIST = "黑名单"
    L.WHITELIST = "白名单"
    L.EDIT_BLACK = "编辑黑名单"
    L.EDIT_WHITE = "编辑白名单"
    L.EDIT_PRIO = "编辑重点追踪"
    L.FILTER_INPUT_TIP = "输入法术/物品 ID 或名称，或直接从法术书/装备栏拖拽至此"
    L.ADD = "新增"
    L.TEST_SPELL_1 = "测试技能 1"
    L.TEST_SPELL_2 = "测试技能 2"
    L.TEST_SPELL_3 = "重点测试"
    L.TRACKER_SIZE = "重点图标大小"
    L.TRACKER_ANCHOR = "重点追踪定位区"
    L.TRACKER_SHOW_NAME = "显示重点技能名称:"
    L.TRACKER_NAME_POS = "名称位置 (1=左, 2=中, 3=右)"
    L.TRACKER_NAME_FONT_SIZE = "重点文字大小"
    L.PRIORITY_LIST_TITLE = "重点技能追踪 (依专精)"
    L.BLACKLIST_TITLE = "黑名单 (全专精共用)"
    L.WHITELIST_TITLE = "白名单 (依专精)"

elseif locale == "koKR" then
    L.ENABLE_DCP = "중앙 깜박임 활성화"
    L.ENABLE_TRACKER = "우선순위 추적 활성화"
    L.ICON_SIZE = "깜박임 아이콘 크기"
    L.FADE_IN_TIME = "페이드 인 시간"
    L.FADE_OUT_TIME = "페이드 아웃 시간"
    L.MAX_OPACITY = "최대 불투명도"
    L.HOLD_TIME = "유지 시간"
    L.ANIM_SCALE = "애니메이션 비율"
    L.BTN_CLOSE = "닫기"
    L.BTN_TEST = "테스트"
    L.BTN_UNLOCK = "잠금 해제"
    L.BTN_LOCK = "잠금"
    L.BTN_DEFAULTS = "기본값"
    L.PET_COLOR = "소환수 쿨다운 색상:"
    L.PET_COLOR_TIP = "참고: 소환수 쿨다운에 색상 오버레이를 원하지 않으면 흰색을 사용하세요"
    L.SHOW_SPELL_NAME = "깜박임 이름 표시:"
    L.FONT_SIZE = "깜박임 글꼴 크기"
    L.FILTER_MODE = "중앙 깜박임 필터:"
    L.BLACKLIST = "블랙리스트"
    L.WHITELIST = "화이트리스트"
    L.EDIT_BLACK = "블랙리스트 편집"
    L.EDIT_WHITE = "화이트리스트 편집"
    L.EDIT_PRIO = "우선순위 편집"
    L.FILTER_INPUT_TIP = "주문/아이템 ID 또는 이름을 입력하거나 여기로 드래그하세요"
    L.ADD = "추가"
    L.TEST_SPELL_1 = "테스트 주문 1"
    L.TEST_SPELL_2 = "테스트 주문 2"
    L.TEST_SPELL_3 = "우선순위 테스트"
    L.TRACKER_SIZE = "우선순위 아이콘 크기"
    L.TRACKER_ANCHOR = "우선순위 앵커"
    L.TRACKER_SHOW_NAME = "우선순위 이름 표시:"
    L.TRACKER_NAME_POS = "이름 위치 (1=왼쪽, 2=가운데, 3=오른쪽)"
    L.TRACKER_NAME_FONT_SIZE = "우선순위 글꼴 크기"
    L.PRIORITY_LIST_TITLE = "우선순위 추적"
    L.BLACKLIST_TITLE = "블랙리스트"
    L.WHITELIST_TITLE = "화이트리스트"

elseif locale == "ruRU" then
    L.ENABLE_DCP = "Вкл. центральное мигание"
    L.ENABLE_TRACKER = "Вкл. приоритетное отслеживание"
    L.ICON_SIZE = "Размер значка"
    L.FADE_IN_TIME = "Время появления"
    L.FADE_OUT_TIME = "Время исчезновения"
    L.MAX_OPACITY = "Макс. непрозрачность"
    L.HOLD_TIME = "Время удержания"
    L.ANIM_SCALE = "Масштаб анимации"
    L.BTN_CLOSE = "Закрыть"
    L.BTN_TEST = "Тест"
    L.BTN_UNLOCK = "Разблокировать"
    L.BTN_LOCK = "Заблокировать"
    L.BTN_DEFAULTS = "По умолчанию"
    L.PET_COLOR = "Цвет питомца:"
    L.PET_COLOR_TIP = "Примечание: используйте белый, если не хотите наложения для питомца"
    L.SHOW_SPELL_NAME = "Показывать название:"
    L.FONT_SIZE = "Размер шрифта"
    L.FILTER_MODE = "Фильтр центра:"
    L.BLACKLIST = "Черный список"
    L.WHITELIST = "Белый список"
    L.EDIT_BLACK = "Ред. черный список"
    L.EDIT_WHITE = "Ред. белый список"
    L.EDIT_PRIO = "Ред. приоритет"
    L.FILTER_INPUT_TIP = "Введите ID/Название заклинания или предмета, или перетащите сюда"
    L.ADD = "Добавить"
    L.TEST_SPELL_1 = "Тестовое закл. 1"
    L.TEST_SPELL_2 = "Тестовое закл. 2"
    L.TEST_SPELL_3 = "Тест приоритета"
    L.TRACKER_SIZE = "Размер значка приоритета"
    L.TRACKER_ANCHOR = "Якорь приоритета"
    L.TRACKER_SHOW_NAME = "Имя приоритета:"
    L.TRACKER_NAME_POS = "Позиция (1=Л, 2=Ц, 3=П)"
    L.TRACKER_NAME_FONT_SIZE = "Шрифт приоритета"
    L.PRIORITY_LIST_TITLE = "Приоритетное отслеживание"
    L.BLACKLIST_TITLE = "Черный список"
    L.WHITELIST_TITLE = "Белый список"

elseif locale == "deDE" then
    L.ENABLE_DCP = "Zentrales Blinken aktivieren"
    L.ENABLE_TRACKER = "Prioritätsverfolgung aktivieren"
    L.ICON_SIZE = "Symbolgröße"
    L.FADE_IN_TIME = "Einblendzeit"
    L.FADE_OUT_TIME = "Ausblendzeit"
    L.MAX_OPACITY = "Max. Deckkraft"
    L.HOLD_TIME = "Haltezeit"
    L.ANIM_SCALE = "Animationsskalierung"
    L.BTN_CLOSE = "Schließen"
    L.BTN_TEST = "Test"
    L.BTN_UNLOCK = "Entsperren"
    L.BTN_LOCK = "Sperren"
    L.BTN_DEFAULTS = "Standard"
    L.PET_COLOR = "Begleiterfarbe:"
    L.PET_COLOR_TIP = "Hinweis: Weiß verwenden, um das Begleiter-Overlay zu deaktivieren"
    L.SHOW_SPELL_NAME = "Zaubername anzeigen:"
    L.FONT_SIZE = "Schriftgröße"
    L.FILTER_MODE = "Zentraler Filter:"
    L.BLACKLIST = "Blacklist"
    L.WHITELIST = "Whitelist"
    L.EDIT_BLACK = "Blacklist bearbeiten"
    L.EDIT_WHITE = "Whitelist bearbeiten"
    L.EDIT_PRIO = "Priorität bearbeiten"
    L.FILTER_INPUT_TIP = "Zauber-/Gegenstands-ID oder Name eingeben oder hierher ziehen"
    L.ADD = "Hinzufügen"
    L.TEST_SPELL_1 = "Testzauber 1"
    L.TEST_SPELL_2 = "Testzauber 2"
    L.TEST_SPELL_3 = "Prioritätstest"
    L.TRACKER_SIZE = "Prioritätssymbolgröße"
    L.TRACKER_ANCHOR = "Prioritätsanker"
    L.TRACKER_SHOW_NAME = "Prioritätsnamen anzeigen:"
    L.TRACKER_NAME_POS = "Namensposition (1=L, 2=M, 3=R)"
    L.TRACKER_NAME_FONT_SIZE = "Prioritäts-Schriftgröße"
    L.PRIORITY_LIST_TITLE = "Prioritätsverfolgung"
    L.BLACKLIST_TITLE = "Blacklist"
    L.WHITELIST_TITLE = "Whitelist"

elseif locale == "frFR" then
    L.ENABLE_DCP = "Activer le clignotement central"
    L.ENABLE_TRACKER = "Activer le suivi prioritaire"
    L.ICON_SIZE = "Taille de l'icône"
    L.FADE_IN_TIME = "Temps d'apparition"
    L.FADE_OUT_TIME = "Temps de disparition"
    L.MAX_OPACITY = "Opacité max"
    L.HOLD_TIME = "Temps de maintien"
    L.ANIM_SCALE = "Échelle d'animation"
    L.BTN_CLOSE = "Fermer"
    L.BTN_TEST = "Test"
    L.BTN_UNLOCK = "Déverrouiller"
    L.BTN_LOCK = "Verrouiller"
    L.BTN_DEFAULTS = "Défaut"
    L.PET_COLOR = "Couleur du familier:"
    L.PET_COLOR_TIP = "Note: utilisez blanc si vous ne voulez pas de couleur de familier"
    L.SHOW_SPELL_NAME = "Afficher le nom:"
    L.FONT_SIZE = "Taille de la police"
    L.FILTER_MODE = "Filtre central:"
    L.BLACKLIST = "Liste noire"
    L.WHITELIST = "Liste blanche"
    L.EDIT_BLACK = "Modifier Liste noire"
    L.EDIT_WHITE = "Modifier Liste blanche"
    L.EDIT_PRIO = "Modifier Priorité"
    L.FILTER_INPUT_TIP = "Entrez l'ID/Nom du sort ou de l'objet, ou glissez-le ici"
    L.ADD = "Ajouter"
    L.TEST_SPELL_1 = "Sort de test 1"
    L.TEST_SPELL_2 = "Sort de test 2"
    L.TEST_SPELL_3 = "Test prioritaire"
    L.TRACKER_SIZE = "Taille d'icône prioritaire"
    L.TRACKER_ANCHOR = "Ancre prioritaire"
    L.TRACKER_SHOW_NAME = "Afficher nom prioritaire:"
    L.TRACKER_NAME_POS = "Position du nom (1=G, 2=C, 3=D)"
    L.TRACKER_NAME_FONT_SIZE = "Taille police prioritaire"
    L.PRIORITY_LIST_TITLE = "Suivi prioritaire"
    L.BLACKLIST_TITLE = "Liste noire"
    L.WHITELIST_TITLE = "Liste blanche"

elseif locale == "esES" or locale == "esMX" then
    L.ENABLE_DCP = "Activar destello central"
    L.ENABLE_TRACKER = "Activar seguimiento prioritario"
    L.ICON_SIZE = "Tamaño del icono"
    L.FADE_IN_TIME = "Tiempo de aparición"
    L.FADE_OUT_TIME = "Tiempo de desaparición"
    L.MAX_OPACITY = "Opacidad máxima"
    L.HOLD_TIME = "Tiempo de espera"
    L.ANIM_SCALE = "Escala de animación"
    L.BTN_CLOSE = "Cerrar"
    L.BTN_TEST = "Prueba"
    L.BTN_UNLOCK = "Desbloquear"
    L.BTN_LOCK = "Bloquear"
    L.BTN_DEFAULTS = "Por defecto"
    L.PET_COLOR = "Color de mascota:"
    L.PET_COLOR_TIP = "Nota: usa blanco si no quieres superposición para mascotas"
    L.SHOW_SPELL_NAME = "Mostrar nombre:"
    L.FONT_SIZE = "Tamaño de fuente"
    L.FILTER_MODE = "Filtro central:"
    L.BLACKLIST = "Lista negra"
    L.WHITELIST = "Lista blanca"
    L.EDIT_BLACK = "Editar Lista negra"
    L.EDIT_WHITE = "Editar Lista blanca"
    L.EDIT_PRIO = "Editar Prioridad"
    L.FILTER_INPUT_TIP = "Introduce el ID o nombre, o arrastra aquí"
    L.ADD = "Añadir"
    L.TEST_SPELL_1 = "Hechizo de prueba 1"
    L.TEST_SPELL_2 = "Hechizo de prueba 2"
    L.TEST_SPELL_3 = "Prueba prioritaria"
    L.TRACKER_SIZE = "Tamaño de icono prioritario"
    L.TRACKER_ANCHOR = "Ancla prioritaria"
    L.TRACKER_SHOW_NAME = "Mostrar nombre prioritario:"
    L.TRACKER_NAME_POS = "Posición nombre (1=Izq, 2=Cen, 3=Der)"
    L.TRACKER_NAME_FONT_SIZE = "Fuente nombre prioritario"
    L.PRIORITY_LIST_TITLE = "Seguimiento prioritario"
    L.BLACKLIST_TITLE = "Lista negra"
    L.WHITELIST_TITLE = "Lista blanca"

elseif locale == "ptBR" then
    L.ENABLE_DCP = "Ativar Piscar Central"
    L.ENABLE_TRACKER = "Ativar Rastreamento Prioritário"
    L.ICON_SIZE = "Tamanho do Ícone"
    L.FADE_IN_TIME = "Tempo de Surgimento"
    L.FADE_OUT_TIME = "Tempo de Desaparecimento"
    L.MAX_OPACITY = "Opacidade Máxima"
    L.HOLD_TIME = "Tempo de Retenção"
    L.ANIM_SCALE = "Escala da Animação"
    L.BTN_CLOSE = "Fechar"
    L.BTN_TEST = "Teste"
    L.BTN_UNLOCK = "Desbloquear"
    L.BTN_LOCK = "Bloquear"
    L.BTN_DEFAULTS = "Padrões"
    L.PET_COLOR = "Cor do ajudante:"
    L.PET_COLOR_TIP = "Nota: use branco se não quiser sobreposição para ajudantes"
    L.SHOW_SPELL_NAME = "Mostrar nome:"
    L.FONT_SIZE = "Tamanho da fonte"
    L.FILTER_MODE = "Filtro Central:"
    L.BLACKLIST = "Lista Negra"
    L.WHITELIST = "Lista Branca"
    L.EDIT_BLACK = "Editar Lista Negra"
    L.EDIT_WHITE = "Editar Lista Branca"
    L.EDIT_PRIO = "Editar Prioridade"
    L.FILTER_INPUT_TIP = "Insira o ID ou Nome, ou arraste aqui"
    L.ADD = "Adicionar"
    L.TEST_SPELL_1 = "Feitiço de Teste 1"
    L.TEST_SPELL_2 = "Feitiço de Teste 2"
    L.TEST_SPELL_3 = "Teste Prioritário"
    L.TRACKER_SIZE = "Tamanho do Ícone Prioritário"
    L.TRACKER_ANCHOR = "Âncora Prioritária"
    L.TRACKER_SHOW_NAME = "Mostrar nome prioritário:"
    L.TRACKER_NAME_POS = "Posição Nome (1=Esq, 2=Cen, 3=Dir)"
    L.TRACKER_NAME_FONT_SIZE = "Fonte Nome Prioritário"
    L.PRIORITY_LIST_TITLE = "Rastreamento Prioritário"
    L.BLACKLIST_TITLE = "Lista Negra"
    L.WHITELIST_TITLE = "Lista Branca"

elseif locale == "itIT" then
    L.ENABLE_DCP = "Abilita Lampeggio Centrale"
    L.ENABLE_TRACKER = "Abilita Tracciamento Prioritario"
    L.ICON_SIZE = "Dimensione Icona"
    L.FADE_IN_TIME = "Tempo Comparsa"
    L.FADE_OUT_TIME = "Tempo Scomparsa"
    L.MAX_OPACITY = "Opacità Massima"
    L.HOLD_TIME = "Tempo di Mantenimento"
    L.ANIM_SCALE = "Scala Animazione"
    L.BTN_CLOSE = "Chiudi"
    L.BTN_TEST = "Test"
    L.BTN_UNLOCK = "Sblocca"
    L.BTN_LOCK = "Blocca"
    L.BTN_DEFAULTS = "Predefiniti"
    L.PET_COLOR = "Colore famiglio:"
    L.PET_COLOR_TIP = "Nota: usa bianco se non vuoi sovrapposizioni per i famigli"
    L.SHOW_SPELL_NAME = "Mostra nome:"
    L.FONT_SIZE = "Dimensione carattere"
    L.FILTER_MODE = "Filtro Centrale:"
    L.BLACKLIST = "Lista Nera"
    L.WHITELIST = "Lista Bianca"
    L.EDIT_BLACK = "Modifica Lista Nera"
    L.EDIT_WHITE = "Modifica Lista Bianca"
    L.EDIT_PRIO = "Modifica Priorità"
    L.FILTER_INPUT_TIP = "Inserisci ID o Nome, oppure trascina qui"
    L.ADD = "Aggiungi"
    L.TEST_SPELL_1 = "Incantesimo Test 1"
    L.TEST_SPELL_2 = "Incantesimo Test 2"
    L.TEST_SPELL_3 = "Test Prioritario"
    L.TRACKER_SIZE = "Dimensione Icona Priorità"
    L.TRACKER_ANCHOR = "Ancora Priorità"
    L.TRACKER_SHOW_NAME = "Mostra nome prioritario:"
    L.TRACKER_NAME_POS = "Posizione Nome (1=Sin, 2=Cen, 3=Des)"
    L.TRACKER_NAME_FONT_SIZE = "Carattere Nome Priorità"
    L.PRIORITY_LIST_TITLE = "Tracciamento Prioritario"
    L.BLACKLIST_TITLE = "Lista Nera"
    L.WHITELIST_TITLE = "Lista Bianca"
end
-- =====================================================================

local defaultSettings = {
    enableDCP = true,
    enableTracker = true,
    fadeInTime = 0.3, fadeOutTime = 0.7, maxAlpha = 0.7, animScale = 1.5, iconSize = 75,
    holdTime = 0, petOverlay = {1,1,1}, showSpellName = nil, spellNameFontSize = 14,
    x = 0, y = 0, 
    trackerX = 200, trackerY = 150, 
    trackerIconSize = 40,
    trackerShowName = true, trackerNamePos = 3, trackerNameFontSize = 14,
    anchorFixedV3 = false,
}

local defaultSettingsPerCharacter = { 
    useWhitelist = false,
    blacklistData = {}, 
    whitelistData = {},
    priorityData = {},
    migratedSpecs = false
}

local DCP = CreateFrame("Frame", "DCP_MainAnchor", UIParent)
DCP:SetScript("OnEvent", function(self, event, ...) self[event](self, ...) end)
DCP:SetMovable(true); DCP:RegisterForDrag("LeftButton")
DCP:SetScript("OnDragStart", function(self) self:StartMoving() end)
DCP:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local uiX, uiY = UIParent:GetCenter(); local myX, myY = self:GetCenter()
    DCP_Saved.x = myX - uiX; DCP_Saved.y = myY - uiY
    self:ClearAllPoints(); self:SetPoint("CENTER", UIParent, "CENTER", DCP_Saved.x, DCP_Saved.y)
end)
DCP:SetSize(75, 75); DCP:SetAlpha(0)
local DCPT = DCP:CreateTexture(nil,"BACKGROUND"); DCPT:SetAllPoints(DCP)

local DCPTrackerAnchor = CreateFrame("Frame", "DCP_TrackerAnchor", UIParent)
DCPTrackerAnchor:SetMovable(true); DCPTrackerAnchor:RegisterForDrag("LeftButton")
DCPTrackerAnchor:SetScript("OnDragStart", function(self) self:StartMoving() end)
DCPTrackerAnchor:SetScript("OnDragStop", function(self)
    self:StopMovingOrSizing()
    local uiX, uiY = UIParent:GetCenter(); local myX, myY = self:GetCenter()
    DCP_Saved.trackerX = myX - uiX; DCP_Saved.trackerY = myY - uiY
    self:ClearAllPoints(); self:SetPoint("CENTER", UIParent, "CENTER", DCP_Saved.trackerX, DCP_Saved.trackerY)
end)
DCPTrackerAnchor:SetSize(40, 40); DCPTrackerAnchor:SetAlpha(0)
local DCPTrackerT = DCPTrackerAnchor:CreateTexture(nil,"BACKGROUND"); DCPTrackerT:SetAllPoints(DCPTrackerAnchor)
DCPTrackerT:SetColorTexture(0, 1, 0, 0.5)
local DCPTrackerText = DCPTrackerAnchor:CreateFontString(nil, "OVERLAY", "GameFontNormal")
DCPTrackerText:SetPoint("BOTTOM", DCPTrackerAnchor, "TOP", 0, 2); DCPTrackerText:SetText(L.TRACKER_ANCHOR)

local function GetCurrentSpecID() 
    local spec = GetSpecialization()
    if spec then 
        local id = GetSpecializationInfo(spec)
        if id then return tostring(id) end
    end
    return "default"
end

local function tcount(tab) local n = 0; for _ in pairs(tab) do n = n + 1 end; return n end

local function SafeMergeTable(destination, source) 
    for i, v in pairs(source) do 
        if destination[i] == nil then 
            if type(v) == "table" then destination[i] = {}; SafeMergeTable(destination[i], v) else destination[i] = v end 
        end 
    end 
end

local function InitializeSavedVariables()
    if DCP_Saved == nil then DCP_Saved = {} end
    if DCP_SavedPerCharacter == nil then DCP_SavedPerCharacter = {} end
    SafeMergeTable(DCP_Saved, defaultSettings)
    SafeMergeTable(DCP_SavedPerCharacter, defaultSettingsPerCharacter)
    
    if not DCP_Saved.anchorFixedV3 then
        DCP_Saved.x = 0; DCP_Saved.y = 0; DCP_Saved.trackerX = 200; DCP_Saved.trackerY = 150
        DCP_Saved.anchorFixedV3 = true
    end
    
    if DCP_Saved.enableDCP == nil then DCP_Saved.enableDCP = true end
    if DCP_Saved.enableTracker == nil then DCP_Saved.enableTracker = true end
end

local function RefreshLocals()
    fadeInTime = DCP_Saved.fadeInTime; fadeOutTime = DCP_Saved.fadeOutTime
    maxAlpha = DCP_Saved.maxAlpha; animScale = DCP_Saved.animScale
    iconSize = DCP_Saved.iconSize; holdTime = DCP_Saved.holdTime; showSpellName = DCP_Saved.showSpellName
    
    for _, f in ipairs(animFrames) do if f.text then f.text:SetFont(STANDARD_TEXT_FONT, DCP_Saved.spellNameFontSize, "OUTLINE") end end
    
    for _, f in ipairs(trackerFramesPool) do
        if f.text then
            if DCP_Saved.trackerShowName then
                f.text:SetFont(STANDARD_TEXT_FONT, DCP_Saved.trackerNameFontSize, "OUTLINE"); f.text:ClearAllPoints()
                if DCP_Saved.trackerNamePos == 1 then f.text:SetPoint("RIGHT", f, "LEFT", -5, 0)
                elseif DCP_Saved.trackerNamePos == 3 then f.text:SetPoint("LEFT", f, "RIGHT", 5, 0)
                else f.text:SetPoint("CENTER", f, "CENTER", 0, 0) end
                f.text:Show()
            else f.text:Hide() end
        end
    end
    DCPTrackerAnchor:SetSize(DCP_Saved.trackerIconSize, DCP_Saved.trackerIconSize)
end

local function GetTargetListByMode(mode)
    local spec = GetCurrentSpecID()
    if mode == 3 then
        if type(DCP_SavedPerCharacter.priorityData[spec]) ~= "table" then DCP_SavedPerCharacter.priorityData[spec] = {} end
        return DCP_SavedPerCharacter.priorityData[spec]
    elseif mode == 2 then
        if type(DCP_SavedPerCharacter.whitelistData[spec]) ~= "table" then DCP_SavedPerCharacter.whitelistData[spec] = {} end
        return DCP_SavedPerCharacter.whitelistData[spec]
    else
        if type(DCP_SavedPerCharacter.blacklistData) ~= "table" then DCP_SavedPerCharacter.blacklistData = {} end
        return DCP_SavedPerCharacter.blacklistData
    end
end

local function IsIgnored(name, id)
    if not name then return false end
    local isWhiteList = DCP_SavedPerCharacter.useWhitelist
    local spec = GetCurrentSpecID()
    local list = isWhiteList and (DCP_SavedPerCharacter.whitelistData[spec] or {}) or (DCP_SavedPerCharacter.blacklistData or {})
    local found = false
    if id and list[tostring(id)] and list[tostring(id)].enabled then found = true end
    if not found and list[name] and list[name].enabled then found = true end
    if isWhiteList then return not found else return found end
end

local function IsPriority(name, id)
    if not name then return false end
    local spec = GetCurrentSpecID()
    local list = DCP_SavedPerCharacter.priorityData[spec] or {}
    if id and list[tostring(id)] and list[tostring(id)].enabled then return true end
    if list[name] and list[name].enabled then return true end
    return false
end

local function GetPriorityOrder(name, id)
    local spec = GetCurrentSpecID()
    local list = DCP_SavedPerCharacter.priorityData[spec] or {}
    if id and list[tostring(id)] then return list[tostring(id)].order or 999 end
    if name and list[name] then return list[name].order or 999 end
    return 999
end

local function GetSafeSpellCooldown(spellID)
    local chargeInfo = C_Spell.GetSpellCharges(spellID)
    if chargeInfo then
        local isSafe = pcall(function() local _ = chargeInfo.maxCharges + 0 end)
        if isSafe and chargeInfo.maxCharges > 1 then
            if chargeInfo.currentCharges > 0 then
                return "NONE", 0, 0
            else
                return "NORMAL", chargeInfo.cooldownStartTime, chargeInfo.cooldownDuration
            end
        end
    end

    local cdInfo = C_Spell.GetSpellCooldown(spellID)
    if not cdInfo then return "NONE", 0, 0 end
    local ok = pcall(function() local _ = cdInfo.duration + 0 end)
    if not ok then return "SECRET", cdInfo.startTime, cdInfo.duration end
    return "NORMAL", cdInfo.startTime, cdInfo.duration
end

local function GetSafeItemCooldown(itemID)
    local s, d = C_Container.GetItemCooldown(itemID)
    if not s or not d then return "NONE", 0, 0 end
    local ok = pcall(function() local _ = d + 0 end)
    if not ok then return "SECRET", s, d end
    return "NORMAL", s, d
end

local function GetGCD()
    local dur = 1.5; pcall(function() local cdInfo = C_Spell.GetSpellCooldown(61304); if cdInfo and cdInfo.duration then local d = cdInfo.duration + 0; if d > 0 then dur = d end end end); return dur
end

local function IsTrackerValid(id, ctype)
    if not id then return true end
    local status, s, d
    if ctype == "item" then status, s, d = GetSafeItemCooldown(id) else status, s, d = GetSafeSpellCooldown(id) end
    if status == "NONE" then return true end
    if status == "SECRET" then return true end
    if status == "NORMAL" then
        if d <= GetGCD() + 0.05 then return true else return false end
    end
    return false
end

local TaintBucket = CreateFrame("Frame", nil, UIParent)
TaintBucket:SetSize(1, 1); TaintBucket:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", -10000, -10000); TaintBucket:Show() 

local MAX_SHADOW_FRAMES = 30
for i = 1, MAX_SHADOW_FRAMES do
    local f = CreateFrame("Cooldown", nil, TaintBucket, "CooldownFrameTemplate")
    f:SetAllPoints(); f:SetDrawSwipe(false); f:SetDrawEdge(false); f:SetDrawBling(false); f:SetHideCountdownNumbers(true); f.noCooldownCount = true
    f.isTracking = false
    f:SetScript("OnCooldownDone", function(self)
        if self.isTracking then
            self.isTracking = false
            if self.ctype == "item" then secretItemsTracked[self.id] = nil end
            local tex, p, n, id, ct = self.tex, self.isP, self.nm, self.id, self.ctype
            C_Timer.After(0.05, function() ProcessCooldownReady(id, ct, tex, p, n) end)
        end
    end)
    tinsert(shadowFrames, f)
end

local function AssignShadowFrame(id, ctype, name, texture, isPet, start, duration)
    for _, f in ipairs(shadowFrames) do if f.isTracking and f.id == id and f.ctype == ctype then pcall(function() f:SetCooldown(start, duration) end); return end end
    for _, f in ipairs(shadowFrames) do if not f.isTracking then f.nm = name; f.tex = texture; f.isP = isPet; f.id = id; f.ctype = ctype; f.isTracking = true; pcall(function() f:SetCooldown(start, duration) end); return end end
end

local OnUpdate 
local MAX_ANIM_FRAMES = 5
local SLOT_OFFSETS = {0, 1, -1, 2, -2} 
local slotInUse = {false, false, false, false, false}

for i = 1, MAX_ANIM_FRAMES do
    local f = CreateFrame("Frame", nil, UIParent)
    f:SetFrameStrata("BACKGROUND"); f.texture = f:CreateTexture(nil, "BACKGROUND"); f.texture:SetAllPoints(f)
    f.text = f:CreateFontString(nil, "ARTWORK", "GameFontNormal"); f.text:SetShadowOffset(2, -2)
    f.text:SetPoint("CENTER", f, "CENTER"); f.text:SetTextColor(1, 1, 1); f.inUse = false; f.slotIndex = i; f:Hide()
    tinsert(animFrames, f)
end

for i = 1, 15 do
    local f = CreateFrame("Frame", nil, UIParent)
    f:SetFrameStrata("BACKGROUND"); f:SetSize(40, 40)
    f.texture = f:CreateTexture(nil, "BACKGROUND"); f.texture:SetAllPoints()
    f.text = f:CreateFontString(nil, "OVERLAY", "GameFontNormal"); f.text:SetShadowOffset(2, -2); f.text:SetTextColor(1, 1, 1); f:Hide()
    tinsert(trackerFramesPool, f)
end

local function SpawnPriorityTracker(id, ctype, name, texture, startX, startY)
    local frame = nil
    for _, f in ipairs(trackerFramesPool) do if not f.inUse then frame = f; break end end
    if not frame then return end
    
    frame.inUse = true; frame:SetSize(DCP_Saved.trackerIconSize, DCP_Saved.trackerIconSize)
    frame.texture:SetTexture(texture); frame:SetPoint("CENTER", UIParent, "CENTER", startX, startY)
    
    if DCP_Saved.trackerShowName and name then
        frame.text:SetText(name); frame.text:SetFont(STANDARD_TEXT_FONT, DCP_Saved.trackerNameFontSize, "OUTLINE"); frame.text:ClearAllPoints()
        if DCP_Saved.trackerNamePos == 1 then frame.text:SetPoint("RIGHT", frame, "LEFT", -5, 0)
        elseif DCP_Saved.trackerNamePos == 3 then frame.text:SetPoint("LEFT", frame, "RIGHT", 5, 0)
        else frame.text:SetPoint("CENTER", frame, "CENTER", 0, 0) end
        frame.text:Show()
    else frame.text:Hide() end
    
    frame:SetAlpha(maxAlpha); frame:Show()
    
    local expiration = (id == nil) and (GetTime() + 4) or nil 
    tinsert(activeTrackers, {id = id, name = name, ctype = ctype, frame = frame, state = "FLYING", flyProgress = 0, startX = startX, startY = startY, expiration = expiration})
    if not DCP:GetScript("OnUpdate") then DCP:SetScript("OnUpdate", OnUpdate) end
end

local function GetTrackerTargetPosition(queueIndex)
    return DCP_Saved.trackerX, DCP_Saved.trackerY - ((queueIndex - 1) * (DCP_Saved.trackerIconSize + 5))
end

function TriggerFlash(id, ctype, texture, isPet, name)
    for _, f in ipairs(animFrames) do if f.inUse and f.spellName == name then return end end
    local freeSlot = nil; for i = 1, MAX_ANIM_FRAMES do if not slotInUse[i] then freeSlot = i; break end end; if not freeSlot then return end
    local animFrame = nil; for _, f in ipairs(animFrames) do if not f.inUse then animFrame = f; break end end; if not animFrame then return end

    slotInUse[freeSlot] = true; animFrame.slotIndex = freeSlot; animFrame.inUse = true; animFrame.runtimer = 0
    animFrame.spellID = id; animFrame.ctype = ctype; animFrame.spellName = name; animFrame.rawTexture = texture; animFrame.texture:SetTexture(texture)
    if isPet then animFrame.texture:SetVertexColor(unpack(DCP_Saved.petOverlay)) else animFrame.texture:SetVertexColor(1, 1, 1) end
    if showSpellName and name then animFrame.text:SetText(name) else animFrame.text:SetText("") end
    animFrame:SetAlpha(0); animFrame:Show()
    if not DCP:GetScript("OnUpdate") then DCP:SetScript("OnUpdate", OnUpdate) end
end

function ProcessCooldownReady(id, ctype, texture, isPet, name)
    local isIgnored = IsIgnored(name, id); local isPriority = IsPriority(name, id)
    local willFlash = DCP_Saved.enableDCP and not isIgnored
    
    if willFlash then TriggerFlash(id, ctype, texture, isPet, name) end
    
    if DCP_Saved.enableTracker and isPriority then
        local s = DCP:GetEffectiveScale()
        SpawnPriorityTracker(id, ctype, name, texture, DCP_Saved.x * s, DCP_Saved.y * s)
    end
end

local elapsed = 0
OnUpdate = function(self, update)
    local activeCount = 0
    for _, f in ipairs(animFrames) do
        if f.inUse then
            activeCount = activeCount + 1
            local totalTime = fadeInTime + holdTime + fadeOutTime
            f.runtimer = f.runtimer + update
            if f.runtimer > totalTime then
                f.inUse = false; slotInUse[f.slotIndex] = false; f:Hide()
            else
                local alpha = maxAlpha
                if f.runtimer < fadeInTime then alpha = maxAlpha * (f.runtimer / fadeInTime)
                elseif f.runtimer >= fadeInTime + holdTime then alpha = maxAlpha - (maxAlpha * ((f.runtimer - holdTime - fadeInTime) / fadeOutTime)) end
                f:SetAlpha(alpha); local scale = iconSize + (iconSize * ((animScale - 1) * (f.runtimer / totalTime)))
                f:SetSize(scale, scale)
                local offsetX = SLOT_OFFSETS[f.slotIndex] * (iconSize * animScale + 10)
                f:SetPoint("CENTER", DCP, "CENTER", offsetX, 0)
            end
        end
    end

    table.sort(activeTrackers, function(a, b) return GetPriorityOrder(a.name, a.id) < GetPriorityOrder(b.name, b.id) end)

    local i = 1; local queueIndex = 1
    while i <= #activeTrackers do
        local tracker = activeTrackers[i]
        if not tracker.id and tracker.expiration and GetTime() >= tracker.expiration then
            tracker.frame:Hide(); tracker.frame.inUse = false; table.remove(activeTrackers, i)
        elseif not IsTrackerValid(tracker.id, tracker.ctype) then
            tracker.frame:Hide(); tracker.frame.inUse = false; table.remove(activeTrackers, i)
        else
            activeCount = activeCount + 1
            local targetX, targetY = GetTrackerTargetPosition(queueIndex)
            if tracker.state == "FLYING" then
                tracker.flyProgress = tracker.flyProgress + (update / 0.3)
                if tracker.flyProgress >= 1 then tracker.state = "QUEUED"; tracker.frame:SetPoint("CENTER", UIParent, "CENTER", targetX, targetY)
                else tracker.frame:SetPoint("CENTER", UIParent, "CENTER", tracker.startX + (targetX - tracker.startX) * tracker.flyProgress, tracker.startY + (targetY - tracker.startY) * tracker.flyProgress) end
            elseif tracker.state == "QUEUED" then
                tracker.frame:SetPoint("CENTER", UIParent, "CENTER", targetX, targetY)
            end
            queueIndex = queueIndex + 1; i = i + 1
        end
    end

    elapsed = elapsed + update
    if elapsed > 0.1 then
        local now = GetTime(); local currentGCD = GetGCD()
        for slot = 1, 19 do
            local itemID = GetInventoryItemID("player", slot)
            if itemID and not activeItems[itemID] and not secretItemsTracked[itemID] then
                local status, s, d = GetSafeItemCooldown(itemID)
                if status ~= "NONE" then
                    local name = C_Item.GetItemInfo(itemID)
                    if name then
                        if status == "SECRET" then secretItemsTracked[itemID] = true; AssignShadowFrame(itemID, "item", name, C_Item.GetItemIconByID(itemID), false, s, d)
                        elseif status == "NORMAL" and d > currentGCD + 0.05 then activeItems[itemID] = { expiration = s + d, name = name, texture = C_Item.GetItemIconByID(itemID), isPet = false } end
                    end
                end
            end
        end
        for key, data in pairs(watching) do
            if now >= data.time + 0.5 then
                local status, s, d = GetSafeSpellCooldown(data.id)
                if status ~= "NONE" then
                    if data.name and not IsIgnored(data.name, data.id) then
                        if status == "SECRET" then AssignShadowFrame(data.id, "spell", data.name, data.icon, data.isPet, s, d)
                        elseif status == "NORMAL" and d > currentGCD + 0.05 then activeSpells[data.id] = { expiration = s + d, name = data.name, texture = data.icon, isPet = data.isPet } end
                    end
                end
                watching[key] = nil
            end
        end
        for id, v in pairs(activeSpells) do
            if now >= v.expiration then ProcessCooldownReady(id, "spell", v.texture, v.isPet, v.name); activeSpells[id] = nil end
        end
        for id, v in pairs(activeItems) do
            if now >= v.expiration then ProcessCooldownReady(id, "item", v.texture, v.isPet, v.name); activeItems[id] = nil end
        end
        elapsed = 0
    end

    local hasActiveFrames = false
    for _, cf in ipairs(shadowFrames) do if cf.isTracking then hasActiveFrames = true; break end end
    if activeCount == 0 and tcount(watching) == 0 and tcount(activeSpells) == 0 and tcount(activeItems) == 0 and not hasActiveFrames then self:SetScript("OnUpdate", nil) end
end

function DCP:ADDON_LOADED(addon)
    if addon == "OrzUI" then
        InitializeSavedVariables(); RefreshLocals()
        self:SetPoint("CENTER",UIParent,"CENTER",DCP_Saved.x,DCP_Saved.y)
        DCPTrackerAnchor:SetPoint("CENTER",UIParent,"CENTER",DCP_Saved.trackerX,DCP_Saved.trackerY)
        self:UnregisterEvent("ADDON_LOADED")
    end
end
DCP:RegisterEvent("ADDON_LOADED")

function DCP:PLAYER_SPECIALIZATION_CHANGED()
    wipe(activeTrackers); for _, f in ipairs(trackerFramesPool) do f.inUse = false; f:Hide() end
    if DCP_FilterFrame and DCP_FilterFrame:IsShown() then DCP:UpdateFilterList() end
end
DCP:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")

function DCP:UNIT_SPELLCAST_SUCCEEDED(unit, castGUID, spellID)
    if unit == "player" or unit == "pet" then
        if spellID then
            local info = C_Spell.GetSpellInfo(spellID)
            local sName = info and info.name
            local sIcon = info and info.iconID
            
            if sName and sIcon then
                watching["spell"..spellID] = { 
                    time = GetTime(), 
                    type = "spell", 
                    id = spellID, 
                    isPet = (unit == "pet"),
                    name = sName,
                    icon = sIcon
                }
                if not self:IsMouseEnabled() then self:SetScript("OnUpdate", OnUpdate) end
                for i = #activeTrackers, 1, -1 do
                    if activeTrackers[i].id == spellID and activeTrackers[i].ctype == "spell" then
                        activeTrackers[i].frame:Hide(); activeTrackers[i].frame.inUse = false; table.remove(activeTrackers, i)
                    end
                end
            end
        end
    end
end
DCP:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player", "pet")

local function UpdateDynamicCooldowns()
    local currentGCD = GetGCD()
    for id, v in pairs(activeSpells) do
        local status, s, d = GetSafeSpellCooldown(id)
        if status == "NORMAL" then if d > currentGCD + 0.05 then v.expiration = s + d else v.expiration = GetTime() end
        elseif status == "SECRET" then AssignShadowFrame(id, "spell", v.name, v.texture, v.isPet, s, d); activeSpells[id] = nil end
    end
    for id, v in pairs(activeItems) do
        local status, s, d = GetSafeItemCooldown(id)
        if status == "NORMAL" then if d > currentGCD + 0.05 then v.expiration = s + d else v.expiration = GetTime() end
        elseif status == "SECRET" then secretItemsTracked[id] = true; AssignShadowFrame(id, "item", v.name, v.texture, v.isPet, s, d); activeItems[id] = nil end
    end
    for _, cf in ipairs(shadowFrames) do
        if cf.isTracking then
            if cf.ctype == "spell" then local status, s, d = GetSafeSpellCooldown(cf.id); if status ~= "NONE" then pcall(function() cf:SetCooldown(s, d) end) end
            elseif cf.ctype == "item" then local status, s, d = GetSafeItemCooldown(cf.id); if status ~= "NONE" then pcall(function() cf:SetCooldown(s, d) end) end end
        end
    end
end

function DCP:SPELL_UPDATE_COOLDOWN() if not DCP:GetScript("OnUpdate") then DCP:SetScript("OnUpdate", OnUpdate) end; UpdateDynamicCooldowns() end
function DCP:BAG_UPDATE_COOLDOWN() if not DCP:GetScript("OnUpdate") then DCP:SetScript("OnUpdate", OnUpdate) end; UpdateDynamicCooldowns() end
DCP:RegisterEvent("SPELL_UPDATE_COOLDOWN"); DCP:RegisterEvent("BAG_UPDATE_COOLDOWN")

function DCP:PLAYER_ENTERING_WORLD()
    local inInstance, instanceType = IsInInstance()
    if inInstance and instanceType == "arena" then
        self:SetScript("OnUpdate", nil)
        wipe(activeSpells); wipe(activeItems); wipe(watching); wipe(secretItemsTracked); wipe(activeTrackers)
        for _, cf in ipairs(shadowFrames) do cf.isTracking = false; pcall(function() cf:SetCooldown(0, 0) end) end
        for _, f in ipairs(animFrames) do f.inUse = false; f:Hide() end
        for _, f in ipairs(trackerFramesPool) do f.inUse = false; f:Hide() end
        for i = 1, MAX_ANIM_FRAMES do slotInUse[i] = false end
    end
end
DCP:RegisterEvent("PLAYER_ENTERING_WORLD")

-- =====================================================================
-- 🖼️ 圖形化過濾面板設定 (Filter GUI)
-- =====================================================================
local function RebuildPriorityOrder()
    if not DCP_FilterFrame or not DCP_FilterFrame.currentMode then return end
    if DCP_FilterFrame.currentMode ~= 3 then return end
    local targetList = GetTargetListByMode(3)
    local sorted = {}
    for k, v in pairs(targetList) do v.key = k; table.insert(sorted, v) end
    table.sort(sorted, function(a, b) return (a.order or 999) < (b.order or 999) end)
    for i, v in ipairs(sorted) do v.order = i end
    return sorted
end

local function AddItemToFilterList(idOrName, itemTypeHint)
    if not idOrName or idOrName == "" then return end
    local mode = DCP_FilterFrame.currentMode or 1
    local targetList = GetTargetListByMode(mode)
    local id, name, icon
    
    local numID = tonumber(idOrName)
    if numID then
        if itemTypeHint == "spell" or not itemTypeHint then local info = C_Spell.GetSpellInfo(numID); if info then id, name, icon, itemTypeHint = numID, info.name, info.iconID, "spell" end end
        if not name and (itemTypeHint == "item" or not itemTypeHint) then name = C_Item.GetItemInfo(numID); if name then id, icon, itemTypeHint = numID, C_Item.GetItemIconByID(numID), "item" end end
    else name = idOrName; local info = C_Spell.GetSpellInfo(name); if info then id, icon, itemTypeHint = info.spellID, info.iconID, "spell" end end
    
    local key = id and tostring(id) or name
    if key and name then
        targetList[key] = { itemType = itemTypeHint or "unknown", id = id, name = name, icon = icon or 136243, enabled = true, order = 999 }
        RebuildPriorityOrder()
        if DCP_FilterFrame and DCP_FilterFrame:IsShown() then DCP:UpdateFilterList() end
    end
end

local filterRows = {}
function DCP:UpdateFilterList()
    local mode = DCP_FilterFrame.currentMode or 1
    local targetList = GetTargetListByMode(mode)
    for _, row in ipairs(filterRows) do row:Hide() end
    
    local yOffset = -5; local rowIndex = 1
    local sortedKeys = {}
    for k, v in pairs(targetList) do table.insert(sortedKeys, k) end
    if mode == 3 then table.sort(sortedKeys, function(a,b) return (targetList[a].order or 999) < (targetList[b].order or 999) end)
    else table.sort(sortedKeys) end
    
    for i, key in ipairs(sortedKeys) do
        local data = targetList[key]
        local row = filterRows[rowIndex]
        if not row then
            row = CreateFrame("Frame", nil, DCP_FilterScrollChild)
            row:SetSize(280, 30)
            row.cb = CreateFrame("CheckButton", nil, row, "UICheckButtonTemplate"); row.cb:SetSize(24, 24); row.cb:SetPoint("LEFT", 5, 0)
            row.icon = row:CreateTexture(nil, "ARTWORK"); row.icon:SetSize(20, 20); row.icon:SetPoint("LEFT", row.cb, "RIGHT", 5, 0)
            row.nameStr = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight"); row.nameStr:SetPoint("LEFT", row.icon, "RIGHT", 10, 0); row.nameStr:SetWidth(150); row.nameStr:SetJustifyH("LEFT"); row.nameStr:SetWordWrap(false)
            row.delBtn = CreateFrame("Button", nil, row, "UIPanelCloseButton"); row.delBtn:SetSize(24, 24); row.delBtn:SetPoint("RIGHT", row, "RIGHT", -5, 0)
            row.upBtn = CreateFrame("Button", nil, row)
            row.upBtn:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Up"); row.upBtn:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Down"); row.upBtn:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD"); row.upBtn:SetSize(20, 20); row.upBtn:SetPoint("RIGHT", row.delBtn, "LEFT", 0, 0)
            row.downBtn = CreateFrame("Button", nil, row)
            row.downBtn:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up"); row.downBtn:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down"); row.downBtn:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD"); row.downBtn:SetSize(20, 20); row.downBtn:SetPoint("RIGHT", row.upBtn, "LEFT", 0, 0)
            row:EnableMouse(true)
            row:SetScript("OnEnter", function(self) GameTooltip:SetOwner(self, "ANCHOR_RIGHT"); if self.data.itemType == "spell" and self.data.id then GameTooltip:SetSpellByID(self.data.id) elseif self.data.itemType == "item" and self.data.id then GameTooltip:SetItemByID(self.data.id) else GameTooltip:SetText(self.data.name) end; GameTooltip:Show() end)
            row:SetScript("OnLeave", function() GameTooltip:Hide() end)
            filterRows[rowIndex] = row
        end
        
        row.data = data; row.key = key; row.cb:SetChecked(data.enabled)
        row.cb:SetScript("OnClick", function(self) targetList[row.key].enabled = self:GetChecked(); RefreshLocals() end)
        row.icon:SetTexture(data.icon); row.nameStr:SetText(data.name)
        row.delBtn:SetScript("OnClick", function() targetList[row.key] = nil; RebuildPriorityOrder(); DCP:UpdateFilterList(); RefreshLocals() end)
        
        if mode == 3 then
            row.upBtn:Show(); row.downBtn:Show()
            row.upBtn:SetScript("OnClick", function() local list = RebuildPriorityOrder(); if i > 1 then list[i].order, list[i-1].order = list[i-1].order, list[i].order; DCP:UpdateFilterList() end end)
            row.downBtn:SetScript("OnClick", function() local list = RebuildPriorityOrder(); if i < #list then list[i].order, list[i+1].order = list[i+1].order, list[i].order; DCP:UpdateFilterList() end end)
        else
            row.upBtn:Hide(); row.downBtn:Hide()
        end
        row:SetPoint("TOPLEFT", 0, yOffset); row:Show()
        yOffset = yOffset - 30; rowIndex = rowIndex + 1
    end
    DCP_FilterScrollChild:SetHeight(math.abs(yOffset))
    if mode == 3 then DCP_FilterTitle:SetText(L.PRIORITY_LIST_TITLE) elseif mode == 2 then DCP_FilterTitle:SetText(L.WHITELIST_TITLE) else DCP_FilterTitle:SetText(L.BLACKLIST_TITLE) end
end

local function OpenFilterGUI(mode)
    if not DCP_FilterFrame then
        DCP_FilterFrame = CreateFrame("Frame", "DCP_FilterFrame", UIParent, "BasicFrameTemplateWithInset")
        DCP_FilterFrame:SetSize(320, 400)
        DCP_FilterFrame:SetPoint("TOPLEFT", DCP_OptionsFrame, "TOPRIGHT", 10, 0)
        DCP_FilterFrame:SetFrameStrata("DIALOG"); DCP_FilterFrame:SetMovable(true); DCP_FilterFrame:EnableMouse(true); DCP_FilterFrame:RegisterForDrag("LeftButton")
        DCP_FilterFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
        DCP_FilterFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
        DCP_FilterTitle = DCP_FilterFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlight"); DCP_FilterTitle:SetPoint("TOP", 0, -5)
        
        local input = CreateFrame("EditBox", nil, DCP_FilterFrame, "InputBoxTemplate")
        input:SetSize(180, 24); input:SetPoint("TOPLEFT", 15, -35); input:SetAutoFocus(false)
        input:SetScript("OnEnter",function(self) GameTooltip:SetOwner(self, "ANCHOR_TOP") GameTooltip:SetText(L.FILTER_INPUT_TIP) end)
        input:SetScript("OnLeave",function(self) GameTooltip:Hide() end)
        input:SetScript("OnEnterPressed", function(self) AddItemToFilterList(self:GetText()); self:SetText(""); self:ClearFocus() end)
        
        local addBtn = CreateFrame("Button", nil, DCP_FilterFrame, "UIPanelButtonTemplate")
        addBtn:SetSize(80, 24); addBtn:SetPoint("LEFT", input, "RIGHT", 10, 0); addBtn:SetText(L.ADD)
        addBtn:SetScript("OnClick", function() AddItemToFilterList(input:GetText()); input:SetText(""); input:ClearFocus() end)
        
        local function HandleCursorDrop()
            local infoType, data1, data2, data3 = GetCursorInfo()
            if not infoType then return end
            if infoType == "spell" then local spellID = data3 or data1; if spellID then AddItemToFilterList(spellID, "spell") end; ClearCursor()
            elseif infoType == "item" then if data1 then AddItemToFilterList(data1, "item") end; ClearCursor()
            elseif infoType == "action" then local actType, id = GetActionInfo(data1); if actType == "spell" and id then AddItemToFilterList(id, "spell") elseif actType == "item" and id then AddItemToFilterList(id, "item") end; ClearCursor() end
        end
        local function AttachDropHandler(frame)
            if not frame then return end
            frame:HookScript("OnReceiveDrag", HandleCursorDrop)
            frame:HookScript("OnMouseUp", function() if GetCursorInfo() then HandleCursorDrop() end end)
        end
        AttachDropHandler(DCP_FilterFrame)
        local scrollFrame = CreateFrame("ScrollFrame", "DCP_FilterScrollFrame", DCP_FilterFrame, "UIPanelScrollFrameTemplate")
        scrollFrame:SetPoint("TOPLEFT", 10, -70); scrollFrame:SetPoint("BOTTOMRIGHT", -30, 10); AttachDropHandler(scrollFrame)
        local scrollChild = CreateFrame("Frame", "DCP_FilterScrollChild", scrollFrame)
        scrollChild:SetSize(280, 100); scrollChild:EnableMouse(true); AttachDropHandler(scrollChild); scrollFrame:SetScrollChild(scrollChild)
    end
    DCP_FilterFrame.currentMode = mode
    DCP:UpdateFilterList()
    DCP_FilterFrame:Show()
end

-------------------
-- Options Frame --
-------------------
SlashCmdList["DOOMCOOLDOWNPULSE"] = function() if not DCP_OptionsFrame then DCP:CreateOptionsFrame() end DCP_OptionsFrame:Show() end
SLASH_DOOMCOOLDOWNPULSE1 = "/dcp"
SLASH_DOOMCOOLDOWNPULSE2 = "/cooldownpulse"
SLASH_DOOMCOOLDOWNPULSE3 = "/doomcooldownpulse"

function DCP:CreateOptionsFrame()
    local optionsframe = CreateFrame("frame","DCP_OptionsFrame",UIParent,BackdropTemplateMixin and "BackdropTemplate")
    optionsframe:SetBackdrop({ bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", tile=1, tileSize=32, edgeSize=32, insets={left=11, right=12, top=12, bottom=11} })
    optionsframe:SetSize(640, 620)
    optionsframe:SetPoint("CENTER", UIParent, "CENTER", -180, 0)
    optionsframe:EnableMouse(true); optionsframe:SetMovable(true); optionsframe:RegisterForDrag("LeftButton")
    optionsframe:SetScript("OnDragStart", function(self) self:StartMoving() end)
    optionsframe:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
    optionsframe:SetFrameStrata("FULLSCREEN_DIALOG")
    optionsframe:SetScript("OnHide", function() RefreshLocals() end)
    tinsert(UISpecialFrames, "DCP_OptionsFrame")

    local header = optionsframe:CreateTexture(nil,"ARTWORK")
    header:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header.blp"); header:SetSize(350, 68); header:SetPoint("TOP",0,12)
    local headertext = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormal")
    headertext:SetPoint("TOP",header,"TOP",0,-14); headertext:SetText("Doom_CooldownPulse")

    local enableDCP_CBT = CreateFrame("CheckButton","DCP_OptionsFrameEnableDCPCBT",optionsframe,"UICheckButtonTemplate")
    enableDCP_CBT:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 30, -40); enableDCP_CBT:SetChecked(DCP_Saved.enableDCP)
    enableDCP_CBT:SetScript("OnClick", function(self) DCP_Saved.enableDCP = self:GetChecked(); RefreshLocals() end)
    local enableDCP_Text = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    enableDCP_Text:SetPoint("LEFT", enableDCP_CBT, "RIGHT", 6, 0); enableDCP_Text:SetText(L.ENABLE_DCP)

    local filterModeText = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    filterModeText:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 30, -80); filterModeText:SetText(L.FILTER_MODE)

    local radioBlack = CreateFrame("Checkbutton","DCP_OptionsFrameListButtonBlacklist",optionsframe,"UIRadioButtonTemplate")
    radioBlack:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 30, -100); radioBlack:SetChecked(not DCP_SavedPerCharacter.useWhitelist)
    radioBlack:SetScript("OnClick", function() DCP_SavedPerCharacter.useWhitelist = false; DCP_OptionsFrameListButtonWhitelist:SetChecked(false); RefreshLocals() end)
    local txtBlacklist = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    txtBlacklist:SetPoint("LEFT", radioBlack, "RIGHT", 2, 0); txtBlacklist:SetText(L.BLACKLIST)

    local radioWhite = CreateFrame("Checkbutton","DCP_OptionsFrameListButtonWhitelist",optionsframe,"UIRadioButtonTemplate")
    radioWhite:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 130, -100); radioWhite:SetChecked(DCP_SavedPerCharacter.useWhitelist)
    radioWhite:SetScript("OnClick", function() DCP_SavedPerCharacter.useWhitelist = true; DCP_OptionsFrameListButtonBlacklist:SetChecked(false); RefreshLocals() end)
    local txtWhitelist = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    txtWhitelist:SetPoint("LEFT", radioWhite, "RIGHT", 2, 0); txtWhitelist:SetText(L.WHITELIST)

    local btnEditBlack = CreateFrame("Button", nil, optionsframe, "UIPanelButtonTemplate"); btnEditBlack:SetSize(120, 24)
    btnEditBlack:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 30, -130); btnEditBlack:SetText(L.EDIT_BLACK)
    btnEditBlack:SetScript("OnClick", function() OpenFilterGUI(1) end)

    local btnEditWhite = CreateFrame("Button", nil, optionsframe, "UIPanelButtonTemplate"); btnEditWhite:SetSize(120, 24)
    btnEditWhite:SetPoint("LEFT", btnEditBlack, "RIGHT", 10, 0); btnEditWhite:SetText(L.EDIT_WHITE)
    btnEditWhite:SetScript("OnClick", function() OpenFilterGUI(2) end)

    local slidersLeft = {
        { text = L.ICON_SIZE, value = "iconSize", min = 30, max = 125, step = 5 },
        { text = L.FADE_IN_TIME, value = "fadeInTime", min = 0, max = 1.5, step = 0.1 },
        { text = L.FADE_OUT_TIME, value = "fadeOutTime", min = 0, max = 1.5, step = 0.1 },
        { text = L.MAX_OPACITY, value = "maxAlpha", min = 0, max = 1, step = 0.1 },
        { text = L.HOLD_TIME, value = "holdTime", min = 0, max = 1.5, step = 0.1 },
        { text = L.ANIM_SCALE, value = "animScale", min = 0, max = 2, step = 0.1 }
    }
    
    for i,v in pairs(slidersLeft) do
        local slider = CreateFrame("slider", "DCP_OptionsFrameSlider"..i, optionsframe, "OptionsSliderTemplate"); slider:SetWidth(150)
        if i == 1 then slider:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 50, -190)
        else slider:SetPoint("TOP", _G["DCP_OptionsFrameSlider"..(i-1)], "BOTTOM", 0, -35) end
        
        local valuetext = slider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
        valuetext:SetPoint("TOP",slider,"BOTTOM",0,-1); valuetext:SetText(format("%.1f",DCP_Saved[v.value]))
        _G["DCP_OptionsFrameSlider"..i.."Text"]:SetText(v.text)
        _G["DCP_OptionsFrameSlider"..i.."Low"]:SetText(v.min); _G["DCP_OptionsFrameSlider"..i.."High"]:SetText(v.max)
        slider:SetMinMaxValues(v.min,v.max); slider:SetValueStep(v.step); slider:SetObeyStepOnDrag(true); slider:SetValue(DCP_Saved[v.value])
        slider:SetScript("OnValueChanged",function()
            local value = slider:GetValue(); DCP_Saved[v.value] = value; RefreshLocals(); valuetext:SetText(format("%.1f", value))
            if DCP:IsMouseEnabled() then DCP:SetSize(DCP_Saved.iconSize, DCP_Saved.iconSize) end
        end)
    end

    local spellnamecbt = CreateFrame("CheckButton","DCP_OptionsFrameSpellNameCheckButton",optionsframe,"UICheckButtonTemplate")
    spellnamecbt:SetPoint("TOPLEFT", _G["DCP_OptionsFrameSlider6"], "BOTTOMLEFT", -20, -30); spellnamecbt:SetChecked(DCP_Saved.showSpellName)
    spellnamecbt:SetScript("OnClick", function(self) DCP_Saved.showSpellName = self:GetChecked(); RefreshLocals() end)
    local spellnametext = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    spellnametext:SetPoint("LEFT",spellnamecbt,"RIGHT",6,0); spellnametext:SetText(L.SHOW_SPELL_NAME)

    local fontSlider = CreateFrame("slider", "DCP_OptionsFrameFontSlider", optionsframe, "OptionsSliderTemplate"); fontSlider:SetWidth(150)
    fontSlider:SetPoint("TOPLEFT", spellnamecbt, "BOTTOMLEFT", 20, -25)
    local fontValuetext = fontSlider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    fontValuetext:SetPoint("TOP",fontSlider,"BOTTOM",0,-1); fontValuetext:SetText(DCP_Saved.spellNameFontSize)
    _G["DCP_OptionsFrameFontSliderText"]:SetText(L.FONT_SIZE); _G["DCP_OptionsFrameFontSliderLow"]:SetText("10"); _G["DCP_OptionsFrameFontSliderHigh"]:SetText("30")
    fontSlider:SetMinMaxValues(10,30); fontSlider:SetValueStep(1); fontSlider:SetObeyStepOnDrag(true); fontSlider:SetValue(DCP_Saved.spellNameFontSize)
    fontSlider:SetScript("OnValueChanged",function() local value = fontSlider:GetValue(); DCP_Saved.spellNameFontSize = value; RefreshLocals(); fontValuetext:SetText(value) end)

    local enableTracker_CBT = CreateFrame("CheckButton","DCP_OptionsFrameEnableTrackerCBT",optionsframe,"UICheckButtonTemplate")
    enableTracker_CBT:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 340, -40); enableTracker_CBT:SetChecked(DCP_Saved.enableTracker)
    enableTracker_CBT:SetScript("OnClick", function(self) DCP_Saved.enableTracker = self:GetChecked(); RefreshLocals() end)
    local enableTracker_Text = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    enableTracker_Text:SetPoint("LEFT", enableTracker_CBT, "RIGHT", 6, 0); enableTracker_Text:SetText(L.ENABLE_TRACKER)

    local btnEditPrio = CreateFrame("Button", nil, optionsframe, "UIPanelButtonTemplate"); btnEditPrio:SetSize(160, 24)
    btnEditPrio:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 340, -130); btnEditPrio:SetText(L.EDIT_PRIO)
    btnEditPrio:SetScript("OnClick", function() OpenFilterGUI(3) end)

    local trackerSizeSlider = CreateFrame("slider", "DCP_OptionsFrameTrackerSizeSlider", optionsframe, "OptionsSliderTemplate"); trackerSizeSlider:SetWidth(150)
    trackerSizeSlider:SetPoint("TOPLEFT", optionsframe, "TOPLEFT", 360, -190)
    local tsvt = trackerSizeSlider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    tsvt:SetPoint("TOP",trackerSizeSlider,"BOTTOM",0,-1); tsvt:SetText(DCP_Saved.trackerIconSize)
    _G["DCP_OptionsFrameTrackerSizeSliderText"]:SetText(L.TRACKER_SIZE); _G["DCP_OptionsFrameTrackerSizeSliderLow"]:SetText("20"); _G["DCP_OptionsFrameTrackerSizeSliderHigh"]:SetText("80")
    trackerSizeSlider:SetMinMaxValues(20,80); trackerSizeSlider:SetValueStep(1); trackerSizeSlider:SetObeyStepOnDrag(true); trackerSizeSlider:SetValue(DCP_Saved.trackerIconSize)
    trackerSizeSlider:SetScript("OnValueChanged",function() local value = trackerSizeSlider:GetValue(); DCP_Saved.trackerIconSize = value; RefreshLocals(); tsvt:SetText(value) end)

    local trackerNameCBT = CreateFrame("CheckButton","DCP_OptionsFrameTrackerNameCBT",optionsframe,"UICheckButtonTemplate")
    trackerNameCBT:SetPoint("TOPLEFT", trackerSizeSlider, "BOTTOMLEFT", -20, -30); trackerNameCBT:SetChecked(DCP_Saved.trackerShowName)
    trackerNameCBT:SetScript("OnClick", function(self) DCP_Saved.trackerShowName = self:GetChecked(); RefreshLocals() end)
    local trackerNameText = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    trackerNameText:SetPoint("LEFT", trackerNameCBT, "RIGHT", 6, 0); trackerNameText:SetText(L.TRACKER_SHOW_NAME)

    local trackerNamePosSlider = CreateFrame("slider", "DCP_OptionsFrameTrackerNamePosSlider", optionsframe, "OptionsSliderTemplate"); trackerNamePosSlider:SetWidth(150)
    trackerNamePosSlider:SetPoint("TOPLEFT", trackerNameCBT, "BOTTOMLEFT", 20, -25)
    local tnpt = trackerNamePosSlider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    tnpt:SetPoint("TOP",trackerNamePosSlider,"BOTTOM",0,-1); tnpt:SetText(DCP_Saved.trackerNamePos)
    _G["DCP_OptionsFrameTrackerNamePosSliderText"]:SetText(L.TRACKER_NAME_POS); _G["DCP_OptionsFrameTrackerNamePosSliderLow"]:SetText("1"); _G["DCP_OptionsFrameTrackerNamePosSliderHigh"]:SetText("3")
    trackerNamePosSlider:SetMinMaxValues(1,3); trackerNamePosSlider:SetValueStep(1); trackerNamePosSlider:SetObeyStepOnDrag(true); trackerNamePosSlider:SetValue(DCP_Saved.trackerNamePos)
    trackerNamePosSlider:SetScript("OnValueChanged",function() local value = trackerNamePosSlider:GetValue(); DCP_Saved.trackerNamePos = value; RefreshLocals(); tnpt:SetText(value) end)

    local trackerFontSizeSlider = CreateFrame("slider", "DCP_OptionsFrameTrackerFontSizeSlider", optionsframe, "OptionsSliderTemplate"); trackerFontSizeSlider:SetWidth(150)
    trackerFontSizeSlider:SetPoint("TOP", trackerNamePosSlider, "BOTTOM", 0, -35)
    local tfs = trackerFontSizeSlider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    tfs:SetPoint("TOP",trackerFontSizeSlider,"BOTTOM",0,-1); tfs:SetText(DCP_Saved.trackerNameFontSize)
    _G["DCP_OptionsFrameTrackerFontSizeSliderText"]:SetText(L.TRACKER_NAME_FONT_SIZE); _G["DCP_OptionsFrameTrackerFontSizeSliderLow"]:SetText("10"); _G["DCP_OptionsFrameTrackerFontSizeSliderHigh"]:SetText("30")
    trackerFontSizeSlider:SetMinMaxValues(10,30); trackerFontSizeSlider:SetValueStep(1); trackerFontSizeSlider:SetObeyStepOnDrag(true); trackerFontSizeSlider:SetValue(DCP_Saved.trackerNameFontSize)
    trackerFontSizeSlider:SetScript("OnValueChanged",function() local value = trackerFontSizeSlider:GetValue(); DCP_Saved.trackerNameFontSize = value; RefreshLocals(); tfs:SetText(value) end)

    local pettext = optionsframe:CreateFontString(nil,"ARTWORK","GameFontNormalSmall")
    pettext:SetPoint("TOPLEFT", trackerFontSizeSlider, "BOTTOMLEFT", -20, -35); pettext:SetText(L.PET_COLOR)
    local petcolorselect = CreateFrame('Button',"DCP_OptionsFramePetColorBox",optionsframe)
    petcolorselect:SetPoint("LEFT",pettext,"RIGHT",10,0); petcolorselect:SetSize(20,20)
    petcolorselect:SetNormalTexture('Interface/ChatFrame/ChatFrameColorSwatch'); petcolorselect:GetNormalTexture():SetVertexColor(unpack(DCP_Saved.petOverlay))
    petcolorselect:SetScript("OnEnter",function(self) GameTooltip:SetOwner(self, "ANCHOR_CURSOR") GameTooltip:SetText(L.PET_COLOR_TIP) end)
    petcolorselect:SetScript("OnLeave",function(self) GameTooltip:Hide() end)
    petcolorselect:SetScript('OnClick', function(self)
        local r, g, b = unpack(DCP_Saved.petOverlay)
        ColorPickerFrame:SetupColorPickerAndShow({
            swatchFunc = function() DCP_Saved.petOverlay={ColorPickerFrame:GetColorRGB()} petcolorselect:GetNormalTexture():SetVertexColor(ColorPickerFrame:GetColorRGB()) end,
            cancelFunc = function() DCP_Saved.petOverlay={r,g,b} petcolorselect:GetNormalTexture():SetVertexColor(unpack(DCP_Saved.petOverlay)) end,
            hasOpacity = false, r = r, g = g, b = b
        })
        ColorPickerFrame:SetPoint("TOPLEFT",optionsframe,"TOPRIGHT")
    end)
    local petcolorselectbg = petcolorselect:CreateTexture(nil, 'BACKGROUND'); petcolorselectbg:SetSize(17,17); petcolorselectbg:SetTexture(1,1,1); petcolorselectbg:SetPoint('CENTER')

    local buttons = {
        { text = L.BTN_CLOSE, func = function(self) self:GetParent():Hide() end },
        { text = L.BTN_TEST, func = function(self)
            DCP_OptionsFrameButton3:SetText(L.BTN_UNLOCK)
            DCP:EnableMouse(false); DCPTrackerAnchor:EnableMouse(false); DCP:SetAlpha(0); DCPTrackerAnchor:SetAlpha(0); RefreshLocals()
            wipe(activeTrackers)
            for _, f in ipairs(trackerFramesPool) do f.inUse = false; f:Hide() end
            if DCP_Saved.enableDCP then
                TriggerFlash(nil, "spell", "Interface\\Icons\\Spell_Nature_Earthbind", false, L.TEST_SPELL_1)
                C_Timer.After(0.15, function() TriggerFlash(nil, "spell", "Interface\\Icons\\Spell_Fire_Fireball", false, L.TEST_SPELL_2) end)
                C_Timer.After(0.30, function() TriggerFlash(nil, "spell", "Interface\\Icons\\Spell_Frost_FrostBolt", false, L.TEST_SPELL_3) end)
            end
            if DCP_Saved.enableTracker then
                local cx, cy = DCP_Saved.x, DCP_Saved.y
                local s = DCP:GetEffectiveScale()
                SpawnPriorityTracker(nil, "spell", L.TEST_SPELL_1, "Interface\\Icons\\Spell_Nature_Earthbind", cx*s, cy*s)
                C_Timer.After(0.15, function() SpawnPriorityTracker(nil, "spell", L.TEST_SPELL_2, "Interface\\Icons\\Spell_Fire_Fireball", cx*s, cy*s) end)
                C_Timer.After(0.30, function() SpawnPriorityTracker(nil, "spell", L.TEST_SPELL_3, "Interface\\Icons\\Spell_Frost_FrostBolt", cx*s, cy*s) end)
            end
        end },
        { text = L.BTN_UNLOCK, func = function(self)
            if self:GetText() == L.BTN_UNLOCK then
                RefreshLocals(); DCP:SetSize(iconSize, iconSize); self:SetText(L.BTN_LOCK)
                DCP:SetScript("OnUpdate", nil); DCP:SetAlpha(1); DCPTrackerAnchor:SetAlpha(1)
                DCPT:SetTexture("Interface\\Icons\\Spell_Nature_Earthbind")
                DCP:EnableMouse(true); DCPTrackerAnchor:EnableMouse(true)
                wipe(activeTrackers)
                for _, f in ipairs(trackerFramesPool) do f.inUse = false; f:Hide() end
                for _, f in ipairs(animFrames) do f.inUse = false; f:Hide() end
                for i = 1, MAX_ANIM_FRAMES do slotInUse[i] = false end
            else
                DCP:SetAlpha(0); DCPTrackerAnchor:SetAlpha(0)
                self:SetText(L.BTN_UNLOCK); DCP:EnableMouse(false); DCPTrackerAnchor:EnableMouse(false)
            end end },
        { text = L.BTN_DEFAULTS, func = function(self)
            for i,v in pairs(defaultSettings) do DCP_Saved[i] = v end
            for i,v in ipairs(slidersLeft) do _G["DCP_OptionsFrameSlider"..i]:SetValue(DCP_Saved[v.value]) end
            DCP_OptionsFrameEnableDCPCBT:SetChecked(DCP_Saved.enableDCP); DCP_OptionsFrameEnableTrackerCBT:SetChecked(DCP_Saved.enableTracker)
            DCP_OptionsFrameFontSlider:SetValue(DCP_Saved.spellNameFontSize); DCP_OptionsFrameTrackerSizeSlider:SetValue(DCP_Saved.trackerIconSize)
            DCP_OptionsFrameTrackerNameCBT:SetChecked(DCP_Saved.trackerShowName); DCP_OptionsFrameTrackerNamePosSlider:SetValue(DCP_Saved.trackerNamePos)
            DCP_OptionsFrameTrackerFontSizeSlider:SetValue(DCP_Saved.trackerNameFontSize); DCP_OptionsFramePetColorBox:GetNormalTexture():SetVertexColor(unpack(DCP_Saved.petOverlay))
            DCP_OptionsFrameListButtonBlacklist:SetChecked(true); DCP_OptionsFrameListButtonWhitelist:SetChecked(false)
            DCP:ClearAllPoints(); DCP:SetPoint("CENTER",UIParent,"CENTER",DCP_Saved.x,DCP_Saved.y)
            DCPTrackerAnchor:ClearAllPoints(); DCPTrackerAnchor:SetPoint("CENTER",UIParent,"CENTER",DCP_Saved.trackerX,DCP_Saved.trackerY)
            if DCP_FilterFrame and DCP_FilterFrame:IsShown() then DCP:UpdateFilterList() end
            end },
    }

    local btnWidth = 80; local spacing = 15
    local totalWidth = (btnWidth * #buttons) + (spacing * (#buttons - 1))
    local startX = (optionsframe:GetWidth() - totalWidth) / 2
    for i,v in ipairs(buttons) do
        local button = CreateFrame("Button", "DCP_OptionsFrameButton"..i, optionsframe, "UIPanelButtonTemplate")
        button:SetSize(btnWidth, 24)
        button:SetPoint("BOTTOMLEFT", optionsframe, "BOTTOMLEFT", startX + ((i - 1) * (btnWidth + spacing)), 20)
        button:SetText(v.text); button:SetScript("OnClick", function(self) PlaySound(852) v.func(self) end)
    end
end