Commit Graph

2352 Commits

Author SHA1 Message Date
Brues fa3f191eea castbar: pixel-snap progress via PixelUtil.SetStatusBarValue
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.
2026-06-22 10:11:33 -05:00
Brues e6664a4eb6 focus: hand /focus and /clearfocus to ClassicAPI
ClassicAPI's SlashCommandsRegistry now owns the canonical /focus and
/clearfocus commands (matching modern Blizzard's no-alias convention
and delegating to the engine's FocusUnit / ClearFocus polyfills).
pfUI no longer needs to register them.

The TargetByName-based name-resolution workflow that the old
SlashCmdList.PFFOCUS provided ("/focus Hogger" → swap-target, capture
focus) doesn't fit the modern /focus contract, which only takes unit
tokens. Move that behavior into a separate /focusname (alias
/pffocusname) so users who relied on typed names still have a path —
just under a different command name.
2026-06-21 01:32:10 -05:00
Brues 740940dece actionbar: defer to SuperCleveRoidMacros for managed macros
In ButtonMacroScan, when SuperCleveRoidMacros manages the slot
(CleveRoids.IsManagedAction), leave spellslot/booktype unset and return
early. This lets the button's icon, cooldown, and tooltip flow through
the addon's hooked GetActionTexture / GetActionCooldown /
GameTooltip:SetAction so they follow the active conditional dynamically,
instead of being frozen to the first statically-scanned spell.
2026-06-21 00:11:29 -05:00
Brues 5772cf2f91 GetUnitData -> GetUnitInfo
avoid clash
2026-06-17 20:19:42 -05:00
Brues 17291e4d9b nameplates: cut per-tick allocations in OnUpdate path
Six allocation sites firing per visible plate per central tick (or per
throttled OnDataChanged tick) were generating heavy GC pressure visible
in pfDebug's analyzer:

- IterDebuffs callback in PlateCacheDebuffs was a fresh closure per call.
  Hoisted to a module-level function with state in _pcdSelf / _pcdNow /
  _pcdId, matching the pattern the icon-grid IterDebuffs already used.

- string.format("%.2f", remaining) in UpdateCastbar fired every frame per
  active castbar. Factored into SetCastbarText helper that compares a
  rounded integer (centi- or deci-seconds) and only formats + SetTexts
  when the displayed value would actually change. lastTextTick reset
  alongside lastEndTime so back-to-back casts don't collide.

- guid.."target" concat in GetCombatStateColor hit Lua's string intern
  pool every call. Cached in guidTargetTokenCache, evicted alongside
  the other per-guid caches in NAME_PLATE_UNIT_REMOVED.

- plate.name:SetText was unconditional; moved inside the existing
  plate.cache.name change gate so it only fires on real unit changes.

- plate.level:SetText (string.format) was unconditional; gated behind a
  new (level, elite) cache so the format only runs on level-up or
  elite-state flips.

- C_NamePlate.GetNamePlates() poll every 0.5s allocated a fresh Lua
  table just to read its length. Replaced with a visiblePlateCount
  counter maintained event-driven from NAME_PLATE_UNIT_ADDED/_REMOVED.
2026-06-17 13:30:31 -05:00
Brues 50d06f2e7a nameplates: cachedGuid + percent-mode guard + name text position
- 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.
2026-06-16 19:53:52 -05:00
Brues 8397c09a8d Bags: Simplify Extract Spell Logic 2026-06-08 11:33:05 -05:00
Brues 8ddcf73da6 extract bagsort logic 2026-06-08 11:16:48 -05:00
Brues f586bd2285 it's back to 113% :( 2026-06-07 02:39:53 -05:00
Brues ca9c305663 octo removed 113% cap 2026-06-04 02:13:02 -05:00
Brues 7956463927 questitem: use C_QuestLog.GetQuestDetails
Replaces the SelectQuestLogEntry-based scrape (1..50 quest indices, save/
restore log selection, per-quest GetQuestLogQuestText + GetNumQuestLeaderBoards
+ GetQuestLogLeaderBoard, string-blob with _obj_req_ markers, substring-by-
name match in AddTooltip) with a single C_QuestLog.GetQuestIDForLogIndex +
C_QuestLog.GetQuestDetails round-trip per quest.

Build an itemID-keyed { index, count } map up front and look up directly
in AddTooltip. Fixes false matches where an item name happens to substring-
match unrelated quest text.
2026-06-04 02:10:03 -05:00
Brues e12409de60 utilize IsModifierKeyDown 2026-05-31 13:18:20 -05:00
Brues 38b0c74363 cleanup 2026-05-31 13:13:48 -05:00
brues-code 013fcc6c90 add equipment manager (#3)
* equipmentmanager

* use BAG_UPDATE_DELAYED

* CharacterFrame.backdrop doesn't on creation so check onShow

* adjust button positioning

* Show Equipment Set tooltip

* fix icon select scrolling issue

* Equip Set on double click

* slot tooltips: use location-aware GameTooltip methods

Branch on EquipmentManager_GetLocationData: SetBagItem for bagged items,
SetInventoryItem for equipped, fall back to SetInventoryItemByID for
missing or unresolved entries. Surfaces durability/charges/enchants in
the tooltip when the item is reachable.

Also cache C_EquipmentSet.GetItemLocations once per Refresh and store
it on each slot button instead of re-fetching inside the missing-item
border branch.

* purg esome comments

* use GameTooltip_Hide from !!!classicapi

* moved flyout to character frame

* ignore item icon shows on item slots now

* defer ignored-slot toggles until Save

Toggling a slot's ignored state in the flyout no longer immediately
calls SaveEquipmentSet. Instead the change is recorded in a per-set
pendingIgnoredToggles table; the overlay reflects the effective
state (persisted XOR pending) so the user sees the visual change
right away, and Save commits the toggles by replaying them into
ClassicAPI's session ignored list before SaveEquipmentSet.

Pending toggles clear on Save (committed) and on set deletion.

* overhaul icon picker

* fixed width

* fixed issue where selected icon got cleared out

* correct flyout button order

* backport more equipment manager icons

* refactor window

* fixed gear menu

* remove set cap

* bump minimum classicapi version

* bump minimum version to 1.3.3

* row menu: close on outside click

GLOBAL_MOUSE_DOWN fires regardless of which frame absorbs the click
and (unlike a veil frame) doesn't consume the original event, so
clicking another row's gear closes the old menu and opens the new
one in a single click. The gear's own OnClick handles the same-row
toggle close, since GLOBAL_MOUSE_DOWN's anchorBtn check excludes it.

Bumps minimum ClassicAPI to 1.3.4 for GLOBAL_MOUSE_DOWN /
IsMouseButtonDown.

* use pfUI.path

* comment cleanup

* bump min classicapi version

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-05-31 12:56:30 -05:00
Brues 671939fe9e add auto sort on open feature - disabled by default 2026-05-30 00:31:47 -05:00
Brues d1e51e5202 durability panel shows your collective durability 2026-05-30 00:10:12 -05:00
Brues 23cc286a0c Fixed issue where item durability tooltip was missing items 2026-05-29 23:15:04 -05:00
Brues 841399a0c7 classicapi migration 2026-05-29 22:56:12 -05:00
Brues 07377ad92e rep remaining label 2026-05-29 21:30:43 -05:00
Brues 6af7da55d6 Reputation Frame: Enhance faction standing bars 2026-05-29 18:49:38 -05:00
Brues ed5ecb1f93 character: durability % per inventory slot
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.
2026-05-29 18:31:58 -05:00
Brues 201a77ccbd IsContainerItemOpenable will now tell us if we can actually open the item 2026-05-29 00:31:53 -05:00
Brues b8da28ebac use IsContainerItemOpenable and bump minimum ClassicAPI version to 1.2.1 2026-05-28 14:59:31 -05:00
Brues f988dff29f no need to show count for unique items 2026-05-28 14:31:09 -05:00
Brues 7c5e4a0e08 add Item Count module 2026-05-28 13:24:50 -05:00
Brues 1fd7895ca7 move away from RAID_CLASS_COLORS dependency 2026-05-28 13:06:00 -05:00
Brues 8e92cf47b3 whitespace 2026-05-28 00:25:05 -05:00
Brues d47287da3a use GetContainerItemCharges from classicapi 2026-05-27 23:50:10 -05:00
Brues 3ef78044fb bags: third-party sort addons rebind existing buttons
pfUI's built-in sorter (modules/bags.lua) always creates the sort
buttons on the right/left bag frames now, so the third-party
RegisterBagSort path's "if not pfUI.bag.right.sort" guard would skip
button-creation entirely — yet still set pfUI.thirdparty.bagsort to
the registering addon's name. Net effect: user installed SortBags or
MrPlow, our internal sort runs anyway. Silent override of user intent.

Strip the dead button-creation paths; rebind the existing buttons'
OnClick + OnEnter to the registered third-party functions instead.
Third-party addons now actually fire when the user clicks. -53 lines.
2026-05-27 23:30:01 -05:00
Brues 9a618e4709 bags: add /pfBagSort + /pfBankSort buttons
Inline the bag sorter from Bagnon/lib/BagSort.lua — two-pointer stack
consolidation, then a category-then-name sort with poor (gray) items
placed back-to-front so they sit at the end of the last bag. Sort
buttons land in the existing button row on both the bag (right) and
bank (left) frames.

Fixes a destructuring mismatch in the port: pfUI's compat/vanilla.lua
shims GetItemInfo to the modern 10-field return signature (inserts nil
for itemLevel between quality and minlevel), so itype/subtype sit at
positions 6/7 — not 5/6 like the Bagnon original assumes against raw
vanilla. Without the extra placeholder every item fell through to the
"non-gear by quality" bucket.
2026-05-27 23:22:32 -05:00
Brues 6cf5ef4468 Update easteregg.lua 2026-05-27 23:11:44 -05:00
Brues b99f650694 Cap rested xp at 113% on turtle wow 2026-05-27 23:11:44 -05:00
Brues ebbafd0478 refresh gametooltip for rested gains 2026-05-27 23:11:44 -05:00
Brues 04f10b0998 Bag Sort 2026-05-27 23:11:23 -05:00
brues-code f0d9dca63f Classic API Focus (#2)
* 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>
2026-05-27 20:35:15 -05:00
brues-code ed06968fa3 C_NamePlate (#1)
* nameplates: drive discovery + GUID lookup via C_NamePlate

ClassicAPI now provides the modern C_NamePlate API and the matching
NAME_PLATE_CREATED / NAME_PLATE_UNIT_ADDED / NAME_PLATE_UNIT_REMOVED
events. Replace the WorldFrame:GetChildren polling + manual GUID
dictionary with event-driven plumbing.

- Drop the per-tick WorldFrame:GetNumChildren / GetChildren scan
- Delete IsNamePlate() border-texture filter (events only deliver
  nameplate frames)
- Delete the guidRegistry table + its OnShow/OnUpdate maintenance;
  every lookup now goes through C_NamePlate.GetNamePlateForGUID
- Move per-GUID cache cleanup (debuffCache / threatMemory /
  combatColorCache / libdebuff_casts) from the OnUpdate visibility
  scan to a NAME_PLATE_UNIT_REMOVED handler — fires exactly once
  per unit going out of range
- NAME_PLATE_CREATED drives nameplates.OnCreate; NAME_PLATE_UNIT_ADDED
  sets cachedGuid and drives nameplates.OnShow (so OnCreate no longer
  calls OnShow directly and no longer HookScripts the parent)
- visiblePlateCount uses table.getn(C_NamePlate.GetNamePlates())

Net -64 lines, with a meaningful reduction in per-tick CPU (no more
WorldFrame child iteration + region/texture filter every 50ms).

* nameplates: switch UNIT_ADDED/REMOVED handlers to nameplateN tokens

ClassicAPI's NAME_PLATE_UNIT_ADDED / _REMOVED events now ship the
"nameplateN" unit token as arg1 (matching modern WoW) instead of the
GUID string.

- UNIT_ADDED: resolve plate via C_NamePlate.GetNamePlateForUnit(arg1);
  derive the cache-key GUID via UnitGUID(arg1)
- UNIT_REMOVED: same lookup pattern; compute UnitGUID(arg1) inside the
  handler before the slot recycles (per docs guarantee)

The other six GetNamePlateForGUID call sites (libdebuff aura callbacks,
UNIT_FLAGS_GUID, PLAYER_TARGET_CHANGED, combo-point handler, castbar
frame) stay on the GUID flavor — they receive real GUIDs from other
event paths.

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-05-27 00:40:31 -05:00
Brues aaba09a17c GetContainerItemID directly 2026-05-25 20:22:07 -05:00
Brues 710be52f35 phase 3 removing tbc
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
2026-05-25 18:50:53 -05:00
Brues ba566cdd53 phase 2 removing tbc
Strip dead pfUI.client > 11200 and pfUI.expansion == "tbc" branches now
that vanilla is the only supported client.

- pfUI.lua: hardcode expansion = "vanilla"; collapse force_region matrix
- compat/vanilla.lua, libs/{libcast,libdebuff,libtotem,libunitscan,
  focus}: drop always-false early-returns
- api/unitframes.lua: delete TargetBuffOnUpdate poller + tbc visibility
  and secure-template branches; click-cast keeps only vanilla path
- api/ui-widgets.lua: unconditional EffectiveScale divide
- env/tables.lua: drop unused focus/focustarget pfValidUnits entries
- modules/actionbar.lua: simplify EnablePaging, drop ButtonSwitch/
  petvisibility, unwrap prowl block, single keybind-remap path
- modules/{castbar,chat,cooldown,loot,questitem,totems}: unwrap
  always-true conditionals
- modules/nameplates.lua: cfg-only fake-cooldown gate, unwrap overlap/
  clickthrough + mouselook blocks
- modules/friends.lua + skins/blizzard/friends.lua: maxtab = 4
- skins/blizzard/{help,game_menu,questlog}: drop tbc-only branches
2026-05-25 18:40:17 -05:00
Brues 58609b8c5c phase 1 removing tbc 2026-05-25 18:16:02 -05:00
Brues 199842e220 prevent 'npctarget' invalid unit calls 2026-05-23 15:59:09 -05:00
Brues 1d27f7bed3 class color cleanup 2026-05-23 02:56:00 -05:00
Brues 90fdd8cfc3 typo 2026-05-22 22:50:41 -05:00
Brues 7c752f0264 xpbar: rested gain rate in per-minute instead of per-hour
Per-hour numbers are too coarse to feel actionable while watching a
tent fill — switching to /min so the displayed rate updates in a more
useful range for the actual sampling cadence.
2026-05-22 22:48:04 -05:00
Brues c30dfae7a6 xpbar: track rested-XP gain rate + time to cap
The data frame now subscribes to UPDATE_EXHAUSTION and stores a sliding
5-minute window of (time, exhaustion) samples. The XP tooltip uses the
first/last samples in the window to compute rest gain per hour, and
projects time to reach UnitXPMax * 1.5 at the current rate.

Useful for Turtle WoW's tent mechanic — when you stand under a tent
you accumulate rested XP at an accelerated rate, and now you can see
how long until you cap.

Samples reset on PLAYER_ENTERING_WORLD (zone change can change the
gain rate) and PLAYER_LEVEL_UP (Blizzard rescales exhaustion
proportionally on level-up so prior samples no longer describe the
same gain curve).

The rate/time lines only appear when there are at least 2 samples in
the window and the gain is positive — so they stay hidden when not
actively gaining rested XP.
2026-05-22 22:40:35 -05:00
Brues 00a3e2f691 tracking: PlayerIsDruidInCatForm via GetShapeshiftFormID
The byte-2-of-UNIT_BYTES_1 extraction this function did manually
(math.floor(b/65536) - math.floor(b/16777216)*256 on the GetUnitField
return) is precisely what GetShapeshiftFormID reads under the hood —
descriptor +0x212. Collapses to a one-liner that doesn't need to
mirror the engine's byte layout in Lua.
2026-05-21 17:30:39 -05:00
Brues 11f9cd848b tooltip:GetUnit via HasUnit + UnitTokenFromGUID
GameTooltip:HasUnit() / GameTooltip:GetUnitGUID() ask the tooltip
directly what unit it's displaying (returns the GUID), and
UnitTokenFromGUID resolves that to a unit token. So instead of
iterating mouseover/player/pet/target plus party1..4, partypet1..4,
raid1..40, raidpet1..40 and string-matching each against
GameTooltipTextLeft1:GetText(), this is now a three-line lookup.

Bonus: drops the UnitName / UnitPVPName fallback string match (which
broke on duplicate names across the realm and on tooltips showing PVP
titles that didn't exactly match a token's UnitPVPName output). GUID
is canonical.
2026-05-21 17:27:27 -05:00
Brues 2619b0bd77 autovendor: all paths through C_MerchantFrame
Two-part collapse:

Button-click sell path: was a 0.1s-per-item manual iteration via
UseContainerItem (with processed[] tracking + a merchant-state flag +
OnShow/OnUpdate/OnHide handler trio). Now calls SellJunkAndReport
which fires C_MerchantFrame.SellAllJunkItems and a 0.3s deferred
GetMoney delta reporter. Engine handles per-frame sell pacing.

Button enable/disable check: HasGreyItems() iterated bags 0..4 with
C_Item.GetItemQuality manually. Replaced with
C_MerchantFrame.GetNumJunkItems() > 0 (the engine's canonical answer
to the same question). The button's Update only runs from MERCHANT_SHOW
/ MERCHANT_UPDATE handlers so the "merchant must be open" gate on
GetNumJunkItems is always satisfied.

Side effect: the MERCHANT_SHOW auto-sell path NOW reports income to
chat. Previously only the manual button reported it.

Deleted: HasGreyItems, processed/count/gold state, GetNextGreyItem,
autovendor.merchant flag, OnShow/OnUpdate/OnHide handlers, the
MERCHANT_CLOSED branch.

170 → 104 lines.
2026-05-21 17:24:14 -05:00
Brues 802d47312f pfSellData: drop sell column, use C_Item.GetItemSellPriceByID
Each pfSellData entry was a "sell,buy" string. The sell price duplicates
what C_Item.GetItemSellPriceByID returns from the engine's item DBC, so
it's redundant in our table — only the buy price (curated from real
vendor encounters, not a static item property) needs to live here.

Transformed every [id]="X,Y" entry across env/tables.lua, compat/tbc.lua,
and modules/turtle-wow.lua to [id]=Y via sed. Format is now itemid →
buyPrice (number, copper).

sellvalue.lua: reads sell from C_Item.GetItemSellPriceByID(id), reads
buy from pfSellData[id]. Items that have only a sell price (not in
pfSellData) now display sell-only — previously they got no tooltip
addition because the lookup gated on table membership. Items with
buy-only (sell == 0 in old data) still display buy correctly.

autovendor.lua: replaces the pfSellData[id] gate (which only checked
table membership and unused the parsed sell/buy) with a direct
C_Item.GetItemSellPriceByID > 0 check — the engine's canonical
"is this item sellable to a vendor" answer.
2026-05-21 17:19:29 -05:00
Brues d27db55abf modernize monkey-patched hooks to hooksecurefunc
sellvalue.lua: SetItemRef hook follows the same pattern questitem.lua
got earlier — replaces the local-Hook + _G.SetItemRef monkey-patch with
hooksecurefunc, drops the string.find link-parser, and reads itemID
directly from ItemRefTooltip:GetItem after the original populates the
tooltip. The OnShow path also moves from libtooltip:GetItemLink/GetItemID
to GameTooltip:HasItem/GetItem (libtooltip:GetItemCount stays — it's
populated by libtooltip's own SetBagItem hook and carries the stack
count, which GetItem doesn't return).

unusable.lua: the two post-hooks (pfUI.bag.UpdateSlot and
BankFrameItemButton_UpdateLock) are textbook hooksecurefunc cases —
both ran the original then did extra work, no conditional skip or
return-value tampering. The table form of hooksecurefunc handles
pfUI.bag.UpdateSlot cleanly.
2026-05-21 17:04:00 -05:00