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.
* Migrate player castbar to ClassicAPI UNIT_SPELLCAST_* events
Drive the player cast path off ClassicAPI's synthesized, player-only
UNIT_SPELLCAST_* events instead of the vanilla SPELLCAST_* plus nampower
SPELL_{START,GO,DELAYED}_SELF mix. START/CHANNEL_START poll UnitCastingInfo/
UnitChannelInfo and stamp; STOP/CHANNEL_STOP/FAILED/INTERRUPTED clear;
SUCCEEDED drives tradeskill craft counting; DELAYED/CHANNEL_UPDATE re-poll.
Since UNIT_SPELLCAST_START fires for chained same-spell recasts, the nampower
SELF deps and their RunNextFrame co-hook-ordering workarounds are gone.
Remote target/focus bars keep SPELL_*_OTHER (the new events are player-only).
Fix the cast-start flash: StampBar now primes the fill on the stamp frame,
and ClearBar no longer snaps the bar to full (that snap lingered in the fade
tail and flashed when the next cast stamped).
Add a red flash on a cancelled cast: new appearance.castbar.failcolor,
flashed by ClearBar only when a cast was actually in progress.
Handle pushback Quartz-style: DELAYED/CHANNEL_UPDATE re-poll just the times
and accumulate the endMs shift into this.delay for the +/- indicator (cast
+X, channel -X), rather than a full restamp.
* Route target/focus castbars through remote UNIT_SPELLCAST_*
ClassicAPI now fires UNIT_SPELLCAST_* for remote tokens (target/focus/...)
via PollRemote, so drop the nampower SPELL_*_OTHER path and drive all three
bars off one event model. The gate is now token-based (arg1 == this.unitstr,
plus the arg1=="player" + UnitIsUnit case for target=self); the RunNextFrame
defer is gone since the remote poll fires after UnitCastingInfo is readable.
Cast detection is now nampower-free across every bar.
Use the event's rank payload (arg5): thread it through RefreshBar into
StampBar, which now only calls C_Spell.GetSpellSubtext as a fallback on the
retarget re-poll (PLAYER_TARGET/FOCUS_CHANGED) where no event is in hand.
* Drive nameplate castbars off UNIT_SPELLCAST_*
ClassicAPI now fires UNIT_SPELLCAST_* for nameplate tokens, so populate the
per-GUID castState cache from UNIT_SPELLCAST_{START,CHANNEL_START} (poll
PollCastInfo for the timing the payload omits) and clear it on
{STOP,CHANNEL_STOP} -- the remote poll fires STOP for natural end, interrupt,
and cancel alike. Replaces the nampower SPELL_{START,FAILED}_OTHER path; the
^nameplate arg1 gate ignores the target/focus/party fires of the same events,
and the NAME_PLATE_UNIT_ADDED seed still catches a plate spawning mid-cast.
Nameplate cast detection is now nampower-free.
* We no longer use these features from SuperWoW/Nampower
* Use secure unit attributes; remove mouseover scripts
Switch unitframes to attribute-driven clicks and remove mouseover tooltip handlers. Adds SetAttribute("unit") in UpdateVisibility and sets frame attributes (unit and type1='target') when creating frames. Removes OnEnter/OnLeave functions and their SetScript registrations and eliminates direct TargetUnit calls in ClickAction
* Use SetShown and simplify component default
Replace explicit Show/Hide conditionals in api/unitframes.lua with SetShown(...) for ressIcon, leaderIcon, lootIcon, pvpIcon, and restIcon to reduce branching and improve readability. Also remove the local shadowing of the parameter 'component' by using assignment (component = component or ""). No intended behavior changes.
* Drive unit-frame clicks via secure attributes
Replace the Lua OnClick dispatch (OnClick/ClickAction/RightClickAction) with
secure frame attributes: default type1=target / type2=menu, and EnableClickCast
now writes type/spell/macrotext/menu/target/focus attributes per button+modifier
instead of caching a clickactions table read at click time. Drops the now-dead
clickactions table and buttons list.
Also set a "unit" attribute on each frame (at creation and re-synced in
UpdateVisibility to the live token, so a party shown on the raid grid reports
partyN, not raidN) so the hovered unit resolves from the frame.
Match the target/focus/menu click keywords exactly rather than by prefix, so a
spell whose name starts with "focus"/"target"/"menu" (e.g. Focus Magic) casts
instead of being swallowed as the keyword action.
* Simplify marktracking colors and refresh per mark
Replace the hand-rolled ParseColor plus its default-color table with
GetStringColor -- the raidmarkercolor_* config keys already carry those same
defaults, so the fallback was dead code.
Restructure the refresh off observed mark tokens: UNIT_HEALTH/UNIT_MAXHEALTH
now arrive as arg1 == "markN", so refresh just that one row (UpdateRow) instead
of rescanning all eight on every nearby unit's health tick. A visibility flip
(into range / death / hp crossing 0) re-packs the rows, so UpdateRow hands off
to a full UpdateDisplay; RAID_TARGET_UPDATE / PLAYER_ENTERING_WORLD stay full
refreshes, with the 1s poll as the range-change backstop.
* not true anymore
* Poll marktracking fallback via C_Timer instead of OnUpdate
The 1s range-change safety net ran a per-frame OnUpdate that no-oped ~59 of
every 60 frames. Replace it with C_Timer.NewTicker(FALLBACK_INTERVAL,
UpdateDisplay) -- one wakeup per second off the shared timer driver -- and drop
the elapsed accumulator. The scanner frame is now purely event-driven.
* SuperWoW is now optional
* Update README feature list
Add 'Mouseover Unit Frames' and 'Click-casting' to the main features list. Update the SuperWoW entry to reflect that it tracks party/raid units on the minimap (replacing the prior SetMouseoverUnit note).
* Replace fixed-interval OnUpdate polls with C_Timer/RunNextFrame
Swap hand-rolled per-frame throttles for the modern timer primitives:
- turtle-wow: one-shot next-frame defer (self-hiding OnUpdate frame) -> RunNextFrame
- panel: clock, combat, and fps widgets -> NewTicker(1); guild roster -> NewTicker(60)
- minimap: coordinates text -> NewTicker(0.1)
- addonbuttons: one-shot init -> RunNextFrame; 5s button rescan -> NewTicker(5),
guarded by IsShown() to preserve the old "paused while hidden" behavior
Ticker callbacks reference frames via upvalue since 'this' is unbound outside
OnUpdate. Genuine per-frame work (bar fills, fades, drag) and polls already
coordinated through pfUI.throttle are left as-is.
* update pfQuest link
* Use SetShown/SetSize in panel module
Replace manual Show/Hide toggles with SetShown(not ... ) and replace SetWidth/SetHeight with SetSize in modules/panel.lua. Changes simplify toggle logic (timer, WorldMap, chat hide buttons) and unify sizing calls for timer, frames, textures, and microbutton. No behavior changes intended; purely refactor for conciseness and consistency.
* Bump ClassicAPI min version to 1.9.0
* Drive reagent counter from events instead of a polling OnUpdate
The pfReagentCounter frame ran an OnUpdate that (1) progressively rescanned
all 120 action slots one-per-100ms on any slot change and (2) recounted
reagent inventory on a 1s throttle. Replace both with direct event handling:
- ACTIONBAR_SLOT_CHANGED updates just the changed slot (arg1), full-scanning
only when arg1 is 0/nil, instead of restarting a ~12s rescan on every edit
- BAG_UPDATE_DELAYED recounts tracked reagents directly (it is already
Blizzard's coalesced bag event, so the extra 1s throttle was redundant)
- PLAYER_ENTERING_WORLD seeds the full reagent map once
UpdateSlot now seeds a new reagent's real count via GetItemCount so a freshly
placed reagent spell shows the correct number immediately. The updatecache ->
BarsUpdate render path and the IsReagentAction/GetReagentCount accessors are
unchanged.
Replace separate GetWidth/GetHeight and SetWidth/SetHeight calls with GetSize/SetSize for consistency and brevity. Updated modules: addonbuttons (store size via GetSize), chatcopy (use SetSize), easteregg (use SetSize for explosions), farmmode (use GetSize/SetSize when adjusting map size), and game_menu (use GetSize/SetSize when resizing frame).
hasTarget was always snapshotted alongside targetGuid (UnitExists("target")
next to UnitGUID("target")), so it was just targetGuid ~= nil. Drop the
field and derive it from targetGuid at both read sites; remove the now-unused
UnitExists perf-cache local.
frameState.hasTarget/targetGuid were polled via UnitExists("target") every
central-loop tick, while targetPlateGuid held the same guid but was already
event-driven -- redundant. Set frameState target fields in PLAYER_TARGET_CHANGED
(seeded on PLAYER_ENTERING_WORLD for a target held across reload), remove the
poll, and collapse targetPlateGuid into frameState.targetGuid (the castbar
frame now reads that). The central loop's only remaining per-tick poll is
GetTime().
Store the buffbar color/bordercolor/textcolor as cached read-only ColorMixins
via GetStringColorObject instead of building per-bar {r,g,b,a} tables from
GetStringColor. These color tables are only ever read (SetStatusBarColor /
SetTextColor / the border check), so sharing the cached objects is safe and
drops the intermediate locals and per-bar allocations.
GetStringColorObject's components are numbers, so fix CreateBuffBarFrame's
border check from ~= "0" (string) to ~= 0.
Route the three buffbar color config splits through GetStringColor (cached)
instead of bare strsplit. GetStringColor returns strings, so this is
behavior-preserving -- including the bordercolor ~= "0" check in
CreateBuffBarFrame -- and just adds the shared color cache and consistency
with the rest of the codebase.
- GetStringColor returns strings again (its lifelong contract). The earlier
numeric conversion was a return-type change that could break any caller
doing string comparisons on components (e.g. buffwatch's `~= "0"`); every
caller was written against strings and color setters coerce, so reverting
is the safe fix without a codebase-wide audit.
- GetStringColorObject now tonumbers its own inputs, so its ColorMixin stays
numerically correct (GenerateHexColor/IsEqualTo) independent of the above.
- cmatch: move its a-e / va-ve / ra-re scratch out of module scope into clear
function-locals (idx*/val*/out*). Strict 1:1 rename, logic unchanged; also
drops the shared-upvalue reentrancy concern.
Replaced numerous SetWidth/SetHeight calls with SetSize for consistency and brevity across UI code. Touched api/ui-widgets.lua, api/unitframes.lua and multiple modules (actionbar, addonbuttons, addons, afkcam, autovendor, bags, bgscore, buff, buffwatch, nameplates). Also simplified some sizing math in buff module. No functional behavior intended to change — code modernization only.
ClassicAPI polyfills UPDATE_MOUSEOVER_UNIT to fire on mouseover loss as well
as gain (src/unit/Mouseover.cpp), so the per-tick UnitGUID("mouseover") poll
in the central loop is no longer needed. Update frameState.mouseoverGuid on
the event and flag just the plates losing/gaining hover (via plateByGuid,
which holds overlays) for an immediate refresh.
No unit frame ever has a GUID label -- CreateUnitFrame is only ever called
with fixed type names (Player/Target/Focus/Party/Raid/...), and .label is only
ever assigned those. The string.find(this.label, "^0x") branch was a leftover
from an older focus implementation; focus now uses the real "focus" unit
token, so OnEnter always resolves the mouseover via the normal unitstr path.
- 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.