casts: route everything through C_Spell, drop libcast + libdebuff_casts

ClassicAPI's recent C_Spell additions cover remote-unit casts natively
(SMSG_SPELL_START co-hook caching per caster GUID), so the two parallel
cast trackers pfUI was running — libcast.lua and the libdebuff_casts
table inside libdebuff.lua — can both retire.

Migrations:
- modules/castbar.lua: focus/player cast-info gathering reads
  C_Spell.UnitCastingInfo / UnitChannelInfo directly. Fallback ladder
  (libdebuff_casts → pfGetCastInfo → pfGetChannelInfo) collapses into a
  single call. Pushback handlers stop writing back into a non-existent
  cache; the local this.endTime is the source of truth.
- modules/nameplates.lua: GetCastInfo(guid) now resolves to a unit token
  via UnitTokenFromGUID and queries C_Spell, returning the same compact
  struct shape downstream code expected. UpdateCastbar collapses from a
  three-branch hierarchy (dead IterDebuffs / libdebuff_casts / libcast)
  to one C_Spell read.
- modules/afkcam.lua: pfGetCastInfo+pfGetChannelInfo round-trip becomes
  a single C_Spell.UnitCastingInfo("player") or UnitChannelInfo fallback.
- libs/libpredict.lua: HealComm timing uses C_Spell on the sender's unit
  token after a small group-roster walk to resolve the sender's name.

Deletions:
- libs/libcast.lua entirely (-571 lines) plus its init/libs.xml entry.
- libdebuff_casts / libdebuff_item_icons tables and their write sites
  in libs/libdebuff.lua (the SPELL_START_*, SPELL_GO_*, SPELL_FAILED_*
  event handlers stop maintaining them but keep firing the
  libdebuff_*_hooks broadcast surface for actionbar / swingtimer /
  libtotem). SPELLCAST_CHANNEL_STOP now reads the active channel from
  C_Spell.ChannelInfo.
- modules/superwow.lua's supercast block — UNIT_CASTEVENT writes into
  libcast.db are redundant now that C_Spell co-hooks the same packet.
- The cast-bar item-icon override that swapped in a potion/trinket
  icon for item-triggered casts. Spell icon stays; the item-icon
  metadata path (libdebuff_item_icons) went with libdebuff_casts.

Steady Shot synthetic cast bar — Turtle WoW-specific:
- castbar.lua gains a pfUI.synthetic_casts[unit] fallback that fires
  only when C_Spell returns nil, so abilities the engine treats as
  instant but which have a meaningful wait window can still render a
  cast bar.
- modules/turtle-wow.lua replaces the old libcast.customcast block with
  a Nampower SPELL_QUEUE_EVENT subscriber. ON_SWING_QUEUED matching
  the localized "Steady Shot" name writes a 1.4s synthetic entry;
  ON_SWING_QUEUE_POPPED clears it; castbar's endMs guard self-expires
  the entry as a safety net. Note: haste scaling (libcast.ApplyShotHaste)
  is gone — bar may finish slightly early under +ranged haste buffs.

Net: 152 insertions, 972 deletions.
This commit is contained in:
Brues
2026-06-25 01:18:22 -05:00
parent d62a1222df
commit b79132b9f4
9 changed files with 152 additions and 972 deletions
+11 -66
View File
@@ -15,7 +15,8 @@ setfenv(1, pfUI:GetEnvironment())
-- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and
-- non-player expirationTime). What remains in libdebuff is the cast-event
-- bookkeeping consumed by GetBestAuraCast / GetEnhancedDebuffs and the
-- libdebuff_casts / libdebuff_*_hooks broadcast surface.
-- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer
-- / libtotem react to SPELL_GO and SPELL_FAILED).
-- return instantly when another libdebuff is already active
if pfUI.api.libdebuff then return end
@@ -144,8 +145,6 @@ local iconCache = pfUI.libdebuff_icon_cache
-- Cast Tracking: [casterGuid] = {spellID, spellName, icon, startTime, duration, endTime}
-- Shared with nameplates for cast-bar display
pfUI.libdebuff_casts = pfUI.libdebuff_casts or {}
pfUI.libdebuff_item_icons = pfUI.libdebuff_item_icons or {} -- [casterGuid] = icon (persists across SPELL_GO)
-- Cleveroids API: [targetGUID][spellID] = {start, duration, caster, stacks}
pfUI.libdebuff_objects_guid = pfUI.libdebuff_objects_guid or {}
@@ -965,23 +964,19 @@ if hasNampower then
return
elseif event == "SPELLCAST_CHANNEL_STOP" then
-- Channel interrupted by player - clear ownDebuffs for the channeled spell immediately.
-- DEBUFF_REMOVED fires later (0.5-1s server lag), causing phantom debuff display.
-- We look up the active channel cast and pre-clear ownDebuffs for its target.
local myGuid = GetPlayerGUID()
local castData = myGuid and pfUI.libdebuff_casts[myGuid]
if castData and castData.event == "CHANNEL" and castData.spellName then
local spellName = castData.spellName
-- Channel interrupted by player - clear ownDebuffs for the channeled spell
-- via C_Spell.ChannelInfo. DEBUFF_REMOVED fires later (0.5-1s server lag),
-- causing phantom debuff display without this pre-clear.
local spellName = select(1, C_Spell.ChannelInfo())
if spellName then
local targetGuid = UnitGUID and UnitGUID("target")
if targetGuid and ownDebuffs[targetGuid] and ownDebuffs[targetGuid][spellName] then
local data = ownDebuffs[targetGuid][spellName]
-- Only clear if the timer is still active (not already expired naturally)
local remaining = (data.startTime + data.duration) - GetTime()
if remaining > 0 then
ownDebuffs[targetGuid][spellName] = nil
end
end
pfUI.libdebuff_casts[myGuid] = nil
end
elseif event == "PLAYER_ENTERING_WORLD" then
@@ -1003,48 +998,15 @@ if hasNampower then
end
elseif event == "SPELL_START_SELF" or event == "SPELL_START_OTHER" then
local itemId = arg1
local spellId = arg2
local casterGuid = arg3
local spellType = arg8 or 0 -- 0=Normal, 1=Channel, 2=Autorepeating
-- arg6=castTime, arg7=channel duration
-- prefer arg6 if present — some spells (e.g. Volley post-rework) still send
-- arg8=1 but now have a real cast time in arg6, so we only fall back to arg7
-- when arg6 is nil (true channels like Blizzard)
-- arg6=castTime (ms), arg7=channel duration (ms), arg8=spellType
-- For channels: arg6=0 (no cast time), arg7=duration, spellType=1
-- For normal casts: arg6=castTime, arg7=0, spellType=0
-- Note: "not arg6" is wrong in Lua since 0 is truthy - use arg6 == 0 or nil
-- arg6=castTime (ms), arg7=channel duration (ms). Prefer arg6 when set —
-- some spells (e.g. post-rework Volley) flag as channel via arg8 but ship a
-- real cast time in arg6, so we only fall back to arg7 for true channels
-- like Blizzard where arg6 is 0/nil.
local castTime = (arg6 and arg6 > 0) and arg6 or arg7
local isChannel = spellType == 1 and (not arg6 or arg6 == 0)
if not casterGuid or not spellId then return end
local spellName = C_Spell.GetSpellName(spellId)
local icon = libdebuff:GetSpellIcon(spellId)
-- Use item icon for item-triggered casts
if itemId and itemId > 0 then
icon = C_Item.GetItemIconByID(itemId) or icon
pfUI.libdebuff_item_icons[casterGuid] = {
icon = icon,
name = GetItemInfo(itemId),
}
else
pfUI.libdebuff_item_icons[casterGuid] = nil
end
pfUI.libdebuff_casts[casterGuid] = {
spellID = spellId,
itemID = itemId and itemId > 0 and itemId or nil,
spellName = spellName,
icon = icon,
startTime = GetTime(),
duration = castTime and castTime / 1000 or 0,
endTime = castTime and (GetTime() + castTime / 1000) or nil,
event = isChannel and "CHANNEL" or "START"
}
if event == "SPELL_START_SELF" and pfUI.libdebuff_spell_start_self_hooks then
for _, fn in pairs(pfUI.libdebuff_spell_start_self_hooks) do
fn(spellId, casterGuid, arg4, castTime)
@@ -1063,15 +1025,6 @@ if hasNampower then
local numHit = arg6 or 0
local numMissed = arg7 or 0
-- Clear cast bar only if SPELL_GO matches the active cast
-- (Reactive procs like Frost Armor trigger SPELL_GO but shouldn't clear the castbar)
-- Don't clear channels on SPELL_GO - channels persist until duration expires or SPELL_FAILED
if casterGuid and pfUI.libdebuff_casts[casterGuid] then
if pfUI.libdebuff_casts[casterGuid].spellID == spellId and pfUI.libdebuff_casts[casterGuid].event ~= "CHANNEL" then
pfUI.libdebuff_casts[casterGuid] = nil
end
end
-- 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
@@ -1195,14 +1148,6 @@ if hasNampower then
elseif event == "SPELL_FAILED_OTHER" then
local casterGuid = arg1
local spellId = arg2
if casterGuid and pfUI.libdebuff_casts[casterGuid] then
-- Only clear if spellID matches to avoid clearing a cast that already moved on
if pfUI.libdebuff_casts[casterGuid].spellID == spellId then
pfUI.libdebuff_casts[casterGuid] = nil
end
end
if pfUI.libdebuff_spell_failed_other_hooks then
for _, fn in pairs(pfUI.libdebuff_spell_failed_other_hooks) do
fn(casterGuid, arg2)