The mana spark modelled a tick clock that does not exist. Spending mana
restarted the sweep with a five-second span, then it cycled at two seconds
until the next spend -- so every cast reset the phase, and the spark drew a
period the server never runs. There is one regen timer for every power,
REGEN_TIME_FULL (2s), re-armed with `+=` in Player::RegenerateAll and never
touched by casting. The five-second rule is a separate countdown that
SetLastManaUse arms on any mana-costing cast; it changes what a tick pays,
never when ticks land. Inside the window the tick still fires and still pays
mp5 plus whatever share of spirit the player keeps.
That share is common and not computable here. The amounts are readable now
(C_Spell.GetSpellEffectInfo), and the talent, racial and buff sources could be
enumerated -- but the Casting Regen item ladder is equip auras that never
appear in the buff list, and m_modManaRegenInterrupt is never sent. A floor
that is wrong whenever the player wears such an item is worse than showing
what happens: the spark starts an FSR window dim and goes solid on the first
tick that lands inside it.
The sweep is a free-running phase lock on that one clock. Every positive mana
delta used to re-anchor it, and most are not the tick -- Illumination refunds,
Judgement of Wisdom, potions, a Mana Spring totem on its own 2s clock at
another phase -- so the spark snapped back to the edge mid-sweep several times
a cycle. A gain re-anchors only if it lands within 250ms of the predicted
boundary. Two rejected gains exactly one period apart are the real clock and
move the lock, which is how a wrong first lock heals. Rollover advances by
whole periods from the anchor instead of restarting from now, so frame timing
no longer accumulates into drift the lock then has to chase.
Even a correct tick used to hitch the spark: re-anchoring to arrival time
moved it by the jitter, 20-80ms, right at the wrap. Inside an 80ms band the
tick now confirms the sweep and leaves the anchor alone; only the excess is
pulled in. With no self-drift left there is nothing systematic in that band.
The rule is armed from UNIT_SPELLCAST_SUCCEEDED filtered through
C_Spell.GetSpellPowerCost, mirroring Spell::TakePower's condition, rather than
from a mana decrease -- Mana Burn lowers mana without arming the rule.
UNIT_SPELLCAST_CHANNEL_STOP re-arms it, since Unit::Update will not expire it
while the spending spell is still channeling.
The energy period no longer names a spell. Player::RegenerateAll sums
SPELL_AURA_MOD_ENERGY_REGEN_TIME and takes amount * agility / 10 off the timer
in milliseconds; everything in that sum is readable here. The player's own
spells carrying aura 217 come from C_SpellBook.GetPlayerSpellsByAura, passives
count while known (current rank only), anything else while it is up in the
buff list, and each amount is basePoints + baseDice from the effect data. So
Blade Rush is found without GetTalentInfo(2, 16) -- an ordinal into the talent
grid that does not fail when the tree changes but reads another talent's rank
(index 16 is Vigor one tab over) -- and a retune or a second source needs no
edit. The sum is cached and dropped on SPELLS_CHANGED and
PLAYER_AURAS_CHANGED, the two events its two halves move on, so a tick costs
no spellbook walk; agility stays live, it is one call.
The CHAT_MSG_SPELL_SELF_BUFF "You gain ... Energy from" filter is gone. The
phase lock rejects Relentless Strikes and Thistle Tea the same way it rejects
a paladin's refunds, it was English-only, and its ignore-the-next-gain logic
could eat a real tick if events reordered.
ClassicAPI's RegisterUnitEvent registers for an event but only delivers it
when arg1 is one of the given units, so a handler for one unit stops waking
for every other one in the world. Convert the 26 registrations whose unit set
is fixed and known at registration time.
The rule throughout is register the superset and keep the handler's own
check: the filter narrows what arrives, it does not decide what to act on.
castbar is the case that matters -- the player's own casts only ever fire
arg1=="player", never "target"/"focus", so the target and focus bars must
register "player" too, and their UnitIsUnit test is what still rejects a
"player" event while you are targeting a mob.
Two that are not one-line swaps:
actionbar's two unit events are keys in tables that also drive dispatch, so
they cannot leave the tables -- and they cannot be plain-registered first,
because a registration keeps its kind and RegisterUnitEvent over a plain
one stays plain. They route through a small event_units map instead.
swingtimer's UNIT_DIED carries a GUID, not a token. The filter is a plain
case-insensitive string compare with no GUID resolution, so the player's
own GUID -- fixed for the session -- filters it exactly.
Guards that are now unreachable stay put: the filter applies only when arg1
is a string, so an event that ever fires with a number or no argument is
delivered as if plainly registered, and the handler's own test is what still
rejects it.
Left alone: api/unitframes.lua (the frame's unit changes at runtime and the
handler also matches on GUID), nameplates' six nameplateN events, libdebuff
and libpredict's UNIT_HEALTH (genuinely any unit), and raid.lua's UNIT_PET
(any raid member can own the pet).
- 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.
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
Bugfixes:
- Fixed 40-yard range check not working for raid/party frames
- Fixed aggro indicator not displaying properly on raid/party frames
- Improved aggro cache to only cache positive results for instant detection
- Fixed HP/Mana not updating with "Use Raid Frames for group" enabled
- Added SuperWoW nil-check for SpellInfo
- Added missing events: PARTY_MEMBER_ENABLE, PARTY_MEMBER_DISABLE, PLAYER_UPDATE_RESTING
UI Improvements:
- Share/Hoverbind buttons now show warning when module is disabled