Commit Graph

3870 Commits

Author SHA1 Message Date
Brues c2d4106170 Update README.md 2026-07-03 22:59:29 -05:00
Brues dbfd337d42 Bump pfUI min to 1.5.8 2026-07-03 21:45:04 -05:00
Brues d834be9f0b Revert "nameplates: hide castbar on remote interrupt / caster death (#11)"
This reverts commit 703d7770ca.
2026-07-03 21:38:05 -05:00
Brues 82a07e1e35 share: rebuild profile export/import on C_EncodingUtil (CBOR+zlib+base64)
Export now diffs the config against defaults and emits
SerializeCBOR -> CompressString (zlib) -> EncodeBase64 with a "!pf1!"
prefix. Import reverses that into a plain table — no loadstring, so a
pasted profile is data and can't execute code. The Decode/Encode button
converts blob <-> editable JSON (SerializeJSON/DeserializeJSON) for
inspection and hand-edits.

Old-format strings still import: standard base64 via DecodeBase64, the
custom LZW decompressor kept import-only, and the resulting Lua source
runs in an EMPTY setfenv sandbox that can only assign its config table.
Decode on a legacy string yields the JSON view, so Encode re-emits it
as a new-format blob (migration path).

Roughly half the paste size of the old LZW format, C-speed instead of
the old bit-string base64 (which froze the client on large configs),
and the format is fully standard — external tools can decode profiles
with stock base64/zlib/CBOR libraries.

Requires ClassicAPI with the SerializeCBOR buffer-growth fix (payloads
over 256 bytes returned nil before it).
2026-07-03 21:19:07 -05:00
Brues 703d7770ca nameplates: hide castbar on remote interrupt / caster death (#11)
ClassicAPI's remote-cast cache is stamped from SMSG_SPELL_START and only
expires by computed end time — 1.12 keeps no per-unit interrupt record,
so an interrupted cast kept animating on the plate until its would-be
finish (BG flag caps being the loudest repro).

Nampower does surface the missing signal in Lua: SPELL_FAILED_OTHER
(casterGuid, spellId; fired from the SMSG_SPELL_FAILED_OTHER handler)
and UNIT_DIED (guid). Stamp a guid-keyed suppression time on either
event and have GetCastInfo drop any cast that started before the stamp;
a newer cast clears its unit's entry. Both castbar paths (dedicated
target frame + central loop) already funnel through GetCastInfo, so one
check covers them. The handler only stamps when the unit actually has a
tracked cast, and flags the plate via castUpdate for a same-tick hide.
2026-07-03 19:28:34 -05:00
Brues bd722b0f0f bump classicapi min to 1.5.7 v9.0.6 2026-07-03 02:29:15 -05:00
Brues e5c51fff48 eqcompare: rewrite on top of ClassicAPI SetHyperlinkCompareItem
Drop the manual C_Item.GetItemStatDelta rendering (inline annotations
and bottom-block summary) in favor of driving native shopping tooltips
via SetHyperlinkCompareItem — the 3.3.5 flow now available through
ClassicAPI. Way less code, and Blizzard's own comparison rendering
handles all stat types uniformly.

Also revert the mode dropdown back to a single basestats checkbox — the
new implementation doesn't distinguish base vs extended (Blizzard's
tooltip shows everything the item exposes), so the two-level control
was redundant. Existing basestats configs pass through unchanged.

AtlasLootTooltip now goes through the shared HookTooltip helper instead
of a bespoke OnShow shim.
2026-07-03 02:10:20 -05:00
Brues 6570afb70a eqcompare: collapse basestats/extendedstats checkboxes into a mode dropdown
Replace the two-checkbox arrangement (Compare Base Stats + Compare
Extended Stats, with the latter gated on the former) with a single
"Item Comparison" dropdown offering Off / Base / Extended. Migrate
existing configs.

Also route CreateConfig's value-change sites through pfUI.events
("config:changed", category, config) so callers can react to arbitrary
setting changes without frame-specific plumbing. Use it here to grey
out "Always Show Item Comparison" when the mode is Off.
2026-07-02 03:05:56 -05:00
Brues 0f97e8b0bd eqcompare: skip paperdoll owners in showalways mode
Comparing an equipped slot's tooltip against itself is pointless — every
delta is zero. Skip that case when `showalways=1`. Shift-hover still
forces the comparison unconditionally.
2026-07-02 01:50:48 -05:00
Brues 64c62a2a25 eqcompare: hook Set* via hooksecurefunc + unify base/extended stat block
Refactor delta rendering:
- extendedstats on  -> all stats (base + extended + DPS + block value)
  appended at the bottom via AddDoubleLine
- extendedstats off -> base stats annotated inline; nothing at the bottom

Requires ClassicAPI's 60-line tooltip fix so the bottom block isn't
truncated. DPS and block value were previously extended-only inline
matches — moved into BASE_STAT_KEYS so they also show at the bottom.

Also extract MakeDependent(child, parent) helper from the local
gate-lambda in gui.lua so the base/extended checkbox pairing generalizes.
2026-07-02 01:22:10 -05:00
Brues 4d316ed43a no select 2026-07-01 20:53:47 -05:00
Brues db0195f0f7 Update README.md 2026-07-01 17:20:54 -05:00
Brues 3d2d5f9254 bump ClassicAPI version 2026-07-01 17:06:31 -05:00
Brues dfbeeb454a eqcompare: use C_Item.GetItemStatDelta for the comparison math
Replaces the twin-tooltip text extraction/comparison pair
(ExtractAttributes + CompareAttributes) with a single delta pull from
ClassicAPI's C_Item.GetItemStatDelta(equippedLink, newLink).

Base stats (Str/Agi/Sta/Int/Spi/Mana/Health + Armor + resistances) stay
annotated inline on their existing tooltip line: iterate the tooltip's
FontString regions (no more _G["...TextLeft"..i] name lookup), match the
"+N Foo" prefix, look up the trailing noun in a label→key map, append
(+delta)/(-delta) from the ClassicAPI delta table.

Extended stats (attack power / ranged AP / spell damage/healing / crit
ratings / hit ratings / mana regen / defense / DPS) don't emit their own
line — vanilla mixes them into equip-spell descriptions ("Equip:
Increases your critical strike chance by 1%") — so aggregate them into
a "Compared to equipped:" block at the bottom via AddDoubleLine. New
`tooltip.compare.extendedstats` config knob (default 1) gates that
block; it depends on `basestats` being on (its GUI checkbox grays out
otherwise). DPS rounded to one decimal — ClassicAPI derives it from
damage/delay so it lands as a raw float.

Random-suffix bonuses ("of the Bear" etc.) now count correctly since
GetItemStatDelta walks item-record + equip-spell auras + suffix
enchants server-side.
2026-07-01 17:03:32 -05:00
Brues 439d5a397a eqcompare: numeric InventoryType lookup, drop bagtypes/itemtypes locales
Both sub-tables carried per-locale strings only so pfUI's own code could
match against localized text. ClassicAPI's numeric item APIs replace
both:

- GetBagFamily now reads classID/subClassID from C_Item.GetItemInfoInstant
  and switches on the numbers (class 1 = Container, class 11 = Quiver).
- eqcompare pulls the itemID from GameTooltip:GetItem(), fetches the
  numeric invType via C_Item.GetItemInventoryTypeByID, and looks up the
  destination slot(s) in a numeric slotTable keyed by Enum.InventoryType.
  Pair-slot invtypes (finger / trinket / one-hand weapon) list both
  destinations directly, so the "_other" string-concat hack is gone.

Removes the setglobal INVTYPE_* injection, the tooltip text scan, and
the itemtypes + bagtypes locale sub-tables across all 7 files.
2026-07-01 15:43:03 -05:00
Brues 5524ad3b91 locales: strip 4 dead sub-tables (hunterpaging, interrupts, spells, icons)
Audited every consumer of pfUI_locale[*][key] across the codebase. Four
sub-tables have no non-locale-file readers left:

  - hunterpaging  — old auto-page trigger removed
  - interrupts    — replaced by Nampower SPELL_INTERRUPTED events
  - spells        — replaced by ClassicAPI Spell.dbc lookups
  - icons         — replaced by ClassicAPI C_Spell.GetSpellName / icon path

Removed the entries from all 7 locale files. ~18k dead lines gone,
~70% shrink per file.
2026-07-01 12:13:25 -05:00
Brues 0f59a3a9b5 replace poll-until-cancel OnUpdate frames with C_Timer / RunNextFrame
Seven ad-hoc OnUpdate handlers were only spinning long enough to reach
a known deadline or a next-frame defer, then unhooking themselves.
Convert them to their proper primitives:

- autovendor: 0.3s wait after junk sell → C_Timer.After(0.3, ...)
- innervatecall: cooldown-expiry ready ping → C_Timer.After(cd, ...)
- focus: re-arm UI_ERROR_MESSAGE next tick → RunNextFrame
- macrotweak: conflict scan after addons load → RunNextFrame
- ui-widgets (CreateQuestionDialog): font-measure resize → RunNextFrame
- libdebuff: post-PEW Nampower init → RunNextFrame
- bubbles: WorldFrame scan after chat event → RunNextFrame

Net -18 lines and no more throwaway frames sitting on the OnUpdate list.
2026-07-01 10:43:46 -05:00
Brues ffdf376ac4 add pfUI.events callback registry, replace addoncompat OnUpdate poll
Introduce a central pfUI.events registry (ClassicAPI's
CallbackRegistryMixin, undefined events allowed) initialized in pfUI.lua
before any module body runs, so publishers/subscribers don't depend on
module load order.

firstrun sets `pfUI.firstrun.completed` and fires `firstrun:complete` at
the point NextStep detects no pending steps. PLAYER_ENTERING_WORLD re-
enters this path on every zone, so the flag is a one-shot guard.

addoncompat drops its 0.1s OnUpdate poll and either RunQueues immediately
(returning user, all steps already done) or subscribes to the event.
2026-07-01 10:20:34 -05:00
Brues 9bdde06150 unitframes: clear stale aura swirl on target swap
Buff and debuff slots only called CooldownFrame_SetTimer on the
`expirationTime > 0` (or `duration > 0` for buffs) paths. When the new
target's aura at the same slot index had neither — permanent / passive
auras like Retribution Aura — neither branch fired and the slot kept
displaying the previous target's swirl/timer.

Add an explicit 0/0/0 clear on every path that doesn't set a real
timer, so the button always starts from a known state.

Fixes #13.
2026-06-28 12:41:31 -05:00
Brues 8bf6672114 buffs: cancel by spellID instead of GetPlayerBuff slot index
`GetPlayerBuff(PLAYER_BUFF_START_ID + this.id, filter)` assumes the
visual index pfUI shows matches the engine's slot order. When that
mismapping happens — most easily reproduced by stacking buffs that
share a slot family — right-clicking one buff cancels another.

`C_Spell.CancelSpellByID(spellID)` ships CMSG_CANCEL_AURA keyed to the
spell, not a slot, so it's immune to whatever order the slot table is
in. Cache `spellId` on the button at refresh time in buff.lua; in the
unitframes/buffwatch handlers fetch the aura fresh via
`C_UnitAuras.GetAuraDataByIndex` at click time.

Fixes #10.
v9.0.5
2026-06-28 04:28:59 -05:00
Brues 29b948e6fa character cleanup 2026-06-28 00:14:40 -05:00
Brues 5f61f94a24 Texture ArenaFrame v9.0.4 2026-06-27 23:31:02 -05:00
Brues 65678da3d2 tooltip: optional movement-speed line via GetUnitSpeed
New tooltip.movespeed config knob (default off, checkbox in the GUI's
tooltip page). When on, the unit tooltip gains a "Speed: N%" line where
N is the unit's run speed normalized to vanilla's 7.0 yd/s base — 100
unmounted, 160 on a 60% mount, 200 on epic, less under snares.

Uses runSpeed (return 2 of GetUnitSpeed), not currentSpeed, so the
number reflects what the unit *would* be running at — visible even
while they're standing still. runSpeed is 0 for out-of-range units, so
the line is skipped in that case.
2026-06-27 19:28:40 -05:00
Brues 9b7aca8541 show CLASSIC_API_VERSION in /pfdll 2026-06-27 19:20:06 -05:00
Brues 60525eec3a Clean up throttle 2026-06-27 19:19:51 -05:00
Brues c3829c2bd2 player: drop talent-side modCastingTime fudge from haste display
The player frame's "Effective Haste" mode was hardcoded talent-position
scrapes: GetTalentInfo(1, 16) for the Mage "Accelerated Arcana"
(flat 5%) and GetTalentInfo(1, 14) for the Warlock "Rapid Deterioration"
(3% per rank), folded into the displayed haste % via
`(1 / (modCastSpeed * modCastingTime) - 1) * 100`.

That's two problems in one:
- Hardcoded talent indices and effect percentages — brittle to any
  Turtle tree reshuffle or retune.
- Conceptually muddled: it folds gear-haste and talent-cast-reduction
  into one number that's hard to read as anything specific. The actual
  effective cast time is already shown on the cast bar via
  C_Spell.UnitCastingInfo (engine helper accounts for SpellMod op 10).

Drop modCastingTime, the LEARNED_SPELL_IN_TAB watcher frame that
maintained it, the per-class talent scrape, and the hasteMode == "2"
display branch. Collapse the now-binary "display_haste" config from a
3-option dropdown to a checkbox. Users on legacy "2" will see the
checkbox unchecked once and can re-enable with a single click.
2026-06-27 19:13:49 -05:00
Brues 28de6fd835 finish UnitInRaid("player") → IsInRaid() sweep
Four more sites: GetUnbuffedRoster + SendChatMessageWide in api.lua,
the loot menu's inRaid local, and the raid module's early-return
guard. Same intent, named helper.
2026-06-27 18:57:21 -05:00
Brues fc75e091a4 finish IsInRaid sweep across remaining "raid count > 0" sites
Three more `if GetNumRaidMembers() > 0 then` raid-vs-party branches
switched to `if IsInRaid()`. Same intent, named helper. Repo is now
clean of the legacy idiom (verified with a final grep).

Loops that actually need the count (`for i = 1, GetNumRaidMembers() do
GetRaidRosterInfo(i)`) keep the call — only the boolean form changes.
2026-06-27 18:56:50 -05:00
Brues ef478b0552 thirdparty-vanilla: collapse solo check to not IsInGroup()
The HealComm self-message guard was `not UnitInRaid("player") and
GetNumPartyMembers() < 1` — the long form of "in no group at all."
2026-06-27 18:55:49 -05:00
Brues 760d8992b6 switch group-membership checks to IsInGroup / IsInRaid
ClassicAPI ships modern IsInGroup() / IsInRaid() backports — drop the
GetNumPartyMembers() > 0 and GetNumRaidMembers() > 0 idioms (and the
GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 conflation) for the
named-intent variants. UnitInRaid("player") → IsInRaid() at the same
sites.
2026-06-27 18:54:04 -05:00
Brues 62a99ac410 mouseover/libpredict: drop legacy spell-target plumbing
With Nampower as a hard dep, /pfcast for spell names always takes the
early CastSpellByName(msg, unit) path. The fallback branch that did the
SpellTargetUnit dance (resolve a friendly unit token, disable AutoSelf
Cast, call SpellTargetUnit) hasn't been reachable in a while, and it
dragged a pile of supporting infrastructure with it.

modules/mouseover.lua:
- Drop the st_units token list, GetUnitString helper, and the
  UnitTokenFromGUID rewrite of GetUnitString — all only used by the
  dead fallback.
- Drop the NoSelfCast helper (only the dead fallback called it).
- Drop the pfMouseOver frame; its only purpose was to hold a .unit
  field the dead fallback wrote and libpredict's hook read.
- The macro path collapses to: if not the current target, swap target,
  run the loadstring'd func, restore the previous target.
- 99 lines → 34.

libs/libpredict.lua:
- Drop the dead `local mouseover = pfUI.uf.mouseover.unit` plumbing in
  the CastSpellByName hook — pfUI.uf.mouseover is gone and the field
  was permanently nil anyway. The three `target or mouseover or default`
  fallback chains collapse to `target or default`.

Modern mouseover/click-to-cast detection in libpredict goes through
pfUI.libpredict_pending_cast (populated by libdebuff from Nampower's
SPELL_CAST_EVENT) — that path is GUID-based, server-authoritative, and
untouched.
2026-06-27 18:41:11 -05:00
Brues deeec89955 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.
v9.0.3
2026-06-27 18:22:03 -05:00
Brues cca9771ef0 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).
2026-06-27 17:52:31 -05:00
Brues 371872bb90 swingtimer: route wand Shoot to ranged bar without resetting MH
Wand Shoot (spellID 5019) wasn't recognized as ranged anywhere, so it
fell through every branch of the SPELL_GO_SELF dispatch and hit the
catch-all interruptFlags > 0 reset, wiping the mainhand swing on every
shot. Casters melee-weaving between MH swings and wand fires lost their
swing visualization.

Replace the hardcoded RANGED_SPELLIDS / WAND_SHOOT_SPELLIDS tables with
C_Spell.IsRangedAutoAttackSpell (Spell.dbc AUTO_REPEAT bit) — catches
both Auto Shot and Shoot today, plus any future auto-repeat ranged
spell. ResetRanged now takes a replaceMH flag: true for Auto Shot /
Throw (Hunter ranged replaces melee), false for wand (independent
timers, both tick concurrently).

Closes #5.
2026-06-27 17:14:48 -05:00
Brues 591045a606 config: stamp sentinel version on dev builds to stop migration re-fire
Dev / git-cloned installs carry "@project-version@" in the toc, which
pfUI normalizes to pfUI.version.string = "dev". MigrateConfig was
writing that literal back into pfUI_config.version; on the next /reload
checkversion() parsed "dev" through tonumber() → nil → 0, evaluated
curversion as 0/0/0, and re-fired every version-gated migration block.
The >3.6.1 buff migration unconditionally rewrites buffs.{buffs,debuffs,
weapons} from the legacy global.hide{,w}buff knobs, so user toggles for
those checkboxes were getting wiped on every reload.

Stamp "999.999.999" on dev builds so subsequent comparisons turn false.
Affected users will need to re-toggle the buffs/debuffs/weapon-buffs
checkboxes once after this update.

Refs #7.
2026-06-27 16:46:56 -05:00
Brues f657a9e7e8 drop MobHealth3 / MobHealthFrame fallbacks
libhealth ships with pfUI and Nampower's GetUnitField (hard-dep) covers
the real-HP read; the MobHealth integration was a chained last-resort
fallback that could never actually win, and on the nameplate path it
also mixed percentage and real-value scales into the bar's SetMinMaxValues
since it overwrote hp/hpmax without hpmin.
2026-06-27 16:08:39 -05:00
Brues 0cbd9ecf98 Revert "nameplates: drop redundant name check from plate-reassignment gate"
This reverts commit 6ca482fcaa.
2026-06-27 16:01:24 -05:00
Brues 6ca482fcaa nameplates: drop redundant name check from plate-reassignment gate
GUID is the unique identity; the name check was leftover from the
name-only era and became dead weight once cachedGuid (NAME_PLATE_UNIT_
ADDED) was added alongside it.
2026-06-27 15:59:16 -05:00
Brues 8902d43f40 nameplates: wipe full plate.cache on reassignment
The plate-reuse reset only cleared name/guid/player/cdCache, leaving
hp/hpmax/rgb/namecolor/levelcolor/target/mouseover/inCombat behind. The
PERF gates downstream ("only update X when X changed") then skipped bar
fill / color / text updates when the new occupant happened to share a
cached value with the previous one — most easily reproduced on
plate-pool reuse in starting zones (mobs churn fast, percentage values
collide). Nuke the whole cache via table.wipe.

Refs #8.
2026-06-27 15:48:53 -05:00
brues-code 8f006c0643 Update issue templates 2026-06-27 15:07:04 -05:00
Brues de7dacf788 xpbar: route rep tracking through ClassicAPI faction APIs
Drop CHAT_MSG_COMBAT_FACTION_CHANGE + SanitizePattern(FACTION_STANDING_
INCREASED) string parsing in favor of FACTION_STANDING_CHANGED, which
ships (factionID, newStanding, repGained) directly — locale-independent,
no chat-string scrape. Track factionID instead of faction name.

Collapse the two for i=1, 99 GetFactionInfo loops (tooltip + bar fill)
into a single GetRepDisplay helper: GetFactionInfoByID for the remembered
faction, C_Reputation.GetWatchedFactionData for the watched one.

Watched-faction-changed detection now compares factionIDs.
v9.0.2
2026-06-27 01:09:15 -05:00
Brues e0bcbbf2dc castbar: Quartz-style tradeskill merge with per-craft spark
Hook DoTradeSkill to capture the requested count, then on the first
SPELLCAST_START of an isTradeskill cast stretch endTime to span all
crafts so the player bar fills continuously across the chain. Mid-chain
SPELLCAST_START / SPELL_START_SELF events refresh the "(N)" remaining
label and reset a per-craft spark that crosses the bar once per craft.
SPELL_GO_SELF counts completions; SPELLCAST_STOP no-ops while merged.

Gated by a new C.castbar.player.mergetradeskill knob (default on).
2026-06-26 18:43:31 -05:00
Brues 65bbea6fa9 bump min again 2026-06-25 23:46:34 -05:00
Brues 822c873132 castbar: event-driven rewrite on ClassicAPI C_Spell + nampower events
Drive the cast bar from cast lifecycle events instead of polling C_Spell
every frame. OnUpdate now only animates a stamped start/end and fades out;
all state transitions come from events:

- player: vanilla SPELLCAST_START / _STOP / _FAILED / _INTERRUPTED /
  _CHANNEL_START / _CHANNEL_STOP, plus nampower SPELL_START_SELF (the only
  signal for a chained same-spell recast, which never runs the client cast
  path) and SPELL_DELAYED_SELF for pushback (applied from its delayMs arg).
- non-player (target/focus): nampower SPELL_START_OTHER / SPELL_FAILED_OTHER
  + PLAYER_TARGET_CHANGED / PLAYER_FOCUS_CHANGED.

Data comes from ClassicAPI's C_Spell.UnitCastingInfo / UnitChannelInfo
(player exact; other units from the SMSG_SPELL_START cache). SPELL_START_*
re-polls are deferred one frame so ClassicAPI's packet co-hook has stamped
before the read. SPELLCAST_CHANNEL_STOP only clears when a channel is shown,
so a lagged channel-stop doesn't wipe a following cast's bar.

Remove the now-unused CASTBAR_EVENT_* constants from compat/vanilla.lua.
2026-06-25 22:38:53 -05:00
Brues 96fd486187 bump min 2026-06-25 01:49:17 -05:00
Brues b79132b9f4 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.
2026-06-25 01:18:22 -05:00
Brues d62a1222df feigndeath: use UnitIsFeignDeath + GetUnitField, drop tooltip cache
ClassicAPI's UnitIsFeignDeath reads UNIT_FIELD_FLAGS bit 29 directly —
the authoritative server flag, no detection guesswork needed. Combined
with Nampower's GetUnitField("health"), we read live HP off the
descriptor instead of caching the moment-of-death healthbar value via
libtipscan.

Drops the name-keyed cache, the UNIT_HEALTH / PLAYER_TARGET_CHANGED
event handlers, and the tooltip scanner. Also fixes the staleness bug
where a feigning hunter taking further damage kept showing the cached
snapshot from when feign first triggered — live read updates with
every UnitHealth call now.
2026-06-24 22:49:43 -05:00
Brues be6ae25315 libdebuff/nameplates: drop dead per-aura readers and debuff cache
With every external caller of libdebuff:UnitDebuff / :UnitOwnDebuff
now on C_UnitAuras, the two public per-aura readers and the
nameplate-side cache they were feeding have no consumers.

- libs/libdebuff.lua: removes libdebuff:UnitDebuff (~120 lines),
  libdebuff:UnitOwnDebuff (~75 lines), the _ownDebuffSortFunc helper,
  and the local cache table. The slotOwnership / ownDebuffs /
  allAuraCasts / pendingCasts bookkeeping stays — GetBestAuraCast
  (libpredict) and GetEnhancedDebuffs (CleveRoids) still read it, and
  the event handlers maintain it. GetSlotCaster / GetDebuffSlotMap
  stay too; the DEBUFF_ADDED_OTHER handler and the debug printer use
  them. File goes 2010 → 1870 lines.
- modules/nameplates.lua: deletes PlateCacheDebuffs (was already
  rewritten on C_UnitAuras and unused once the display loop bypassed
  the cache), PlateUnitDebuff, the cachedVerify scaffolding, and the
  nameplate.UnitDebuff / nameplate.CacheDebuffs registrations.
- api/config.lua + modules/gui.lua: drops the now-defunct
  "guessdebuffs" knob — its only effect was gating the dead cache.
2026-06-24 22:41:36 -05:00
Brues d6951b386e unitframes/buffwatch/libpredict: route aura reads through C_UnitAuras
ClassicAPI's recent C_UnitAuras additions (sourceUnit / sourceGUID /
non-player expirationTime / the PLAYER filter token) finally cover
everything libdebuff:UnitDebuff and :UnitOwnDebuff were doing — caster
attribution, accurate timing for non-player units, and the own-debuffs
filter. Migrating the remaining callers off the libdebuff readers.

- api/unitframes.lua: collapses the debuff render path's three-branch
  if/else into a single C_UnitAuras.GetAuraDataByIndex with a HARMFUL
  or HARMFUL|PLAYER filter selected by the selfdebuff config; tooltip
  slot-finders match by sourceGUID instead of libdebuff's caster flag;
  custom-debuff indicator scan unifies the same way.
- modules/buffwatch.lua: GetBuffData drops the libdebuff fallback;
  tooltip slot-finder mirrors the unitframes pattern.
- libs/libpredict.lua: drops the orphaned UnitHasBuff slot-loop (no
  callers left).
- api/api.lua: pfUI.api.UnitHasBuff tightens from a HELPFUL iteration
  to a single GetAuraDataBySpellName lookup.

Each site that builds a cooldown ring from expirationTime carries the
talent-extension guard — when expirationTime exceeds the dbc base
duration (e.g. Shadow Affinity → SW:P), clamp start to now and use the
remaining time as the effective duration so CooldownFrame_SetTimer
doesn't get a future start it treats as "not yet begun".
2026-06-24 22:41:13 -05:00
Brues fd21b24a74 utilize C_UnitAuras for nameplate auras 2026-06-24 19:14:36 -05:00