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
1 change: 1 addition & 0 deletions EllesmereUI.toc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ EllesmereUI_Startup.lua

# Shared EllesmereUI Files
EllesmereUI.lua
EllesmereUI_Kick.lua
EllesmereUI_Widgets.lua
EllesmereUI_Visibility.lua
EllesmereUI_Presets.lua
Expand Down
77 changes: 19 additions & 58 deletions EllesmereUINameplates/EllesmereUINameplates.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1834,55 +1834,20 @@ local function InitDB()
-- Legacy stub: NewDB + DeepMergeDefaults handles defaults now.
-- Kept as a no-op so any stray call sites don't error.
end
local kickSpellsByClass = {
DEATHKNIGHT = {47528},
WARRIOR = {6552},
WARLOCK = {19647, 89766, 119910, 1276467, 132409},
SHAMAN = {57994},
ROGUE = {1766},
PRIEST = {15487},
PALADIN = {31935, 96231},
MONK = {116705},
MAGE = {2139},
HUNTER = {187707, 147362},
EVOKER = {351338},
DRUID = {38675, 78675, 106839},
DEMONHUNTER = {183752},
}
local activeKickSpell
function ns.GetActiveKickSpell() return activeKickSpell end
local function RefreshKickAbility()
local playerClass = UnitClassBase("player")
local classKicks = kickSpellsByClass[playerClass]
activeKickSpell = nil
if not classKicks then return end
for i = 1, #classKicks do
local spellId = classKicks[i]
if C_SpellBook and C_SpellBook.IsSpellKnownOrInSpellBook then
local known = C_SpellBook.IsSpellKnownOrInSpellBook(spellId)
local petKnown = Enum and Enum.SpellBookSpellBank and C_SpellBook.IsSpellKnownOrInSpellBook(spellId, Enum.SpellBookSpellBank.Pet)
if known or petKnown then activeKickSpell = spellId end
elseif IsSpellKnown and IsSpellKnown(spellId) then
activeKickSpell = spellId
end
end
end
local function ComputeCastBarTint(readyTint, baseTint)
if not activeKickSpell then return baseTint.r, baseTint.g, baseTint.b end
if not (C_Spell and C_Spell.GetSpellCooldownDuration) then return baseTint.r, baseTint.g, baseTint.b end
if not (C_CurveUtil and C_CurveUtil.EvaluateColorValueFromBoolean) then return baseTint.r, baseTint.g, baseTint.b end
local cdTime = C_Spell.GetSpellCooldownDuration(activeKickSpell)
if not (cdTime and cdTime.IsZero) then return baseTint.r, baseTint.g, baseTint.b end
local offCooldown = cdTime:IsZero()
local rVal = C_CurveUtil.EvaluateColorValueFromBoolean(offCooldown, baseTint.r, readyTint.r)
local gVal = C_CurveUtil.EvaluateColorValueFromBoolean(offCooldown, baseTint.g, readyTint.g)
local bVal = C_CurveUtil.EvaluateColorValueFromBoolean(offCooldown, baseTint.b, readyTint.b)
return rVal, gVal, bVal
end
-- Exposed for the cast overlay file (EllesmereUINameplates_CastOverlay.lua)
-- so the overlay bar can apply the same interrupt-ready tint as the on-plate
-- cast bar without duplicating the logic.
ns.ComputeCastBarTint = ComputeCastBarTint
function ns.GetActiveKickSpell()
return EllesmereUI and EllesmereUI.GetActiveKickSpell and EllesmereUI.GetActiveKickSpell()
end
-- Cast overlay uses the same tint as the on-plate cast bar.
ns.ComputeCastBarTint = function(readyTint, baseTint)
if EllesmereUI and EllesmereUI.ComputeCastBarTint then
return EllesmereUI.ComputeCastBarTint(readyTint, baseTint)
end
return baseTint.r, baseTint.g, baseTint.b
end
local function GetActiveKickSpell()
return ns.GetActiveKickSpell()
end
local ComputeCastBarTint = ns.ComputeCastBarTint
function ns.RefreshBorder()
-- Bump appearance gen so pooled/off-screen plates pick up the
-- change on their next SetUnit (cache-hit re-spawns check this).
Expand Down Expand Up @@ -1978,8 +1943,6 @@ function ns.RefreshAllSettings()
if ns.ApplyClassPowerSetting then ns.ApplyClassPowerSetting() end
end
local kickWatcher = CreateFrame("Frame")
kickWatcher:RegisterEvent("PLAYER_LOGIN")
kickWatcher:RegisterEvent("SPELLS_CHANGED")
local activeCastCount = 0
-- PERF: set of plates currently casting so kick/color updates iterate only
-- the 1-3 casting plates instead of all 20+ plates in the scene.
Expand Down Expand Up @@ -2008,8 +1971,6 @@ kickWatcher:SetScript("OnEvent", function(self, event)
end
end
end
else
RefreshKickAbility()
end
end)
local _castColorTicker
Expand All @@ -2019,7 +1980,7 @@ local function NotifyCastStarted(plate)
if activeCastCount == 1 then
kickWatcher:RegisterEvent("SPELL_UPDATE_COOLDOWN")
kickWatcher:RegisterEvent("SPELL_UPDATE_USABLE")
if activeKickSpell and not _castColorTicker then
if GetActiveKickSpell() and not _castColorTicker then
_castColorTicker = C_Timer.NewTicker(0.2, function()
for pl in pairs(ns._castingPlates) do
if pl.isCasting and pl.unit and pl._kickProtected ~= nil then
Expand Down Expand Up @@ -4829,7 +4790,7 @@ function NameplateFrame:HideKickTick()
end
end
function NameplateFrame:UpdateKickTick(kickProtected, isChannel, isEmpowered)
if not GetKickTickEnabled() or not activeKickSpell then
if not GetKickTickEnabled() or not GetActiveKickSpell() then
self:HideKickTick()
return
end
Expand Down Expand Up @@ -4858,7 +4819,7 @@ function NameplateFrame:UpdateKickTick(kickProtected, isChannel, isEmpowered)
return
end
local totalDur = castDuration:GetTotalDuration()
local interruptCD = C_Spell.GetSpellCooldownDuration(activeKickSpell)
local interruptCD = C_Spell.GetSpellCooldownDuration(GetActiveKickSpell())
if not interruptCD then
self:HideKickTick()
return
Expand Down Expand Up @@ -4925,14 +4886,14 @@ function NameplateFrame:UpdateKickTick(kickProtected, isChannel, isEmpowered)
-- activeKickSpell can go nil mid-cast if a spec/talent change
-- fires SPELLS_CHANGED and the new spec doesn't have a kick
-- learned. Bail rather than pass nil to C_Spell.
if not activeKickSpell then
if not GetActiveKickSpell() then
self:HideKickTick()
return
end
-- Compute tick visibility: show only when kick is on CD AND cast is interruptible.
-- Both are secret booleans chain EvaluateColorValueFromBoolean calls
-- to combine conditions into a single secret alpha.
local icd = C_Spell.GetSpellCooldownDuration(activeKickSpell)
local icd = C_Spell.GetSpellCooldownDuration(GetActiveKickSpell())
if icd and icd.IsZero and C_CurveUtil and C_CurveUtil.EvaluateColorValueFromBoolean then
local interruptible = C_CurveUtil.EvaluateColorValueFromBoolean(self._kickProtected, 0, 1)
local kickReady = icd:IsZero()
Expand Down
173 changes: 148 additions & 25 deletions EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2990,6 +2990,8 @@ initFrame:SetScript("OnEvent", function(self)
showCastDuration = { player=true, target=true, focus=true },
showCastTarget = { player=true, target=true, focus=true },
castbarFillColor = { player=true, target=true, focus=true },
castbarInterruptReadyColor = { target=true, focus=true },
castbarKickTickEnabled = { target=true, focus=true },
showClassPowerBar = { player=true },
lockClassPowerToFrame= { player=true },
classPowerStyle = { player=true },
Expand Down Expand Up @@ -5253,48 +5255,70 @@ initFrame:SetScript("OnEvent", function(self)
disabled=cbhDis, disabledTooltip=cbhTip, rawTooltip=cbhRaw,
getValue=GetCastbarHeight,
setValue=function(v) SetCastbarHeight(v); ReloadAndUpdate(); UpdatePreview() end }); y = y - h
-- Inline fill color swatch on Show Cast Bar
-- Inline cast color swatch(es) on Show Cast Bar
do
local leftRgn = sharedCastRow1._leftRegion
local cbSw = EllesmereUI.BuildColorSwatch(leftRgn, leftRgn:GetFrameLevel() + 5,
function()
local c = SGetSupported("castbarFillColor")
c = c or { r=1, g=0.7, b=0 }
return c.r, c.g, c.b, 1
end,
function(r, g, b)
UNIT_DB_MAP[selectedUnit]().castbarFillColor = { r=r, g=g, b=b }
ReloadAndUpdate(); UpdatePreview()
end, false, 20)
cbSw:SetPoint("RIGHT", leftRgn._lastInline or leftRgn._control, "LEFT", -12, 0)
cbSw:SetScript("OnEnter", function(self) EllesmereUI.ShowWidgetTooltip(self, "Fill Color") end)
cbSw:SetScript("OnLeave", function() EllesmereUI.HideWidgetTooltip() end)
leftRgn._lastInline = cbSw
local function AddCastColorSwatch(tooltip, colorKey, fallback)
local sw = EllesmereUI.BuildColorSwatch(leftRgn, leftRgn:GetFrameLevel() + 5,
function()
local c = SGetSupported(colorKey)
c = c or fallback
return c.r, c.g, c.b, 1
end,
function(r, g, b)
SSetSupported(colorKey, { r = r, g = g, b = b })
ReloadAndUpdate(); UpdatePreview()
end, false, 20)
sw:SetPoint("RIGHT", leftRgn._lastInline or leftRgn._control, "LEFT", -12, 0)
sw:SetScript("OnEnter", function(self) EllesmereUI.ShowWidgetTooltip(self, tooltip) end)
sw:SetScript("OnLeave", function() EllesmereUI.HideWidgetTooltip() end)
leftRgn._lastInline = sw
end
if selectedUnit == "target" or selectedUnit == "focus" then
-- Inline swatches anchor right-to-left; add CD first so interruptible sits left of it
AddCastColorSwatch("Interrupt on CD", "castbarInterruptReadyColor", { r = 0.92, g = 0.35, b = 0.20 })
AddCastColorSwatch("Interruptible Cast", "castbarFillColor", { r = 0.863, g = 0.820, b = 0.639 })
else
AddCastColorSwatch("Fill Color", "castbarFillColor", { r = 1, g = 0.7, b = 0 })
end
end
-- Sync icon: Show Cast Bar + Fill Color (left region)
do
local rgn = sharedCastRow1._leftRegion
local isKickUnit = selectedUnit == "target" or selectedUnit == "focus"
EllesmereUI.BuildSyncIcon({
region = rgn,
tooltip = "Apply Show Cast Bar and Fill Color to all Frames",
tooltip = isKickUnit and "Apply Show Cast Bar and Cast Color to Target and Focus"
or "Apply Show Cast Bar and Fill Color to all Frames",
onClick = function()
local v = GetCastbarEnabled(selectedUnit)
local c = UNIT_DB_MAP[selectedUnit]().castbarFillColor
for _, key in ipairs(GROUP_UNIT_ORDER) do
local readyC = isKickUnit and UNIT_DB_MAP[selectedUnit]().castbarInterruptReadyColor
local keys = isKickUnit and { "target", "focus" } or GROUP_UNIT_ORDER
for _, key in ipairs(keys) do
SetCastbarEnabled(key, v)
if c then UNIT_DB_MAP[key]().castbarFillColor = { r=c.r, g=c.g, b=c.b } end
if c then UNIT_DB_MAP[key]().castbarFillColor = { r = c.r, g = c.g, b = c.b } end
if readyC then UNIT_DB_MAP[key]().castbarInterruptReadyColor = { r = readyC.r, g = readyC.g, b = readyC.b } end
end
ReloadAndUpdate(); EllesmereUI:RefreshPage()
end,
isSynced = function()
local v = GetCastbarEnabled(selectedUnit)
local c = UNIT_DB_MAP[selectedUnit]().castbarFillColor
for _, key in ipairs(GROUP_UNIT_ORDER) do
local readyC = isKickUnit and UNIT_DB_MAP[selectedUnit]().castbarInterruptReadyColor
local keys = isKickUnit and { "target", "focus" } or GROUP_UNIT_ORDER
for _, key in ipairs(keys) do
if GetCastbarEnabled(key) ~= v then return false end
local kc = UNIT_DB_MAP[key]().castbarFillColor
if c and kc then
if kc.r ~= c.r or kc.g ~= c.g or kc.b ~= c.b then return false end
elseif c ~= kc then return false end
if isKickUnit then
local kr = UNIT_DB_MAP[key]().castbarInterruptReadyColor
if readyC and kr then
if kr.r ~= readyC.r or kr.g ~= readyC.g or kr.b ~= readyC.b then return false end
elseif readyC ~= kr then return false end
end
end
return true
end,
Expand All @@ -5306,9 +5330,13 @@ initFrame:SetScript("OnEvent", function(self)
onApply = function(checkedKeys)
local v = GetCastbarEnabled(selectedUnit)
local c = UNIT_DB_MAP[selectedUnit]().castbarFillColor
local readyC = isKickUnit and UNIT_DB_MAP[selectedUnit]().castbarInterruptReadyColor
for _, key in ipairs(checkedKeys) do
SetCastbarEnabled(key, v)
if c then UNIT_DB_MAP[key]().castbarFillColor = { r=c.r, g=c.g, b=c.b } end
if c then UNIT_DB_MAP[key]().castbarFillColor = { r = c.r, g = c.g, b = c.b } end
if readyC and (key == "target" or key == "focus") then
UNIT_DB_MAP[key]().castbarInterruptReadyColor = { r = readyC.r, g = readyC.g, b = readyC.b }
end
end
ReloadAndUpdate(); EllesmereUI:RefreshPage()
end,
Expand Down Expand Up @@ -5356,7 +5384,7 @@ initFrame:SetScript("OnEvent", function(self)
})
end

-- Row 2: Show Icon | Hide When Idle
-- Row 2: Show Icon | Hide When Idle (or kick tick for target/focus)
local function GetShowIcon()
if selectedUnit == "player" then
local v = UNIT_DB_MAP.player().showPlayerCastIcon
Expand Down Expand Up @@ -5384,14 +5412,37 @@ initFrame:SetScript("OnEvent", function(self)
UNIT_DB_MAP[selectedUnit]().castbarHideWhenInactive = val
end

local isKickCastUnit = selectedUnit == "target" or selectedUnit == "focus"
local castRow2Right
if isKickCastUnit then
castRow2Right = {
type = "toggle",
text = "Show Tick at Kick Ready Spot",
tooltip = "Shows a small white tick mark on the cast bar at the point where the cast will be when your interrupt comes off cooldown.",
getValue = function()
local v = SGetSupported("castbarKickTickEnabled")
if v == nil then return true end
return v
end,
setValue = function(v)
SSetSupported("castbarKickTickEnabled", v)
ReloadAndUpdate(); UpdatePreview()
end,
}
else
castRow2Right = {
type = "toggle",
text = "Hide When Idle",
getValue = GetHideInactive,
setValue = function(v) SetHideInactive(v); ReloadAndUpdate(); UpdatePreview() end,
}
end
local castRow2
castRow2, h = W:DualRow(parent, y,
{ type="toggle", text="Show Icon",
getValue=GetShowIcon,
setValue=function(v) SetShowIcon(v); ReloadAndUpdate(); UpdatePreview() end },
{ type="toggle", text="Hide When Idle",
getValue=GetHideInactive,
setValue=function(v) SetHideInactive(v); ReloadAndUpdate(); UpdatePreview() end }); y = y - h
castRow2Right); y = y - h
-- Sync icon: Show Icon (left)
do
local rgn = castRow2._leftRegion
Expand Down Expand Up @@ -5437,9 +5488,34 @@ initFrame:SetScript("OnEvent", function(self)
},
})
end
-- Sync icon: Hide When Idle (right)
-- Sync icon: Hide When Idle or kick tick (right)
do
local rgn = castRow2._rightRegion
if isKickCastUnit then
EllesmereUI.BuildSyncIcon({
region = rgn,
tooltip = "Apply Kick Tick Setting to Target and Focus",
onClick = function()
local v = UNIT_DB_MAP[selectedUnit]().castbarKickTickEnabled
for _, key in ipairs({ "target", "focus" }) do
UNIT_DB_MAP[key]().castbarKickTickEnabled = v
end
ReloadAndUpdate(); EllesmereUI:RefreshPage()
end,
isSynced = function()
local v = UNIT_DB_MAP[selectedUnit]().castbarKickTickEnabled
for _, key in ipairs({ "target", "focus" }) do
if key ~= selectedUnit then
local kv = UNIT_DB_MAP[key]().castbarKickTickEnabled
if (v == nil) ~= (kv == nil) then return false end
if v ~= nil and kv ~= nil and v ~= kv then return false end
end
end
return true
end,
flashTargets = function() return { rgn } end,
})
else
EllesmereUI.BuildSyncIcon({
region = rgn,
tooltip = "Apply Hide When Idle to all Frames",
Expand Down Expand Up @@ -5473,6 +5549,53 @@ initFrame:SetScript("OnEvent", function(self)
end,
},
})
end
end

if isKickCastUnit then
local castHideRow
castHideRow, h = W:DualRow(parent, y,
{ type="toggle", text="Hide When Idle",
getValue=GetHideInactive,
setValue=function(v) SetHideInactive(v); ReloadAndUpdate(); UpdatePreview() end },
{ text="" })
y = y - h
do
local rgn = castHideRow._leftRegion
EllesmereUI.BuildSyncIcon({
region = rgn,
tooltip = "Apply Hide When Idle to all Frames",
onClick = function()
local v = GetHideInactive()
for _, key in ipairs(GROUP_UNIT_ORDER) do
UNIT_DB_MAP[key]().castbarHideWhenInactive = v
end
ReloadAndUpdate(); EllesmereUI:RefreshPage()
end,
isSynced = function()
local v = GetHideInactive()
for _, key in ipairs(GROUP_UNIT_ORDER) do
local kv = UNIT_DB_MAP[key]().castbarHideWhenInactive
if kv == nil then kv = true end
if kv ~= v then return false end
end
return true
end,
flashTargets = function() return { rgn } end,
multiApply = {
elementKeys = GROUP_UNIT_ORDER,
elementLabels = SHORT_LABELS,
getCurrentKey = function() return selectedUnit end,
onApply = function(checkedKeys)
local v = GetHideInactive()
for _, key in ipairs(checkedKeys) do
UNIT_DB_MAP[key]().castbarHideWhenInactive = v
end
ReloadAndUpdate(); EllesmereUI:RefreshPage()
end,
},
})
end
end

-- Row 3: Spell Name Size (with inline color swatch) | Duration Size (with inline color swatch)
Expand Down
Loading