From 89e9790889714b63588b609f8ccb029ad3ea43f4 Mon Sep 17 00:00:00 2001 From: Reveax Date: Wed, 27 May 2026 01:03:35 +0200 Subject: [PATCH 1/2] Add support for interrupt unavailable castbar color and related settings --- .../Core/MSUF_ColorsCore.lua | 7 +- .../Foundation/MSUF_Defaults.lua | 6 + .../Foundation/MSUF_Profiles.lua | 1 + .../Menu2/MSUF_Menu2_Bindings.lua | 2 + .../Menu2/MSUF_Menu2_SearchData.lua | 2 +- .../Menu2/Pages/MSUF_Menu2_AdvancedColors.lua | 6 + .../Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua | 25 ++- .../Modules/MSUF_InterruptReady.lua | 204 ++++++++++++++---- .../Castbars/MSUF_CastbarDriver.lua | 7 +- .../Castbars/MSUF_CastbarUtils.lua | 90 +++++++- .../MidnightSimpleUnitFrames_BossCastbars.lua | 7 +- 11 files changed, 306 insertions(+), 51 deletions(-) diff --git a/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua b/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua index 4f6eba2e..1fa000f7 100644 --- a/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua +++ b/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua @@ -509,13 +509,16 @@ local function ResetCastbarBackgroundColor() g.castbarBgR, g.castbarBgG, g.castbarBgB, g.castbarBgA = nil, nil, nil, nil; PushVisualUpdates() end --- ── Cast Colors (interruptible / non-interruptible / feedback) ── +-- ── Cast Colors (interruptible / non-interruptible / interrupt unavailable / feedback) ── local function GetInterruptibleCastColor() return _getRGBPalette("castbarInterruptibleR", "castbarInterruptibleG", "castbarInterruptibleB", "castbarInterruptibleColor", "turquoise", 0, 0.9, 0.8) end MSUF_GetInterruptibleCastColor = GetInterruptibleCastColor local function SetInterruptibleCastColor(r, g, b) _setRGB("castbarInterruptibleR", "castbarInterruptibleG", "castbarInterruptibleB", r, g, b, 0, 0.9, 0.8) end local function GetNonInterruptibleCastColor() return _getRGBTonumber("castbarNonInterruptibleR", "castbarNonInterruptibleG", "castbarNonInterruptibleB", "castbarNonInterruptibleColor", "red", 0.4, 0.01, 0.01) end MSUF_GetNonInterruptibleCastColor = GetNonInterruptibleCastColor local function SetNonInterruptibleCastColor(r, g, b) _setRGB("castbarNonInterruptibleR", "castbarNonInterruptibleG", "castbarNonInterruptibleB", r, g, b, 0.4, 0.01, 0.01) end +local function GetInterruptUnavailableCastColor() return _getRGBTonumber("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", "castbarInterruptUnavailableColor", "yellow", 1.0, 0.55, 0.05) end +MSUF_GetInterruptUnavailableCastColor = GetInterruptUnavailableCastColor +local function SetInterruptUnavailableCastColor(r, g, b) _setRGB("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", r, g, b, 1.0, 0.55, 0.05) end local function GetInterruptFeedbackCastColor() return _getRGBTonumber("castbarInterruptFeedbackR", "castbarInterruptFeedbackG", "castbarInterruptFeedbackB", "castbarInterruptFeedbackColor", "yellow", 1.0, 0.82, 0.0) end local function SetInterruptFeedbackCastColor(r, g, b) _setRGB("castbarInterruptFeedbackR", "castbarInterruptFeedbackG", "castbarInterruptFeedbackB", r, g, b, 1.0, 0.82, 0.0) end @@ -680,6 +683,8 @@ ns._colorsAPI = { SetInterruptibleCastColor = SetInterruptibleCastColor, GetNonInterruptibleCastColor = GetNonInterruptibleCastColor, SetNonInterruptibleCastColor = SetNonInterruptibleCastColor, + GetInterruptUnavailableCastColor = GetInterruptUnavailableCastColor, + SetInterruptUnavailableCastColor = SetInterruptUnavailableCastColor, GetInterruptFeedbackCastColor = GetInterruptFeedbackCastColor, SetInterruptFeedbackCastColor = SetInterruptFeedbackCastColor, GetPlayerCastbarOverrideEnabled = GetPlayerCastbarOverrideEnabled, diff --git a/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua b/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua index c7b99e83..47ef16c7 100644 --- a/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua +++ b/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua @@ -973,6 +973,12 @@ end if g.castbarInterruptColor == nil then g.castbarInterruptColor = "red" end + if g.castbarInterruptUnavailableColorEnabled == nil then + g.castbarInterruptUnavailableColorEnabled = false + end + if g.castbarInterruptUnavailableColor == nil then + g.castbarInterruptUnavailableColor = "yellow" + end if g.playerCastbarOverrideEnabled == nil then g.playerCastbarOverrideEnabled = true end diff --git a/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua b/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua index f3fccaa8..48c0c0cc 100644 --- a/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua +++ b/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua @@ -830,6 +830,7 @@ end local function MSUF_IsColorKey(k) if type(k) ~= "string" then return false end local lk = string.lower(k) + if lk == "castbarinterruptunavailablecolorenabled" then return false end -- Obvious markers if lk:find("color", 1, true) then return true end -- Global theme/mode keys diff --git a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua index aabfbe32..c6b545d8 100644 --- a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua +++ b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua @@ -909,6 +909,7 @@ local CASTBAR_GENERAL_KEYS = { kickReadyAnchor = true, kickReadyOffsetX = true, kickReadyOffsetY = true, + castbarInterruptUnavailableColorEnabled = true, } local CASTBAR_EXCLUDED_KEYS = { @@ -1023,6 +1024,7 @@ end local function IsColorKey(key) if type(key) ~= "string" then return false end + if key == "castbarInterruptUnavailableColorEnabled" then return false end if COLOR_GENERAL_KEYS[key] == true then return true end local lower = string.lower(key) if lower:find("color", 1, true) then return true end diff --git a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_SearchData.lua b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_SearchData.lua index cb285458..9a77ebaf 100644 --- a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_SearchData.lua +++ b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_SearchData.lua @@ -24,7 +24,7 @@ Data.KEYWORDS = { opt_bars = "global style bars textures texture gradient gradient direction hp power absorb display heal prediction incoming heals highlight priority prio display overlay highlight borders outline border aggro purge boss target glow dispel overlay unitframe unit frame debuff tint any debuff dispellable rounded round corners rounded texture rounded frames rounded frame texture rounded unit frames rounded group frames rounded power bars rounded mouseover highlights mouseover bar colors background tint backdrop bg dark mode shared texture opacity alpha health texture power texture frame outline abgerundet abrundung runde kanten ecken abrunden einschalten ausschalten", opt_fonts = "global style fonts font family size outline shadow color text readability name hp power health spell cooldown bigger smaller text size name shortening realm names truncate font color", auras2 = "global style unit auras buffs debuffs icon size caps rows spacing sorting cooldown timer text tooltip private aura filter override dispel stealable only mine own buffs own debuffs pandemic reminders click through clickthrough aura position aura size", - opt_castbar = "global style castbar textures outline shake fill direction empowered casts empower stages evoker augmentation devastation preservation hold release interrupt ready focus kick kick cooldown demon hunter demonhunter dh havoc vengeance devour consume magic disrupt counterspell pummel rebuke wind shear mind freeze skull bash muzzle spear hand strike counter shot quell silence name shortening latency spark channel ticks gcd global cooldown boss castbar target castbar focus castbar player castbar", + opt_castbar = "global style castbar textures outline shake fill direction empowered casts empower stages evoker augmentation devastation preservation hold release interrupt ready focus kick kick cooldown interrupt unavailable unavailable kick unavailable cooldown color demon hunter demonhunter dh havoc vengeance devour consume magic disrupt counterspell pummel rebuke wind shear mind freeze skull bash muzzle spear hand strike counter shot quell silence name shortening latency spark channel ticks gcd global cooldown boss castbar target castbar focus castbar player castbar", opt_colors = "global style colors class bar colors background backgrond backround bg backdrop tint opacity alpha unitframe colors npc type colors bar colors bar outline border color unit frame border group frame border dispel castbar mouseover highlight gameplay superellipse color swatches portrait colors power colors font color health color reaction color aura colors crosshair colors dark mode custom color missing health white background bar background tint preserve hp color hp track black mana rage energy focus runic power insanity fury pain essence astral power lunar power maelstrom combo points holy power soul shards chi arcane charges runes stagger class power", opt_misc = "global style miscellaneous misc language localization localisation locale translation range fade range check range checker distance check out of range unit frame range check ui behavior tooltip tooltips combat settings general blizzard frames default frames hide blizzard disable blizzard update intervals performance minimap minimap icon target sounds version check menu behavior snap edge snap", classpower = "class resources combo points holy power soul shards chi maelstrom eclipse essence evoker runes runic power stagger brewmaster resource prediction auto hide detached power bar alternative mana behavior style quick actions class power resource bar alternate mana monk druid rogue paladin warlock death knight", diff --git a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua index 9015e75e..67ec2e13 100644 --- a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua +++ b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua @@ -92,6 +92,7 @@ local function ApplyCastbarColors() ApplyColors() if ns and type(ns.MSUF_UpdateCastbarVisuals) == "function" then pcall(ns.MSUF_UpdateCastbarVisuals) end if ns and type(ns.MSUF_UpdateCastbarTextures_Immediate) == "function" then pcall(ns.MSUF_UpdateCastbarTextures_Immediate) end + CallGlobal("MSUF_KickReady_RefreshAll") end local function ApplyGameplayColors() @@ -875,6 +876,9 @@ local function BuildColors(ctx) if type(fn) == "function" then pcall(fn, r, g, c, 0.85) else SetGeneralRGB("castbarBg", r, g, c) end ApplyCastbarColors() end) + ColorValueAt(ctx, castbar, "Interrupt unavailable cast color", 360, -118, + function() return ApiRGB("GetInterruptUnavailableCastColor", 1.0, 0.55, 0.05) end, + function(r, g, c) ApiSetRGB("SetInterruptUnavailableCastColor", r, g, c); ApplyCastbarColors() end) LabelAt(castbar, "Player castbar override", 12, -134, 260, "GameFontNormal", T.colors.text) local overrideModeX, overrideModeW = 300, 190 local overrideColorX = min(max(overrideModeX + overrideModeW + 36, floor(castW * 0.56)), castW - 236) @@ -929,7 +933,9 @@ local function BuildColors(ctx) local g = G() g.castbarInterruptibleR, g.castbarInterruptibleG, g.castbarInterruptibleB = nil, nil, nil g.castbarNonInterruptibleR, g.castbarNonInterruptibleG, g.castbarNonInterruptibleB = nil, nil, nil + g.castbarInterruptUnavailableR, g.castbarInterruptUnavailableG, g.castbarInterruptUnavailableB = nil, nil, nil g.castbarInterruptFeedbackR, g.castbarInterruptFeedbackG, g.castbarInterruptFeedbackB = nil, nil, nil + g.castbarInterruptUnavailableColorEnabled = false g.playerCastbarOverrideEnabled = false g.playerCastbarOverrideMode = "CLASS" g.playerCastbarOverrideR, g.playerCastbarOverrideG, g.playerCastbarOverrideB = nil, nil, nil diff --git a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua index e6cc77ba..a7f45576 100644 --- a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua +++ b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua @@ -708,6 +708,13 @@ local function BuildCastbars(ctx) local ok, r, g, b = pcall(_G.MSUF_ResolveCastbarColors) if ok and r then ir, ig, ib = r, g or ig, b or ib end end + if kind ~= "interrupted" + and ReadGBool("castbarInterruptUnavailableColorEnabled", false) + and (unit == "target" or unit == "focus" or unit == "boss") + and type(_G.MSUF_ResolveInterruptUnavailableCastColor) == "function" then + local ok, r, g, b = pcall(_G.MSUF_ResolveInterruptUnavailableCastColor) + if ok and r and g and b then ir, ig, ib = r, g, b end + end if now < (self.interruptUntil or 0) then ir, ig, ib = 0.90, 0.14, 0.20 end @@ -1293,7 +1300,7 @@ local function BuildCastbars(ctx) M.AddRefresher(ctx, syncFocusKick) syncFocusKick() - local kick = b:CollapsibleSection("castbar_interrupt_ready", "Interrupt Ready Indicator", 360, false) + local kick = b:CollapsibleSection("castbar_interrupt_ready", "Interrupt Ready Indicator", 386, false) W.Text(kick, "Shows a colored indicator on castbars when your interrupt is ready or on cooldown.", 14, -38, ctx.width - 28, T.colors.muted) local kickLeftX, kickRightX = 14, 392 W.LabelAt(kick, "Castbars", kickLeftX, -70, 160, "GameFontNormalSmall", T.colors.accent) @@ -1338,7 +1345,18 @@ local function BuildCastbars(ctx) RefreshCastPreview() if syncKickReady then syncKickReady() end end) - local colorHint = W.Text(kick, "Ready / cooldown colors: Colors menu > Interrupt Ready Indicator", kickRightX, -228, 370, T.colors.muted) + local fillTint = W.Toggle(kick, "Recolor cast fill when interrupt is unavailable") + MoveToggle(fillTint, kick, kickRightX, -228, 360) + M.BindToggle(ctx, fillTint, + function() return ReadGBool("castbarInterruptUnavailableColorEnabled", false) end, + function(v) + SetGBool("castbarInterruptUnavailableColorEnabled", v, "MSUF2_CASTBAR_INTERRUPT_UNAVAILABLE_COLOR", { castbar = true, preview = true }) + ApplyCastbars("MSUF2_CASTBAR_INTERRUPT_UNAVAILABLE_COLOR") + Call("MSUF_KickReady_RefreshAll") + RefreshCastPreview() + if syncKickReady then syncKickReady() end + end) + local colorHint = W.Text(kick, "Fill color: Colors menu > Castbar Colors", kickRightX, -258, 370, T.colors.muted) W.LabelAt(kick, "Placement", kickLeftX, -178, 160, "GameFontNormalSmall", T.colors.accent) local anchor = W.Dropdown(kick, "Anchor", { { value = "RIGHT", text = "Right" }, @@ -1363,9 +1381,10 @@ local function BuildCastbars(ctx) syncKickReady = function() local enabled = ReadGBool("kickReadyShowTarget", false) or ReadGBool("kickReadyShowFocus", false) or ReadGBool("kickReadyShowBoss", false) local autoOn = ReadGBool("kickReadyAutoSize", true) + local fillOn = ReadGBool("castbarInterruptUnavailableColorEnabled", false) SetControlsEnabled({ style, auto, anchor, offX, offY }, enabled) SetControlEnabled(size, enabled and not autoOn) - SetControlEnabled(colorHint, enabled) + SetControlEnabled(colorHint, fillOn) end M.AddRefresher(ctx, syncKickReady) syncKickReady() diff --git a/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua b/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua index 3577f8cc..8b786d08 100644 --- a/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua +++ b/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua @@ -93,6 +93,8 @@ local _state = { local _registeredFrames = {} local _activeFrames = {} local _activeFrameCount = 0 +local _fillActiveFrames = {} +local _fillActiveFrameCount = 0 local _eventFrame local _UpdateCooldownEventRegistration @@ -155,6 +157,16 @@ local function _ShowOnUnit(cfg, unit) return false end +local function _FillColorEnabled(cfg) + return cfg and cfg.castbarInterruptUnavailableColorEnabled == true +end + +local function _FillAppliesToUnit(unit) + if type(unit) ~= "string" then return false end + if unit == "target" or unit == "focus" then return true end + return unit:sub(1, 4) == "boss" +end + -- ============================================================================= -- Spell resolution -- ============================================================================= @@ -225,6 +237,14 @@ local function _GetReadyBoolSecret() return dur:IsZero() -- secret bool — DO NOT compare in Lua end +local function _FillTintRuntimeAvailable() + if not _state.spellID then Resolve() end + if not _state.spellID then return false end + return type(C_Spell and C_Spell.GetSpellCooldownDuration) == "function" + and type(C_CurveUtil and C_CurveUtil.EvaluateColorFromBoolean) == "function" + and type(_G.CreateColor) == "function" +end + -- ============================================================================= -- Color resolution (secret-safe via EvaluateColorFromBoolean) -- ============================================================================= @@ -530,6 +550,25 @@ local function _MarkInactiveFrame(frame) end end +local function _MarkFillActiveFrame(frame) + if not frame or _fillActiveFrames[frame] then return end + _fillActiveFrames[frame] = true + _fillActiveFrameCount = _fillActiveFrameCount + 1 + if _UpdateCooldownEventRegistration then + _UpdateCooldownEventRegistration() + end +end + +local function _MarkFillInactiveFrame(frame) + if not frame or not _fillActiveFrames[frame] then return end + _fillActiveFrames[frame] = nil + _fillActiveFrameCount = _fillActiveFrameCount - 1 + if _fillActiveFrameCount < 0 then _fillActiveFrameCount = 0 end + if _UpdateCooldownEventRegistration then + _UpdateCooldownEventRegistration() + end +end + -- Public: create / reposition the indicator on `frame`. Called from -- MSUF_Castbars.lua after every visuals update. Cheap, idempotent. -- Hide all visual side-effects on a frame (used when feature off, unit out @@ -551,6 +590,28 @@ local function _CastAllowsKickIndicator(frame) return not (frame and (frame.isNotInterruptible == true or frame.MSUF_kickInterruptibleConfirmed == false)) end +local function _TrackFillFrame(frame, state, cfg) + if not frame then return false end + cfg = cfg or _GetCfg() + local unit = frame.unit + + local active = false + if state ~= nil then + active = (state.active == true) + elseif frame.MSUF_castActive == true then + active = true + end + + if not (_FillColorEnabled(cfg) and active and _FillAppliesToUnit(unit) and _CastAllowsKickIndicator(frame) and _FillTintRuntimeAvailable()) then + _MarkFillInactiveFrame(frame) + return false + end + + _RegisterFrame(frame) + _MarkFillActiveFrame(frame) + return true +end + local function _EnsureBox(frame, cfg) if not frame or not frame.statusBar then return nil end if not frame.kickReadyBox then @@ -601,6 +662,7 @@ local _refreshTickerArmed = false local _refreshTimer = nil local _refreshTickerToken = 0 local _TickerStep -- forward decl +local RefreshFillCooldownFrames local function _PlainNumberOrNil(v) local cav = canaccessvalue @@ -767,11 +829,21 @@ local function RefreshFrame(frame, state, cfg, readyBool, style, readyMixin, cdM if not frame then return end cfg = cfg or _GetCfg() + local fillTracked = _TrackFillFrame(frame, state, cfg) + local function ArmFillRefresh() + if fillTracked then + if frame.UpdateColorForInterruptible then + frame:UpdateColorForInterruptible() + end + _ArmRefreshTicker(_GetCooldownPollDelay(), false) + end + end if not cfg then _HideIndicator(frame); return end local unit = frame.unit if not unit or not _ShowOnUnit(cfg, unit) then _HideIndicator(frame) + ArmFillRefresh() return end @@ -823,29 +895,35 @@ function _TickerStep() _refreshTimer = nil local cfg = _GetCfg() - if not cfg or not _AnyShowEnabled(cfg) then return end - if _activeFrameCount <= 0 then return end - local readyBool = _GetReadyBoolSecret() - local style = _GetStyle(cfg) - local readyMixin, cdMixin = _ResolveColorPair(cfg) - local userMixin = (style == "border") and _GetUserOutlineMixin() or nil + if not cfg then return end local anyActive = false - local frame = next(_activeFrames) - while frame do - local nextFrame = next(_activeFrames, frame) - if frame.MSUF_castActive == true - and not (frame.isNotInterruptible == true) - and _CastAllowsKickIndicator(frame) then - if cfg and frame.unit and _ShowOnUnit(cfg, frame.unit) then - _PaintFrame(frame, readyBool, cfg, style, readyMixin, cdMixin, userMixin) - anyActive = true + + if _AnyShowEnabled(cfg) and _activeFrameCount > 0 then + local readyBool = _GetReadyBoolSecret() + local style = _GetStyle(cfg) + local readyMixin, cdMixin = _ResolveColorPair(cfg) + local userMixin = (style == "border") and _GetUserOutlineMixin() or nil + local frame = next(_activeFrames) + while frame do + local nextFrame = next(_activeFrames, frame) + if frame.MSUF_castActive == true + and not (frame.isNotInterruptible == true) + and _CastAllowsKickIndicator(frame) then + if cfg and frame.unit and _ShowOnUnit(cfg, frame.unit) then + _PaintFrame(frame, readyBool, cfg, style, readyMixin, cdMixin, userMixin) + anyActive = true + else + _HideIndicator(frame) + end else _HideIndicator(frame) end - else - _HideIndicator(frame) + frame = nextFrame end - frame = nextFrame + end + + if RefreshFillCooldownFrames and RefreshFillCooldownFrames(cfg) then + anyActive = true end if anyActive then @@ -859,34 +937,71 @@ local function RefreshAll() end end -local function RefreshActiveCooldownFrames() - if _activeFrameCount <= 0 then return false end - - local cfg = _GetCfg() - if not cfg or not _AnyShowEnabled(cfg) then return end +RefreshFillCooldownFrames = function(cfg) + if _fillActiveFrameCount <= 0 then return false end + cfg = cfg or _GetCfg() - local readyBool = _GetReadyBoolSecret() - local style = _GetStyle(cfg) - local readyMixin, cdMixin = _ResolveColorPair(cfg) - local userMixin = (style == "border") and _GetUserOutlineMixin() or nil local didRefresh = false - local frame = next(_activeFrames) + local frame = next(_fillActiveFrames) while frame do - local nextFrame = next(_activeFrames, frame) - if frame.MSUF_castActive == true + local nextFrame = next(_fillActiveFrames, frame) + if _FillColorEnabled(cfg) + and frame.MSUF_castActive == true and not (frame.isNotInterruptible == true) and _CastAllowsKickIndicator(frame) - and frame.unit and _ShowOnUnit(cfg, frame.unit) then - RefreshFrame(frame, nil, cfg, readyBool, style, readyMixin, cdMixin, userMixin) - didRefresh = true + and _FillAppliesToUnit(frame.unit) then + if frame.UpdateColorForInterruptible then + frame:UpdateColorForInterruptible() + didRefresh = true + end else - _HideIndicator(frame) + _MarkFillInactiveFrame(frame) end frame = nextFrame end return didRefresh end +local function RefreshActiveCooldownFrames() + local cfg = _GetCfg() + if not cfg then return false end + + local didRefresh = false + + if _AnyShowEnabled(cfg) and _activeFrameCount > 0 then + local readyBool = _GetReadyBoolSecret() + local style = _GetStyle(cfg) + local readyMixin, cdMixin = _ResolveColorPair(cfg) + local userMixin = (style == "border") and _GetUserOutlineMixin() or nil + local frame = next(_activeFrames) + while frame do + local nextFrame = next(_activeFrames, frame) + if frame.MSUF_castActive == true + and not (frame.isNotInterruptible == true) + and _CastAllowsKickIndicator(frame) + and frame.unit and _ShowOnUnit(cfg, frame.unit) then + RefreshFrame(frame, nil, cfg, readyBool, style, readyMixin, cdMixin, userMixin) + didRefresh = true + else + _HideIndicator(frame) + end + frame = nextFrame + end + elseif _activeFrameCount > 0 then + local frame = next(_activeFrames) + while frame do + local nextFrame = next(_activeFrames, frame) + _HideIndicator(frame) + frame = nextFrame + end + end + + if RefreshFillCooldownFrames(cfg) then + didRefresh = true + end + return didRefresh +end + local _cooldownRefreshQueued = false local function _CooldownRefreshFlush() _cooldownRefreshQueued = false @@ -898,7 +1013,7 @@ local function _CooldownRefreshFlush() end local function _QueueCooldownRefresh() - if _activeFrameCount <= 0 then return end + if _activeFrameCount <= 0 and _fillActiveFrameCount <= 0 then return end if _cooldownRefreshQueued then return end _cooldownRefreshQueued = true if C_Timer and C_Timer.After then @@ -986,11 +1101,13 @@ _eventFrame:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED") _eventFrame:RegisterEvent("TRAIT_CONFIG_UPDATED") -- SPELL_UPDATE_COOLDOWN is hot. Only register it while a supported castbar is --- actively showing an interrupt indicator. FocusKick owns its own watcher, so --- this module stays completely cold when no MSUF castbar needs cooldown flips. +-- actively showing an interrupt indicator or using the interrupt-unavailable +-- fill tint. FocusKick owns its own watcher, so this module stays cold when no +-- MSUF castbar needs cooldown flips. _UpdateCooldownEventRegistration = function() local cfg = _GetCfg() - local want = _activeFrameCount > 0 and _AnyShowEnabled(cfg) + local want = (_activeFrameCount > 0 and _AnyShowEnabled(cfg)) + or (_fillActiveFrameCount > 0 and _FillColorEnabled(cfg)) if want and not _state.eventsOn then _eventFrame:RegisterEvent("SPELL_UPDATE_COOLDOWN") _state.eventsOn = true @@ -1055,6 +1172,15 @@ function _G.MSUF_KickReady_RefreshFrame(frame, state) return RefreshFrame(frame, state) end +function _G.MSUF_KickReady_TrackFillFrame(frame, state) + local tracked = _TrackFillFrame(frame, state) + _UpdateCooldownEventRegistration() + if tracked then + _ArmRefreshTicker(_GetCooldownPollDelay(), false) + end + return tracked +end + function _G.MSUF_KickReady_ApplyLayout(frame) _ApplyEventGating() _InstallOutlineHook() @@ -1111,6 +1237,8 @@ function _G.MSUF_KickReady_Debug() showTarget = cfg.kickReadyShowTarget == true, showFocus = cfg.kickReadyShowFocus == true, showBoss = cfg.kickReadyShowBoss == true, + fillTint = cfg.castbarInterruptUnavailableColorEnabled == true, + fillTracked = _fillActiveFrameCount, size = cfg.kickReadySize, anchor = cfg.kickReadyAnchor, offsetX = cfg.kickReadyOffsetX, diff --git a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarDriver.lua b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarDriver.lua index 50d1c220..43f3b118 100644 --- a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarDriver.lua +++ b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarDriver.lua @@ -296,8 +296,13 @@ local function CreateCastBar(name, unit) rawNI = true end + local ur, ug, ub, ua, readyBool, useUnavailable = nil, nil, nil, nil, nil, false + if type(_G.MSUF_Castbar_GetInterruptUnavailableTintArgs) == "function" then + ur, ug, ub, ua, readyBool, useUnavailable = _G.MSUF_Castbar_GetInterruptUnavailableTintArgs(self) + end + if type(_G.MSUF_Castbar_ApplyNonInterruptibleTint) == "function" then - _G.MSUF_Castbar_ApplyNonInterruptibleTint(self, rawNI, nr, ng, nb, 1, ir, ig, ib, 1, isNonInterruptible) + _G.MSUF_Castbar_ApplyNonInterruptibleTint(self, rawNI, nr, ng, nb, 1, ir, ig, ib, 1, isNonInterruptible, ur, ug, ub, ua, readyBool, useUnavailable) else if isNonInterruptible then _G.MSUF_SetStatusBarColorIfChanged(self.statusBar, nr, ng, nb, 1) diff --git a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua index 1620c408..b6bfab11 100644 --- a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua +++ b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua @@ -265,6 +265,7 @@ end -- These colors only change when the user modifies settings, not per-cast. local _tintCacheNonInt = { r = nil, g = nil, b = nil, a = nil, obj = nil } local _tintCacheInt = { r = nil, g = nil, b = nil, a = nil, obj = nil } +local _tintCacheUnavailable = { r = nil, g = nil, b = nil, a = nil, obj = nil } local function _GetCachedColor(cache, r, g, b, a) if cache.r == r and cache.g == g and cache.b == b and cache.a == a and cache.obj then @@ -280,12 +281,15 @@ end function _G.MSUF_Castbar_ApplyNonInterruptibleTint(frame, rawNotInterruptible, nonIntR, nonIntG, nonIntB, nonIntA, intR, intG, intB, intA, - fallbackIsNonInterruptible) + fallbackIsNonInterruptible, + unavailableR, unavailableG, unavailableB, unavailableA, + interruptReadyBool, useUnavailableColor) local sb = frame and frame.statusBar if not sb then return false end local wantNI = (fallbackIsNonInterruptible == true) + local useUnavailable = (useUnavailableColor == true and unavailableR ~= nil and unavailableG ~= nil and unavailableB ~= nil) local ar = wantNI and nonIntR or intR local ag = wantNI and nonIntG or intG @@ -303,13 +307,18 @@ function _G.MSUF_Castbar_ApplyNonInterruptibleTint(frame, rawNotInterruptible, -- Quick Win #13: reuse cached color objects (only re-created when RGBA changes). local nonCol = _GetCachedColor(_tintCacheNonInt, nonIntR, nonIntG, nonIntB, nonIntA or 1) local intCol = _GetCachedColor(_tintCacheInt, intR, intG, intB, intA or 1) + local activeIntCol = intCol + if useUnavailable and C_CurveUtil and C_CurveUtil.EvaluateColorFromBoolean then + local unavailableCol = _GetCachedColor(_tintCacheUnavailable, unavailableR, unavailableG, unavailableB, unavailableA or 1) + activeIntCol = C_CurveUtil.EvaluateColorFromBoolean(interruptReadyBool, intCol, unavailableCol) + end local v = rawNotInterruptible if v == nil then v = (wantNI == true) end - tex:SetVertexColorFromBoolean(v, nonCol, intCol) + tex:SetVertexColorFromBoolean(v, nonCol, activeIntCol) usedC = true end @@ -320,12 +329,17 @@ function _G.MSUF_Castbar_ApplyNonInterruptibleTint(frame, rawNotInterruptible, -- IMPORTANT (0-regression fix): keep the StatusBar color caches in sync even when -- we color via vertex tint. Otherwise later calls that rely on cached colors -- (e.g. interrupt feedback) may early-return and skip applying red on "2nd interrupt". - sb._msufLastColorR, sb._msufLastColorG, sb._msufLastColorB, sb._msufLastColorA = ar, ag, ab, aa - sb._msufLastR, sb._msufLastG, sb._msufLastB, sb._msufLastA = ar, ag, ab, aa + if useUnavailable then + sb._msufLastColorR, sb._msufLastColorG, sb._msufLastColorB, sb._msufLastColorA = nil, nil, nil, nil + sb._msufLastR, sb._msufLastG, sb._msufLastB, sb._msufLastA = nil, nil, nil, nil + else + sb._msufLastColorR, sb._msufLastColorG, sb._msufLastColorB, sb._msufLastColorA = ar, ag, ab, aa + sb._msufLastR, sb._msufLastG, sb._msufLastB, sb._msufLastA = ar, ag, ab, aa + end end -- Also keep glow-base tracking stable across both tint paths. - if not sb._msufGlowSkipBase then + if not sb._msufGlowSkipBase and not useUnavailable then local br, bg, bb, ba = sb._msufGlowBaseR, sb._msufGlowBaseG, sb._msufGlowBaseB, sb._msufGlowBaseA if br ~= ar or bg ~= ag or bb ~= ab or ba ~= aa then sb._msufGlowBaseR, sb._msufGlowBaseG, sb._msufGlowBaseB, sb._msufGlowBaseA = ar, ag, ab, aa @@ -585,6 +599,9 @@ function _G.MSUF_Castbar_ApplyActiveDuration(frame, state, opts) if frame.UpdateColorForInterruptible then frame:UpdateColorForInterruptible() end + if type(_G.MSUF_KickReady_TrackFillFrame) == "function" then + _G.MSUF_KickReady_TrackFillFrame(frame, state) + end if type(_G.MSUF_RegisterCastbar) == "function" and opts.skipRegister ~= true then _G.MSUF_RegisterCastbar(frame) @@ -762,6 +779,64 @@ function _G.MSUF_GetNonInterruptibleCastColor() end end +function _G.MSUF_GetInterruptUnavailableCastColor() + _EnsureDBLazy() + local g = (MSUF_DB and MSUF_DB.general) or {} + local r = tonumber(g.castbarInterruptUnavailableR) + local gg = tonumber(g.castbarInterruptUnavailableG) + local b = tonumber(g.castbarInterruptUnavailableB) + if r and gg and b then + return r, gg, b, 1 + end +end + +local function _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame) + local unit = frame and frame.unit + if type(unit) ~= "string" then return false end + if unit == "target" or unit == "focus" then return true end + return unit:sub(1, 4) == "boss" +end + +function _G.MSUF_Castbar_ShouldUseInterruptUnavailableColor(frame) + _EnsureDBLazy() + local g = (MSUF_DB and MSUF_DB.general) or {} + if g.castbarInterruptUnavailableColorEnabled ~= true then return false end + return _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame) +end + +function _G.MSUF_ResolveInterruptUnavailableCastColor() + _EnsureDBLazy() + local g = (MSUF_DB and MSUF_DB.general) or {} + local r, gg, b + if type(_G.MSUF_GetInterruptUnavailableCastColor) == "function" then + r, gg, b = _G.MSUF_GetInterruptUnavailableCastColor() + end + if not (r and gg and b) then + local key = g.castbarInterruptUnavailableColor or "yellow" + local c = (type(_G.MSUF_GetColorFromKey) == "function") and _G.MSUF_GetColorFromKey(key) or nil + if c and c.GetRGB then + r, gg, b = c:GetRGB() + end + end + if not (r and gg and b) then + r, gg, b = 1.0, 0.55, 0.05 + end + return r, gg, b, 1 +end + +function _G.MSUF_Castbar_GetInterruptUnavailableTintArgs(frame) + if not _G.MSUF_Castbar_ShouldUseInterruptUnavailableColor(frame) then return nil end + if type(_G.MSUF_KickReady_IsReady) ~= "function" then return nil end + if type(_G.MSUF_KickReady_GetSpellID) == "function" and not _G.MSUF_KickReady_GetSpellID() then return nil end + if not (_G.C_Spell and _G.C_Spell.GetSpellCooldownDuration and _G.C_CurveUtil and _G.C_CurveUtil.EvaluateColorFromBoolean) then return nil end + + local readyBool = _G.MSUF_KickReady_IsReady() + if readyBool == nil then return nil end + + local r, gg, b, a = _G.MSUF_ResolveInterruptUnavailableCastColor() + return r, gg, b, a or 1, readyBool, true +end + -- "Glow effect" (Options -> Castbars -> Behavior): end-of-cast fade to white. -- NOTE: This is intentionally texture-agnostic and does not rely on background/foreground textures matching. -- It simply blends the current fill color towards white as the cast approaches completion. @@ -928,6 +1003,9 @@ function _G.MSUF_CB_ApplyColor(frame, state) if _G.MSUF_KickReady_RefreshFrame then _G.MSUF_KickReady_RefreshFrame(frame, state) end + if type(_G.MSUF_KickReady_TrackFillFrame) == "function" then + _G.MSUF_KickReady_TrackFillFrame(frame, state) + end return r end end @@ -1212,4 +1290,4 @@ function _G.MSUF_CB_ResetStateOnStop(frame, reasonOrState, opts) -- INTERRUPTED: do not apply colors here (interrupt/SSoT handles it). -- Text/Show/Hold timer remain in the caller (SetInterrupted), matching the old code. -end \ No newline at end of file +end diff --git a/MidnightSimpleUnitFrames_Castbars/Modules/MidnightSimpleUnitFrames_BossCastbars.lua b/MidnightSimpleUnitFrames_Castbars/Modules/MidnightSimpleUnitFrames_BossCastbars.lua index a237c035..094787f0 100644 --- a/MidnightSimpleUnitFrames_Castbars/Modules/MidnightSimpleUnitFrames_BossCastbars.lua +++ b/MidnightSimpleUnitFrames_Castbars/Modules/MidnightSimpleUnitFrames_BossCastbars.lua @@ -638,8 +638,13 @@ end nr, ng, nb = 0.9, 0.1, 0.1 end + local ur, ug, ub, ua, readyBool, useUnavailable = nil, nil, nil, nil, nil, false + if type(_G.MSUF_Castbar_GetInterruptUnavailableTintArgs) == "function" then + ur, ug, ub, ua, readyBool, useUnavailable = _G.MSUF_Castbar_GetInterruptUnavailableTintArgs(self) + end + if type(_G.MSUF_Castbar_ApplyNonInterruptibleTint) == "function" then - _G.MSUF_Castbar_ApplyNonInterruptibleTint(self, rawNI, nr, ng, nb, na, ir, ig, ib, ia, isNI) + _G.MSUF_Castbar_ApplyNonInterruptibleTint(self, rawNI, nr, ng, nb, na, ir, ig, ib, ia, isNI, ur, ug, ub, ua, readyBool, useUnavailable) else local r = (isNI and nr or ir) local gcol = (isNI and ng or ig) From d9e68cb61c3f8857922aa15b731c26189b2465fd Mon Sep 17 00:00:00 2001 From: Reveax Date: Wed, 27 May 2026 09:32:46 +0200 Subject: [PATCH 2/2] Integrate unavailable fill as indicator style --- .../Core/MSUF_ColorsCore.lua | 4 +- .../Foundation/MSUF_Defaults.lua | 6 --- .../Foundation/MSUF_Profiles.lua | 1 - .../Menu2/MSUF_Menu2_Bindings.lua | 2 - .../Menu2/Pages/MSUF_Menu2_AdvancedColors.lua | 7 ++-- .../Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua | 39 +++++++++---------- .../Modules/MSUF_InterruptReady.lua | 11 ++++-- .../Castbars/MSUF_CastbarUtils.lua | 25 ++++++++---- 8 files changed, 49 insertions(+), 46 deletions(-) diff --git a/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua b/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua index 1fa000f7..1791135a 100644 --- a/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua +++ b/MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua @@ -516,9 +516,9 @@ local function SetInterruptibleCastColor(r, g, b) _setRGB("castbarInterruptibleR local function GetNonInterruptibleCastColor() return _getRGBTonumber("castbarNonInterruptibleR", "castbarNonInterruptibleG", "castbarNonInterruptibleB", "castbarNonInterruptibleColor", "red", 0.4, 0.01, 0.01) end MSUF_GetNonInterruptibleCastColor = GetNonInterruptibleCastColor local function SetNonInterruptibleCastColor(r, g, b) _setRGB("castbarNonInterruptibleR", "castbarNonInterruptibleG", "castbarNonInterruptibleB", r, g, b, 0.4, 0.01, 0.01) end -local function GetInterruptUnavailableCastColor() return _getRGBTonumber("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", "castbarInterruptUnavailableColor", "yellow", 1.0, 0.55, 0.05) end +local function GetInterruptUnavailableCastColor() return _getRGBTonumber("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", "castbarInterruptUnavailableColor", nil, 1.0, 0.494117647, 0.137254902) end MSUF_GetInterruptUnavailableCastColor = GetInterruptUnavailableCastColor -local function SetInterruptUnavailableCastColor(r, g, b) _setRGB("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", r, g, b, 1.0, 0.55, 0.05) end +local function SetInterruptUnavailableCastColor(r, g, b) _setRGB("castbarInterruptUnavailableR", "castbarInterruptUnavailableG", "castbarInterruptUnavailableB", r, g, b, 1.0, 0.494117647, 0.137254902) end local function GetInterruptFeedbackCastColor() return _getRGBTonumber("castbarInterruptFeedbackR", "castbarInterruptFeedbackG", "castbarInterruptFeedbackB", "castbarInterruptFeedbackColor", "yellow", 1.0, 0.82, 0.0) end local function SetInterruptFeedbackCastColor(r, g, b) _setRGB("castbarInterruptFeedbackR", "castbarInterruptFeedbackG", "castbarInterruptFeedbackB", r, g, b, 1.0, 0.82, 0.0) end diff --git a/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua b/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua index 47ef16c7..c7b99e83 100644 --- a/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua +++ b/MidnightSimpleUnitFrames/Foundation/MSUF_Defaults.lua @@ -973,12 +973,6 @@ end if g.castbarInterruptColor == nil then g.castbarInterruptColor = "red" end - if g.castbarInterruptUnavailableColorEnabled == nil then - g.castbarInterruptUnavailableColorEnabled = false - end - if g.castbarInterruptUnavailableColor == nil then - g.castbarInterruptUnavailableColor = "yellow" - end if g.playerCastbarOverrideEnabled == nil then g.playerCastbarOverrideEnabled = true end diff --git a/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua b/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua index 48c0c0cc..f3fccaa8 100644 --- a/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua +++ b/MidnightSimpleUnitFrames/Foundation/MSUF_Profiles.lua @@ -830,7 +830,6 @@ end local function MSUF_IsColorKey(k) if type(k) ~= "string" then return false end local lk = string.lower(k) - if lk == "castbarinterruptunavailablecolorenabled" then return false end -- Obvious markers if lk:find("color", 1, true) then return true end -- Global theme/mode keys diff --git a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua index c6b545d8..aabfbe32 100644 --- a/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua +++ b/MidnightSimpleUnitFrames/Menu2/MSUF_Menu2_Bindings.lua @@ -909,7 +909,6 @@ local CASTBAR_GENERAL_KEYS = { kickReadyAnchor = true, kickReadyOffsetX = true, kickReadyOffsetY = true, - castbarInterruptUnavailableColorEnabled = true, } local CASTBAR_EXCLUDED_KEYS = { @@ -1024,7 +1023,6 @@ end local function IsColorKey(key) if type(key) ~= "string" then return false end - if key == "castbarInterruptUnavailableColorEnabled" then return false end if COLOR_GENERAL_KEYS[key] == true then return true end local lower = string.lower(key) if lower:find("color", 1, true) then return true end diff --git a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua index 67ec2e13..a0b12705 100644 --- a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua +++ b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_AdvancedColors.lua @@ -876,9 +876,6 @@ local function BuildColors(ctx) if type(fn) == "function" then pcall(fn, r, g, c, 0.85) else SetGeneralRGB("castbarBg", r, g, c) end ApplyCastbarColors() end) - ColorValueAt(ctx, castbar, "Interrupt unavailable cast color", 360, -118, - function() return ApiRGB("GetInterruptUnavailableCastColor", 1.0, 0.55, 0.05) end, - function(r, g, c) ApiSetRGB("SetInterruptUnavailableCastColor", r, g, c); ApplyCastbarColors() end) LabelAt(castbar, "Player castbar override", 12, -134, 260, "GameFontNormal", T.colors.text) local overrideModeX, overrideModeW = 300, 190 local overrideColorX = min(max(overrideModeX + overrideModeW + 36, floor(castW * 0.56)), castW - 236) @@ -925,6 +922,9 @@ local function BuildColors(ctx) ColorValueAt(ctx, castbar, "Not ready color (kick on cooldown)", 12, -310, function() return TableRGB(G(), "kickNotReadyColor", 1, 0, 0) end, function(r, g, c) SetTableRGB(G(), "kickNotReadyColor", r, g, c); ApplyCastbarColors() end) + ColorValueAt(ctx, castbar, "Unavailable fill color", 12, -346, + function() return ApiRGB("GetInterruptUnavailableCastColor", 1.0, 0.494117647, 0.137254902) end, + function(r, g, c) ApiSetRGB("SetInterruptUnavailableCastColor", r, g, c); ApplyCastbarColors() end) ButtonAt(castbar, "Reset castbar colors", 12, -470, 170, function() local api = ColorAPI() if type(api.ResetCastbarTextColorToGlobal) == "function" then pcall(api.ResetCastbarTextColorToGlobal) end @@ -935,7 +935,6 @@ local function BuildColors(ctx) g.castbarNonInterruptibleR, g.castbarNonInterruptibleG, g.castbarNonInterruptibleB = nil, nil, nil g.castbarInterruptUnavailableR, g.castbarInterruptUnavailableG, g.castbarInterruptUnavailableB = nil, nil, nil g.castbarInterruptFeedbackR, g.castbarInterruptFeedbackG, g.castbarInterruptFeedbackB = nil, nil, nil - g.castbarInterruptUnavailableColorEnabled = false g.playerCastbarOverrideEnabled = false g.playerCastbarOverrideMode = "CLASS" g.playerCastbarOverrideR, g.playerCastbarOverrideG, g.playerCastbarOverrideB = nil, nil, nil diff --git a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua index a7f45576..8812a519 100644 --- a/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua +++ b/MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua @@ -708,8 +708,10 @@ local function BuildCastbars(ctx) local ok, r, g, b = pcall(_G.MSUF_ResolveCastbarColors) if ok and r then ir, ig, ib = r, g or ig, b or ib end end + local kickKey = KickReadyKey(unit) if kind ~= "interrupted" - and ReadGBool("castbarInterruptUnavailableColorEnabled", false) + and kickKey and ReadGBool(kickKey, false) + and ReadG("kickReadyStyle", "border") == "fill" and (unit == "target" or unit == "focus" or unit == "boss") and type(_G.MSUF_ResolveInterruptUnavailableCastColor) == "function" then local ok, r, g, b = pcall(_G.MSUF_ResolveInterruptUnavailableCastColor) @@ -722,7 +724,6 @@ local function BuildCastbars(ctx) if self.bar.SetBackdropBorderColor then self.bar:SetBackdropBorderColor(T.colors.borderSoft[1], T.colors.borderSoft[2], T.colors.borderSoft[3], T.colors.borderSoft[4] or 0.7) - local kickKey = KickReadyKey(unit) if kickKey and ReadGBool(kickKey, false) and ReadG("kickReadyStyle", "border") == "border" then self.bar:SetBackdropBorderColor(0.24, 0.86, 0.46, 0.95) end @@ -1300,7 +1301,7 @@ local function BuildCastbars(ctx) M.AddRefresher(ctx, syncFocusKick) syncFocusKick() - local kick = b:CollapsibleSection("castbar_interrupt_ready", "Interrupt Ready Indicator", 386, false) + local kick = b:CollapsibleSection("castbar_interrupt_ready", "Interrupt Ready Indicator", 360, false) W.Text(kick, "Shows a colored indicator on castbars when your interrupt is ready or on cooldown.", 14, -38, ctx.width - 28, T.colors.muted) local kickLeftX, kickRightX = 14, 392 W.LabelAt(kick, "Castbars", kickLeftX, -70, 160, "GameFontNormalSmall", T.colors.accent) @@ -1325,11 +1326,18 @@ local function BuildCastbars(ctx) local style = W.Dropdown(kick, "Indicator style", { { value = "border", text = "Castbar border" }, { value = "box", text = "Color box next to cast" }, + { value = "fill", text = "Unavailable cast fill" }, }, 260) W.MoveWidget(style, kick, kickRightX, -88, 300) M.BindDropdown(ctx, style, function() return ReadG("kickReadyStyle", "border") end, - function(v) SetG("kickReadyStyle", v or "border", "MSUF2_KICK_READY_STYLE", { castbar = true, preview = true }); ApplyCastbars("MSUF2_KICK_READY_STYLE"); RefreshCastPreview() end) + function(v) + SetG("kickReadyStyle", v or "border", "MSUF2_KICK_READY_STYLE", { castbar = true, preview = true }) + ApplyCastbars("MSUF2_KICK_READY_STYLE") + Call("MSUF_KickReady_RefreshAll") + RefreshCastPreview() + if syncKickReady then syncKickReady() end + end) local size = W.Slider(kick, "Indicator size", 8, 32, 1, 300) W.MoveWidget(size, kick, kickRightX, -142, 320) M.BindSlider(ctx, size, @@ -1345,18 +1353,7 @@ local function BuildCastbars(ctx) RefreshCastPreview() if syncKickReady then syncKickReady() end end) - local fillTint = W.Toggle(kick, "Recolor cast fill when interrupt is unavailable") - MoveToggle(fillTint, kick, kickRightX, -228, 360) - M.BindToggle(ctx, fillTint, - function() return ReadGBool("castbarInterruptUnavailableColorEnabled", false) end, - function(v) - SetGBool("castbarInterruptUnavailableColorEnabled", v, "MSUF2_CASTBAR_INTERRUPT_UNAVAILABLE_COLOR", { castbar = true, preview = true }) - ApplyCastbars("MSUF2_CASTBAR_INTERRUPT_UNAVAILABLE_COLOR") - Call("MSUF_KickReady_RefreshAll") - RefreshCastPreview() - if syncKickReady then syncKickReady() end - end) - local colorHint = W.Text(kick, "Fill color: Colors menu > Castbar Colors", kickRightX, -258, 370, T.colors.muted) + local colorHint = W.Text(kick, "Colors: Colors menu > Castbar Colors", kickRightX, -228, 370, T.colors.muted) W.LabelAt(kick, "Placement", kickLeftX, -178, 160, "GameFontNormalSmall", T.colors.accent) local anchor = W.Dropdown(kick, "Anchor", { { value = "RIGHT", text = "Right" }, @@ -1381,10 +1378,12 @@ local function BuildCastbars(ctx) syncKickReady = function() local enabled = ReadGBool("kickReadyShowTarget", false) or ReadGBool("kickReadyShowFocus", false) or ReadGBool("kickReadyShowBoss", false) local autoOn = ReadGBool("kickReadyAutoSize", true) - local fillOn = ReadGBool("castbarInterruptUnavailableColorEnabled", false) - SetControlsEnabled({ style, auto, anchor, offX, offY }, enabled) - SetControlEnabled(size, enabled and not autoOn) - SetControlEnabled(colorHint, fillOn) + local isFill = ReadG("kickReadyStyle", "border") == "fill" + SetControlEnabled(style, enabled) + SetControlEnabled(auto, enabled and not isFill) + SetControlEnabled(size, enabled and not isFill and not autoOn) + SetControlsEnabled({ anchor, offX, offY }, enabled and not isFill) + SetControlEnabled(colorHint, enabled) end M.AddRefresher(ctx, syncKickReady) syncKickReady() diff --git a/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua b/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua index 8b786d08..a0a9760f 100644 --- a/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua +++ b/MidnightSimpleUnitFrames/Modules/MSUF_InterruptReady.lua @@ -158,7 +158,7 @@ local function _ShowOnUnit(cfg, unit) end local function _FillColorEnabled(cfg) - return cfg and cfg.castbarInterruptUnavailableColorEnabled == true + return cfg and cfg.kickReadyStyle == "fill" end local function _FillAppliesToUnit(unit) @@ -522,7 +522,7 @@ end local function _GetStyle(cfg) local s = cfg and cfg.kickReadyStyle - if s == "box" or s == "border" then return s end + if s == "box" or s == "border" or s == "fill" then return s end return "border" -- default end @@ -877,6 +877,11 @@ local function RefreshFrame(frame, state, cfg, readyBool, style, readyMixin, cdM if readyBool == nil then readyBool = _GetReadyBoolSecret() end style = style or _GetStyle(cfg) + if style == "fill" then + _HideIndicator(frame) + ArmFillRefresh() + return + end if style == "box" then _EnsureBox(frame, cfg) end @@ -1237,7 +1242,7 @@ function _G.MSUF_KickReady_Debug() showTarget = cfg.kickReadyShowTarget == true, showFocus = cfg.kickReadyShowFocus == true, showBoss = cfg.kickReadyShowBoss == true, - fillTint = cfg.castbarInterruptUnavailableColorEnabled == true, + fillTint = cfg.kickReadyStyle == "fill", fillTracked = _fillActiveFrameCount, size = cfg.kickReadySize, anchor = cfg.kickReadyAnchor, diff --git a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua index b6bfab11..131823cf 100644 --- a/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua +++ b/MidnightSimpleUnitFrames_Castbars/Castbars/MSUF_CastbarUtils.lua @@ -790,18 +790,27 @@ function _G.MSUF_GetInterruptUnavailableCastColor() end end -local function _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame) +local function _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame, g) local unit = frame and frame.unit if type(unit) ~= "string" then return false end - if unit == "target" or unit == "focus" then return true end - return unit:sub(1, 4) == "boss" + local shouldUse = _G.MSUF_ShouldUseMSUFCastbar + local function owns(which) + if type(shouldUse) == "function" then + return shouldUse(which, g) == true + end + return true + end + if unit == "target" then return g.kickReadyShowTarget == true and owns("target") end + if unit == "focus" then return g.kickReadyShowFocus == true and owns("focus") end + if unit:sub(1, 4) == "boss" then return g.kickReadyShowBoss == true and owns("boss") end + return false end function _G.MSUF_Castbar_ShouldUseInterruptUnavailableColor(frame) _EnsureDBLazy() local g = (MSUF_DB and MSUF_DB.general) or {} - if g.castbarInterruptUnavailableColorEnabled ~= true then return false end - return _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame) + if g.kickReadyStyle ~= "fill" then return false end + return _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame, g) end function _G.MSUF_ResolveInterruptUnavailableCastColor() @@ -812,14 +821,14 @@ function _G.MSUF_ResolveInterruptUnavailableCastColor() r, gg, b = _G.MSUF_GetInterruptUnavailableCastColor() end if not (r and gg and b) then - local key = g.castbarInterruptUnavailableColor or "yellow" - local c = (type(_G.MSUF_GetColorFromKey) == "function") and _G.MSUF_GetColorFromKey(key) or nil + local key = g.castbarInterruptUnavailableColor + local c = (key and type(_G.MSUF_GetColorFromKey) == "function") and _G.MSUF_GetColorFromKey(key) or nil if c and c.GetRGB then r, gg, b = c:GetRGB() end end if not (r and gg and b) then - r, gg, b = 1.0, 0.55, 0.05 + r, gg, b = 1.0, 0.494117647, 0.137254902 end return r, gg, b, 1 end