- The central OnUpdate looped the entire plate pool (registry) every tick with
an IsVisible guard. Maintain a visiblePlates set via NAME_PLATE_UNIT_ADDED/
_REMOVED and iterate that instead, so hidden pool slots aren't touched. The
IsVisible guard stays as a safety net.
- Fix the global refresh propagation: it set .eventcache on the base frame,
but OnUpdate reads/clears it on the overlay (nameplate) -- so the propagation
was a no-op and global refreshes (e.g. target-change alpha/strata) fell
through to the 0.5s catch-all timer. Set plate.nameplate.eventcache and scope
to visiblePlates, so those refreshes land on the next tick as intended.
OnConfigChange still iterates the full registry (hidden plates must pick up
config changes before they next show).
Both mouseover checks relied on original.glow:IsShown() (a fragile proxy for
the hovered plate, on a Blizzard texture pfUI hides/restyles) gated by
UnitExists("mouseover"). Cache the engine mouseover unit's GUID once per
central-loop tick (frameState.mouseoverGuid) and have each plate compare its
cachedGuid against it.
More correct for the data path: the "mouseover" unit token is only chosen
for the plate whose GUID actually matches the engine's mouseover unit, so
overlapping plates can't double-match and paint the wrong unit's data. Also
cheaper -- one UnitGUID("mouseover") per tick plus a per-plate table compare,
versus the old per-plate IsShown gated on a per-tick UnitExists.
The unthrottled target castbar frame called C_NamePlate.GetNamePlateForUnit
every frame. Stash the target's GUID on PLAYER_TARGET_CHANGED (seeded on
PLAYER_ENTERING_WORLD for a target held across reload) and resolve the plate
through plateByGuid instead -- a table lookup plus a real idle skip when
there's no target. The plate is looked up per frame (not cached) so a plate
spawning/despawning while the unit stays targeted still resolves correctly.
Previously every visible plate polled C_Spell.UnitCastingInfo each throttled
tick just to detect casts. Now cast state is event-driven:
- SPELL_START_OTHER (nampower) stamps a per-GUID castState cache (spellId,
timing, channel flag from spellType); SPELL_FAILED_OTHER clears it; normal
completion expires at endTime. GetCastInfo just reads the cache, so all
call sites (castbar update, non-target detection, casting name-color) stop
polling.
- plateByGuid routes events to plates in O(1) and bounds the cache to on-screen
casters; NAME_PLATE_UNIT_ADDED seeds an already-casting unit with one poll.
- The vestigial nameplate.castUpdate flag now fires on cast start to bypass the
throttle for an immediate bar.
With the poll gone, per-tick work is a cache read + SetValue, so the dedicated
target castbar frame now runs unthrottled (every frame) for the smoothest
sweep, and the non-target nameplates_castbar default rises 50 -> 100 FPS.
Known limits: other-unit cast pushback isn't reported by nampower, and a
mob-cancelled channel with no fail event lingers until endTime.
The cursor-follow OnUpdate polled GetCursorPosition() 10x/second forever,
even with no tooltip visible. Rework it so the follower frame is created
once, hidden, and only shown while a tooltip is up -- an OnUpdate fires only
while its frame is shown, so the poll now runs solely during tooltip display.
Position the follower immediately on show to avoid a one-frame flash.
Also bump the tooltip_cursor throttle default from custom/10 FPS to the
fastest preset (50 FPS) so cursor tracking is smooth out of the box while
the Throttling tooltip knob stays available for low-end machines.
BigWigs (SpellRequests) redefines the global string:split to return a table,
and another addon clobbers the strsplit global too. pfUI.api.strsplit and the
bare strsplit callers inherited the broken versions depending on load order,
producing 'attempt to compare number with nil' from GetStringColor.
- Make pfUI.api.strsplit fully self-contained (no delegation to global
strsplit / string.split), so no override can reach it.
- Optimize the hot path: single-char delimiters use plain-text find (no
pattern compilation, no per-call char-class string); localize string.find
and string.sub.
- Route the per-frame castbar/nameplate color splits through GetStringColor,
which caches, instead of re-splitting a constant string every update.
C_UnitAuras supplies Turtle-adjusted durations directly to the aura readers,
so the L["debuffs"] database fallback for these four custom debuffs is dead.
libbagsort:Sort now accepts an opts table:
- reverse: place the first-ranked item into the last slot of the last
bag (junk fills from the opposite end)
- reversePrio: flip the category ranking (e.g. hearthstone sorts last)
Wired to two new checkboxes under Bags & Bank, both defaulting off.
commit 70b1e66c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sun Jul 26 18:30:18 2026 -0500
Use SetSize/SetShown and simplify minimap/map
commit 7b6bfe9975
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 11:27:14 2026 -0500
Include raidpet frames in /pftest test mode
The unit-frame test toggle (showall) only previews frames that exist and
are positioned. Raidpet frames are created when raidpet.visible == "1",
but LayoutPets' collapse mode only positions pets whose raidpet<N> unit
actually exists, so solo/in test mode they stayed hidden.
Add a showall branch to LayoutPets that mirror-lays every pet cell and
shows it (bypassing collapse and the roster gate), still guarded by the
existing showpets check so nothing happens when raidpet is disabled. Call
LayoutPets from the /pftest handler so the grid updates immediately on
toggle-on and restores to the normal layout on toggle-off.
commit 4c65c38647
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Remove unused RangeCache local in UnitInRange
commit 6c96bbbe6c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Use Clamp() for the two-sided clamps in ui-widgets
Replace the hand-rolled math.min/math.max and if/elseif clamp idioms in the
status bar and scroll frame with Clamp(). Equivalent for every value seen
(min <= max always holds), just clearer.
commit 830e0a0be1
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:34:33 2026 -0500
Drop vanilla GetItemInfo shim for C_Item.GetItemInfo
The compat/vanilla.lua GetItemInfo override reshaped vanilla's tuple into
retail order by inserting nil at slot 4 (itemLevel), but truncated at 8
fields, dropping itemEquipLoc/itemTexture. ClassicAPI's global GetItemInfo
keeps the vanilla shape (its hook only warms the item cache), so the shim
stayed necessary but incomplete.
Remove it and point the three callers at ClassicAPI's C_Item.GetItemInfo,
which returns the full 18-field retail tuple. Positions 1-8 are identical
to the old shim output, so bags/roll are unchanged; character.lua's
itemSlot (equipLoc, field 9) was always nil under the truncating shim and
now resolves correctly for ShaguScore.
commit c0da63657d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 10:09:34 2026 -0500
Various cleanup
Removes version compatibility checks for TBC/Vanilla, consolidating code to target a single WoW version. Refactors repetitive SetWidth/SetHeight calls to SetSize for cleaner code. Removes duplicate function definitions from unitxp module and simplifies conditional show/hide operations using SetShown.
commit 11a6302c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 09:53:37 2026 -0500
Delegate pfUI.api.strsplit to ClassicAPI's strsplit
Replace the Lua pattern-based implementation with a thin wrapper around
ClassicAPI's C-level strsplit. Keeps the pfUI.api.strsplit entry point for
backwards compatibility with addons that call it, while dropping the
redundant reimplementation.
Behavioral note: the old version used ([^delim]+) which silently collapsed
empty fields; delegating to real strsplit now preserves them
("a,,b" -> "a", "", "b"). All in-repo callers split numeric color tuples,
version strings, or build name-keyed lookup tables, none of which are
affected by empty-field preservation.
commit 67c126eac8
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 01:01:02 2026 -0500
Bump ClassicAPI minimum version to 1.8.0
commit a6cf157518
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:49:50 2026 -0500
read spell rank via C_Spell.GetSpellSubtext
The rank string comes from the spell subtext ("Rank N"), which ClassicAPI's
C_Spell.GetSpellSubtext returns directly -- so drop nampower's
GetSpellRecField(spellId, "rank") here. The existing "Rank " gsub parse is
unchanged.
commit 4b1ba99b4f
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:33:20 2026 -0500
Move player info overlay onto ClassicAPI (drop Nampower)
The haste/spell-power overlay read both values through Nampower and disabled
itself entirely without it. Move both to ClassicAPI so it works on any
ClassicAPI client:
- Spell power: GetSpellPower("net") (nampower) -> GetSpellBonusDamage(i)
(ClassicAPI, same per-school field). Merged the build+scan into one loop;
the default school seeds the tiebreak so equal schools still favor it.
- Haste: GetUnitField("player", "modCastSpeed") -> UnitSpellHaste("player"),
which returns the haste percentage directly (the (1/modCastSpeed-1)*100
conversion is now baked in, off the same UNIT_MOD_CAST_SPEED field, signed).
- Dropped the "if not GetUnitField then return" gate -- UpdateInfoText no
longer touches Nampower, so the overlay runs everywhere.
commit 7df4aa6d50
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sat Jul 25 00:32:44 2026 -0500
Hook the real global _G.UnitHealth for feign death
Inside a RegisterModule body, `function UnitHealth(...)` defines UnitHealth on
the pfUI environment, not the real global -- so the feign-death real-HP fix
only reached callers that resolve UnitHealth through pfUI's env, and missed
_G consumers (Blizzard frames, other addons). Hook _G.UnitHealth explicitly
(and capture oldUnitHealth from _G) so the un-gate applies everywhere.
commit b0bf2fd869
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 21:10:58 2026 -0500
Refactor nampower module
Simplify reactive spell storage by using spell IDs instead of texture/name tables. Update to use modern C_Spell APIs (GetSpellTexture, GetSpellName) instead of manual texture paths. Consolidate SetWidth/SetHeight calls to SetSize and use print() instead of DEFAULT_CHAT_FRAME:AddMessage().
commit 0b06961333
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:54:23 2026 -0500
Use GetNamePlateForUnit for target lookups; drop dead ScanGuid block
- Replace the three GetNamePlateForGUID(UnitGUID("target")) round-trips with
GetNamePlateForUnit("target"), which resolves the token to a GUID internally
-- no UnitGUID string detour, and it returns nil for no-target/no-plate so
the UnitExists guards collapse. GetNamePlateForGUID is left for raw-GUID cases.
- Remove the dead libunitscan.ScanGuid nameplate block (ScanGuid was deleted
from libunitscan long ago, so the guard was never true) -- it carried the
last GetUnitField("npcFlags") read.
commit 3e6210086e
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:54:23 2026 -0500
Drop Nampower stats system and polling from unit frames
GetUnitStats now reads health/power straight from UnitHealth/UnitPower
(the descriptor fields the server broadcasts) -- for a real unit token
GetUnitField read nothing different, so the whole Nampower-vs-fallback
apparatus was measuring a distinction that no longer exists. Removed:
- The stats system: pfUI.uf.stats, pfUIStatsFrame + UpdateStatsDisplay, the
lastUnitStats change-cache, the /pfuistats slash command, and every counter
increment (event/heartbeat/earlyReturn/throttledSkip/nampower/fallback).
- All GetUnitField health reads (GetUnitStats, heal-prediction, health-gradient
color) -- collapsed to the UnitHealth/UnitHealthMax they already fell back to.
- The heartbeat-polling backstop and its fallbackThrottle/lastEventUpdate deps.
Frames now refresh on events only; eventless frames still use their own tick,
and range/aggro still run in the eventless-actions block.
commit 63001b7b0c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 20:09:42 2026 -0500
Move nameplates onto ClassicAPI stable nameplate tokens
ClassicAPI now assigns retail-exact, per-plate-stable "nameplateN" tokens and
fires vanilla UNIT_* events for them, so the nameplate module can key off the
token instead of Nampower/SuperWoW GUID primitives:
- Events: UNIT_AURA_GUID/UNIT_FLAGS_GUID -> UNIT_AURA/UNIT_FLAGS, matched by
the "nameplateN" token (guarded on the token prefix, since these also fire
for target/party/raid). Registered unconditionally -- no GetUnitField gate.
- Health: GetUnitField(guid, "health"/"maxHealth") -> UnitHealth/UnitHealthMax
on the plate's cached token. Same UNIT_FIELD_HEALTH the server broadcasts
(real HP on Turtle; the ~= 100 scaled-vs-real guard is unchanged).
- Combat/target in GetCombatStateColor: GetUnitField("flags") + HasFlag ->
UnitAffectingCombat(token); GetUnitField("target") + SuperWoW "<guid>target"
-> the "nameplateNtarget" chain. Dropped the now-dead HasFlag,
UNIT_FLAG_IN_COMBAT, and guidTargetTokenCache.
- GetCastInfo now takes the token directly; every caller already holds it, so
the UnitTokenFromGUID(guid) round-trip is gone (unused module-wide now).
NAME_PLATE_UNIT_ADDED caches the token as plate.nameplate.unit alongside the
GUID; REMOVED clears both. GUID is retained only as the stable cache key
(debuffCache/threatMemory/combatColorCache). Only GetUnitField("npcFlags")
remains -- it has no token/vanilla equivalent.
commit ce1c49fcbb
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri Jul 24 18:20:06 2026 -0500
Make focus/focustarget event-driven via ClassicAPI unit events
ClassicAPI now fires UNIT_* (health/mana/aura/...) with arg1 == "focus" and
arg1 == "focustarget", observed per-unit like target/party/raid. Both frames
already registered those events (focus/focustarget are in pfValidUnits) and
their OnEvent already matches arg1 == label, so the 0.2s polling ticks were
pure workarounds for the missing events. Drop both ticks; the frames now
refresh on-event like target, with range/glow still on the shared 0.5s state
pass and PLAYER_FOCUS_CHANGED still driving assign/clear.
Replace SetWidth/SetHeight with SetSize for pfUI.spellqueue and reactive icons. Use UnitClassBase("player") to obtain the player's class token. Simplify reactive icon visibility by using SetShown and aggregating a single anyVisible flag, then calling SetShown on the parent frame. Minor readability and API modernization changes.
- Replace all UnitMana/UnitManaMax calls with UnitPower/UnitPowerMax. The
no-arg form returns the unit's primary power from the same field vanilla's
UnitMana read, so these are behavior-preserving. Drops two dead
UnitMana/UnitManaMax local caches in nameplates.lua.
- Replace magic power-type numbers (0/1/2/3) with Enum.PowerType.Mana/Rage/
Focus/Energy in the GetUnitStats branches, the power-bar color block, the
druid mana bar, GetStatusValue's powerdyn, and energytick's mode checks.
The druid secondary mana bar (shown while shapeshifted into a form that
uses energy/rage) lived in nampower.lua and read base mana through
nampower's GetUnitField. Extract it into the unit frame proper and drive
it with ClassicAPI instead:
- Create pfDruidMana_<unit> as f.druidmana in CreateUnitFrame (player and
target), lay it out in UpdateConfig from the existing C.unitframes.druidmana*
keys, and update it in a new pfUI.uf:UpdateDruidMana driven by the frame's
own base-refresh pass (UNIT_MANA / UNIT_DISPLAYPOWER). No separate event
frames, no nampower dependency.
- Read mana via UnitPower(unit, 0) / UnitPowerMax(unit, 0), the ClassicAPI
slot getters that return the mana pool regardless of the active power, so
it works while in Cat/Bear form.
- Add a "Show Druid Mana Bar Text" toggle (druidmanatext) so the current/max
readout can be hidden while keeping the bar; config default, GUI checkbox,
and locale stubs.
- Remove the now-dead block from nampower.lua.
Replace separate SetWidth/SetHeight calls with a single SetSize using precomputed 'thickness' and 'length' values. This refactors the totems layout math into clearer variables (thickness = iconsize + spacing*2, length = thickness * count) for horizontal and vertical directions, improving readability and maintainability without changing behavior.
- selfinraid now gates on `not IsInGroup()`, so "show self in raid frames"
applies only when truly solo (both party and raid suppress it), matching
the option's actual behavior.
- Hide the redundant group frames when a party is promoted to the raid grid
(raidforgroup + hide_in_raid), not just in an actual raid. A shared
hide_group local drives both the party-member and self branches; the
party-member branch is scoped to cache_raid == 0 so the raidforgroup-mapped
raid frames (which themselves carry label "party") aren't hidden too.
- Rename "Always Show Self In Raid Frames" to "Show Self In Raid Frames When
Solo" in gui.lua and all locale files; the four previously-translated
strings are reset to nil stubs since the meaning changed.
Replace manual color tables with CreateColor() function calls for better code consistency and API usage. Update tooltip color handling to use WHITE_FONT_COLOR:GetRGB() instead of hardcoded hex color codes. Rename 'slots' variable to 'slotColors' for clarity.
commit f19d7402810637fc32460864104cb792ac5af863
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Wed Jul 22 01:05:52 2026 -0500
remove comment
commit 60f4968f06d19d42bdc6347f98ff4d5a34785e97
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Wed Jul 22 01:05:06 2026 -0500
Bump ClassicAPI minimum version to 10705
Update the minimum required ClassicAPI version from 10704 (1.7.4) to 10705 (1.7.5).
commit 088dba4c23f4aa7ce98b9ce9d75bc5892cd40520
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Wed Jul 22 01:04:04 2026 -0500
Add raid-pet frames: an independent, roster-driven pet grid
New "raidpet" unitframe type that shows raid members' pets (raidpet1..40)
in their own movable block (pfRaidPetCluster), off by default. It's a
flat pool of frames laid out by pfUI.uf.raid:LayoutPets straight from the
raid roster -- cell N shows raidpet<N> -- so it's fully decoupled from how
the raid grid arranges its own slots.
Layout is independent of the raid grid: raidpet carries its own
width/height plus a Layout section (raidlayout / raidpadding / raidfill),
and its own Collapse Empty Slots toggle that packs only the pets that
exist into the leading cells. The raid grid gets the same collapse option
(sequential slot assignment in AddUnitToGroup instead of by subgroup).
Collapsed pets re-pack on roster changes and on UNIT_PET so summons and
dismisses track live without polling.
Also:
- unitframes: a "raidpet" branch in UpdateVisibility (hide when the pet is
out of range or its raid<N> owner is gone), and fix cache_raid so the
"pfRaid" prefix check doesn't misread pfRaidPet<n> frames (char 7 is
non-numeric -> nil compare crash).
- New "Owner Name" text option: on any pet frame (raidpet/partypet/pet) it
shows the owner's class-colored name so you can tell whose pet it is.
- unlock: a pfRaidPet drag cluster, a numeric-suffix guard so pfRaid no
longer matches (and crashes on) pet frames, and RaidPet config mappings.
- config/gui/translations for all of the above.
commit 52f02c8963fe7ec90f36100ebe069c7807529301
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Mon Jul 20 23:09:06 2026 -0500
Enumerate chat bubbles via ClassicAPI instead of scanning WorldFrame
ClassicAPI's C_ChatBubbles.GetAllChatBubbles() walks the engine's own
bubble list and returns the exact set of live bubble frames, with real
GetRegions(), so the decoration idiom works unchanged. Replace the
WorldFrame:GetChildren() sweep and drop the IsBubble heuristic (unnamed
frame whose first region is the ChatBubble-Background texture) -- the API
only ever hands back bubbles, so that guess is both redundant and more
fragile than the engine list. Cheaper too: it iterates only live bubbles
rather than every world child on each chat event.
Add a Left Click / Right Click hint to the totem icon tooltip via
AddDoubleLine, matching the panel.lua convention. Reuses the existing
"Left Click" / "Right Click" strings and adds "Recast Totem" /
"Target Totem" as new translation keys (stubbed across all locales; they
fall back to English until translated).
Also drop the now-orphaned "Range Check Interval" string from every
locale manifest -- the setting was removed when librange stopped
scanning, but its translation stubs were left behind.
ClassicAPI now ships GetTotemInfo/GetTotemTimeLeft/GetTotemDuration/
TargetTotem plus a native PLAYER_TOTEM_UPDATE event, backed by a
data-driven tracker (slot from the Spell.dbc summon effect, duration from
SpellDuration.dbc, and object-manager death detection). That's exactly
what libtotem hand-rolled -- and better -- so delete the library outright:
its spellid/icon tables, the CastSpellByName/CastSpell/UseAction hooks,
the SPELL_GO commit path, and the active-totem bookkeeping.
modules/totems.lua becomes a thin consumer of the native API:
- Driven by PLAYER_TOTEM_UPDATE; drop the shaman tick-poller that only
existed because vanilla had no totem event.
- Fix the GetTotemInfo call sites: the native 1st return is tool presence,
not "summoned", so key active state on name/start instead.
- Right-click a totem icon to TargetTotem it; tooltip shows remaining time
via GetTotemTimeLeft.
modules/turtle-wow.lua: drop the Totemic Recall handler that poked
libtotem:Clean() -- the native tracker detects the totems despawning and
clears the slots itself. Also clean up the now-orphaned translation string
and a stale libtotem mention in libdebuff's comment.
Replace custom wipe implementation with a call to table.wipe in api/api.lua and update its doc comment to explain behavior (resets Lua 5.0 length via luaL_setn, advises using table.insert). Replace manual table.getn(t)+1 array appends with table.insert in modules/loot.lua (two sites). Makes table operations safer and more idiomatic, avoiding manual metatable handling and getn-based append idioms.
librange was a per-frame position scanner: it swept party/raid unit
tokens, cached each one's distance via UnitPosition, and answered range
queries from that cache. All of it existed only because 1.12 had no cheap
way to check an arbitrary unit's distance. ClassicAPI's UnitInRange does
exactly that C-side (fixed 40y healing range, position miss reported via
the second return), so the whole library collapses to a direct call.
Wins from dropping the cache:
- No staleness. The scanner's zone-death and roster-reindex bugs simply
can't exist without a cache to go stale, so this supersedes the
keep-alive fixes from 756e8840.
- All classes get target-frame range fading. The old target path faked a
40y check via IsActionInRange on a healing spell found on the action
bar, so classes without such a spell (GetRangeSlot returned nil) never
had a working target range check.
The rangecheck == "0" master switch used to be enforced by hiding the
scanner; with no scanner, move that gate into pfUI.api.UnitInRange so
disabling the check still means nothing fades. Threshold is now 40y (the
ClassicAPI constant) rather than the old 45y. Drop the now-dead
rangechecki (Range Check Interval) setting, its GUI row, and migration.
Mirror the druid-stealth page switch for priests: swap to the auto page
(8) while in Shadowform, back to the default page when it drops. Driven
by UPDATE_SHAPESHIFT_FORM (form ID 28), which fully covers shadowform on
its own -- no prowl-style stealth substate to chase.
Fold the new state into the existing prowl machinery instead of
duplicating it: prowl/shadowform are mutually exclusive by class, so one
shared `formpaging` flag and one page constant serve both, and the
OnUpdate switch collapses to a single class-gated block.
The item->quest map only changes when a quest enters or leaves the log,
not on progress, yet it was fully rebuilt on every QUEST_LOG_UPDATE burst
(a whole log walk + GetQuestDetails per quest). Maintain it incrementally
from ClassicAPI's QUEST_ACCEPTED / QUEST_REMOVED, keyed on the stable
questID with title/level cached at add time (the old log index shifted on
any add/remove). QUEST_LOG_UPDATE now only drives a one-time login seed
until the detail cache warms, since QUEST_ACCEPTED is suppressed for the
bulk sync; afterward it's a no-op and questing does no rebuilds.
UpdateSlot previously created the glow texture and OnEnter acknowledge
hook on the first pass over every slot, then toggled visibility. Move
both inside the "is new" branch so a slot only allocates when it actually
holds a new item -- most bag/bank slots never do. Behavior is unchanged:
the glow is still created once per slot and reused, and the hook is
installed exactly when it's first needed.
New module built on ClassicAPI's C_NewItems + BAG_NEW_ITEMS_UPDATED.
Glows bag slots (bags 0-4) holding items acquired since login, keyed on
item GUID so the flag survives rearranging. Hovering an item
acknowledges it (RemoveNewItem); closing the bags clears the rest via
ClearAll. Glow is UI-ActionButton-Border, sized off the slot width so it
tracks the icon_size config.
To stay decoupled from the bag frames, the bag module now broadcasts a
reusable "bag:closed" event through pfUI.events from its OnHide (carrying
the container so subscribers can tell backpack from bank), guarded so the
initial setup Hide() doesn't fire a phantom close.
Config: appearance.bags.newitem + newitem_color, with GUI toggles.
Replace the previous RunNextFrame conflict check with per-addon EventUtil.ContinueOnAddOnLoaded calls in modules/macrotweak.lua so macrotweak is disabled as soon as known conflicting addons load. Simplify variable naming and ensure the disabled flag is set correctly. In skins/blizzard/macro.lua replace separate SetHeight/SetWidth calls with a single SetSize(150,22) for MacroEditButton to tidy UI sizing.
EnterTradeskillMerge sized the merged bar as startMs + single*count, a
zero-latency assumption. Each craft boundary actually costs a server
round-trip, so the real chain runs ~(N-1)*lag longer than the bar
assumed. At the clear the overall fill was clamped to full and looked
done, but the fast per-craft spark tracks real time and came up short by
the accumulated lag -- freezing partway on the final craft.
StartTradeskillCraft now re-pins endTime to each craft's real start
(remaining crafts each still take `single`), so on the last craft
remaining==1 and endTime lands on its true completion. The spark and the
overall progress now reach the right edge together.
The RegisterSlashCommand helper in api/api.lua was effectively unused
(only macrotweak called it); every other command hand-rolled the
SLASH_*/SlashCmdList pair. Convert the existing manual registrations to
the helper with force=true, preserving the current always-bind behavior
while centralizing the pattern behind one code path (and its _G. and
conflict-check handling).
Left as-is: pfUI.lua's /rl, /pfui, /gm (registered before api.lua
defines the helper) and the vendored libs' debug commands.
The libtipscan approach scanned each bag/bank item's tooltip for red
text, then had to carve out broken (0-durability) items since those also
color red. C_PlayerInfo.CanUseItem checks item requirements directly
(proficiency, level, class/race, skill/spell/rep) and ignores item
state, so broken-but-equippable gear is never flagged and the durability
exclusion drops out entirely. Bank slots resolve through
C_Container.GetContainerItemID(-1, slot) instead of the inventory-slot
workaround the scanner needed.
Handful of addons will polyfill their own GetSpellInfo that only accept (bookSlot, bookType) so it's only safe to use C_Spell.GetSpellInfo with just a spell id
A pfUI-native group-loot roll history window built on ClassicAPI's
C_LootHistory backport, adapted from the anniversary Blizzard reference.
- Movable/scrollable window (ESC-closable, Clear button) listing rolled
items; each row expands to per-player rolls.
- Item icon/name/quality rendered via the !!!ClassicAPI Item mixin
(Item:CreateFromItemLink + ContinueOnItemLoad), so uncached items load
asynchronously and repaint their row.
- Winner shown on the collapsed row (name/roll/roll-type) and marked in the
expanded list with a checkmark left of the name (matches the reference).
- Expansion state keys on the stable rollID; events (FULL_UPDATE /
ROLL_CHANGED / ROLL_COMPLETE) drive a rebuild while shown, and re-attach
the scroll child so a growing list scrolls without a /reload.
- Toggle via /loothistory or /lh; optional auto-show on new rolls behind
loothistory.autoshow (default off).
Add display of required and optional addon dependencies to the addons tooltip. Introduce AddDependencyLines helper in modules/addons.lua which lists dependencies with color coding: green for loaded, yellow for present but unloaded, and red for missing (uses new T["Missing"]). Store dependency arrays on addon frames (adeps / aoptdeps) using GetAddOnDependencies and C_AddOns.GetAddOnOptionalDependencies. Add translation keys 'Dependencies', 'Optional Dependencies', and 'Missing' to env/translations_enUS.lua.
When the MerchantFrame is not shown, display the item's total vendor sell value on the tooltip. Adds a guard to call SetTooltipMoney(frame, sell * count) if sell > 0 so stacked items show their combined sell price.
Replace the single GameTooltip hook with a comprehensive hooking system that displays vendor prices across 17+ tooltip methods, including loot, quests, bags, mail, auctions, trades, merchants, and crafting. This ensures players see vendor prices consistently regardless of where they view items.
Pressing Esc (or re-pressing) to cancel a queued Heroic Strike / Cleave /
Maul left the swing bar stuck in its queued color. The color is set on the
on-swing press and only cleared on nampower's ON_SWING_QUEUE_POPPED, which
fires solely when a queued-behind on-swing resolves; nampower's cancel path
touches no on-swing state and emits no event, so the flag never cleared.
Reconcile the event flag against the client's IsCurrentAction, which it does
clear on cancel: ReconcileQueued drops the flag once the client has confirmed
the ability as current and then stops showing it (the true->false transition).
It only acts once current has been seen, so a nampower-initiated cast the
client never flags as current keeps its color until its own pop/resolve --
preserving the reason the event-driven path exists.
RebuildQueueSlotCache now caches Maul slots and runs for druids, and no longer
bails in event mode so the caches stay fresh for reconciliation.
commit d63057474083aa67fda2aa28ee6301334ffc4fdf
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Mon Jul 6 00:59:17 2026 -0500
Use C_Map.GetMapOverlays instead of hardcoded pfMapOverlayData
ClassicAPI's C_Map.GetMapOverlays reads WorldMapOverlay.dbc directly and
returns the full overlay list for a zone (explored + unexplored) — the
data vanilla's GetMapOverlayInfo withholds. mapreveal now iterates it
straight (named fields: textureName/texturePath/width/height/offsets),
dropping unpack_hash and the pfMapOverlayData tables entirely.
Also fixes the explored-check: it compared the full texture path against
GetMapOverlayInfo's bare-name keys, so the magnifying glass never
suppressed on explored overlays. Now matches on the bare name.
Removes ~870 lines of hand-measured overlay data (base + Turtle).
commit ad12c8209a80cd1211fe08e83f7013d24b5efd2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date: Sun Jul 5 23:45:09 2026 -0500
utilize HookScript from ClassicAPI
The SPELL_CAST_EVENT hook only called SetQueuedKind, but the queued
color path is gated behind S.useSpellQueueEvent. Nampower fires
SPELL_CAST_EVENT on the actual HS/Cleave/Maul press (not just the
rarer ON_SWING_QUEUED), so flip the event-driven flag there. The
queued color now shows even when the client IsCurrentAction state
does not reflect a natively-queued on-swing ability.
The castbar options were one long list. Break them into four subentries
under the Castbar parent (matching the Settings/Actionbar layout):
General (fonts, colors, texture, disable-blizzard) plus one page each for
Player, Target, and Focus. Drops the now-redundant per-unit header rows.
Adds per-unit (player/target/focus) dropdowns to align the castbar
spell name (left text) and cast timer (right text) Left/Center/Right.
Both share a castbaralign dropdown; defaults preserve current behavior
(name LEFT, timer RIGHT). Applied at castbar creation, so takes effect
on /reload like the other castbar options.
The pet bar force-shows in unlock mode, and its OnShow/OnHide dodge
handlers re-anchor the stance bar above the pet bar. That yanked the
stance bar off its real position while unlocking (dodging a pet bar
that isn't actually active), so it appeared to vanish and only returned
when unlock ended. Skip the dodge re-anchor while unlock is active so
the stance bar stays put and can be positioned.