diff --git a/AFKScreen.lua b/AFKScreen.lua index 08e3316..03d7e70 100644 --- a/AFKScreen.lua +++ b/AFKScreen.lua @@ -220,9 +220,9 @@ end function AFK:BuildModel(parent) local model = CreateFrame("PlayerModel", NextName("Model"), parent) - model:SetWidth(420) - model:SetHeight(520) - model:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 30, 70) + model:SetWidth(520) + model:SetHeight(640) + model:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 10, 65) model:SetFrameLevel(parent:GetFrameLevel() + 1) -- Soft darkening on the right side so the info panel is readable @@ -868,10 +868,12 @@ function AFK:RefreshSkills() currentHeader = name or "" elseif name then local isWeapon = (currentHeader == "Weapon Skills" or currentHeader == "武器技能" - or currentHeader == "Weapons" or currentHeader == "Combat") + or currentHeader == "Weapons" or currentHeader == "Combat" + or currentHeader == "武器") local isTrade = (currentHeader == "Trade Skills" or currentHeader == "专业技能" - or currentHeader == "Professions") - local isSecondary = (currentHeader == "Secondary Skills" or currentHeader == "辅助技能") + or currentHeader == "Professions" or currentHeader == "专业") + local isSecondary = (currentHeader == "Secondary Skills" or currentHeader == "辅助技能" + or currentHeader == "Secondary") if isWeapon and skillMax and skillMax > 0 then table.insert(weapons, { name = name, rank = skillRank or 0, max = skillMax }) diff --git a/ActionBars.lua b/ActionBars.lua index d6638db..1fa2bed 100644 --- a/ActionBars.lua +++ b/ActionBars.lua @@ -1526,6 +1526,9 @@ local function ShortKeyText(key) key = string.gsub(key, "ALT%-", "A-") key = string.gsub(key, "MOUSEWHEELUP", "WhlUp") key = string.gsub(key, "MOUSEWHEELDOWN", "WhlDn") + key = string.gsub(key, "BUTTON3", "M3") + key = string.gsub(key, "BUTTON4", "M4") + key = string.gsub(key, "BUTTON5", "M5") return key end @@ -1547,21 +1550,29 @@ local function UpdateOverlayText(button) ov.label:SetText(t) end +local CLICK_TO_KEY = { + MiddleButton = "BUTTON3", + Button4 = "BUTTON4", + Button5 = "BUTTON5", +} + local function CreateBindOverlay(button) if keyBindOverlays[button] then return keyBindOverlays[button] end local ov = CreateFrame("Button", nil, button) ov:SetAllPoints(button) ov:SetFrameLevel(button:GetFrameLevel() + 10) - ov:RegisterForClicks("RightButtonUp") + ov:RegisterForClicks("RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up") local bg = ov:CreateTexture(nil, "BACKGROUND") bg:SetAllPoints() bg:SetTexture(0, 0, 0, 0.55) local label = ov:CreateFontString(nil, "OVERLAY") - label:SetFont(SFrames:GetFont(), 9, "OUTLINE") + label:SetFont(SFrames:GetFont(), 7, "OUTLINE") label:SetPoint("CENTER", ov, "CENTER", 0, 0) + label:SetWidth(button:GetWidth() - 2) + label:SetJustifyH("CENTER") label:SetTextColor(1, 0.82, 0) ov.label = label @@ -1576,11 +1587,11 @@ local function CreateBindOverlay(button) if cmd then GameTooltip:AddLine(cmd, 1, 0.82, 0) local k1, k2 = GetBindingKey(cmd) - if k1 then GameTooltip:AddLine("Key 1: " .. k1, 1, 1, 1) end - if k2 then GameTooltip:AddLine("Key 2: " .. k2, 0.7, 0.7, 0.7) end + if k1 then GameTooltip:AddLine("按键 1: " .. k1, 1, 1, 1) end + if k2 then GameTooltip:AddLine("按键 2: " .. k2, 0.7, 0.7, 0.7) end end - GameTooltip:AddLine("Press key to bind / Wheel to bind", 0.5, 0.8, 0.5) - GameTooltip:AddLine("Right-click to clear", 0.8, 0.5, 0.5) + GameTooltip:AddLine("按下按键/鼠标键/滚轮绑定", 0.5, 0.8, 0.5) + GameTooltip:AddLine("右键点击清除绑定", 0.8, 0.5, 0.5) GameTooltip:Show() end) ov:SetScript("OnLeave", function() @@ -1597,7 +1608,22 @@ local function CreateBindOverlay(button) SaveBindings(2) UpdateOverlayText(button) RefreshButtonHotkey(button) - DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami-UI]|r Cleared bindings for " .. cmd) + DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami-UI]|r 已清除 " .. cmd .. " 的绑定") + end + else + local mouseKey = CLICK_TO_KEY[arg1] + if mouseKey then + local cmd = GetButtonBindingCmd(button) + if not cmd then return end + local keyStr = BuildKeyString(mouseKey) + if not keyStr then return end + local old = GetBindingAction(keyStr) + if old and old ~= "" and old ~= cmd then SetBinding(keyStr, nil) end + SetBinding(keyStr, cmd) + SaveBindings(2) + UpdateOverlayText(button) + RefreshButtonHotkey(button) + DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami-UI]|r " .. keyStr .. " -> " .. cmd) end end end) @@ -1702,7 +1728,7 @@ function AB:EnterKeyBindMode() desc:SetPoint("TOP", title, "BOTTOM", 0, -6) desc:SetWidth(300) desc:SetJustifyH("CENTER") - desc:SetText("悬停按钮 + 按键绑定 | 右键清除 | 滚轮绑定") + desc:SetText("悬停按钮 + 按键/鼠标键/滚轮绑定 | 右键清除") desc:SetTextColor(0.82, 0.82, 0.82) local saveBtn = CreateFrame("Button", "SFramesKeyBindSave", statusFrame, "UIPanelButtonTemplate") @@ -1713,12 +1739,57 @@ function AB:EnterKeyBindMode() saveBtn:SetScript("OnClick", function() AB:ExitKeyBindMode() end) + + local _T = SFrames.ActiveTheme + local function HideBindBtnTex(tex) + if not tex then return end + if tex.SetTexture then tex:SetTexture(nil) end + if tex.SetAlpha then tex:SetAlpha(0) end + if tex.Hide then tex:Hide() end + end + HideBindBtnTex(saveBtn:GetNormalTexture()) + HideBindBtnTex(saveBtn:GetPushedTexture()) + HideBindBtnTex(saveBtn:GetHighlightTexture()) + HideBindBtnTex(saveBtn:GetDisabledTexture()) + for _, sfx in ipairs({"Left","Right","Middle"}) do + local t = _G["SFramesKeyBindSave"..sfx] + if t then t:SetAlpha(0); t:Hide() end + end + saveBtn:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 14, + insets = { left = 3, right = 3, top = 3, bottom = 3 }, + }) + saveBtn:SetBackdropColor(_T.btnBg[1], _T.btnBg[2], _T.btnBg[3], _T.btnBg[4]) + saveBtn:SetBackdropBorderColor(_T.btnBorder[1], _T.btnBorder[2], _T.btnBorder[3], _T.btnBorder[4]) local fs = saveBtn:GetFontString() - if fs then fs:SetFont(SFrames:GetFont(), 11) end + if fs then + fs:SetFont(SFrames:GetFont(), 11, "OUTLINE") + fs:SetTextColor(_T.btnText[1], _T.btnText[2], _T.btnText[3]) + end + saveBtn:SetScript("OnEnter", function() + this:SetBackdropColor(_T.btnHoverBg[1], _T.btnHoverBg[2], _T.btnHoverBg[3], _T.btnHoverBg[4]) + this:SetBackdropBorderColor(_T.btnHoverBorder[1], _T.btnHoverBorder[2], _T.btnHoverBorder[3], _T.btnHoverBorder[4]) + local t = this:GetFontString() + if t then t:SetTextColor(_T.btnActiveText[1], _T.btnActiveText[2], _T.btnActiveText[3]) end + end) + saveBtn:SetScript("OnLeave", function() + this:SetBackdropColor(_T.btnBg[1], _T.btnBg[2], _T.btnBg[3], _T.btnBg[4]) + this:SetBackdropBorderColor(_T.btnBorder[1], _T.btnBorder[2], _T.btnBorder[3], _T.btnBorder[4]) + local t = this:GetFontString() + if t then t:SetTextColor(_T.btnText[1], _T.btnText[2], _T.btnText[3]) end + end) + saveBtn:SetScript("OnMouseDown", function() + this:SetBackdropColor(_T.btnDownBg[1], _T.btnDownBg[2], _T.btnDownBg[3], _T.btnDownBg[4]) + end) + saveBtn:SetScript("OnMouseUp", function() + this:SetBackdropColor(_T.btnHoverBg[1], _T.btnHoverBg[2], _T.btnHoverBg[3], _T.btnHoverBg[4]) + end) end statusFrame:Show() - DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami-UI]|r 按键绑定模式已开启。悬停按钮后按键绑定,右键清除,点击保存退出。") + DEFAULT_CHAT_FRAME:AddMessage("|cff88ccff[Nanami-UI]|r 按键绑定模式已开启。悬停按钮后按键/鼠标键/滚轮绑定,右键清除,ESC或点击保存退出。") end function AB:ExitKeyBindMode() diff --git a/BookUI.lua b/BookUI.lua index badbb08..2b71930 100644 --- a/BookUI.lua +++ b/BookUI.lua @@ -74,6 +74,33 @@ local function TextHeight(fs, fallback) return fallback or 14 end +local function StripItemTextHTML(text) + if not text or text == "" then return text end + if not string.find(text, "<") then return text end + + text = string.gsub(text, "", "") + text = string.gsub(text, "", "") + text = string.gsub(text, "", "") + text = string.gsub(text, "", "") + text = string.gsub(text, "]*>", "") + text = string.gsub(text, "]*>", "") + text = string.gsub(text, "", "\n") + text = string.gsub(text, "", "\n") + text = string.gsub(text, "]*>", "") + text = string.gsub(text, "]*>", "") + text = string.gsub(text, "

", "\n") + text = string.gsub(text, "

", "\n") + text = string.gsub(text, "
", "\n") + text = string.gsub(text, "
", "\n") + text = string.gsub(text, "
", "\n") + text = string.gsub(text, "<[^>]+>", "") + + text = string.gsub(text, "^[\n%s]+", "") + text = string.gsub(text, "[\n%s]+$", "") + text = string.gsub(text, "\n\n\n+", "\n\n") + return text +end + local function IsTrue(v) return v == true or v == 1 end @@ -178,7 +205,7 @@ local function UpdateBookContent() text = ItemTextPageText:GetText() or "" end if text and text ~= "" then - -- Keep paragraph gap readable: collapse 3+ consecutive newlines. + text = StripItemTextHTML(text) text = string.gsub(text, "\n%s*\n%s*\n+", "\n\n") end local pageNum = ItemTextGetPage and tonumber(ItemTextGetPage()) or 1 diff --git a/Chat.lua b/Chat.lua index 83d3445..9e63ff8 100644 --- a/Chat.lua +++ b/Chat.lua @@ -6016,6 +6016,68 @@ function SFrames.Chat:StyleEditBox() self.emoteButton = eb end + if not self.langButton then + if RDbItems and RDbItems.TriStateToggle then + local lb = CreateFrame("Button", "SFramesChatLangButton", buttonParent) + lb:SetWidth(18) + lb:SetHeight(18) + lb:SetFrameStrata("DIALOG") + lb:SetFrameLevel(btnLevel) + + local fs = lb:CreateFontString(nil, "ARTWORK") + fs:SetFont(cfg.fontPath or "Fonts\\ARIALN.TTF", 11, "OUTLINE") + fs:SetAllPoints(lb) + fs:SetJustifyH("CENTER") + fs:SetJustifyV("MIDDLE") + lb.fontString = fs + + local function UpdateLangButtonText() + local mode = RDbItemsCfg and RDbItemsCfg.mode + if mode == "en" then + fs:SetText("E") + fs:SetTextColor(0.4, 0.8, 1.0) + elseif mode == "cn" then + fs:SetText("中") + fs:SetTextColor(1.0, 0.82, 0.4) + else + fs:SetText("--") + fs:SetTextColor(0.6, 0.6, 0.6) + end + end + + lb:SetScript("OnClick", function() + RDbItems.TriStateToggle(this) + UpdateLangButtonText() + end) + lb:SetScript("OnEnter", function() + GameTooltip:SetOwner(this, "ANCHOR_TOP") + local mode = RDbItemsCfg and RDbItemsCfg.mode + if mode == "en" then + GameTooltip:SetText("物品链接语言: 英文") + elseif mode == "cn" then + GameTooltip:SetText("物品链接语言: 中文") + else + GameTooltip:SetText("物品链接语言: 自动") + end + GameTooltip:AddLine("点击切换 Shift+点击物品时的链接语言", 0.7, 0.7, 0.7) + GameTooltip:Show() + end) + lb:SetScript("OnLeave", function() GameTooltip:Hide() end) + + lb:EnableMouse(true) + local hl = lb:CreateTexture(nil, "HIGHLIGHT") + hl:SetTexture("Interface\\Buttons\\WHITE8X8") + hl:SetAllPoints(lb) + hl:SetAlpha(0.15) + + UpdateLangButtonText() + lb._UpdateText = UpdateLangButtonText + self.langButton = lb + end + elseif self.langButton and self.langButton._UpdateText then + self.langButton._UpdateText() + end + -- Re-parent in case buttons were created by an older version. self.rollButton:SetParent(self.editBackdrop) self.emoteButton:SetParent(self.editBackdrop) @@ -6025,12 +6087,22 @@ function SFrames.Chat:StyleEditBox() if self.emoteButton.SetFrameLevel then self.emoteButton:SetFrameLevel((editBox:GetFrameLevel() or 1) + 2) end + if self.langButton then + self.langButton:SetParent(self.editBackdrop) + if self.langButton.SetFrameLevel then + self.langButton:SetFrameLevel((editBox:GetFrameLevel() or 1) + 2) + end + end -- Re-anchor buttons every call so they track editBackdrop position self.rollButton:ClearAllPoints() self.rollButton:SetPoint("RIGHT", self.editBackdrop, "RIGHT", -4, 0) self.emoteButton:ClearAllPoints() self.emoteButton:SetPoint("RIGHT", self.rollButton, "LEFT", -2, 0) + if self.langButton then + self.langButton:ClearAllPoints() + self.langButton:SetPoint("RIGHT", self.emoteButton, "LEFT", -2, 0) + end local function SaveFreeEditBoxPosition() if EnsureDB().editBoxPosition ~= "free" then return end @@ -6073,7 +6145,8 @@ function SFrames.Chat:StyleEditBox() editBox:SetParent(self.editBackdrop) editBox:ClearAllPoints() editBox:SetPoint("TOPLEFT", self.editBackdrop, "TOPLEFT", 4, -3) - editBox:SetPoint("BOTTOMRIGHT", self.editBackdrop, "BOTTOMRIGHT", -45, 3) + local rightPad = self.langButton and -65 or -45 + editBox:SetPoint("BOTTOMRIGHT", self.editBackdrop, "BOTTOMRIGHT", rightPad, 3) editBox:EnableMouse(true) self.editBackdrop:SetBackdropColor(CFG_THEME.panelBg[1], CFG_THEME.panelBg[2], CFG_THEME.panelBg[3], 0.96) local borderR, borderG, borderB = self:GetBorderColorRGB() diff --git a/ConfigUI.lua b/ConfigUI.lua index 91298ac..717177c 100644 --- a/ConfigUI.lua +++ b/ConfigUI.lua @@ -476,6 +476,7 @@ local function EnsureDB() if SFramesDB.smoothBars == nil then SFramesDB.smoothBars = true end if SFramesDB.mobRealHealth == nil then SFramesDB.mobRealHealth = true end if SFramesDB.showItemLevel == nil then SFramesDB.showItemLevel = true end + if SFramesDB.showTooltipIDs == nil then SFramesDB.showTooltipIDs = true end if SFramesDB.itemCompare == nil then SFramesDB.itemCompare = true end if SFramesDB.gearScore == nil then SFramesDB.gearScore = true end @@ -992,6 +993,13 @@ function SFrames.ConfigUI:BuildUIPage() )) CreateDesc(enhSection, "Tooltip 显示当前职业各天赋EP评分及硬核评分,需要 ItemBonusLib", 36, -142, font, 480) + table.insert(controls, CreateCheckBox(enhSection, + "显示物品/法术ID", 270, -126, + function() return SFramesDB.showTooltipIDs ~= false end, + function(checked) SFramesDB.showTooltipIDs = checked end + )) + CreateDesc(enhSection, "在 Tooltip 中显示物品ID和法术ID", 292, -142, font, 218) + CreateLabel(enhSection, "提示:以上功能需要安装对应的 !Libs 库才能生效。", 14, -172, font, 10, 0.6, 0.6, 0.65) -- ── ShaguTweaks 功能移植 ────────────────────────────────────── @@ -1911,7 +1919,7 @@ function SFrames.ConfigUI:BuildActionBarPage() end) CreateDesc(abSection, - "悬停动作条/姿态栏/宠物栏按钮,按下键盘键或滚轮绑定。右键清除。ESC 退出。也可用 /nui bind", + "悬停动作条/姿态栏/宠物栏按钮,按下键盘键/鼠标键(3-5)/滚轮绑定。右键清除。ESC 退出。也可用 /nui bind", 14, -100, font, 480) table.insert(controls, CreateSlider(abSection, "按钮大小", 14, -130, 150, 24, 48, 1, diff --git a/Core.lua b/Core.lua index bcf2191..ca3752f 100644 --- a/Core.lua +++ b/Core.lua @@ -187,6 +187,28 @@ function SFrames:GetAuraTimeLeft(unit, index, isBuff) end end end + + -- Nanami-Plates SpellDB: combat log + spell DB tracking (most accurate for debuffs) + if not isBuff and NanamiPlates_SpellDB and NanamiPlates_SpellDB.UnitDebuff then + local effect, rank, tex, stacks, dtype, duration, timeleft, isOwn = NanamiPlates_SpellDB:UnitDebuff(unit, index) + if timeleft and timeleft > 0 then + return timeleft, effect + end + if effect and effect ~= "" and duration and duration > 0 + and NanamiPlates_Auras and NanamiPlates_Auras.timers then + local unitKey = (UnitGUID and UnitGUID(unit)) or UnitName(unit) or "" + local cached = NanamiPlates_Auras.timers[unitKey .. "_" .. effect] + if not cached and UnitName(unit) then + cached = NanamiPlates_Auras.timers[UnitName(unit) .. "_" .. effect] + end + if cached and cached.startTime and cached.duration then + local remaining = cached.duration - (GetTime() - cached.startTime) + if remaining > 0 then + return remaining, effect + end + end + end + end -- Fallback to ShaguTweaks libdebuff if available (Debuffs only usually) if ShaguTweaks and ShaguTweaks.libdebuff then @@ -374,6 +396,43 @@ function SFrames:InitSlashCommands() else SFrames:Print("ActionBars module unavailable.") end + elseif cmd == "debugbuffs" or cmd == "db" then + local hex = SFrames.Theme and SFrames.Theme:GetAccentHex() or "ffffb3d9" + DEFAULT_CHAT_FRAME:AddMessage("|c" .. hex .. "[Nanami-UI]|r 当前所有 Buff:") + for i = 0, 31 do + local buffIndex = GetPlayerBuff(i, "HELPFUL") + if buffIndex and buffIndex >= 0 then + local name = SFrames:GetBuffName(buffIndex) + local tex = GetPlayerBuffTexture(buffIndex) or "?" + local tl = GetPlayerBuffTimeLeft(buffIndex) + local timeStr = (tl and tl > 0 and tl < 99999) and string.format("%.1fs", tl) or "N/A" + local hidden = SFrames:IsBuffHidden(buffIndex) and "|cffff4444[隐藏]|r" or "" + DEFAULT_CHAT_FRAME:AddMessage(string.format(" #%d: |cffffd100%s|r %s %s", i, name or "(nil)", timeStr, hidden)) + end + end + elseif cmd == "hidebuff" or cmd == "hb" then + if args == "" then + SFrames:Print("用法: /nui hidebuff ") + else + if not SFramesDB.hiddenBuffs then SFramesDB.hiddenBuffs = {} end + SFramesDB.hiddenBuffs[args] = true + SFrames:Print("已隐藏 Buff: |cffffd100" .. args .. "|r") + end + elseif cmd == "unhidebuff" or cmd == "uhb" then + if args == "" then + SFrames:Print("用法: /nui unhidebuff ") + else + if SFramesDB.hiddenBuffs then SFramesDB.hiddenBuffs[args] = nil end + SFrames:Print("已取消隐藏 Buff: |cffffd100" .. args .. "|r") + end + elseif cmd == "listhidden" or cmd == "lh" then + local hex = SFrames.Theme and SFrames.Theme:GetAccentHex() or "ffffb3d9" + DEFAULT_CHAT_FRAME:AddMessage("|c" .. hex .. "[Nanami-UI]|r 已隐藏的 Buff 列表:") + if SFramesDB.hiddenBuffs then + for name, _ in pairs(SFramesDB.hiddenBuffs) do + DEFAULT_CHAT_FRAME:AddMessage(" |cffffd100" .. name .. "|r (用户自定义)") + end + end elseif cmd == "config" or cmd == "" then if SFrames.ConfigUI and SFrames.ConfigUI.Build then SFrames.ConfigUI:Build("ui") end else diff --git a/MinimapBuffs.lua b/MinimapBuffs.lua index aa5cd81..ee16e1b 100644 --- a/MinimapBuffs.lua +++ b/MinimapBuffs.lua @@ -8,6 +8,31 @@ local UPDATE_INTERVAL = 0.2 local BASE_X = -209 local BASE_Y = -26 +-------------------------------------------------------------------------------- +-- Buff Blacklist: Turtle WoW internal/proc buffs that should not appear +-------------------------------------------------------------------------------- +local HIDDEN_BUFF_NAMES = { + ["防御状态"] = true, +} + +function SFrames:GetBuffName(buffIndex) + if not SFrames.Tooltip then return nil end + SFrames.Tooltip:ClearLines() + SFrames.Tooltip:SetPlayerBuff(buffIndex) + local nameObj = SFramesScanTooltipTextLeft1 + return nameObj and nameObj:GetText() +end + +function SFrames:IsBuffHidden(buffIndex) + local name = self:GetBuffName(buffIndex) + if not name or name == "" then return false end + if HIDDEN_BUFF_NAMES[name] then return true end + if SFramesDB and type(SFramesDB.hiddenBuffs) == "table" and SFramesDB.hiddenBuffs[name] then + return true + end + return false +end + local ROUND_BACKDROP = { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", @@ -298,37 +323,39 @@ function MB:UpdateBuffs() for i = 0, 31 do local buffIndex, untilCancelled = GetPlayerBuff(i, "HELPFUL") if buffIndex and buffIndex >= 0 then - slotIdx = slotIdx + 1 - if slotIdx > MAX_BUFFS then break end + if not SFrames:IsBuffHidden(buffIndex) then + slotIdx = slotIdx + 1 + if slotIdx > MAX_BUFFS then break end - local btn = self.buffSlots[slotIdx] - local texture = GetPlayerBuffTexture(buffIndex) - if texture then - btn.icon:SetTexture(texture) - btn.buffIndex = buffIndex - btn._sfSimulated = false - btn._isWeaponEnchant = false - btn._weaponSlotID = nil + local btn = self.buffSlots[slotIdx] + local texture = GetPlayerBuffTexture(buffIndex) + if texture then + btn.icon:SetTexture(texture) + btn.buffIndex = buffIndex + btn._sfSimulated = false + btn._isWeaponEnchant = false + btn._weaponSlotID = nil - local apps = GetPlayerBuffApplications(buffIndex) - if apps and apps > 1 then - btn.count:SetText(apps) - btn.count:Show() + local apps = GetPlayerBuffApplications(buffIndex) + if apps and apps > 1 then + btn.count:SetText(apps) + btn.count:Show() + else + btn.count:SetText("") + btn.count:Hide() + end + + local timeLeft = GetPlayerBuffTimeLeft(buffIndex) + SetTimerFromSeconds(btn, timeLeft, untilCancelled, showTimer) + + btn:SetBackdropBorderColor(0.25, 0.25, 0.30, 1) + btn:Show() else - btn.count:SetText("") - btn.count:Hide() + btn:Hide() + btn.buffIndex = -1 + btn._isWeaponEnchant = false + btn._weaponSlotID = nil end - - local timeLeft = GetPlayerBuffTimeLeft(buffIndex) - SetTimerFromSeconds(btn, timeLeft, untilCancelled, showTimer) - - btn:SetBackdropBorderColor(0.25, 0.25, 0.30, 1) - btn:Show() - else - btn:Hide() - btn.buffIndex = -1 - btn._isWeaponEnchant = false - btn._weaponSlotID = nil end end end diff --git a/Nanami-UI.toc b/Nanami-UI.toc index 1dd3af1..c5f6796 100644 --- a/Nanami-UI.toc +++ b/Nanami-UI.toc @@ -3,7 +3,7 @@ ## Notes: 现代极简猫系单位框架插件 - 喵~ (Nanami-UI) ## Author: AI Assistant ## Version: 1.0.0 -## OptionalDeps: ShaguTweaks, Blizzard_CombatLog, HealComm-1.0, DruidManaLib-1.0, !Libs, pfQuest +## OptionalDeps: ShaguTweaks, Blizzard_CombatLog, HealComm-1.0, DruidManaLib-1.0, !Libs, pfQuest, RDbItems ## SavedVariablesPerCharacter: SFramesDB ## SavedVariables: SFramesGlobalDB Bindings.xml diff --git a/Tooltip.lua b/Tooltip.lua index 23eab43..d0d349c 100644 --- a/Tooltip.lua +++ b/Tooltip.lua @@ -822,9 +822,18 @@ local function IC_AddSellPrice(tooltip, link, count) end -------------------------------------------------------------------------------- +local function IC_AppendItemId(tooltip, link) + if SFramesDB and SFramesDB.showTooltipIDs == false then return end + local itemId = IC_GetItemIdFromLink(link) + if itemId then + tooltip:AddLine("物品ID: " .. itemId, 0.55, 0.55, 0.70) + end +end + local function IC_EnhanceTooltip(tooltip, link, count, skipSellPrice) if not link then return end IC_AppendItemLevel(tooltip, link) + IC_AppendItemId(tooltip, link) if not SFramesDB or SFramesDB.itemCompare ~= false then local lib = IC_GetLib() if lib then @@ -1174,8 +1183,180 @@ function IC:HookTooltips() SetTooltipMoney(ItemRefTooltip, price) ItemRefTooltip:Show() end + if itemId then + ItemRefTooltip:AddLine("物品ID: " .. itemId, 0.55, 0.55, 0.70) + ItemRefTooltip:Show() + end end end) end end + + --------------------------------------------------------------------------- + -- Spell ID display + --------------------------------------------------------------------------- + local spellIdCache = {} + + local function TT_AddSpellIdLine(tooltip, sid) + if not tooltip or not sid then return end + if SFramesDB and SFramesDB.showTooltipIDs == false then return end + tooltip:AddLine("法术ID: " .. sid, 0.55, 0.55, 0.70) + tooltip:Show() + end + + local function TT_ExtractSpellId(link) + if not link then return nil end + local _, _, id = string.find(link, "spell:(%d+)") + return id + end + + local function TT_CleanName(text) + if not text then return nil end + text = string.gsub(text, "|c%x%x%x%x%x%x%x%x", "") + text = string.gsub(text, "|r", "") + text = string.gsub(text, "|H.-|h", "") + text = string.gsub(text, "|h", "") + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + return text + end + + local function TT_BuildSpellCache() + spellIdCache = {} + local function ScanBook(bookType, count) + if not GetSpellLink or count == 0 then return end + for i = 1, count do + local name, rank = GetSpellName(i, bookType) + if name then + local link = GetSpellLink(i, bookType) + local sid = TT_ExtractSpellId(link) + if sid then + spellIdCache[name] = sid + local clean = TT_CleanName(name) + if clean and clean ~= name then + spellIdCache[clean] = sid + end + end + end + end + end + + local bookType = BOOKTYPE_SPELL or "spell" + local numTabs = GetNumSpellTabs and GetNumSpellTabs() or 0 + local total = 0 + for t = 1, numTabs do + local _, _, _, count = GetSpellTabInfo(t) + total = total + (count or 0) + end + ScanBook(bookType, total) + + if HasPetSpells then + local numPet = HasPetSpells() or 0 + if numPet > 0 then + ScanBook(BOOKTYPE_PET or "pet", numPet) + end + end + end + + local spellCacheFrame = CreateFrame("Frame") + spellCacheFrame:RegisterEvent("SPELLS_CHANGED") + spellCacheFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + spellCacheFrame:RegisterEvent("LEARNED_SPELL_IN_TAB") + spellCacheFrame:RegisterEvent("PET_BAR_UPDATE") + spellCacheFrame:SetScript("OnEvent", function() + TT_BuildSpellCache() + end) + + local function TT_LookupSpellId(spellBookIdx, bookType) + if GetSpellLink then + local link = GetSpellLink(spellBookIdx, bookType) + local sid = TT_ExtractSpellId(link) + if sid then return sid end + end + local name = GetSpellName(spellBookIdx, bookType) + if name then + if spellIdCache[name] then return spellIdCache[name] end + local clean = TT_CleanName(name) + if clean and spellIdCache[clean] then return spellIdCache[clean] end + end + return nil + end + + local function TT_LookupByTooltipName(tooltip) + local ttName = tooltip and tooltip:GetName() + if not ttName then return nil end + local left1 = _G[ttName .. "TextLeft1"] + if not left1 then return nil end + local raw = left1:GetText() + if not raw or raw == "" then return nil end + + if spellIdCache[raw] then return spellIdCache[raw] end + + local clean = TT_CleanName(raw) + if clean and spellIdCache[clean] then return spellIdCache[clean] end + + local noRank = string.gsub(clean or raw, "%s*%(.-%)%s*$", "") + if noRank ~= (clean or raw) and spellIdCache[noRank] then + return spellIdCache[noRank] + end + + return nil + end + + local orig_SetSpell = GameTooltip.SetSpell + if orig_SetSpell then + GameTooltip.SetSpell = function(self, id, bookType) + orig_SetSpell(self, id, bookType) + pcall(function() + local sid = TT_LookupSpellId(id, bookType) + if sid then + TT_AddSpellIdLine(self, sid) + end + end) + end + end + + local orig_SetUnitBuff = GameTooltip.SetUnitBuff + if orig_SetUnitBuff then + GameTooltip.SetUnitBuff = function(self, unit, idx, filter) + orig_SetUnitBuff(self, unit, idx, filter) + pcall(function() + local sid = TT_LookupByTooltipName(self) + if sid then TT_AddSpellIdLine(self, sid) end + end) + end + end + + local orig_SetUnitDebuff = GameTooltip.SetUnitDebuff + if orig_SetUnitDebuff then + GameTooltip.SetUnitDebuff = function(self, unit, idx, filter) + orig_SetUnitDebuff(self, unit, idx, filter) + pcall(function() + local sid = TT_LookupByTooltipName(self) + if sid then TT_AddSpellIdLine(self, sid) end + end) + end + end + + local orig_SetCraftSpell = GameTooltip.SetCraftSpell + if orig_SetCraftSpell then + GameTooltip.SetCraftSpell = function(self, idx) + orig_SetCraftSpell(self, idx) + pcall(function() + local sid = TT_LookupByTooltipName(self) + if sid then TT_AddSpellIdLine(self, sid) end + end) + end + end + + local orig_SetTrackingSpell = GameTooltip.SetTrackingSpell + if orig_SetTrackingSpell then + GameTooltip.SetTrackingSpell = function(self) + orig_SetTrackingSpell(self) + pcall(function() + local sid = TT_LookupByTooltipName(self) + if sid then TT_AddSpellIdLine(self, sid) end + end) + end + end end diff --git a/Tweaks.lua b/Tweaks.lua index c67d7eb..df120a4 100644 --- a/Tweaks.lua +++ b/Tweaks.lua @@ -789,6 +789,13 @@ local function InitPopupSkin() text = _A.nameText or { 0.90, 0.88, 0.94 }, } + local function HidePopupTex(tex) + if not tex then return end + if tex.SetTexture then tex:SetTexture(nil) end + if tex.SetAlpha then tex:SetAlpha(0) end + if tex.Hide then tex:Hide() end + end + local function SkinButton(btn) if not btn then return end local regions = { btn:GetRegions() } @@ -801,6 +808,17 @@ local function InitPopupSkin() end end + HidePopupTex(btn.GetNormalTexture and btn:GetNormalTexture()) + HidePopupTex(btn.GetPushedTexture and btn:GetPushedTexture()) + HidePopupTex(btn.GetHighlightTexture and btn:GetHighlightTexture()) + HidePopupTex(btn.GetDisabledTexture and btn:GetDisabledTexture()) + + local btnName = btn:GetName() or "" + for _, sfx in ipairs({"Left", "Right", "Middle"}) do + local t = _G[btnName .. sfx] + if t then t:SetAlpha(0); t:Hide() end + end + btn:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", @@ -815,6 +833,39 @@ local function InitPopupSkin() fs:SetFont(font, 12, "OUTLINE") fs:SetTextColor(P.text[1], P.text[2], P.text[3]) end + + if not btn.nanamiPopupStyled then + btn.nanamiPopupStyled = true + local origEnter = btn:GetScript("OnEnter") + local origLeave = btn:GetScript("OnLeave") + + btn:SetScript("OnEnter", function() + if origEnter then origEnter() end + this:SetBackdropColor(_A.btnHoverBg[1], _A.btnHoverBg[2], _A.btnHoverBg[3], _A.btnHoverBg[4]) + if _A.btnHoverBorder then + this:SetBackdropBorderColor(_A.btnHoverBorder[1], _A.btnHoverBorder[2], _A.btnHoverBorder[3], _A.btnHoverBorder[4]) + elseif _A.btnHoverBd then + this:SetBackdropBorderColor(_A.btnHoverBd[1], _A.btnHoverBd[2], _A.btnHoverBd[3], _A.btnHoverBd[4]) + end + local t = this:GetFontString() + if t and _A.btnActiveText then t:SetTextColor(_A.btnActiveText[1], _A.btnActiveText[2], _A.btnActiveText[3]) end + end) + btn:SetScript("OnLeave", function() + if origLeave then origLeave() end + this:SetBackdropColor(P.btnBg[1], P.btnBg[2], P.btnBg[3], P.btnBg[4]) + this:SetBackdropBorderColor(P.btnBd[1], P.btnBd[2], P.btnBd[3], P.btnBd[4]) + local t = this:GetFontString() + if t then t:SetTextColor(P.text[1], P.text[2], P.text[3]) end + end) + btn:SetScript("OnMouseDown", function() + if _A.btnDownBg then + this:SetBackdropColor(_A.btnDownBg[1], _A.btnDownBg[2], _A.btnDownBg[3], _A.btnDownBg[4]) + end + end) + btn:SetScript("OnMouseUp", function() + this:SetBackdropColor(_A.btnHoverBg[1], _A.btnHoverBg[2], _A.btnHoverBg[3], _A.btnHoverBg[4]) + end) + end end local function SkinPopupFrame(frame) diff --git a/Units/Pet.lua b/Units/Pet.lua index eee161a..1eaed82 100644 --- a/Units/Pet.lua +++ b/Units/Pet.lua @@ -137,6 +137,8 @@ function SFrames.Pet:Initialize() self.frame.unit = "pet" f:Hide() + self:CreateCastbar() + SFrames:RegisterEvent("UNIT_PET", function() if arg1 == "player" then self:UpdateAll() end end) SFrames:RegisterEvent("PET_BAR_UPDATE", function() self:UpdateAll() end) SFrames:RegisterEvent("UNIT_HEALTH", function() if arg1 == "pet" then self:UpdateHealth() end end) @@ -785,3 +787,157 @@ function SFrames.Pet:UpdateFoodButton() if self.foodPanel then self.foodPanel:Hide() end end end + +-------------------------------------------------------------------------------- +-- Pet Castbar +-------------------------------------------------------------------------------- + +function SFrames.Pet:CreateCastbar() + local f = self.frame + local cbHeight = SFrames.Config.castbarHeight + + local cb = SFrames:CreateStatusBar(f, "SFramesPetCastbar") + cb:SetHeight(cbHeight) + cb:SetPoint("BOTTOMLEFT", f, "TOPLEFT", 0, 4) + cb:SetPoint("BOTTOMRIGHT", f, "TOPRIGHT", -(cbHeight + 6), 4) + + local cbbg = CreateFrame("Frame", nil, f) + cbbg:SetPoint("TOPLEFT", cb, "TOPLEFT", -1, 1) + cbbg:SetPoint("BOTTOMRIGHT", cb, "BOTTOMRIGHT", 1, -1) + cbbg:SetFrameLevel(cb:GetFrameLevel() - 1) + SFrames:CreateUnitBackdrop(cbbg) + + cb.bg = cb:CreateTexture(nil, "BACKGROUND") + cb.bg:SetAllPoints() + cb.bg:SetTexture(SFrames:GetTexture()) + cb.bg:SetVertexColor(_A.slotBg[1], _A.slotBg[2], _A.slotBg[3], _A.slotBg[4] or 1) + cb:SetStatusBarColor(1, 0.7, 0) + + cb.text = SFrames:CreateFontString(cb, 10, "LEFT") + cb.text:SetPoint("LEFT", cb, "LEFT", 4, 0) + + cb.time = SFrames:CreateFontString(cb, 10, "RIGHT") + cb.time:SetPoint("RIGHT", cb, "RIGHT", -4, 0) + + cb.icon = cb:CreateTexture(nil, "ARTWORK") + cb.icon:SetWidth(cbHeight + 2) + cb.icon:SetHeight(cbHeight + 2) + cb.icon:SetPoint("LEFT", cb, "RIGHT", 4, 0) + cb.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93) + + local ibg = CreateFrame("Frame", nil, f) + ibg:SetPoint("TOPLEFT", cb.icon, "TOPLEFT", -1, 1) + ibg:SetPoint("BOTTOMRIGHT", cb.icon, "BOTTOMRIGHT", 1, -1) + ibg:SetFrameLevel(cb:GetFrameLevel() - 1) + SFrames:CreateUnitBackdrop(ibg) + + cb:Hide() + cbbg:Hide() + cb.icon:Hide() + ibg:Hide() + + f.castbar = cb + f.castbar.cbbg = cbbg + f.castbar.ibg = ibg + + f.castbarUpdater = CreateFrame("Frame", nil, f) + f.castbarUpdater:SetScript("OnUpdate", function() SFrames.Pet:CastbarOnUpdate() end) +end + +function SFrames.Pet:CastbarOnUpdate() + local cb = self.frame.castbar + if not UnitExists("pet") then + cb:Hide() + cb.cbbg:Hide() + cb.icon:Hide() + cb.ibg:Hide() + return + end + + local cast, texture, startTime, endTime, channel + + -- 1) Try native UnitCastingInfo / UnitChannelInfo (TurtleWoW extended API) + local _UnitCastingInfo = UnitCastingInfo or (ShaguTweaks and ShaguTweaks.UnitCastingInfo) + if _UnitCastingInfo then + local c, _, _, tex, st, et = _UnitCastingInfo("pet") + if c then + cast, texture, startTime, endTime = c, tex, st, et + end + end + + if not cast then + local _UnitChannelInfo = UnitChannelInfo or (ShaguTweaks and ShaguTweaks.UnitChannelInfo) + if _UnitChannelInfo then + local c, _, _, tex, st, et = _UnitChannelInfo("pet") + if c then + cast, texture, startTime, endTime = c, tex, st, et + channel = true + end + end + end + + -- 2) Fallback: SuperWoW castdb (GUID-based) + if not cast and SFrames.castdb and UnitGUID then + local guid = UnitGUID("pet") + if guid then + local entry = SFrames.castdb[guid] + if entry and entry.cast and entry.start and entry.casttime then + local elapsed = GetTime() - entry.start + local duration = entry.casttime / 1000 + if elapsed < duration + 0.5 then + cast = entry.cast + texture = entry.icon + startTime = entry.start * 1000 + endTime = (entry.start + duration) * 1000 + channel = entry.channel + end + end + end + end + + if cast and startTime and endTime then + local duration = (endTime - startTime) / 1000 + local cur = GetTime() - (startTime / 1000) + + if channel then + cur = duration + (startTime / 1000) - GetTime() + end + + if cur > duration then cur = duration end + if cur < 0 then cur = 0 end + + cb:SetMinMaxValues(0, duration) + cb:SetValue(cur) + cb.text:SetText(cast) + cb.time:SetText(string.format("%.1f", channel and cur or math.max(duration - cur, 0))) + + if texture then + cb.icon:SetTexture(texture) + end + + cb:SetAlpha(1) + cb.cbbg:SetAlpha(1) + cb.icon:SetAlpha(1) + cb.ibg:SetAlpha(1) + + cb:Show() + cb.cbbg:Show() + cb.icon:Show() + cb.ibg:Show() + else + if cb:IsShown() then + local alpha = cb:GetAlpha() - 0.05 + if alpha > 0 then + cb:SetAlpha(alpha) + cb.cbbg:SetAlpha(alpha) + cb.icon:SetAlpha(alpha) + cb.ibg:SetAlpha(alpha) + else + cb:Hide() + cb.cbbg:Hide() + cb.icon:Hide() + cb.ibg:Hide() + end + end + end +end diff --git a/Units/Player.lua b/Units/Player.lua index 36f1981..89e42c8 100644 --- a/Units/Player.lua +++ b/Units/Player.lua @@ -1418,30 +1418,35 @@ function SFrames.Player:CreateAuras() end function SFrames.Player:UpdateAuras() - local timeNow = GetTime() - for i = 1, 16 do - local b = self.frame.buffs[i] - local buffIndex, untilCancelled = GetPlayerBuff(i - 1, "HELPFUL") - if buffIndex >= 0 then - local texture = GetPlayerBuffTexture(buffIndex) - if texture then - b.icon:SetTexture(texture) - b.buffIndex = buffIndex - b:Show() - - local timeLeft = GetPlayerBuffTimeLeft(buffIndex) - if timeLeft and timeLeft > 0 and timeLeft < 9999 then - b.cdText:SetText(SFrames:FormatTime(timeLeft)) + local slotIdx = 0 + for i = 0, 31 do + local buffIndex, untilCancelled = GetPlayerBuff(i, "HELPFUL") + if buffIndex and buffIndex >= 0 then + if not SFrames:IsBuffHidden(buffIndex) then + slotIdx = slotIdx + 1 + if slotIdx > 16 then break end + local b = self.frame.buffs[slotIdx] + local texture = GetPlayerBuffTexture(buffIndex) + if texture then + b.icon:SetTexture(texture) + b.buffIndex = buffIndex + b:Show() + + local timeLeft = GetPlayerBuffTimeLeft(buffIndex) + if timeLeft and timeLeft > 0 and timeLeft < 9999 then + b.cdText:SetText(SFrames:FormatTime(timeLeft)) + else + b.cdText:SetText("") + end else - b.cdText:SetText("") + b:Hide() end - else - b:Hide() end - else - b:Hide() end end + for j = slotIdx + 1, 16 do + self.frame.buffs[j]:Hide() + end end -- Initialization Hook for Auras and Castbar @@ -1455,8 +1460,8 @@ function SFrames.Player:Initialize() self.auraUpdater.timer = 0 self.auraUpdater:SetScript("OnUpdate", function() this.timer = this.timer + arg1 - SFrames.Player:UpdateFiveSecondRule() if this.timer >= 0.2 then + SFrames.Player:UpdateFiveSecondRule() SFrames.Player:UpdateAuras() SFrames.Player:UpdatePower() SFrames.Player:UpdateHealPrediction() diff --git a/Units/TalentTree.lua b/Units/TalentTree.lua index 7217f27..2d6596f 100644 --- a/Units/TalentTree.lua +++ b/Units/TalentTree.lua @@ -935,36 +935,86 @@ function SFrames.TalentTree:ApplyVirtualPoints() return end - if not self.applyQueue then self.applyQueue = {} end self.applyQueue = {} for tb = 1, GetNumTalentTabs() do + local treeTalents = {} for idx = 1, GetNumTalents(tb) do local name, icon, tier, column, realRank = GetTalentInfo(tb, idx) local virtRank = self:GetVirtualRank(tb, idx) local diff = virtRank - realRank if diff > 0 then for i = 1, diff do - table.insert(self.applyQueue, {tab = tb, index = idx}) + table.insert(treeTalents, {tab = tb, index = idx, tier = tier or 1}) end end end + table.sort(treeTalents, function(a, b) return a.tier < b.tier end) + for _, entry in ipairs(treeTalents) do + table.insert(self.applyQueue, entry) + end end - if table.getn(self.applyQueue) > 0 then - self.frame:SetScript("OnUpdate", function() - if table.getn(SFrames.TalentTree.applyQueue) > 0 then - local t = table.remove(SFrames.TalentTree.applyQueue, 1) - LearnTalent(t.tab, t.index) - else - this:SetScript("OnUpdate", nil) - SFrames.TalentTree.simMode = false - SFrames.TalentTree:UpdateSimModeLabel() - end - end) - else + if table.getn(self.applyQueue) == 0 then DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 没有新的天赋点数需要应用。") + return end + + local total = table.getn(self.applyQueue) + DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 开始应用 " .. total .. " 个天赋点...") + + if not self.applyEventFrame then + self.applyEventFrame = CreateFrame("Frame", "NanamiTalentApplyFrame") + end + self.applyEventFrame:UnregisterAllEvents() + + self.applyStallTimer = 0 + self.applyWaiting = false + + local function FinishApply() + self.applyEventFrame:UnregisterAllEvents() + self.applyEventFrame:SetScript("OnUpdate", nil) + self.applyQueue = {} + self.applyWaiting = false + self.simMode = false + self:UpdateSimModeLabel() + DEFAULT_CHAT_FRAME:AddMessage("|c" .. GetHex() .. "Nanami:|r 天赋应用完成。") + end + + local function TryNextTalent() + if table.getn(self.applyQueue) == 0 then + FinishApply() + return + end + local t = table.remove(self.applyQueue, 1) + LearnTalent(t.tab, t.index) + self.applyWaiting = true + self.applyStallTimer = 0 + end + + self.applyEventFrame:RegisterEvent("CHARACTER_POINTS_CHANGED") + self.applyEventFrame:SetScript("OnEvent", function() + if not SFrames.TalentTree.applyWaiting then return end + SFrames.TalentTree.applyWaiting = false + SFrames.TalentTree.applyStallTimer = 0 + TryNextTalent() + end) + + self.applyEventFrame:SetScript("OnUpdate", function() + if not SFrames.TalentTree.applyWaiting then return end + SFrames.TalentTree.applyStallTimer = (SFrames.TalentTree.applyStallTimer or 0) + (arg1 or 0) + if SFrames.TalentTree.applyStallTimer >= 2.0 then + SFrames.TalentTree.applyStallTimer = 0 + SFrames.TalentTree.applyWaiting = false + if table.getn(SFrames.TalentTree.applyQueue) > 0 then + TryNextTalent() + else + FinishApply() + end + end + end) + + TryNextTalent() end -------------------------------------------------------------------------------- diff --git a/Units/Target.lua b/Units/Target.lua index 966ba88..5240561 100644 --- a/Units/Target.lua +++ b/Units/Target.lua @@ -819,27 +819,69 @@ end function SFrames.Target:TickAuras() if not UnitExists("target") then return end - + local timeNow = GetTime() - + local npFormat = NanamiPlates_Auras and NanamiPlates_Auras.FormatTime + local hasNP = NanamiPlates_SpellDB and NanamiPlates_SpellDB.FindEffectData + + local targetName, targetLevel, targetGUID + if hasNP then + targetName = UnitName("target") + targetLevel = UnitLevel("target") or 0 + targetGUID = UnitGUID and UnitGUID("target") + end + + -- Buffs for i = 1, 16 do local b = self.frame.buffs[i] if b:IsShown() and b.expirationTime then local timeLeft = b.expirationTime - timeNow if timeLeft > 0 and timeLeft < 3600 then - b.cdText:SetText(SFrames:FormatTime(timeLeft)) + if npFormat then + local text, r, g, bc, a = npFormat(timeLeft) + b.cdText:SetText(text) + if r then b.cdText:SetTextColor(r, g, bc, a or 1) end + else + b.cdText:SetText(SFrames:FormatTime(timeLeft)) + end else b.cdText:SetText("") end end end - + + -- Debuffs: re-query SpellDB for live-accurate timers for i = 1, 16 do local b = self.frame.debuffs[i] - if b:IsShown() and b.expirationTime then - local timeLeft = b.expirationTime - timeNow - if timeLeft > 0 and timeLeft < 3600 then - b.cdText:SetText(SFrames:FormatTime(timeLeft)) + if b:IsShown() then + local timeLeft = nil + + if hasNP and b.effectName then + local data = targetGUID and NanamiPlates_SpellDB:FindEffectData(targetGUID, targetLevel, b.effectName) + if not data and targetName then + data = NanamiPlates_SpellDB:FindEffectData(targetName, targetLevel, b.effectName) + end + if data and data.start and data.duration then + local remaining = data.duration + data.start - timeNow + if remaining > 0 then + timeLeft = remaining + b.expirationTime = timeNow + remaining + end + end + end + + if not timeLeft and b.expirationTime then + timeLeft = b.expirationTime - timeNow + end + + if timeLeft and timeLeft > 0 and timeLeft < 3600 then + if npFormat then + local text, r, g, bc, a = npFormat(timeLeft) + b.cdText:SetText(text) + if r then b.cdText:SetTextColor(r, g, bc, a or 1) end + else + b.cdText:SetText(SFrames:FormatTime(timeLeft)) + end else b.cdText:SetText("") end @@ -895,29 +937,65 @@ function SFrames.Target:UpdateAuras() end -- Debuffs + local hasNP = NanamiPlates_SpellDB and NanamiPlates_SpellDB.UnitDebuff + local npFormat = NanamiPlates_Auras and NanamiPlates_Auras.FormatTime + for i = 1, 16 do local texture = UnitDebuff("target", i) local b = self.frame.debuffs[i] - b:SetID(i) -- Ensure ID is set for tooltips + b:SetID(i) if texture then b.icon:SetTexture(texture) - - -- Scrape tooltip for duration - SFrames.Tooltip:SetOwner(UIParent, "ANCHOR_NONE") - SFrames.Tooltip:ClearLines() - SFrames.Tooltip:SetUnitDebuff("target", i) - local timeLeft = SFrames:GetAuraTimeLeft("target", i, false) + + local timeLeft = 0 + local effectName = nil + + if hasNP then + local effect, rank, _, stacks, dtype, duration, npTimeLeft, isOwn = NanamiPlates_SpellDB:UnitDebuff("target", i) + effectName = effect + if npTimeLeft and npTimeLeft > 0 then + timeLeft = npTimeLeft + elseif effect and effect ~= "" and duration and duration > 0 + and NanamiPlates_Auras and NanamiPlates_Auras.timers then + local unitKey = (UnitGUID and UnitGUID("target")) or UnitName("target") or "" + local cached = NanamiPlates_Auras.timers[unitKey .. "_" .. effect] + if not cached and UnitName("target") then + cached = NanamiPlates_Auras.timers[UnitName("target") .. "_" .. effect] + end + if cached and cached.startTime and cached.duration then + local remaining = cached.duration - (GetTime() - cached.startTime) + if remaining > 0 then timeLeft = remaining end + end + end + end + + if timeLeft <= 0 then + SFrames.Tooltip:SetOwner(UIParent, "ANCHOR_NONE") + SFrames.Tooltip:ClearLines() + SFrames.Tooltip:SetUnitDebuff("target", i) + timeLeft = SFrames:GetAuraTimeLeft("target", i, false) + end + if timeLeft and timeLeft > 0 then b.expirationTime = GetTime() + timeLeft - b.cdText:SetText(SFrames:FormatTime(timeLeft)) + b.effectName = effectName + if npFormat then + local text, r, g, bc, a = npFormat(timeLeft) + b.cdText:SetText(text) + if r then b.cdText:SetTextColor(r, g, bc, a or 1) end + else + b.cdText:SetText(SFrames:FormatTime(timeLeft)) + end else b.expirationTime = nil + b.effectName = nil b.cdText:SetText("") end - + b:Show() else b.expirationTime = nil + b.effectName = nil b.cdText:SetText("") b:Hide() end