Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion MidnightSimpleUnitFrames/Castbars/MSUF_CastbarDriver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,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)
Expand Down
99 changes: 93 additions & 6 deletions MidnightSimpleUnitFrames/Castbars/MSUF_CastbarUtils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -762,6 +779,73 @@ 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, g)
local unit = frame and frame.unit
if type(unit) ~= "string" then return false end
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.kickReadyStyle ~= "fill" then return false end
return _MSUF_CastbarUnitSupportsInterruptUnavailableTint(frame, g)
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
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.494117647, 0.137254902
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.
Expand Down Expand Up @@ -928,6 +1012,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
Expand Down Expand Up @@ -1212,4 +1299,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
end
7 changes: 6 additions & 1 deletion MidnightSimpleUnitFrames/Core/MSUF_ColorsCore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -510,13 +510,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", 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.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

Expand Down Expand Up @@ -681,6 +684,8 @@ MSUF._colorsAPI = {
SetInterruptibleCastColor = SetInterruptibleCastColor,
GetNonInterruptibleCastColor = GetNonInterruptibleCastColor,
SetNonInterruptibleCastColor = SetNonInterruptibleCastColor,
GetInterruptUnavailableCastColor = GetInterruptUnavailableCastColor,
SetInterruptUnavailableCastColor = SetInterruptUnavailableCastColor,
GetInterruptFeedbackCastColor = GetInterruptFeedbackCastColor,
SetInterruptFeedbackCastColor = SetInterruptFeedbackCastColor,
GetPlayerCastbarOverrideEnabled = GetPlayerCastbarOverrideEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ local function ApplyCastbarColors()
ApplyColors()
if MSUF and type(MSUF.MSUF_UpdateCastbarVisuals) == "function" then pcall(MSUF.MSUF_UpdateCastbarVisuals) end
if MSUF and type(MSUF.MSUF_UpdateCastbarTextures_Immediate) == "function" then pcall(MSUF.MSUF_UpdateCastbarTextures_Immediate) end
CallGlobal("MSUF_KickReady_RefreshAll")
end

local function ApplyGameplayColors()
Expand Down Expand Up @@ -892,6 +893,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
Expand All @@ -900,6 +904,7 @@ 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.playerCastbarOverrideEnabled = false
g.playerCastbarOverrideMode = "CLASS"
Expand Down
28 changes: 23 additions & 5 deletions MidnightSimpleUnitFrames/Menu2/Pages/MSUF_Menu2_GlobalCastbars.lua
Original file line number Diff line number Diff line change
Expand Up @@ -697,14 +697,22 @@ 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 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)
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
ir, ig, ib = GlowBlend(ir, ig, ib, progress)

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
Expand Down Expand Up @@ -1281,11 +1289,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,
Expand All @@ -1301,7 +1316,7 @@ 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 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" },
Expand All @@ -1326,8 +1341,11 @@ local function BuildCastbars(ctx)
syncKickReady = function()
local enabled = ReadGBool("kickReadyShowTarget", false) or ReadGBool("kickReadyShowFocus", false) or ReadGBool("kickReadyShowBoss", false)
local autoOn = ReadGBool("kickReadyAutoSize", true)
SetControlsEnabled({ style, auto, anchor, offX, offY }, enabled)
SetControlEnabled(size, enabled and not autoOn)
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)
Expand Down
Loading