The fireworks screen becomes a shared AFK scene: a fading night, the greeting, and a click or coming back to close it. Hallow's End gets a flickering candlelight edge and a row of undead, seven of fifteen creature displays at random, that rise one after another. Christmas gets endless snowfall and a drift that piles up for as long as the player is AFK. Fireworks now spawn across the scene's own size instead of the UI-scaled screen size.
Explosions are pooled textures that grow and fade with a Scale and Alpha group instead of resizing frames every frame, and the night fades in with an Alpha group instead of stepping alpha per frame.
Each style is now a list of keyframes that a button builds into chained
Scale and Alpha animations the first time it plays. The animations are
timed by the engine instead of stepping by 30/GetFramerate() each frame,
scale the drawing instead of the frame, and run no Lua while they play.
The damped bounce styles ease between the extremes of their old curves.
The glow stepped its color ten times a second and re-checked combat,
health and death on every step. An animation group now shifts the color
along an IN_OUT curve and pauses while the glow is hidden. Combat,
health and death update from their events. Aggro has no event, so a
ticker polls it every .3s, matching UnitHasAggro's cache, and only while
that glow is enabled.
The polling lurker frame is gone. HookAddon gates on
C_AddOns.DoesAddOnExist, waits for PLAYER_LOGIN with
EventUtil.ContinueOnPlayerLogin, then runs on
EventUtil.ContinueOnAddOnLoaded. Addons loaded at startup still hook
only after every pfUI module and skin exists and after the addon's
own VARIABLES_LOADED setup. Load-on-demand addons still hook on their
ADDON_LOADED.
The "or variable" form is gone. Callers that passed a global now pass
the addon that defines it: TheoryCraft, SuperMacro, DPSMate and
Turtle_GroupUI. DPSMate names its windows from its settings, so its
hook now checks for DPSMate_DPSMate itself, as the variable lookup
used to. eqcompare's Guda check uses HookAddon too.
Clicking a chat URL and the GUI's Donate/GitHub/Website buttons now put the address straight on the clipboard and say so in chat. The url link goes through ClassicAPI's LinkUtil handler registry, so SetItemRef is no longer replaced. Exporting a profile also copies the string.
C_SwingTimer checks the range for each shown bar, and
PLAYER_SWING_RANGE_UPDATE turns a bar the new Out Of Range Color (grey by
default, since the main-hand bar and the hunter dead zone are red) while
its swing can't reach the current target. The melee check is the
server's 2D reach; the ranged one covers the hunter's minimum range too.
No check (no target, nothing to attack, no weapon) keeps the normal
color.
The mouse wheel on the chat frames and on the AFK camera's chat moves by
ScrollByAmount instead of looping ScrollUp / ScrollDown. Shift-wheel up
scrolls by the full range, so the oldest line stops on top as it does
with the wheel; ScrollToTop would leave it alone on the bottom line.
The 0.1s rescan only caught timers that change without a loss of control
event: a refreshed debuff fires UNIT_AURA, and a second kick on a locked
school fires SPELL_UPDATE_COOLDOWN, after ClassicAPI has read the lockout
from the packet. The alert listens to both while it shows. pfUI's
cooldown text prints the time left on the icon, so the alert's own
countdown and LOSS_OF_CONTROL_SECONDS go.
A movable alert in the middle of the screen for the strongest active
effect from C_LossOfControl: icon, cooldown sweep, the effect's name and
the seconds left. A school lockout reads "<School> Locked". It opens on
LOSS_OF_CONTROL_ADDED / _UPDATE and rescans every 0.1s while shown,
because a refreshed debuff or a second kick changes the timer without an
event.
The labels are Classic Era's LOSS_OF_CONTROL_DISPLAY_* GlobalStrings,
added to each locale file. The README lists the alert and the dressing
room controls.
EnableClickRotate becomes EnableModelControls, the controls of 4.3.4's
ModelWithControlsTemplate: left-drag turns the model without ClassicAPI's
step animation, the mouse wheel moves the camera toward the face with
SetPortraitZoom, and right-drag pans with 4.3.4's per-race pan speed and
bounds. Showing the frame resets zoom and pan. Every model frame pfUI
skins gets them.
The dressing room gains an Undress button and a column of slot buttons
on each side, in the paperdoll's order; clicking one calls UndressSlot.
SetupMenu reads each radio's selection when the dropdown is set up at module load, before any frame is docked, so IsDockAnchor indexed a nil dock parent.
The inbox package border now waits for the item to load, since another
player's item may not be cached. The panel and loot history drop their
cached branch: ContinueOnItemLoad runs at once for a cached item. The
swing timer reads the off-hand inventory type from the slot instead of
parsing the item link.
Each radio reads its selection from the frame's saved position, and
UpdateDockValues regenerates the menu instead of setting a UIDropDownMenu
selection by hand. A frame without a saved position shows N/A, like its
coordinates, instead of the anchor of the frame selected before it.
The menu opens under the clicked loot button, owned by it. A change to
the candidates or the rolls rebuilds it while it is shown. Request Rolls,
Reroll Ties, Clear Rolls and the roll list sit in the root menu, which
can be rebuilt in place where a submenu can't, so rolls appear as they
come in. With advanced loot off, the raid gets the class lists instead of
Blizzard's menu, whose roster the unregistered LootFrame never filled.
The confirm dialog names the candidate instead of the clicked row's text.
pfUI no longer initializes GroupLootDropDown or overrides
GroupLootDropDown_GiveLoot.
Each if/else that only showed one object in one branch and hid it in the other is now ClassicAPI's SetShown, which shows on any truthy value. 21 sites across 10 modules.
ClassicAPI's SetColorTexture is the same engine call as SetTexture(r, g, b [, a]) under the name that says what it does. 83 calls across 25 files, renamed only.
CreateQuestionDialog builds its frame once and sizes it right away from
the text's unbounded width. A frame per call broke the one-dialog guard
after the first dialog, since 1.12 binds a frame name only once. An
onclose that opens the next question runs on the next frame.
Chat history goes in with BackFillMessage, above the lines the frame
already has. SkinArrowButton dims its icon from OnEnable/OnDisable
instead of an OnUpdate per button.
Config rows and search results took their hover from an OnUpdate that
polled the cursor every frame. Rows now take OnEnter/OnLeave, and their
widgets propagate mouse motion so the row stays lit over them. Search
results fill a pfUIScrollBoxListTemplate from a DataProvider instead of
a hand-pooled button list, and match the search text literally.
pfUIDropdownTemplate (a DropdownButton) replaces CreateDropDownButton in
the config GUI, addon profiles, the trigger editor and the icon picker
filter. The equipment set gear menu and the tracking menu open
MenuUtil context menus. pfUIMenuStyleMixin draws pfUI's backdrop behind
them, and the skin module makes it the default style and draws the
checkboxes and radios as pfUI boxes.
pfUI.events was a bare CallbackRegistry for pfUI's custom events. Build it
from ClassicAPI's EventRegistry instead, so the same object also takes game
events through RegisterFrameEventAndCallback, sharing one frame for all of
them.
Move the frames that existed only to listen for a few events onto it:
libspell, focus, pixelperfect, innervatecall, loothistory, unusable,
equipmentmanager (bank + window refresh), the actionbar grid, updatenotify,
turtle-wow's steady shot, nampower's spell queue, superwow's group tracking
and unitxp's notifications, plus newitem and mapreveal, which used the
global EventRegistry. Handlers take their payload as arguments instead of
the event/arg globals, and the per-frame PLAYER_LOGOUT teardown goes away,
since the registry unregisters its frame on logout.
Spell buttons kept the template's indicator Model for their sweep. The
Spellbook skin now creates a native Cooldown for each one.
Taking the "<button>Cooldown" name was meant to point the FrameXML
lookups at the new frame, but 1.12 binds a frame's name only while that
global is unset. The bag slot Cooldowns from a721f788 were never used:
ContainerFrame_UpdateCooldown still drove the Model, so bags lost their
timer text, ignored Reverse Cooldown Animation and were hidden by Hide
Foreign Cooldown Animations. Both places now rebind the global by hand.
The values anchored to the content's TOPRIGHT, which now spans the whole
panel since the panels moved to pfUIScrollFrameTemplate, so they sat 375px
past its right edge and the scroll frame clipped them. Anchor them to the
content's TOPLEFT, where the labels' offsets already measure from.
The sweep options reached the frames only through the cooldown module's
CooldownFrame_SetTimer hook, which set alpha 0 for a hidden animation and
SetReverse(true) for a reversed one. With that module disabled, Show Timer
Animation and Reverse did nothing, and reverse never turned back off
without a reload. The player buff sweeps had no other path at all.
Each owner now configures its own frame. Player buffs drive their
Cooldown with SetCooldown and Clear, and only while Show Timer Animation
is on. Unit frame auras, raid aura indicators and nameplate debuffs hide
the sweep with SetDrawSwipe and SetDrawBling. Every frame with a reverse
option calls SetReverse. The hook keeps the timer text and Hide Foreign
Cooldown Animations.
Nameplate debuffs hid the sweep with alpha, which the plate's SetAlpha
copied down on every target change (ClassicAPI keeps 1.12's alpha under
default plates), so hidden sweeps came back. Their settings now come from
UpdateDebuffConfig alone.
Bag slots get a native Cooldown like bank slots, in place of the
template's indicator Model, so Reverse Cooldown Animation covers bags and
Hide Foreign Cooldown Animations no longer hides them.
The buff, unit frame, nameplate and totem options apply without a reload.
Fixes#72.
Classic Era dropped QUICKBUTTON_NAME_SELF and its tooltip, so every locale
showed 4.3.4's English text. Each locale now has its own, worded after its
My actions strings.
"Settings" in the Combat Log tab's menu opens Blizzard's Combat Log page,
rebuilt in Lua with pfUI widgets: the filter list with copy, add and
delete, and the Message Sources, Message Types, Colors, Formatting and
Settings tabs. Combat Log Defaults restores the default filters. Closing
the window refills the log only when the active filter changed.
The log now records while ChatFrame2 is hidden, so switching tabs no
longer loses history.
The default filters are 4.3.4's: Self, Everything, What happened to me?
and Kills. Classic Era's My actions left out auras.
ChatFrame2 now shows the 4.x combat log, ported from Classic Era's
Blizzard_CombatLog as the combatlog module. ClassicAPI's COMBAT_LOG_EVENT,
CombatLogAddFilter and kept entries drive it, and changing filter refills
the frame with BackFillMessage. Quick buttons sit above the log, and its
unit, spell, raid icon and action links open Blizzard's filter menus.
Filters save account-wide in pfUI_combatlog.
The 1.12 CHAT_MSG_COMBAT_* and CHAT_MSG_SPELL_* events no longer reach
ChatFrame2. Its message groups stay saved, so disabling the module brings
the text log back.
env/combatlog_<locale>.lua hold the Classic Era GlobalStrings the log reads
that 1.12 lacks.
The hook forwarded the text and seventeen named arguments. It takes them
as ... now, which ClassicAPI rewrites to unpack(arg) with arg.n, so every
argument reaches the frame's AddMessage, nils included. GetMessageInfo
returns all of them, and a hook that drops some loses them for every
frame after it.
"Show Timestamps In Chat Copy" (chat text settings, on by default) is read
each time chat copy opens. With it off, chat copy takes each line through
pfUI.chat.StripTimestamp, which removes the exact timestamp AddMessage
prepends: the configured color and brackets, so other bracketed text
stays.
Whispers are now recolored before the timestamp goes on, not after. The
recolor turned the stamp's closing "|r " into "|r" plus the whisper color,
so whisper lines did not match. The shown text and the saved history come
out the same.
The set list, the unused-items grid and the trigger list each drew a
fixed number of slots and moved them with a mouse-wheel offset. All three
are pfUIScrollBoxListTemplate lists now, with a scroll bar.
The set list's rows are the sets and then the "New Set" entry, set up by
one initializer. Refresh rebuilds them only when the sets change and
otherwise reinitializes the shown rows, so a row keeps its frame between
the two clicks of the native OnDoubleClick that now equips a set, in
place of the hand-timed double click. PickupEquipmentSet is called
without a presence check.
The row's gear passes mouse motion on to the row, so moving onto it no
longer leaves the row and the MouseIsOverHitRect workaround is gone. The
gear rests at half alpha and lights up on hover with an additive copy of
itself, instead of the blue square highlight.
The unused items are a grid view that keeps its scroll position on
refresh. The trigger list scrolls to a new trigger with ScrollToEnd.
pfUIScrollFrameTemplate (api/ui-widgets.xml) is a ScrollFrame with
pfUIScrollBarTemplate inside its right edge and a Content scroll child,
driven by ScrollUtil.InitScrollFrameWithScrollBar. The panels keep a
ScrollFrame because the engine measures its range from the shown frames
inside the content, which the hand-placed config rows rely on. The bar
follows OnScrollRangeChanged instead of an OnUpdate poll per panel.
A panel measures its range once its rows are built, and the search
results measure theirs with UpdateScrollChildRect instead of hiding,
re-attaching and scrolling a frame later. Clearing the search measures
again too.
CreateScrollFrame and CreateScrollChild have no users left, and nothing
called CreateTabFrame or CreateTabChild; all four are gone. pfQuest's
compat layer brings its own copies of the scroll helpers.
pfUIScrollBoxListTemplate (api/ui-widgets.xml) is a WowScrollBoxList with
pfUIScrollBarTemplate beside it, 5px inside the frame, so a list's
backdrop goes on the frame itself. The addon list moves onto it and drops
the sibling backdrop frame.
Loot history builds a flat list of item rows and, under an expanded item,
player rows; the view sizes and indents them. The item frame array, the
player pool, the hand-placed rows and the scroll child re-attach are gone,
and the scroll position holds across roll updates.
The icon picker's FauxScrollFrame and 80 fixed buttons become a grid view
over the provider's icon indices. Picking an icon reinitializes the shown
buttons instead of redrawing the grid, and closing the popup drops the
grid's entries along with the provider.
pfUIScrollingEditBoxTemplate (api/ui-widgets.xml) is ScrollingEditBoxTemplate
with pfUIScrollBarTemplate beside the text. Its mixin's SetFont gives the
box a font object of its own, since the edit box sets its font by object on
every SetText and pfUI fonts are paths. ui-widgets.lua now loads before the
XML, which needs the mixin.
The share box no longer recomputes its scroll child on every text change.
Chat copy reads each chat frame's messages with ClassicAPI's
GetMessageInfo when it opens, so the AddMessage hook, its 100-line copy of
the history and the skip of combat frames are gone. It shows every line
the frame keeps, lines from before login included. It puts the caret at
the end to scroll to the newest line instead of three OnUpdate passes, and
Escape now leaves copy mode on every chat frame.
ClassicAPI's UnitHasIncomingResurrection sees every resurrection cast in
view from SMSG_SPELL_START and ends it with the cast, and
INCOMING_RESURRECT_CHANGED signals each change. libpredict's own
tracking goes: the ress tables and 60s timers, parsing Resurrection and
RES/RESNO messages, clearing on UNIT_HEALTH, and the rez branches of the
cast handlers, along with UNIT_SPELLCAST_STOP and the foreign cast
targets only rez used. libpredict still announces the player's own
resurrections to HealComm and CTRA for clients without ClassicAPI.
The native state ends with the cast, so a rez that landed would vanish
before the target accepts it. libpredict records the SPELL_RESURRECT
combat log event per GUID and keeps the offer until the target is alive
again or the 60s accept popup runs out, unlike Grid2, which cannot tell
a landed rez from a cancelled one. UnitHasResurrection answers both.
The unit frames show the icon in RefreshIndicators, refreshed by
INCOMING_RESURRECT_CHANGED on the frame's own unit and by libpredict for
offers, instead of polling libpredict every 0.1s.
The list built one row per addon in a ScrollFrame, hid the rows out of
view by hand and drove pfUI's own slider. A WowScrollBoxList keeps rows
only for the addons in view and recycles them while scrolling.
pfUIScrollBarTemplate (api/ui-widgets.xml) is a ScrollBarMixin bar with
the old slider's look: a thin track and a flat teal thumb that hides when
nothing scrolls. Its steppers exist for the mixin but stay hidden.
Rows read the addon's enabled state, so profiles enable and disable
addons directly instead of clicking every row's checkbox. A checkbox
now shows whether an addon is enabled; it showed whether it was loaded.
/afk toggles. The overlay sent it on every click and key press, so when
the client or the server had already cleared AFK it set it again. Check
ClassicAPI's IsChatAFK first. It follows the client's /afk state at
once, where UnitIsAFK waits for the server's flag update.
62e0d4eb stopped the camera only while the start delay was shown, but the
delay hides itself once the camera starts. AFK cleared during the spin
left the overlay up, and the next key press sent /afk and set AFK again.
Stop when the camera spins instead, which also keeps stop() and its
SetView(4) from running when AFK clears before the camera started.
The tooltip was built once on enter, so its memory, network and addon
figures stayed at the values from that moment. The widget's one-second
ticker now rebuilds it while the panel still owns GameTooltip. The
tooltip takes its owner as self, not this, so the ticker can call it.
ClassicAPI 1.18's ExpandAllFactionHeaders and CollapseAllFactionHeaders
set every header in one call. The button sits on the "Faction" label's
row, in the headers' +/- column, and shows "+" while any header is
collapsed.
ParseChatMessage tested the raw message for "RessStop", which no
HealComm message is, so ParseComm's Resurrection/stop/ result was never
acted on. CTRA's RESNO was matched against the whole message, but it
carries the target's name, and the CTRA parser only recognised RES. A
cancelled rez from another player left the icon on the target's frame
until they were revived. RessStop still keeps a rez whose cast landed
and whose accept window is open.
With HealComm-1.0 present (HealComm, LunaUnitFrames, NotGrid), the
compat hook unregistered every event on libpredict.sender to stop pfUI
broadcasting a duplicate of the player's casts. Since cast detection
moved onto that frame's UNIT_SPELLCAST_* handlers, that also blinded
libpredict to every cast, so the player's own resurrections never put
the rez icon on the target's frame: ParseChatMessage drops the player's
own Resurrection echo, expecting the cast events to have tracked it.
The compat hook now only mutes the outgoing HealComm and CTRA messages.
The HealComm.SendAddonMessage hook that parsed the player's own messages
while solo goes too, since libpredict tracks its own casts directly.
pfGridmath listed every exact cols x rows grid per bar size; the only
live use was seeding the layout dropdown, which a divisor loop now does.
Everything else that read it was unreachable: every layout option is a
"cols x rows" string, so ResolveBarLayout's BarLayoutFormfactor lookup
never matched, and the actionbar module resets a stored layout that
isn't one before BarLayoutSize/BarButtonAnchor run, so their fallbacks
never fired. BarLayoutFormfactor and env/tables.lua go with it.
CreateUnitFrame and CreateCastbar could take a character name instead of
a unit token, but every caller passes a token, so that mode and the
empty-label guards around it were dead. The OnClick TargetByName branch
could never run anyway: label was always a string, and "" is truthy.
With that gone, the aggro scan was the only reader of pfValidUnits, so
its unit list now lives in unitframes.lua and is built once at load.
/focusname resolves the name with UnitTokenFromName (exact first, then
prefix) instead of swapping the target and muting UIErrorsFrame.
The custom bar sized its textures in Lua from GetWidth(), so it had to
track how ClassicAPI reports the UI scale (#71), and it stretched the bar
texture instead of cropping it. The engine's StatusBar does that layout
itself.
The background of the missing part is a second StatusBar that fills
from the other end (ClassicAPI's SetReverseFill), one frame level below
the fill, so the incoming-heal texture still draws between the two.
SetFrameLevel moves both, and the health and power bars now sit two
levels above their backdrops. SetStatusBarBackgroundTexture becomes
SetStatusBarBackgroundColor, as it only ever took a color.