Skip to content
Merged
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
18 changes: 16 additions & 2 deletions EllesmereUICooldownManager/EUI_CooldownManager_Options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17769,8 +17769,16 @@ initFrame:SetScript("OnEvent", function(self)
-- "47528"). Give it a NAME but deliberately do NOT add it to
-- spellOrder -- it must not appear as a selectable option on a
-- bar that no longer holds it.
--
-- Label it ONLY when this character can actually cast it. The
-- id is profile-level and the spellbook behind it is per-spec,
-- so a spec that shares the profile but not the spell inherits
-- a pick it can never use -- field-reported as a Holy Paladin
-- being shown "Rebuke". Leaving it unlabelled is what makes
-- getValue below fall back to the bar's own contents.
local selSid = BD and BD() and BD().focusKickInterruptSpellID
if selSid then
if selSid and (not ns.ResolveCastableInterrupt
or ns.ResolveCastableInterrupt(selSid)) then
local selKey = tostring(selSid)
if not spellValues[selKey] then
local selInfo = C_Spell and C_Spell.GetSpellInfo
Expand All @@ -17793,7 +17801,13 @@ initFrame:SetScript("OnEvent", function(self)
getValue = function()
local sid = BD().focusKickInterruptSpellID
if not sid then return spellOrder[1] end
return tostring(sid)
-- No label means RebuildSpellOptions rejected it: either
-- this character cannot cast it, or it is not on the bar
-- and not castable. Show what the bar actually holds
-- rather than a selection the player never made.
local key = tostring(sid)
if not spellValues[key] then return spellOrder[1] end
return key
end,
setValue = function(v)
if v == "__none" then
Expand Down
83 changes: 79 additions & 4 deletions EllesmereUICooldownManager/EllesmereUICooldownManager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6565,6 +6565,57 @@ local function RefreshFocusCastProxyUnit()
_focusCastProxy:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", unit)
end
ns.RefreshFocusCastProxyUnit = RefreshFocusCastProxyUnit

function ns.IsSpellInPlayerBook(id)
if IsPlayerSpell and IsPlayerSpell(id) then return true end
if C_SpellBook and C_SpellBook.IsSpellKnownOrInSpellBook
and C_SpellBook.IsSpellKnownOrInSpellBook(id) then
return true
end
return false
end

-- Returns the id of the stored interrupt in the form this character can
-- actually cast, or nil.
--
-- Validate on READ and never write: focusKickInterruptSpellID is profile-level
-- while the spellbook behind it is per-spec, so the id stays correct for the
-- spec that set it. Clearing it here would destroy that spec's setting the
-- first time the player logged in on another one.
--
-- Asks the SPELLBOOK, never a class/spec table. Which specs carry an interrupt
-- is Blizzard's to change, and a hardcoded list of that goes stale on a patch.
-- Pet-bank interrupts (a Warlock's Axe Toss, a Hunter's pet kick) are
-- legitimate picks that IsPlayerSpell cannot see, so check both banks.
--
-- Returning the resolved id rather than a boolean is the point: the caller
-- feeds it to a cooldown check, and a talent swap moves an interrupt between
-- its base and override forms while the stored id stays put. Answering "yes,
-- known" but leaving the caller holding the un-castable form would put an id
-- that is never on cooldown into the readiness gate.
function ns.ResolveCastableInterrupt(sid)
if type(sid) ~= "number" or sid <= 0 then return nil end
local knownInBook = ns.IsSpellInPlayerBook
if knownInBook(sid) then return sid end
-- Talented into a replacement, stored id is the base form.
if C_SpellBook and C_SpellBook.FindSpellOverrideByID then
local ovr = C_SpellBook.FindSpellOverrideByID(sid)
if ovr and ovr > 0 and ovr ~= sid and knownInBook(ovr) then return ovr end
end
-- Talented back out, stored id is the replacement form.
if C_Spell and C_Spell.GetBaseSpell then
local base = C_Spell.GetBaseSpell(sid)
if base and base > 0 and base ~= sid and knownInBook(base) then return base end
end
-- Pet bank last: it has no override/base indirection to walk.
if C_SpellBook and C_SpellBook.IsSpellKnownOrInSpellBook
and Enum and Enum.SpellBookSpellBank
and C_SpellBook.IsSpellKnownOrInSpellBook(sid, Enum.SpellBookSpellBank.Pet) then
return sid
end
return nil
end

local function EnsureFocusCastProxy()
if _focusCastProxy then
-- Demand-gate re-activation: re-register (idempotent; re-applying
Expand All @@ -6584,16 +6635,40 @@ local function EnsureFocusCastProxy()
local soundKey = bd.focusCastSoundKey or "none"
if soundKey == "none" then return end
local spellID = bd.focusKickInterruptSpellID
-- An explicit pick is only trusted while this character can cast it.
-- A stale profile-level id sails through the cooldown gate below
-- forever -- a spell you do not know is never on cooldown -- so a Holy
-- Paladin inheriting a Ret Paladin's Rebuke would be pinged for every
-- focus cast to interrupt something they have no interrupt for.
--
-- Deliberately checked HERE and not at arming time: arming runs during
-- loading screens, when the spellbook reads empty for reasons that have
-- nothing to do with the player's spec, and folding "not loaded yet"
-- into "cannot cast it" is what silently unregistered a working proxy
-- in the first place. This handler only runs on a live cast, by which
-- point the spellbook is settled.
if spellID then spellID = ns.ResolveCastableInterrupt(spellID) end
-- Auto-fallback: if user hasn't explicitly picked a spell, use the
-- first positive spell on the bar. The picker exists for users who
-- want a specific spell when multiple are on the bar.
-- first CASTABLE positive spell on the bar. The picker exists for users
-- who want a specific spell when multiple are on the bar.
--
-- The bar's own list needs the same check as the explicit pick, not
-- because it crosses specs (assignedSpells is per-spec) but because it
-- can hold spells this spec no longer has: importing a shared profile
-- with "Include CDM Spell Layout" writes another character's layout
-- into these spec keys wholesale, and talent changes strand entries the
-- same way. Whichever list supplies the id, an interrupt this character
-- cannot cast reads as permanently ready to the cooldown gate below.
if not spellID or spellID <= 0 then
local sd = ns.GetBarSpellData and ns.GetBarSpellData(FOCUSKICK_BAR_KEY)
if sd and sd.assignedSpells then
for _, sid in ipairs(sd.assignedSpells) do
if type(sid) == "number" and sid > 0 then
spellID = sid
break
local castable = ns.ResolveCastableInterrupt(sid)
if castable then
spellID = castable
break
end
end
end
end
Expand Down
10 changes: 1 addition & 9 deletions Locales/_keys.txt
Original file line number Diff line number Diff line change
@@ -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
# (666 unique). Regenerate after wrapping new strings. Keys passed as
# (658 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)
Expand Down Expand Up @@ -98,7 +98,6 @@ Anchor
Anchored
Apply
Apply All Settings To
Apply the sharer's Blizz UI Enhanced Window Skins and Tooltips, Menus & Popups settings. These are account-wide and will overwrite yours across ALL profiles. Off = keep your own.
Apply to Bar
Apply to Bar (All Specs)
Apply to This Spell
Expand Down Expand Up @@ -318,17 +317,12 @@ Import Full Account Data
Import Profile
Import Selected Addons
Import a profile from string.
Import the anchor & size-match relationships from this profile. Off = keep your own layout; only the selected modules' own positions/settings come in.
Import the sharer's complete override setup: spec and conditional override values, groups, their custom Unlock Mode layouts, and Buff Manager overrides. This replaces ALL of your own overrides. Off = keep yours untouched.
Import will include %1$s of %2$s addons.
Importing %1$s
In-game countdown unavailable in combat; the boss mod pull timer still started.
Include
Include CDM Spell Layout?
Include Overrides
Include This Spell
Include Window Skins
Include layout
Include your Cooldown Manager spell layout (which spells sit on which bars) plus all per-spell settings for any specs you choose.
Include:
Independent
Expand Down Expand Up @@ -585,8 +579,6 @@ This is where you can control the settings of Unlock Mode.\n\nElements can be re
This option requires %1$s to be %2$s
This option requires Subtitle Text to include the Guild Name
This preset
This profile string does not carry any Window & Tooltip Skins settings.
This profile string does not carry any override data.
This profile was made at %1$d%% UI scale; yours is %2$d%%. Change your UI scale to match the imported profile? This will show all profiles at this scale as UI Scale is not a per-profile setting, but can be changed at any time back to your original value.
This setting's active Apply to Bar (All Specs) value will be replaced.
This setting's active Apply to Bar value will be replaced.
Expand Down