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.
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.
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().
- 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.
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.
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.
The unlock preview drives the bar via alpha: the OnUpdate forces alpha=1
while the drag handle is shown. On lock, with no active cast (endTime nil)
and no fadeout, the idle branch returned early and left the empty bar
stuck at alpha 1. Reset leftover alpha to 0 in that branch so the preview
clears once the drag handle hides. Fixes#16.
Ensure the action button OnClick handler is always assigned. Previously SetScript("OnClick", ButtonClick) only ran when HookScript was missing; now HookScript is still added only if absent, but the OnClick script is set unconditionally so existing frames won't miss the click handler. Change is limited to modules/actionbar.lua.