From 1cb7df984b10e82c4989432bd26bb491bfe52d23 Mon Sep 17 00:00:00 2001 From: James Gregory Date: Thu, 25 Jun 2026 23:23:14 +1000 Subject: [PATCH 1/2] fix pandemic glow for lifebloom, add marker on bar --- .../EUI_CooldownManager_Options.lua | 57 +++++++++++- .../EllesmereUICdmBuffBars.lua | 89 +++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) diff --git a/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua b/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua index 1f2a2584..5ba6eec1 100644 --- a/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua +++ b/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua @@ -3952,7 +3952,7 @@ initFrame:SetScript("OnEvent", function(self) if tbbPanRow and tbbPanRow._refreshPreview then tbbPanRow._refreshPreview() end C_Timer.After(0, function() EllesmereUI:RefreshPage() end) end, - tooltip = "Show a glow on the bar when the remaining duration is in the pandemic window (last 30%)" }, + tooltip = "Show a glow on the bar when the remaining duration is in the pandemic window (last 30%). Doesn't work for secret buff (Lifebloom on tank)" }, { type = "label", text = "Pandemic Glow Preview" }); y = y - h BuildPandemicPreview(tbbPanRow, tbbPandemicOff, SelectedTBB) @@ -4004,6 +4004,61 @@ initFrame:SetScript("OnEvent", function(self) end end + -- Row 4: Pandemic Mark -- static line at the 30% threshold. + -- Lifebloom-only: its pandemic window is 30% of current duration, + -- Other auras pandemic off base duration so 30% mark can be inaccurate + -- Only really useful for secret buff on another target + do + local _bd0 = SelectedTBB() + local _lbName = C_Spell.GetSpellName(33763) + local _curName = _bd0 and _bd0.spellID and C_Spell.GetSpellName(_bd0.spellID) + if _lbName and _curName and _lbName == _curName then + local markRow + markRow, h = W:DualRow(parent, y, + { type = "toggle", text = "Pandemic Mark", + getValue = function() local bd = SelectedTBB(); return bd and bd.pandemicMark end, + setValue = function(v) + local bd = SelectedTBB(); if not bd then return end + bd.pandemicMark = v; RefreshTBB(); EllesmereUI:RefreshPage() + end, + tooltip = "Draw a static line on the bar at the pandemic threshold (30%). Useful for Lifebloom on another target since glow doesn't work." }, + { type = "label", text = "" }); y = y - h + -- Inline swatch on the Pandemic Mark toggle (disabled when off). + do + local rgn = markRow._leftRegion + local ctrl = rgn._control + local markSwatch, updateMarkSwatch = EllesmereUI.BuildColorSwatch( + rgn, markRow:GetFrameLevel() + 3, + function() + local bd = SelectedTBB() + if not bd then return 1, 1, 0, 1 end + return bd.pandemicMarkR or 1, bd.pandemicMarkG or 1, bd.pandemicMarkB or 0, bd.pandemicMarkA or 1 + end, + function(r, g, b, a) + local bd = SelectedTBB(); if not bd then return end + bd.pandemicMarkR, bd.pandemicMarkG, bd.pandemicMarkB, bd.pandemicMarkA = r, g, b, a; RefreshTBB() + end, + true, 20) + PP.Point(markSwatch, "RIGHT", ctrl, "LEFT", -8, 0) + local markBlock = CreateFrame("Frame", nil, markSwatch) + markBlock:SetAllPoints(); markBlock:SetFrameLevel(markSwatch:GetFrameLevel() + 10) + markBlock:EnableMouse(true) + markBlock:SetScript("OnEnter", function() + EllesmereUI.ShowWidgetTooltip(markSwatch, EllesmereUI.DisabledTooltip("Pandemic Mark")) + end) + markBlock:SetScript("OnLeave", function() EllesmereUI.HideWidgetTooltip() end) + local function UpdateMarkSwatchState() + local bd = SelectedTBB() + local off = not bd or not bd.pandemicMark + if off then markSwatch:SetAlpha(0.3); markBlock:Show() + else markSwatch:SetAlpha(1); markBlock:Hide() end + end + EllesmereUI.RegisterWidgetRefresh(function() updateMarkSwatch(); UpdateMarkSwatchState() end) + UpdateMarkSwatchState() + end + end + end + -- Ensure bar frames exist before showing placeholders ns.BuildTrackedBuffBars() UpdateTBBPlaceholder() diff --git a/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua b/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua index 5db0c405..a5e48ed6 100644 --- a/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua +++ b/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua @@ -122,14 +122,45 @@ end ------------------------------------------------------------------------------- local _pandemicState = {} -- frame -> true when in pandemic local _pandemicHooked = {} -- frame -> true once hooks are installed +-- cooldownID: true once Blizzard has ever fired pandemic for that cooldown. +-- Used to gate the computed fallback so we only compute pandemic for +-- spells Blizzard's CooldownViewer never flags (e.g. Lifebloom), leaving +-- Blizzard-handled spells on native behaviour. +local _blizzPandemicCD = {} ns._pandemicState = _pandemicState +-- Pandemic refresh window (fraction of full duration remaining). +local PANDEMIC_THRESHOLD = 0.3 + +-- Fallback pandemic detection for auras Blizzard never pandemic-flags. The +-- player's own buff timing is not a secret value, so the refresh window can be +-- computed directly. +-- true -> in the pandemic window +-- false -> not in the window (clean read above threshold) +-- nil -> indeterminate, change nothing +local function ComputePandemicFallback(blzChild) + local iid = blzChild.auraInstanceID + local unit = blzChild.auraDataUnit + if not (iid and unit) then return nil end + local ok, ad = pcall(C_UnitAuras.GetAuraDataByAuraInstanceID, unit, iid) + if not ok or not ad then return nil end + local dur = ad.duration + local exp = ad.expirationTime + if not dur or not exp then return nil end + local isSec = issecretvalue + if isSec and (isSec(dur) or isSec(exp)) then return nil end + if dur <= 0 then return false end -- permanent / no-duration aura + return (exp - GetTime()) <= dur * PANDEMIC_THRESHOLD +end + function ns.HookPandemicState(frame) if not frame or _pandemicHooked[frame] then return end if not frame.ShowPandemicStateFrame then return end _pandemicHooked[frame] = true hooksecurefunc(frame, "ShowPandemicStateFrame", function(self) _pandemicState[self] = true + local cd = self.cooldownID + if cd then _blizzPandemicCD[cd] = true end -- Hide Blizzard's PandemicIcon unless "Blizzard Default" (-1). -- Custom glow styles (>0) replace it; None (0/false) suppresses it. local fc = ns._ecmeFC and ns._ecmeFC[self] @@ -626,6 +657,45 @@ local function ApplyTBBTickMarks(sb, cfg, tickCache, isVert, tickParent) end ns.ApplyTBBTickMarks = ApplyTBBTickMarks +-- Static pandemic-threshold marker. A single line drawn at the pandemic +-- fraction of the bar's fill axis. Static, no valud read, secret-safe. +local function ApplyPandemicMark(sb, cfg, bar, isVert) + local t = bar._pandemicMark + if not cfg.pandemicMark then + if t then t:Hide() end + return + end + local parent = bar._tickOverlay or sb + if not t then + t = parent:CreateTexture(nil, "OVERLAY", nil, 7) + t:SetSnapToPixelGrid(false) + t:SetTexelSnappingBias(0) + bar._pandemicMark = t + end + t:SetColorTexture(cfg.pandemicMarkR or 1, cfg.pandemicMarkG or 1, + cfg.pandemicMarkB or 0, cfg.pandemicMarkA or 1) + + local PP = EllesmereUI and EllesmereUI.PP + local barW, barH = sb:GetWidth(), sb:GetHeight() + if not barW or barW <= 0 or not barH or barH <= 0 then t:Hide(); return end + -- "30% remaining" boundary. reverseFill flips which end the fill drains + -- toward, so flip the line position to match. + local pos = cfg.reverseFill and (1 - PANDEMIC_THRESHOLD) or PANDEMIC_THRESHOLD + local thick = PP and PP.Scale(2) or 2 + t:ClearAllPoints() + if isVert then + local off = PP and PP.Scale(barH * pos) or (barH * pos) + t:SetSize(barW, thick) + t:SetPoint("BOTTOMLEFT", sb, "BOTTOMLEFT", 0, off) + else + local off = PP and PP.Scale(barW * pos) or (barW * pos) + t:SetSize(thick, barH) + t:SetPoint("TOPLEFT", sb, "TOPLEFT", off, 0) + end + t:Show() +end +ns.ApplyPandemicMark = ApplyPandemicMark + ------------------------------------------------------------------------------- -- Apply Visual Settings ------------------------------------------------------------------------------- @@ -892,6 +962,7 @@ local function ApplyTrackedBuffBarSettings(bar, cfg) bar._tickOverlay = to end ApplyTBBTickMarks(sb, cfg, bar._threshTicks, isVert, bar._tickOverlay) + ApplyPandemicMark(sb, cfg, bar, isVert) bar._ticksDirty = true end @@ -1562,6 +1633,23 @@ function ns.UpdateTrackedBuffBarTimers() -- pandemic alerts in Blizzard CDM settings. if _anyPandemic and cfg.pandemicGlow then local inPandemic = blzChild and _pandemicState[blzChild] + -- Fallback for auras Blizzard never pandemic-flags + -- (e.g. Lifebloom): compute the refresh window + -- ourselves + if not inPandemic and blzChild then + local cd = blzChild.cooldownID + if not (cd and _blizzPandemicCD[cd]) then + local fb = ComputePandemicFallback(blzChild) + if fb == true then + inPandemic = true + elseif fb == nil and bar._pandemicGlowActive then + -- Indeterminate read - do nothing + -- A real expiry is caught + -- by the isActive=false branch below. + inPandemic = true + end + end + end -- TBBs always show our glow (including Blizzard Default) -- because Blizzard's native PandemicIcon is on the -- hidden blzChild frame, not our visible TBB bar. @@ -1594,6 +1682,7 @@ function ns.UpdateTrackedBuffBarTimers() if bw and bw > 0 then ApplyTBBTickMarks(sb, cfg, bar._threshTicks, cfg.verticalOrientation, bar._tickOverlay) + ApplyPandemicMark(sb, cfg, bar, cfg.verticalOrientation) bar._ticksDirty = nil end end From b7a4e89562b8b05ec57deda8716ac7397dd63b7a Mon Sep 17 00:00:00 2001 From: James Gregory Date: Fri, 26 Jun 2026 00:28:35 +1000 Subject: [PATCH 2/2] update keys --- Locales/_keys.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Locales/_keys.txt b/Locales/_keys.txt index 3b1975a0..ac91ad14 100644 --- a/Locales/_keys.txt +++ b/Locales/_keys.txt @@ -1,6 +1,6 @@ # Auto-generated by .tools/extract-locale-keys.sh -- do not edit by hand. # Canonical list of translatable English keys passed as string literals -# (273 unique). Regenerate after wrapping new strings. Keys passed as +# (275 unique). Regenerate after wrapping new strings. Keys passed as # variables are not listed here -- use the in-game /euiloc harvester for # the complete runtime set. (Raid) @@ -215,6 +215,7 @@ Select a Spec... Select a bar Select a talent... Select which specs you want %1$s to be assigned to +Server Time Settings Shift + Left-Click Drag to permanently save a panel's position. Show 'Not Needed' Reminder