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.
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.
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.
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.
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.
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.
`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.
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.
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.
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.
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.
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.
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).
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.
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".
Routes player and nameplate castbar progress through
PixelUtil.SetStatusBarValue so a 1.4s cast on a narrow bar no longer
shimmers across fractional pixels as it ticks. Adds GetMinMaxValues /
GetValue handlers to pfUI's custom StatusBar so PixelUtil can read the
current range.
- Switch the four GUID reads in OnDataChanged from plate.parent:GetName(1)
(SuperWoW idiom) to plate.cachedGuid (set by NAME_PLATE_UNIT_ADDED via
UnitGUID(token)). Move the initial OnDataChanged call out of
OnConfigChange's CREATE path so it runs after UNIT_ADDED has populated
cachedGuid; re-add it explicitly in the user-config-change loop.
- Reject GetUnitField's health/maxHealth when maxHealth == 100 — the
engine writes (hp_percent, 100) into UnitFields for non-detailed units
(UPDATE_PARTIAL packets carry percent only). Without this guard,
Nampower's raw field read returns the percent and the nameplate displays
"5 / 100" as if it were real HP. Mirrors libhealth's heuristic so we
fall through to its estimator instead.
- New config: nametextpos (LEFT/CENTER/RIGHT, defaults to CENTER).
Decouple the bar's anchor from the name so the name's JustifyH can
shift left/right without dragging the bar with it.
f.power.bar inherits f.power's frame level at creation. Bumping
f.power's level later doesn't propagate to existing children, so
CreateBackdrop's parent.fl - 1 backdrop ends up rendering above
the bar — a black bar appears where the power fill should be.
Set the bar's frame level explicitly to parent.fl + 1 after
CreateBackdrop to restore the bar-over-backdrop order.
Adds a Fizzle-style durability label to each character pane slot.
Renders at the bottom of the slot icon as "N%" colored by the existing
DURABILITY_THRESHOLD_COLORS table (red < orange < yellow < invasion <
green), or hidden when the item has no durability stat (necks, rings,
trinkets) or the slot is empty.
api/api.lua: new pfUI.api.CreateFontString(f, key, layer, size, flags,
font) helper following the CreateBackdrop pattern — idempotent
get-or-create that attaches the FontString to the parent frame as the
named field. Defaults to pfUI.font_default at C.global.font_size with
OUTLINE on the OVERLAY layer.
skins/blizzard/character.lua: scoreText migrated from the inline
"if not frame.scoreText then ..." block to the new helper as a working
example; new durabilityText created the same way at BOTTOM/size 10.
Durability render added to RefreshCharacterSlot — runs on every
PaperDollItemSlotButton_Update so equip/unequip/repair/damage all
refresh automatically.
api/config.lua + modules/gui.lua: new C.character.inventory.durability
toggle (default "1"). GUI lives under a new top-level "Character" tab
→ "Inventory" sub-tab, leaving room for future "Reputation" / "Skills"
sub-tabs at the same level.
* focus: migrate to FocusUnit / "focus" token / PLAYER_FOCUS_CHANGED
ClassicAPI now polyfills modern WoW's focus system: FocusUnit / ClearFocus
+ "focus" / "focustarget" unit tokens accepted by every UnitX function +
PLAYER_FOCUS_CHANGED event. Drop pfUI's GUID-juggling pseudo-frame.
- env/tables.lua: add "focus" and "focustarget" to pfValidUnits — the
focus frames now follow the standard event-driven CreateUnitFrame path
with f.label = "focus"
- modules/focus.lua: rewrite. Slash commands use FocusUnit / ClearFocus
directly; no more manual frame.label/unitname/id manipulation. /focus
<name> still does a target-swap to resolve names → units, then
FocusUnit("target") captures the GUID before the target is restored.
PLAYER_FOCUS_CHANGED triggers immediate frame refresh on assign/clear.
- api/unitframes.lua: delete the pseudo-focus block (per-tick "scan all
units for matching name" loop) and the "focus"/"focustarget" special-
case in the visibility branch — "focus" is now a real token.
- modules/castbar.lua: bind the focus castbar to unitstr = "focus" once;
drop the OnUpdate that synced pfUI.castbar.focus from
pfUI.uf.focus.label/unitname. UnitGUID("focus") resolves at read time.
- Drop C_Minimap.SetFocusByGUID / SetFocusByName / ClearFocus calls —
C_Minimap subscribes to PLAYER_FOCUS_CHANGED on its own.
Net -183 lines. Focus stops being a pfUI-special pseudo-frame and
becomes "just another unit token" — same treatment as target/player.
* add nameplate to valid units
---------
Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
Drop the now-vestigial expansion plumbing.
- Delete modules/thirdparty-tbc.lua + its xml Include
- Strip 10 tbc-tagged CreateConfig calls in modules/gui.lua
- Drop the expansion arg from CreateConfig() signature + the disabled-
entry rendering path that depended on it
- Drop the showdisabled GUI toggle + its default
- Simplify pfUI:RegisterModule / pfUI:RegisterSkin to (name, func) only
- Strip the leading version arg ("vanilla:tbc", etc.) from all 114
Register call sites
- Delete the pfUI.expansion variable
Drops the legacy pfUI.api.GetItemCount wrapper (name-based bag walk +
GetItemInfo per slot) entirely — both call sites switch to direct
C_Item.GetItemCount(itemID) calls.
actionbar reagent counter: drops the SPELL_REAGENTS tooltip-scan +
libtipscan dependency. UpdateSlot now does GetActionInfo(slot) →
spellID → C_Spell.GetSpellReagents → reagent itemID, stored per slot.
Counts update via C_Item.GetItemCount(itemID). reagent_counts is
keyed by itemID instead of localized name string. Multi-reagent
spells now resolve cleanly (only first reagent counted, matching old
single-name behavior). Macro and bag-item actions skip cleanly.
questitem: switches to hooksecurefunc("SetItemRef", ...) + the
modern GameTooltip:HasItem / :GetItem pair instead of monkey-patching
_G.SetItemRef and string.find-parsing the "item:NNN" out of the link.
Drops the libtooltip indirection for the regular tooltip path —
GameTooltip:GetItem returns name + itemID directly. AddTooltip
extended to accept the itemID (both callers already had it and were
discarding it).
Both indicator scan loops (preset HoT indicators + custom name-based
indicators) now iterate the populated aura array from
C_UnitAuras.GetUnitAuras instead of brute-forcing UnitBuff(1..32). The
Rejuvenation/Renew/Regrowth disambiguation that previously required
scanner:SetUnitBuff + Line(1) tooltip scans now reads aura.name natively.
HoT-icon → name+predict mappings lifted into a HOT_INDICATORS table at
file scope so future additions are one-line edits.
The custom-debuff loop still defers to libdebuff for caster correlation
when available; C_UnitAuras provides the texture/name backstop. Also
unifies the non-selfdebuff debuff display branches through one
GetDebuffDataByIndex call.
Local `scanner` declaration removed (no remaining tooltip-scan uses in
this file).
BuffOnClick/DebuffOnClick switch to lazy bid lookup before CancelPlayerBuff
(matching the pattern in buff.lua/buffwatch.lua). DebuffOnEnter unifies its
player and non-player branches through GameTooltip:SetUnitAura — the only
divergent path is the selfdebuff caster-correlation lookup that still
needs libdebuff.
Mirror the buff-side cleanup: drop the legacy DebuffOnUpdate poller from
player debuff frames, swap the player branch to GetDebuffDataByIndex with
the same future-start cap, and switch the dispel-indicator scan to
AuraData. libdebuff still owns non-player timers. Deletes the now-orphaned
BuffOnUpdate/DebuffOnUpdate/maxdurations.
Fix stale debuff icons/tooltips on target frame after target swap
Fix debuffs not showing on nameplates for timer-less spells (e.g. Hurricane)
Fix buffwatch timer visually speeding up when another debuff fades
Remove 50ms aura cache (slotMapCache/auraFC) - caused stale debuff data
Remove "Show Only Own Debuffs" from unitframes and nameplates GUI
Force selfdebuff=0 for unitframes/nameplates on login to reset old SavedVariables