If the global GetCoinTextureString exists, use it to format money (wrapped in white color codes) for CreateGoldString. Keeps the existing numeric fallback formatting for environments without GetCoinTextureString.
Initialize pfUI.env.L["race"] by iterating C_CreatureInfo.GetRaceInfo and C_CreatureInfo.GetFactionInfo. Builds a table keyed by clientFileString containing raceName, raceID and faction (groupTag).
When "Use Raid Frames To Display Group Members" is on and in a party,
raid pet frames tried to show raidpet1..40 (nonexistent in a party)
instead of the player pet and partypet1..4.
Remap raidpet grid slots to pet / partypet<N> in UpdateVisibility,
keyed on a stable pfRaidPet<N> slot, mirroring the raid player frames.
LayoutPets now mirror-places the party pet slots, and the raid updater
runs LayoutPets when in a party (not just a raid).
Also exclude raid pet frames (label "partypet", cache_raidpet set) from
the hide_in_raid group-hiding clause so they are not hidden.
Expose the built-in IsPlayerGuid helper through pfUI.api so callers can check whether a GUID or unit token matches the local player. This keeps the pfUI API surface consistent with the underlying WoW API while making the check available through the addon namespace.
Accept "macro:<name>" in a click-cast action to run a saved macro by
name via the secure `macro` attribute (leading space after the colon is
trimmed). ClassicAPI dispatches it through RunMacro, so a conditional
addon still evaluates the macro body.
Two costs on the hot path. It concatenated '<u>target' and '<u>targettarget'
per call per unit, though pfValidUnits never changes after load -- now built
once into a static triple list. And only positive results were cached, so the
common case (nothing has aggro) rescanned the entire unit table on every call;
negative results now cache for 0.3s, short enough that aggro still appears
promptly.
Add "Show Spell IDs" and "Show Unit IDs" options alongside the existing
item-ID line. Spell IDs come from GameTooltip:GetSpell() on spell
tooltips and from the aura's spellId on unit-aura tooltips; unit IDs
come from C_CreatureInfo.GetCreatureID on the mouseover unit's GUID
(skipped for players and guarded when the GUID carries no entry).
Register GreedMeter's primary window on the meter dock's damage slot,
mirroring the ShaguDPS integration. GreedMeter builds its windows lazily
and supports several, so frames are resolved at call time.
A second GreedMeter window docks into the panel's other half: pfUI's
dock decides fill-vs-split by whether the threat slot is filled, so hook
GreedMeter's window add/remove to reconcile that slot and re-run the
resize. Only the first two windows fit the panel; any beyond that float.
strtrim collapsed a name like " " to "" and the empty guard then
rejected the save, but a whitespace-only name is valid in vanilla and a
common way to make a name-less macro. Save the raw text and only reject
a truly empty box.
GetPerfectPixel switched to UIParent:GetEffectiveScale(), which reports
the true unclamped scale (e.g. 1.42 on the larger pixelperfect presets,
since the module calls UIParent:SetScale past the uiScale cvar's 1.0
cap). At that scale 768/screenheight/scale drops below half a pixel and
the border edgeSize rounds to zero, so button borders disappeared.
The uiScale cvar's <=1.0 cap was load-bearing, not a bug: it keeps the
1px borders from vanishing. Restore the cvar-based measurement and move
pixelperfect back to its original load position.
The 1.12 client defines Blizzard's own misspelling, UKNOWNBEING
("Unknown Being"); UNKNOWNBEING does not exist and resolves to nil, so
both guards never matched. Point them at the real global.
* chat: detect whispers before the timestamp is prepended
Whisper detection tests for the whisper colour code at position 1, but the
timestamp is prepended first, so with timestamps enabled the code is no longer
at position 1 and every whisper failed the test -- losing both the recolour and
the correct chat-history entry.
(cherry picked from commit 4b69d597631c422d9360a94ff129323288a28cc2)
* macrotweak + libpredict: inverted install guard, misspelled globals
macrotweak: _AddHistoryLine is the backup slot this block creates, so it is nil
until line 18 runs. Guarding on its truthiness meant the chat-history filter
never installed at all -- macro calls kept landing in chat input history.
libpredict: UKNOWNBEING / UNKOWNBEING are misspelled, so both resolve to nil and
the guards never matched the real UNKNOWNBEING. Neutral if the global is absent
on this client, correct if present.
Not needed: his libpredict already fixed the always-true
'event == "A" or "B"' condition (libs/libpredict.lua:828).
(cherry picked from commit 4068110dc4134823f1c09f60ca5f8959302cd14f)
* mapreveal: look the explore cache up by the key it is actually stored under
explorecaches is keyed by the plain area name (line ~160), but the hover frame
carried only a decorated 'mapFileName (area)' display string, so every lookup
missed and the hover highlight never fired. Store the plain name alongside it
and key off that; the tooltip keeps the decorated string.
(cherry picked from commit 5e6fe969a03a88c68e28614548b9d44db562ab9a)
* socialmod: don't clobber the friend-online match with the offline one
The offline match was assigned unconditionally over the online match, so a
friend coming online never had lastseen recorded.
(cherry picked from commit ad927806c46042163f84bde4db1082726bedf28b)
* swingtimer: off-hand weapons are inventory type 22, not 21
The off-hand slot accepts one-hand (13) and off-hand (22) weapons. Type 21 is
INVTYPE_WEAPONMAINHAND and can never be equipped there, so the off-hand swing
timer never recognised a real off-hand weapon.
The other two swingtimer fixes are not needed here: his hunter check already
uses UnitClassBase (locale-independent, better than our UnitClass second
return), and his generic on-next-swing detection covers Raptor Strike without
our hardcoded spell-id list.
(cherry picked from commit 9ab7f5f7224d12361ca6cd5a49181cbb4bebd387)
* superwow: compare the version numerically, not by exact string
SUPERWOW_VERSION == "1.5" silently disables the GUID-to-name combat text hook
on any release past 1.5. Currently 1.5 here, so this is forward-compat only.
Not needed: the clickthrough slash commands already go through
RegisterSlashCommand, which writes _G properly.
(cherry picked from commit 82a37752a5782479849ce7e1304c24733c2ff97a)
* api: measure a real pixel against UIParent, not the uiScale cvar
The uiScale cvar caps at 1.0 while both the pixelperfect module and the
firstrun slider push UIParent past it via SetScale, and it is ignored entirely
while useUiScale is off, so borders came out the wrong thickness on the
Huge/Large presets. Ask the frame for its effective scale instead, and guard an
unparseable gxResolution.
Also cache GetItemLinkByName and count its failures: the scan walked every id
on each call with no memory, so an unresolvable name hitched on every tooltip
hover. Raise the ceiling from 25818 to 61000 as well -- Octo/Turtle custom
items live well past the vanilla range and never resolved.
(cherry picked from commit dac2d3416aef85a4c4b71c0239d93d309e76e30b)
* init: load pixelperfect first so GetPerfectPixel caches the right scale
pixelperfect sets the UI scale that GetPerfectPixel measures against, and that
value is cached on first use. Loading it 56th baked in the previous scale.
(cherry picked from commit dd89a210330583f890a51a82b29e968f3ad36b34)
* modules: route two global overrides through _G so they leave the sandbox
pfUI.env has __index but no __newindex, so a bare global assignment inside a
RegisterModule closure is written into the sandbox table and never reaches _G.
unitxp's BattlefieldFrame_Show override therefore never fired the BG queue-pop
notification, and RaidFrame.lua never saw GROUP_REPLACE_PARTY.
The slash-command half of this fix is not needed here: RegisterSlashCommand
already writes through _G.
(cherry picked from commit 617c8320aca2465af62cf0ff5be7dceaed29a3b2)
* bags: remove the duplicate search OnHide handler
Two byte-identical frame.search OnHide handlers were installed back to back;
the second overwrote the first, so the first was dead code.
(cherry picked from commit e77650448b92e5d7f18ae58632e4531cb2813ea6)
* buffwatch: invalidate the filter cache when a skill is listed
fcache is built once per config table and never cleared, so ctrl/shift-clicking
a skill onto the whitelist or blacklist had no effect until the next reload.
* roll: actually capture the everyone-passed subject
strfind(LOOT_ROLL_ALL_PASSED, LOOT_ROLL_PASSED) has no captures, so `everyone`
was always nil, never reached the blacklist, and "Everyone has passed on: X"
was recorded as a real player passing.
Builds a sample from LOOT_ROLL_ALL_PASSED and runs the same LOOT_ROLL_PASSED
match the scanner uses, so the subject is captured the way it will actually
appear.
* skins: drop includes for two files that were never committed
init/skins.xml referenced custom_merchant.lua and arena_score.lua, neither of
which is tracked in git. Every install -- release zips included, since the
release workflow packages the repo -- throws two 'Error loading' lines at
login and ships without those two skins.
(cherry picked from commit b338b4a16d0def4ae89fde5c6026e58a796b8c00)
* map: don't re-anchor the world map to the frame GetPoint returned
Ctrl+scroll rescales the map and repositions it to keep the top-left fixed, but
it re-anchored using the relative frame GetPoint handed back. Once anything else
is anchored to WorldMapFrame that throws
WorldMapFrame:SetPoint(): <unnamed> is dependent on this
and the error aborts the rest of the zoom handler, so SetScale never runs.
Anchor to the parent instead. That is what the rest of pfUI's movable system
already assumes -- LoadMovable uses the 3-arg form and SaveMovable stores only
xpos/ypos with no relative frame.
(cherry picked from commit bf055d87fc0a04ac912c13fa874a4331f47bdfa8)
* firstrun: return after bailing on a disabled chat module
All three chat setup steps printed 'Chat module is disabled' and then carried on
into the nil pfUI.chat they had just tested for.
(cherry picked from commit 1af427e3b38bb7c13f645ae7a48b8ade3e7455a9)
* unitxp: stop the free-frame distance poller on logout
The PLAYER_LOGOUT handler stops the indicators to avoid the UnitXP crash on
exit, but in free-frame distance mode the polling runs on a separate scanner
frame that was never exposed, so the handler could not reach it and its
OnUpdate kept calling into UnitXP during teardown.
Exposes the frame as pfUI.uf.target.distanceScanner and stops it alongside the
others.
* roll: bail out on an item the client has not cached
C_Item.GetItemInfo returns nil for an item that is not cached yet, and the next
line assigns pfUI.roll.cache[itemName], which throws "table index is nil" on a
nil key. Easy to hit on a fresh login when someone rolls on an item you have
never seen.
* cooldown: return after hiding on a nil parent
Without the return it falls straight through to parent:GetName() on the nil it
just tested for.
Add a "Show Unit Buffs" option that draws a row of buff icons above the
tooltip for mouseover units. Icons come from a ClassicAPI object pool
and are re-anchored left-to-right on each refresh; a single throttled
ticker counts down the remaining time on the visible icons.
The friend right-click menu only opens for online players, so notes
could not be edited on offline friends. The full menu is useless
offline anyway (Whisper/Invite/Target need an online player), so open
a menu of our own with just the note editor for offline friends.
Squashed from the classicapi-next branch:
- Read spell fields through ClassicAPI instead of nampower (libdebuff, swingtimer)
- Read friend and who-list class tokens from C_FriendList (socialmod, libunitscan)
- Count online friends with C_FriendList.GetNumOnlineFriends
- Add a friend notes module (modules/friendnotes.lua)
- Bump ClassicAPI minimum version to 10906
Replace the hardcoded per-locale ["class"] reverse-lookup tables (localized
class name -> token) with a runtime build in GetEnvironment:
L["class"] = tInvert(FillLocalizedClassList({}))
The client's own class list supplies the localized names for whatever locale is
running, so the tables were pure duplication -- and this also covers locales the
hardcoded tables never listed. Consumers (libunitscan, panel, socialmod) are
unchanged. Drop ["class"] from every locale file.
Rewrite libdebuff's duration lookups on ClassicAPI's C_UnitAuras, whose
expirationTime carries the real, talent-modified remaining time:
- GetDuration reads the live duration of a matching aura on the player
(C_UnitAuras.GetAuraDataBySpellName) instead of L["debuffs"][effect][rank].
- GetBestAuraCast reads the active aura's expirationTime directly rather than
scanning the ownDebuffs/allAuraCasts cast tables -- a unit only ever holds one
instance of a given spell, so that is inherently the effective one.
- GetMaxRank deleted (it only fed the old GetDuration); AddPending's
known-debuff guard removed.
The public tracking tables (pfUI.libdebuff_*) are untouched. Drop the now-unused
L["debuffs"] and L["dyndebuffs"] tables from every locale.
- combopoints: only ever show on the target's plate, so re-issue Show/Hide
only when the target's combo count changes, and skip non-target plates once
cleared (they start hidden) -- instead of hiding all five every tick on every
plate. plate.cpShown tracks the shown count and stays in sync with the frames.
- player class color: merge the identical ENEMY_PLAYER / FRIENDLY_PLAYER
branches into one condition.
Use C_UnitAuras.GetBuffDataByIndex(guid, 1) instead of GetUnitAuras(guid)[1]:
a passive totem's provided buff is its single helpful aura, so the buff-
filtered single-slot fetch is both more precise and cheaper than building the
full aura list to take the first entry.
Nameplate totem/critter detection now uses UnitCreatureTypeID instead of
name-matching, so the L["totems"] and L["critters"] tables have no remaining
readers. Drop them from every locale file.
Route the remaining hardcoded English labels through the T translation
table and register the keys in enUS:
- bags: "Sort Bags" / "Sort Bank" tooltips
- farmmode: "FARM MODE" overlay
- unitxp: "BEHIND" / "NO LOS" target indicators
- unitframes: raid group header "Group" (reuses the existing key)
- bgscore: "Battleground Frames" mover title
Replace the localized L["totems"] / L["critters"] name-substring matching
with ClassicAPI's UnitCreatureTypeID (CreatureType.dbc: 8 = Critter,
11 = Totem), cached per plate and reset on reuse. Locale-independent,
covers custom/Turtle units, rejects mobs merely named "...Totem...", and
cheaper than the per-plate name loop.
Totem icons now come straight from the game instead of a hand-picked table:
passive totems self-cast their provided buff, so their single aura's icon
is read via C_UnitAuras.GetUnitAuras; active totems (Searing/Magma/Fire Nova)
carry no self-aura, so their icon is captured from UNIT_SPELLCAST_SUCCEEDED
(C_Spell.GetSpellTexture) and cached. Detection and icons read through the
plate's ClassicAPI-provided GUID (cachedGuid).
Only show "Total" when the item is split across more than one location;
a single-location item made "Total" duplicate the one breakdown line
(e.g. "Equipped: 1 / Total: 1").
Route the Bags/Bank/Equipped/Total labels through the T translation table
and add the keys to every locale file, using the Blizzard GlobalStrings as
the reference for each localized term.
The mouseover-scripts migration dropped the unit frame OnEnter/OnLeave
handlers that read C.unitframes[unit].showtooltip, so nothing consulted the
setting anymore. The engine mouseover (driven by the frame's unit attribute)
now shows the native tooltip unconditionally, so the option could not gate it
even if a reader remained.
Drop the config default, the "Enable Mouseover Tooltip" GUI checkbox, and the
now-orphaned locale string from all translation files.
Read the player class color from PFUI_CLASS_COLORS instead of
GetClassColor in SkinButton, SetHighlight, and SkinDropDown.
GetClassColor returns the stock pink for Shaman, but pfUI remaps it to
blue, so the button highlights and locked borders now match.
Replace nampower's GetSpellIdCooldown with C_Spell.GetSpellCooldown, and
compute the remaining time from startTime plus duration (both seconds).
Drop the presence guard, since ClassicAPI is a hard dependency. The
module still needs nampower for the AURA_CAST events.
Replace nampower's IsSpellInRange with C_Spell.IsSpellInRange. This drops
the last unguarded nampower call. The ClassicAPI function returns true,
false, or nil, so test == true and == false and leave the bar unchanged
on nil.
libspell.GetSpellInfo now returns the spell ID at position 9 and caches
it with SafePack and unpack.
The action bar tracks self.spellID for macro-cast spells and shows the
tooltip with GameTooltip:SetSpellByID. Gather the slot, book type, and
spell ID from one GetSpellInfo call instead of a separate GetSpellIndex
lookup.
Release tags often include a leading 'v' (e.g. "v9.0.18"). Previously the code split the raw tag directly which could yield a nil major version and fall back to 0.
The per-slot popout arrows previously appeared only while the equipment
manager sidecar was open. Add a "Always Show Equipment Slot Flyouts"
option under Character -> Inventory (character.inventory.equipflyout,
off by default) that instead ties them to the paperdoll, so gear can be
swapped without opening the equipment manager.
Centralize the show/hide in a single UpdatePopouts(): by default the
arrows follow the sidecar, with the option on they follow PaperDollFrame.
Both the sidecar OnShow/OnHide and new PaperDollFrame OnShow/OnHide hooks
route through it, and the flyout hides whenever the arrows go inactive.
The power bar's frame level was pinned to f.power's live level
(f.power:GetFrameLevel() + 1). CreateBackdrop caches the backdrop at
f.power's level when it first builds it, but f.power's level can shift
afterward as strata changes propagate across UpdateConfig re-runs. When
it shifts below the cached backdrop, the fill drops behind the dark
backdrop and the bar renders black -- which reproduced on some clients
but not others depending on how the levels resolved.
Anchor the fill to the backdrop's own level instead
(f.power.backdrop:GetFrameLevel() + 1). The backdrop's level is fixed
once created, so the fill is always exactly one level above it
regardless of f.power's value, and the bar can never fall behind it.
Hook GameTooltip:SetUnitAura to append the caster's name; every pfUI aura
tooltip (player buffs, buffwatch, unitframes) routes through it, so one hook
covers them all. Resolve the name from the live sourceUnit token, falling
back to the sourceGUID name cache (players only) when the token is gone.
Gated behind a new tooltip.aurasource option (off by default) with a matching
"Show Aura Caster" checkbox. The toggle reloads the UI, so the hook is
installed conditionally at load rather than re-checking the flag per tooltip.
* Add pfUI-native macro icon picker
Replace Blizzard's MacroPopupFrame (the name + icon dialog) with a
custom pfUI picker driven by IconDataProviderMixin, so the full spell +
item + loose icon set is available instead of the stock spell-only list.
It has a searchable, filterable icon grid and saves through
C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
string -- so arbitrary icons (including index-less INV_* item icons)
persist. The macro-frame API is identical on the modern (Turtle/Octo)
and stock-vanilla macro UIs, so the picker works on both with no client
detection.
The new macroicons module takes over the New / Change-Icon buttons and
retires the stock popup; the blizzard macro skin now only skins
MacroPopupFrame when that module is disabled.
* Gate the macro icon hook behind a config flag
Run the Blizzard_MacroUI hook only when C.disabled["macroicons"] is
"1".
* Add search to the icon pickers and fix set editing
Equipment manager:
- Move the OK button next to Cancel at the bottom-right.
- Add an icon search box in the freed bottom-left space.
- Shift the popup contents up and shrink the window after the title
was removed.
- Show the current icon when you edit a set. Keep a full icon path as
is instead of adding a second prefix, and match grid icons by name.
- Close the name/icon popup when the manager frame closes. Remove the
duplicate OnHide hook.
Both pickers now use Blizzard global strings for the labels and the
icon filters.
* Replace the row hover poll with OnLeave handlers
The gear button sits inside the set row's rectangle, so MouseIsOver(row)
is true for both. Show the gear on row enter. Hide it from a shared
OnLeave on the row and the gear when the cursor leaves the row. This
removes the per-frame OnUpdate poll.
* Handle missing C_Macro.CreateMacro API
Add guards to detect if C_Macro.CreateMacro is unavailable. The macroicons module now returns early if the API doesn't exist, and turtle-wow.lua uses the alternative Blizzard_MacroUI fallback path in such cases.
* Remove the macro picker title and shrink the window
Delete the title font string and its SetText calls. Move every
top-anchored element up 14px and shrink the window height from 498 to
484. The OK, Cancel, and search controls are bottom-anchored, so they
keep their spacing to the grid.
* bump ClassicAPI min version to 1.9.3
* Extract a shared icon picker widget
The macro icon picker and the equipment manager's name/icon popup carried
near-identical copies of the icon grid, All/Spells/Items filter, name search,
and the IconDataProvider state machine. Pull that into a single
pfUI.api.CreateIconPicker helper; each module keeps only its own frame shell,
buttons, and save flow.
Also fix the grid tooltip: pass white to GameTooltip:SetText (it defaults to
red) and derive the icon name with the case-insensitive basename key so
mixed-case provider paths show a clean name instead of the full path.
Compare each plate's guild name against a cached myGuild value instead
of calling UnitIsInMyGuild for every plate. Refresh the cache on
PLAYER_GUILD_UPDATE and PLAYER_ENTERING_WORLD. This replaces a per-plate
C call with a Lua string compare.
Fixes#33.
Clique's pfUI plugin replaces pfUI.uf.ClickAction and calls it from an
OnClick script. The move to secure attribute clicks removed that
function, so Clique stopped working.
Add the legacy Lua click path back for Clique. When Clique is loaded,
EnableScripts sets an OnClick script that runs ClickAction. When Clique
is not loaded, the frames keep the secure attribute path.
Skip the secure attributes in Clique mode. The type1 target attribute
switches the target before Clique can cast. Right-click opens the unit
menu through ClassicAPI_ToggleUnitMenu.
Hook Blizzard_MacroUI and replace UpdateMacroIconFilenames. The new
function reads each icon from IconDataProviderMixin, strips the path
prefix, and adds the name to MACRO_ICON_FILENAMES. A seen table skips
duplicate names.
Replace the cfgColorToHex function with a memoized table. The table
caches the color code for each config string on first use.
Build the code with C_ColorUtil.GenerateTextColorCode instead of
string.format. Change the spell-school colors and the format strings
to 8-character ARGB hex with the |c prefix.
Replace raw strsplit(",", ...) color parsing across action bars, chat,
nameplates, third-party skins, player, and roll with the cached
GetStringColor / GetStringColorObject helpers, dropping the per-build
string-table allocations they created.