mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-28 10:26:02 +00:00
swingtimer: derive spell categories from Spell.dbc attributes
Drop four hardcoded spell-ID tables (swingDelaySpells, hsSpellIDs, cleaveSpellIDs, maulSpellIDs) and the broad interruptFlags > 0 reset heuristic in favor of server-parity checks against Spell.dbc bits via nampower's GetSpellRecField: - "Resets the swing on cast complete" gated on InterruptFlags's SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08) and AttributesEx2's lack of SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS (0x20000) — mirrors the server's Spell::IsMeleeAttackResetSpell. Reset OH alongside MH to match. - "Slam-style delay" derived from absent 0x08 + a cast time existing (implicit since SPELL_START_SELF only fires for cast-time spells). Freezes the swing timer at SPELL_START and adds cast duration on SPELL_GO instead of resetting. - HS / Cleave / Maul classification via IsOnSwingSpell (ATTR_ON_NEXT_ SWING bit 0x04) + name comparison against rank-1 canonical names, centralized into ClassifyOnSwingSpell + SetQueuedKind helpers. Switch the lone GetSpellRec table-allocator call to GetSpellRecField for consistency (single-field hash lookup, no shared-table reuse caveat).
This commit is contained in:
+74
-76
@@ -39,45 +39,20 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local WAND_SHOOT_SPELLID = 5019
|
||||
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
|
||||
|
||||
-- Spells that DELAY the swing timer by their cast duration but do NOT reset it.
|
||||
-- Slam: vanilla behavior on Turtle WoW - delays swing, does not reset.
|
||||
-- Hammer of Wrath: Turtle WoW changed behavior - does not reset swing timer.
|
||||
local swingDelaySpells = {
|
||||
[1464] = true, [8820] = true, [11604] = true, [11605] = true, -- Slam R1-R4
|
||||
[24275] = true, [24274] = true, [24239] = true, -- Hammer of Wrath R1-R3
|
||||
}
|
||||
|
||||
-- SPELL_ATTR_ON_NEXT_SWING (bit 2, value 4): spell replaces next auto-attack swing.
|
||||
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
|
||||
local ATTR_ON_NEXT_SWING = 4
|
||||
local function IsOnSwingSpell(spellId)
|
||||
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
|
||||
local rec = GetSpellRec(spellId)
|
||||
local result = rec and bit.band(rec.attributes, ATTR_ON_NEXT_SWING) ~= 0 or false
|
||||
local attr = GetSpellRecField(spellId, "attributes") or 0
|
||||
local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
|
||||
S.onSwingCache[spellId] = result
|
||||
return result
|
||||
end
|
||||
|
||||
-- Heroic Strike spell IDs (all ranks)
|
||||
local hsSpellIDs = {
|
||||
[78] = true, [284] = true, [285] = true, [1608] = true,
|
||||
[11564] = true, [11565] = true, [11566] = true, [11567] = true,
|
||||
}
|
||||
|
||||
-- Cleave spell IDs (all ranks)
|
||||
local cleaveSpellIDs = {
|
||||
[845] = true, [7369] = true, [11608] = true, [11609] = true,
|
||||
[20569] = true,
|
||||
}
|
||||
|
||||
-- Maul spell IDs (all ranks 1 to 7)
|
||||
local maulSpellIDs = {
|
||||
[6807] = true,
|
||||
[6808] = true,
|
||||
[6809] = true,
|
||||
[8972] = true,
|
||||
[9745] = true, [9880] = true, [9881] = true,
|
||||
}
|
||||
-- HS / Cleave / Maul are detected dynamically via IsOnSwingSpell (the
|
||||
-- SPELL_ATTR_ON_NEXT_SWING bit) + name comparison against the rank-1
|
||||
-- canonical names cached below — no per-rank ID maintenance.
|
||||
|
||||
-- Read config
|
||||
local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200
|
||||
@@ -410,11 +385,35 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
pfUI.swingtimer:Hide()
|
||||
end
|
||||
|
||||
-- HS/Cleave helpers. Canonical rank-1 spellIDs resolve to the localized
|
||||
-- spell name once, so the per-slot comparison is locale-independent without
|
||||
-- per-rank hardcoding (every rank of Heroic Strike returns the same name).
|
||||
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
|
||||
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
|
||||
-- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
|
||||
-- localized spell name once, so per-spell classification is rank- and
|
||||
-- locale-independent without per-rank ID hardcoding (every rank of
|
||||
-- Heroic Strike etc. returns the same name).
|
||||
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
|
||||
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
|
||||
local MAUL_NAME = C_Spell.GetSpellName(6807) -- Maul (Rank 1)
|
||||
|
||||
-- Classify an on-next-swing spell as "hs" / "cleave" / "maul" / nil.
|
||||
-- IsOnSwingSpell gates the family (ATTR_ON_NEXT_SWING bit 0x04) so
|
||||
-- unrelated spells return nil cheaply.
|
||||
local function ClassifyOnSwingSpell(spellId)
|
||||
if not IsOnSwingSpell(spellId) then return nil end
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name == HS_NAME then return "hs"
|
||||
elseif name == CLEAVE_NAME then return "cleave"
|
||||
elseif name == MAUL_NAME then return "maul"
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the queue flags from a ClassifyOnSwingSpell result. nil = no-op
|
||||
-- (preserves prior flag state when the queued spell isn't one we
|
||||
-- color-code, matching the legacy table-lookup behavior).
|
||||
local function SetQueuedKind(kind)
|
||||
if not kind then return end
|
||||
S.hsQueued = (kind == "hs")
|
||||
S.cleaveQueued = (kind == "cleave")
|
||||
S.maulQueued = (kind == "maul")
|
||||
end
|
||||
|
||||
local function RebuildQueueSlotCache()
|
||||
if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end
|
||||
@@ -698,11 +697,17 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local spellStartFrame = CreateFrame("Frame")
|
||||
spellStartFrame:RegisterEvent("SPELL_START_SELF")
|
||||
spellStartFrame:SetScript("OnEvent", function()
|
||||
if arg1 and arg1 > 0 then
|
||||
S.pendingCastSpellId = arg1
|
||||
-- Slam (and other delay-only spells): freeze the swing timer during cast
|
||||
-- so it pauses instead of ticking down and expiring mid-cast
|
||||
if swingDelaySpells[arg1] and S.mhActive then
|
||||
if not (arg1 and arg1 > 0) then return end
|
||||
S.pendingCastSpellId = arg1
|
||||
-- Freeze the swing timer for cast-time spells that DON'T reset auto-
|
||||
-- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
|
||||
-- let the swing resume from where it paused. Detect dynamically via the
|
||||
-- absent AUTOATTACK interrupt flag (0x08); spells with 0x08 reset on
|
||||
-- SPELL_GO_SELF so freezing isn't necessary. Subsumes the old hardcoded
|
||||
-- swingDelaySpells list (no list maintenance for new Slam-style spells).
|
||||
if S.mhActive then
|
||||
local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
|
||||
if bit.band(iflags, 0x08) == 0 then
|
||||
S.mhFrozenAt = GetTime()
|
||||
end
|
||||
end
|
||||
@@ -733,49 +738,48 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
elseif spellId == THROW_SPELLID then
|
||||
ResetRanged(true)
|
||||
return
|
||||
elseif swingDelaySpells[spellId] then
|
||||
-- Swing-delay spells (Slam, Hammer of Wrath on Turtle WoW):
|
||||
-- Delay the swing timer by cast duration, do NOT reset it.
|
||||
if S.mhFrozenAt then
|
||||
local castDuration = GetTime() - S.mhFrozenAt
|
||||
S.mhTimer = S.mhTimer + castDuration
|
||||
S.mhTimerMax = S.mhTimerMax + castDuration
|
||||
S.mhFrozenAt = nil
|
||||
end
|
||||
S.pendingCastSpellId = nil
|
||||
return
|
||||
elseif hsSpellIDs[spellId] or cleaveSpellIDs[spellId] or maulSpellIDs[spellId] or IsOnSwingSpell(spellId) then
|
||||
S.hsQueued = false; S.cleaveQueued = false
|
||||
S.maulQueued = false
|
||||
ResetMH()
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.hsQueued = false; S.cleaveQueued = false
|
||||
elseif IsOnSwingSpell(spellId) then
|
||||
-- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
|
||||
-- — the swing fires as the spell consumes it. Drop the queued color.
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
ResetMH()
|
||||
else
|
||||
-- Any spell with interruptFlags > 0 resets the swing timer
|
||||
-- (Moonfire, Faerie Fire, Wrath, Starfire etc. - NOT Insect Swarm which has flags=0)
|
||||
local _rec = GetSpellRec(spellId)
|
||||
if _rec and _rec.interruptFlags and _rec.interruptFlags > 0 then
|
||||
-- Mirror the server rule for "does this spell reset the auto-attack
|
||||
-- swing" (Spell::IsMeleeAttackResetSpell in Turtle's core):
|
||||
-- InterruptFlags has SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08)
|
||||
-- AND AttributesEx2 doesn't have NOT_RESET_AUTO_ACTIONS (0x20000).
|
||||
-- If neither path resets and we're holding a frozen-swing-during-cast
|
||||
-- (mhFrozenAt set by SPELL_START_SELF for non-0x08 spells), this is a
|
||||
-- Slam-style cast — push the timer forward by the cast duration so
|
||||
-- the bar resumes from where it paused.
|
||||
local FLAG_AUTOATTACK = 0x08 -- SPELL_INTERRUPT_FLAG_AUTOATTACK
|
||||
local ATTR_KEEP_SWINGS = 0x20000 -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
|
||||
local iflags = GetSpellRecField(spellId, "interruptFlags") or 0
|
||||
if bit.band(iflags, FLAG_AUTOATTACK) ~= 0
|
||||
and bit.band(GetSpellRecField(spellId, "attributesEx2") or 0, ATTR_KEEP_SWINGS) == 0 then
|
||||
if S.mhActive and S.mhSpeed > 0 then
|
||||
UpdateWeaponSpeeds()
|
||||
S.mhTimerMax = S.mhSpeed
|
||||
S.mhTimer = S.mhSpeed
|
||||
end
|
||||
if S.ohActive and S.ohSpeed > 0 then
|
||||
S.ohTimerMax = S.ohSpeed
|
||||
S.ohTimer = S.ohSpeed
|
||||
end
|
||||
elseif S.mhFrozenAt then
|
||||
local castDuration = GetTime() - S.mhFrozenAt
|
||||
S.mhTimer = S.mhTimer + castDuration
|
||||
S.mhTimerMax = S.mhTimerMax + castDuration
|
||||
S.mhFrozenAt = nil
|
||||
end
|
||||
end
|
||||
S.pendingCastSpellId = nil
|
||||
end
|
||||
|
||||
-- SPELL_CAST_EVENT hook: HS/Cleave queue tracking
|
||||
-- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
|
||||
pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
|
||||
pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId)
|
||||
if hsSpellIDs[spellId] then
|
||||
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
|
||||
elseif maulSpellIDs[spellId] then
|
||||
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
|
||||
end
|
||||
SetQueuedKind(ClassifyOnSwingSpell(spellId))
|
||||
end
|
||||
|
||||
|
||||
@@ -859,13 +863,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local spellId = arg2 or 0
|
||||
if eventCode == ON_SWING_QUEUED then
|
||||
S.useSpellQueueEvent = true
|
||||
if hsSpellIDs[spellId] then
|
||||
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
|
||||
elseif cleaveSpellIDs[spellId] then
|
||||
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
|
||||
elseif maulSpellIDs[spellId] then
|
||||
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
|
||||
end
|
||||
SetQueuedKind(ClassifyOnSwingSpell(spellId))
|
||||
elseif eventCode == ON_SWING_QUEUE_POPPED then
|
||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user