mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-23 16:16:56 +00:00
predict + libdebuff + swingtimer: drop hardcoded spell data
Replace per-locale name tables and per-rank ID lists with single
canonical-rank lookups through C_Spell. Spell.dbc bits hoisted to
named constants at module top.
libpredict:
- Four 7-locale tables (PRAYER_OF_HEALING / REJUVENATION / RENEW /
REGROWTH) collapsed to one C_Spell.GetSpellName(rank1id) call each.
- 25-entry SPELL_IDS (all ranks of Rejuv + Renew) for SPELL_GO_SELF
HoT detection replaced by name comparison against REJUVENATION /
RENEW. No per-rank ID maintenance.
libdebuff:
- GetSpellRecField(id, "name") → C_Spell.GetSpellName(id) at all call
sites; the presence-guard pattern is gone (ClassicAPI is a hard
dep, per memory).
- GetSpellRecField(id, "rank") → C_Spell.GetSpellSubtext(id).
swingtimer:
- Hoist FLAG_AUTOATTACK / ATTR_KEEP_SWINGS / ATTR_ON_NEXT_SWING to
module-top constants via tonumber("0xNN", 16) so the SPELL_GO_SELF
hot path stops re-parsing them on every call. Lua 5.0 has no hex
number literals; strtoul-backed tonumber handles the "0x" prefix.
This commit is contained in:
+13
-13
@@ -427,7 +427,7 @@ local function GetDebuffSlotMap(guid)
|
||||
local spellId = auras[auraSlot]
|
||||
if spellId and spellId > 0 then
|
||||
displaySlot = displaySlot + 1
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
local texture = libdebuff:GetSpellIcon(spellId)
|
||||
local stacks = (auraApps and auraApps[auraSlot] or 0) + 1
|
||||
local dtype = nil
|
||||
@@ -1024,7 +1024,7 @@ if hasNampower then
|
||||
local targetGuid = arg4
|
||||
local numHit = arg6 or 0
|
||||
local numMissed = arg7 or 0
|
||||
|
||||
|
||||
-- Fire registered SPELL_GO_SELF hooks BEFORE miss guard
|
||||
-- (Swingtimer needs to see ALL casts, even misses, for swing reset)
|
||||
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
|
||||
@@ -1035,15 +1035,15 @@ if hasNampower then
|
||||
|
||||
if numMissed > 0 or numHit == 0 then return end
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellRankString = GetSpellRecField and GetSpellRecField(spellId, "rank")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local spellRankString = C_Spell.GetSpellSubtext(spellId)
|
||||
|
||||
local castRank = 0
|
||||
if spellRankString and spellRankString ~= "" then
|
||||
castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0
|
||||
end
|
||||
|
||||
|
||||
-- Store in pendingCasts for DEBUFF_ADDED correlation.
|
||||
-- If this cast is a downrank of an already active debuff, fire the downrank blocked hook
|
||||
-- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check.
|
||||
@@ -1209,7 +1209,7 @@ if hasNampower then
|
||||
if not spellId then return end
|
||||
if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
-- Deduplicate: Ignore if we processed this exact cast recently (within 100ms)
|
||||
@@ -1469,7 +1469,7 @@ if hasNampower then
|
||||
|
||||
-- Invalidate slot map cache for this GUID
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
if debugStats.enabled then
|
||||
@@ -1599,9 +1599,9 @@ if hasNampower then
|
||||
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
|
||||
|
||||
-- Invalidate slot map cache for this GUID
|
||||
|
||||
local spellName = (GetSpellRecField and GetSpellRecField(spellId, "name")) or "?"
|
||||
|
||||
|
||||
local spellName = C_Spell.GetSpellName(spellId) or "?"
|
||||
|
||||
if debugStats.enabled then
|
||||
debugStats.debuff_removed = debugStats.debuff_removed + 1
|
||||
if IsCurrentTarget(guid) then
|
||||
@@ -1609,13 +1609,13 @@ if hasNampower then
|
||||
GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- If unit is dead, cleanup all
|
||||
if UnitIsDead and UnitIsDead(guid) then
|
||||
CleanupUnit(guid)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Get auraSlot from event parameter (Nampower 2.29+)
|
||||
-- Fallback to displayToAura mapping if not available
|
||||
local foundAuraSlot = auraSlot
|
||||
|
||||
+21
-81
@@ -44,78 +44,13 @@ local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER c
|
||||
local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window)
|
||||
local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s
|
||||
|
||||
local PRAYER_OF_HEALING
|
||||
do -- Prayer of Healing
|
||||
local locales = {
|
||||
["deDE"] = "Gebet der Heilung",
|
||||
["enUS"] = "Prayer of Healing",
|
||||
["esES"] = "Rezo de curación",
|
||||
["frFR"] = "Prière de soins",
|
||||
["koKR"] = "치유의 기원",
|
||||
["ruRU"] = "Молитва исцеления",
|
||||
["zhCN"] = "治疗祷言",
|
||||
}
|
||||
|
||||
PRAYER_OF_HEALING = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local REJUVENATION
|
||||
do -- Rejuvenation
|
||||
local locales = {
|
||||
["deDE"] = "Verjüngung",
|
||||
["enUS"] = "Rejuvenation",
|
||||
["esES"] = "Rejuvenecimiento",
|
||||
["frFR"] = "Récupération",
|
||||
["koKR"] = "회복",
|
||||
["ruRU"] = "Омоложение",
|
||||
["zhCN"] = "回春术",
|
||||
}
|
||||
|
||||
REJUVENATION = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local RENEW
|
||||
do -- Renew
|
||||
local locales = {
|
||||
["deDE"] = "Erneuerung",
|
||||
["enUS"] = "Renew",
|
||||
["esES"] = "Renovar",
|
||||
["frFR"] = "Rénovation",
|
||||
["koKR"] = "소생",
|
||||
["ruRU"] = "Обновление",
|
||||
["zhCN"] = "恢复",
|
||||
}
|
||||
|
||||
RENEW = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
local REGROWTH
|
||||
do -- Regrowth
|
||||
local locales = {
|
||||
["deDE"] = "Nachwachsen",
|
||||
["enUS"] = "Regrowth",
|
||||
["esES"] = "Recrecimiento",
|
||||
["frFR"] = "Rétablissement",
|
||||
["koKR"] = "재생",
|
||||
["ruRU"] = "Восстановление",
|
||||
["zhCN"] = "愈合",
|
||||
}
|
||||
|
||||
REGROWTH = locales[GetLocale()] or locales["enUS"]
|
||||
end
|
||||
|
||||
|
||||
-- Spell IDs for SPELL_GO_SELF callback (Nampower) - Instant HoT detection
|
||||
local SPELL_IDS = {
|
||||
-- Rejuvenation (all ranks)
|
||||
[774] = "Reju", [1058] = "Reju", [1430] = "Reju", [2090] = "Reju", [2091] = "Reju",
|
||||
[3627] = "Reju", [8910] = "Reju", [9839] = "Reju", [9840] = "Reju", [9841] = "Reju",
|
||||
[25299] = "Reju", [26981] = "Reju", [26982] = "Reju",
|
||||
-- Renew (all ranks)
|
||||
[139] = "Renew", [6074] = "Renew", [6075] = "Renew", [6076] = "Renew", [6077] = "Renew",
|
||||
[6078] = "Renew", [10927] = "Renew", [10928] = "Renew", [10929] = "Renew", [25315] = "Renew",
|
||||
[25221] = "Renew", [25222] = "Renew",
|
||||
}
|
||||
-- Localized spell names resolved once from canonical rank-1 spellIDs.
|
||||
-- Every rank shares the same name, so per-rank comparisons elsewhere can
|
||||
-- be done against these constants without per-locale or per-rank tables.
|
||||
local PRAYER_OF_HEALING = C_Spell.GetSpellName(596) -- Prayer of Healing (Rank 1)
|
||||
local REJUVENATION = C_Spell.GetSpellName(774) -- Rejuvenation (Rank 1)
|
||||
local RENEW = C_Spell.GetSpellName(139) -- Renew (Rank 1)
|
||||
local REGROWTH = C_Spell.GetSpellName(8936) -- Regrowth (Rank 1)
|
||||
|
||||
local libpredict = CreateFrame("Frame")
|
||||
libpredict:RegisterEvent("UNIT_HEALTH")
|
||||
@@ -173,7 +108,7 @@ end
|
||||
|
||||
local function isRezSpell(spellId)
|
||||
if not L["resurrections"] then return false end
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
return spellName and L["resurrections"][spellName]
|
||||
end
|
||||
|
||||
@@ -192,7 +127,7 @@ end)
|
||||
-- SPELL_START_SELF: own cast started (heals + rez)
|
||||
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
|
||||
pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local pendingTarget = nil
|
||||
@@ -319,7 +254,7 @@ end
|
||||
-- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer)
|
||||
pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
|
||||
libpredict:HealStop(player)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName == REGROWTH then
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
if libpredict.sender.regrowth_timer then
|
||||
@@ -337,7 +272,7 @@ end
|
||||
-- Signature: fn(spellId, casterGuid, targetGuid, castTime)
|
||||
pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {}
|
||||
pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
|
||||
local casterName = resolveNameFromGuid(casterGuid)
|
||||
@@ -404,8 +339,13 @@ end
|
||||
-- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||||
pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7)
|
||||
-- Instant HoTs
|
||||
local hotType = SPELL_IDS[spellId]
|
||||
-- Instant HoTs — classify by name (rank-independent) instead of a
|
||||
-- hardcoded per-rank ID table.
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
local hotType
|
||||
if spellName == REJUVENATION then hotType = "Reju"
|
||||
elseif spellName == RENEW then hotType = "Renew"
|
||||
end
|
||||
if hotType then
|
||||
local targetGuid = a4
|
||||
local targetName = resolveNameFromGuid(targetGuid)
|
||||
@@ -1236,7 +1176,7 @@ libpredict.sender:SetScript("OnEvent", function()
|
||||
local amount = arg4
|
||||
local isCrit = arg5 == 1
|
||||
local isPeriodic = arg6 == 1
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName and spell_queue[1] == spellName then
|
||||
UpdateCache(spell_queue[2], amount, isCrit)
|
||||
end
|
||||
@@ -1256,9 +1196,9 @@ libpredict.sender:SetScript("OnEvent", function()
|
||||
local casterName = resolveNameFromGuid(casterGuid)
|
||||
if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return end
|
||||
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or ""
|
||||
local rankStr = C_Spell.GetSpellSubtext(spellId)
|
||||
local spellKey = spellName .. (rankStr or "")
|
||||
|
||||
foreignCache[casterName] = foreignCache[casterName] or {}
|
||||
|
||||
+12
-10
@@ -10,6 +10,11 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local ON_SWING_QUEUED = 0
|
||||
local ON_SWING_QUEUE_POPPED = 1
|
||||
|
||||
-- Spell.dbc bits used to mirror server-side swing-reset rules.
|
||||
local FLAG_AUTOATTACK = tonumber("0x08", 16) -- SPELL_INTERRUPT_FLAG_AUTOATTACK
|
||||
local ATTR_KEEP_SWINGS = tonumber("0x20000", 16) -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
|
||||
local ATTR_ON_NEXT_SWING = tonumber("0x04", 16) -- SPELL_ATTR_ON_NEXT_SWING
|
||||
|
||||
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
|
||||
local S = {
|
||||
mhTimer = 0, mhTimerMax = 1,
|
||||
@@ -39,9 +44,8 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
local WAND_SHOOT_SPELLID = 5019
|
||||
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
|
||||
|
||||
-- SPELL_ATTR_ON_NEXT_SWING (bit 2, value 4): spell replaces next auto-attack swing.
|
||||
-- ATTR_ON_NEXT_SWING: 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 attr = GetSpellRecField(spellId, "attributes") or 0
|
||||
@@ -702,12 +706,12 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- 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
|
||||
-- absent AUTOATTACK interrupt flag (8); spells with that bit 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
|
||||
if bit.band(iflags, FLAG_AUTOATTACK) == 0 then
|
||||
S.mhFrozenAt = GetTime()
|
||||
end
|
||||
end
|
||||
@@ -747,13 +751,11 @@ pfUI:RegisterModule("swingtimer", function ()
|
||||
-- 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).
|
||||
-- AND AttributesEx2 lacks 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
|
||||
-- (mhFrozenAt set by SPELL_START_SELF for non-AUTOATTACK spells), this
|
||||
-- is a Slam-style cast — push the timer forward by the cast duration
|
||||
-- so the bar resumes from where it paused.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user