97 Commits

Author SHA1 Message Date
Brues 1c7c91fdf7 Fix power bar rendering black behind its backdrop
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.
2026-08-06 19:16:49 -05:00
brues-code 7f5d18bc30 Update README to simplify project description 2026-08-06 13:29:49 -05:00
Brues 7efc3334ae Add an aura caster line to tooltips
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.
2026-08-06 02:51:08 -05:00
Brues f322fbd6c6 CancelItemTempEnchantment can't exist in vanilla without server change 2026-08-06 02:03:21 -05:00
brues-code 41b77a5728 Add the macro icon picker and improve the equipment manager (#35)
* 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.
2026-08-05 22:33:37 -05:00
Brues 06fea6c889 Cache the player guild name for nameplate coloring
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.
2026-08-05 21:43:08 -05:00
Brues 940a1adae0 Restore Clique support on unit frames
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.
2026-08-05 21:43:08 -05:00
Brues d13710c5a4 Rebuild macro icon list from the icon data provider
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.
2026-08-05 03:02:43 -05:00
Brues c3dd7cb601 Refactor player info: simplify haste and SP display
Minor readability and style cleanup.
2026-08-05 00:28:55 -05:00
Brues 9190b88974 Cache player color codes and use full ARGB hex
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.
2026-08-04 23:00:51 -05:00
Brues fe0ec82ede Route config color strings through GetStringColor cache
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.
2026-08-04 22:27:43 -05:00
Brues 350b1d63ae Revert breaking change for WorldMapFrame 2026-08-04 21:29:03 -05:00
Brues c7f42491ef Update README.md 2026-08-04 16:28:08 -05:00
brues-code 6b458c9e27 Modernize cast bars, unit-frame clicks, and mark tracking with ClassicAPI (#32)
* 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.
2026-08-04 14:27:15 -05:00
Brues f338deebe7 UNITFRAME_SECURE_TEMPLATE is always nil in vanilla 2026-08-01 13:12:14 -05:00
Brues c994d00596 Toggle bagslots visibility via SetShown
Replace explicit if/else calling :Hide()/:Show() with :SetShown(not IsShown())
2026-07-31 13:22:17 -05:00
Brues 4be680183a Use GetSize/SetSize for size operations
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).
2026-07-30 14:29:30 -05:00
Brues 2352f1c6ed Use GetSize/SetSize for map frame and buttons
Replace separate GetWidth/GetHeight and SetWidth/SetHeight calls with GetSize and SetSize in modules/map.lua.
2026-07-30 14:11:34 -05:00
Brues 305ff6c86e Collapse frameState.hasTarget into targetGuid
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.
2026-07-30 13:45:53 -05:00
Brues a8b131898b Event-drive nameplate target state; drop per-tick poll and targetPlateGuid
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().
2026-07-30 11:59:56 -05:00
Brues 1f9be33772 Use GetStringColorObject for buffbar colors; numeric border check
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.
2026-07-30 02:44:56 -05:00
Brues a798e67b7f Use GetStringColor for buffbar color parsing
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.
2026-07-30 02:39:56 -05:00
Brues 8b87e30266 Revert GetStringColor to strings; keep object numeric; tidy cmatch
- 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.
2026-07-30 02:39:16 -05:00
Brues f68b14e53b Use SetSize instead of SetWidth/SetHeight
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.
2026-07-30 02:27:03 -05:00
Brues 8ab6fab04f Event-drive nameplate mouseover via UPDATE_MOUSEOVER_UNIT
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.
2026-07-30 02:01:10 -05:00
Brues 6d1bfe805e Use UnitClassBase for class detection 2026-07-30 01:26:29 -05:00
Brues 838f7f5d25 guard against nil units
UPDATE_MOUSEOVER_UNIT can fire when unit is dropped now so guard against that
2026-07-30 01:20:50 -05:00
Brues fad078fa87 Drop dead GUID-label branch in unit frame OnEnter
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.
2026-07-30 01:09:53 -05:00
Brues 48d76138ed Iterate active nameplates only; fix global eventcache propagation
- 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).
2026-07-29 23:43:27 -05:00
Brues fe22dfc456 Detect nameplate mouseover by GUID compare, not Blizzard glow texture
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.
2026-07-29 16:57:25 -05:00
Brues 5eaab2784f Resolve target castbar plate via cached GUID, not per-frame lookup
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.
2026-07-29 16:36:29 -05:00
Brues f41d5ac6d0 Make nameplate castbars event-driven; unthrottle target bar
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.
2026-07-29 16:29:12 -05:00
Brues af8780bb41 Use GetStringColor for newitem glow color
Replace CreateColor/Color:GetRGBA usage with pfUI.api.GetStringColor in modules/newitem.lua
2026-07-29 14:39:25 -05:00
Brues ea37f41db1 Check r[3] instead of table.getn in rgbhex
Replace table.getn(r) >= 3 with r[3] ~= nil in api/api.lua
2026-07-29 14:35:44 -05:00
Brues 6f36da7aa1 Cache and streamline color helpers in api
- GetStringColor: memoize via a metatable __index cache (single lookup on
  hits) and store numeric components so ColorMixin comparisons/arithmetic
  behave, not just setter coercion.
- GetStringColorObject: new accessor returning a cached, shared read-only
  ColorMixin for callers that want an object instead of raw values.
- rgbhex: memoize the markup keyed on the byte values that actually determine
  the output (Round(x*255)), so continuously-varying inputs like health
  gradients collapse onto a bounded set instead of leaking a cache entry per
  shade. Build misses via C_ColorUtil.GenerateTextColorCode on a plain table
  instead of allocating a throwaway ColorMixin. Fix a latent bug where the
  r/g/b/a temporaries were file-scoped, so a malformed input returned the
  previous call's color instead of an empty string.
2026-07-29 14:06:25 -05:00
Brues 149d5dd362 Scope tooltip cursor-follow to when shown; default it to smooth
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.
2026-07-29 11:42:18 -05:00
Brues 0156d9dfec Harden strsplit against third-party global clobbering
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.
2026-07-29 10:37:29 -05:00
Brues 6b945c2c97 buff min classicapi version to 1.8.2 2026-07-28 19:26:09 -05:00
Brues 452eef3864 Drop custom debuff durations from turtle-wow.lua
C_UnitAuras supplies Turtle-adjusted durations directly to the aura readers,
so the L["debuffs"] database fallback for these four custom debuffs is dead.
2026-07-28 13:29:11 -05:00
Brues 6f7a57f530 Restore UnitDebuff/UnitOwnDebuff as C_UnitAuras adapters
Third-party addons (e.g. pfUI-WeakIcons) still expect libdebuff's legacy
multi-return reader signature. Reimplement UnitDebuff/UnitOwnDebuff as thin
adapters over C_UnitAuras that remap AuraData onto:
  effect, rank, texture, stacks, dtype, duration, timeleft, caster

No GetUnitField slot mapping or ownDebuffs/allAuraCasts bookkeeping involved;
C_UnitAuras already resolves source and expiration. timeleft is gated on
duration > 0 to avoid stale expirationTime on permanent auras.
2026-07-28 10:48:42 -05:00
Brues a55460e543 Add reverse sort direction and priority options to bag sorter
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.
2026-07-27 18:15:07 -05:00
Brues 58aaeef5f0 can't have any fun 2026-07-27 17:17:56 -05:00
Brues 487af0c8f4 Use string.split instead of strsplit
Some users have addons that pollute the global namespace with a version of strsplit that doesn't match Blizzard's spec
Closes #31
2026-07-27 15:47:28 -05:00
Brues 00292ca3b4 Focus/Nameplates now respond solely to UNIT_* events
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.
2026-07-26 18:32:36 -05:00
Brues cba3604906 Use UnitGUID instead of UnitExists for GUID
Replace the previous extended UnitExists GUID retrieval with UnitGUID and simplify the Nampower health lookup flow. Also minor whitespace/formatting cleanup.
2026-07-24 17:06:12 -05:00
Brues a31d10384b Read unit power from ClassicAPI instead of Nampower's GetUnitField
GetUnitStats parsed the power type out of the bytes0 descriptor field and
read each power slot via GetUnitField(guid, "powerN")/"maxPowerN", manually
dividing rage by 10. ClassicAPI's UnitPower/UnitPowerMax read the same
descriptor slots and apply the engine's own power-divisor table (rage /10,
happiness scaling), so:

  powerType = UnitPowerType(unitstr) or 0
  power = UnitPower(unitstr, powerType)
  maxPower = UnitPowerMax(unitstr, powerType)

is equivalent and drops nine GetUnitField calls plus the bytes0 parsing.
Power now resolves through the hard-dep ClassicAPI even without Nampower;
GetUnitField in this path is left only for health, which has no ClassicAPI
real-HP equivalent.
2026-07-24 17:01:09 -05:00
Brues f06af48bc2 nampower: Use SetSize, UnitClassBase and simplify icon logic
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.
2026-07-24 16:49:35 -05:00
Brues d73c4de695 Modernize power-API usage onto ClassicAPI
- 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.
2026-07-24 16:37:34 -05:00
Brues dd8c529463 Move druid mana bar into the unit frame, off nampower
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.
2026-07-24 16:23:28 -05:00
Brues 47a7b4c686 Simplify totems frame sizing
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.
2026-07-24 16:08:36 -05:00
Brues cf3f1e5440 Rework raid self/group frame visibility and rename the solo option
- 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.
2026-07-24 15:02:38 -05:00
Brues 182127bd67 Refactor totems to use CreateColor API
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.
2026-07-24 15:00:16 -05:00
Brues 3d2e6e202b corrected rangecheck vanilla label 2026-07-24 14:14:46 -05:00
Brues 3a062ee2ac Recast Totem will now use the same rank totem 2026-07-24 10:00:22 -05:00
Brues 5ed1d98ecc Raid Pets and Optimized Chat Bubble Styling
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.
2026-07-23 23:08:05 -05:00
Brues fa1dc5637e Bump ClassicAPI min to 1.7.4
Totem functions!
2026-07-20 22:17:49 -05:00
Brues 8506f5ffbf Localize the totem tooltip click hints
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.
2026-07-20 22:16:05 -05:00
Brues 9c529512d1 Replace libtotem with ClassicAPI's native totem tracker
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.
2026-07-20 21:51:08 -05:00
Brues 88d0f7bf74 Use table.wipe to reset spell caches
Clear existing spell cache tables with table.wipe instead of creating new tables when LEARNED_SPELL_IN_TAB fires. This preserves existing table references (spellmaxrank, spellindex, spellinfo), avoiding stale references and potential bugs while being slightly more efficient. Change applied in libs/libspell.lua.
2026-07-20 21:35:31 -05:00
Brues a3ff20782d Use table.wipe and table.insert for tables
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.
2026-07-20 21:16:12 -05:00
Brues 88c2462fbd CLASS_SORT_ORDER is already defined by !!!ClassicAPI 2026-07-20 21:15:25 -05:00
Brues e28a7e5606 Optimize isempty function using next() 2026-07-20 18:46:09 -05:00
Brues dfa74c5756 Make the bag sort family-aware and locale-independent
Two fixes to libbagsort's planning stage.

Family-aware placement: the sort treated every bag in the list as
interchangeable storage, so with a quiver / soul / profession bag in the
set it would try to swap a general item into a slot that can't hold it.
The client rejects that swap, but the grid was updated as if it
succeeded, desyncing the plan and corrupting the sort from there on. Now
each destination slot is tagged with its bag's family (via ClassicAPI's
GetItemFamily, which reports bag families reliably) and every item is
routed to a cell that accepts it -- matching specialty bag first,
overflowing to general -- so nothing lands where it can't go and family
items consolidate into their bags for free.

Locale-independent categories: SortCategoryPrefix compared the localized
itemType/quality strings from GetItemInfo, which silently miscategorize
everything on a non-enUS client. Switch to the numeric classID/quality
via C_Item.GetItemInfo and ClassicAPI's Enum.ItemClass / Enum.ItemQuality.
2026-07-20 17:46:37 -05:00
Brues 1c0c9dc019 Replace librange's position scan with ClassicAPI's UnitInRange
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.
2026-07-20 16:54:27 -05:00
Brues 801935130e Update GetItemLinkByName to use C_Item API
Replace vanilla GetItemInfo and manual link construction with C_Item.GetItemNameByID and C_Item.GetItemInfo
2026-07-20 15:11:50 -05:00
Brues 8cdced5ec0 Simplify modf function
Delegate to ClassicAPI
2026-07-20 15:10:31 -05:00
Brues 756e8840af Keep the rangecheck scanner alive across zones and roster changes
PLAYER_LEAVING_WORLD fires on every loading screen, not just logout, but
librange treated it as terminal: it latched librange_isLoggingOut and
tore OnUpdate off the frame, neither of which was ever restored. So the
first zone (into a BG, dungeon, etc.) permanently killed the distance
scan -- unitdata stopped updating and UnitInSpellRange defaulted every
unit to in-range until /reload, which the next loading screen then undid.
Only PLAYER_LOGOUT is terminal now; PLAYER_LEAVING_WORLD just hides for
the loading screen and PLAYER_ENTERING_WORLD re-shows it.

Also invalidate on RAID_ROSTER_UPDATE / PARTY_MEMBERS_CHANGED: the range
state is cached per unit token, so a roster re-index leaves each unitN
mapped to a different player with stale data. Clear the token cache and
restart the sweep so shifted/joined slots are re-evaluated within one
pass instead of inheriting the previous occupant's range.
2026-07-20 14:12:07 -05:00
Brues b2db091d54 bump min ClassicAPI version 2026-07-20 02:42:34 -05:00
Brues 5fae0bd459 Add priest shadowform auto-paging, sharing the druid-stealth flag
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.
2026-07-20 02:40:37 -05:00
Brues 3deddf2b08 Drive questitem off QUEST_ACCEPTED/QUEST_REMOVED instead of rescanning
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.
2026-07-19 02:25:23 -05:00
Brues 650bcda001 Allocate newitem glow lazily, only for slots that go new
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.
2026-07-17 16:04:03 -05:00
Brues 931fe6c22d Harden the ClassicAPI-missing path so the disable notice can show
pfUI's TOC depends on the !!!ClassicAPI addon, so a nil CLASSIC_API_VERSION
means the addon loaded but the DLL isn't present -- yet the graceful-disable
branch itself called ClassicAPI APIs (EventUtil.ContinueOnPlayerLogin, and
Mixin/CallbackRegistryMixin for pfUI.events), so it crashed instead of
informing the user.

- Defer the popup with a bare PLAYER_ENTERING_WORLD frame (stock 1.12)
  instead of EventUtil/IsLoggedIn/PLAYER_LOGIN (ClassicAPI-era).
- Read the editbox via getglobal rather than _G.
- Guard pfUI.events creation behind `not pfUI.disabled`.
- Reword the notice: since the addon is present but the DLL isn't (or is
  outdated), tell the user the addon ships bundled with the DLL and to
  delete the !!!ClassicAPI folder and (re)install the latest release.
2026-07-17 15:12:30 -05:00
Brues bd4a40b0f2 Add newitem module: highlight freshly-acquired bag items
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.
2026-07-17 14:47:22 -05:00
Brues 31ff2773b3 Defer macrotweak conflicts; use SetSize
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.
2026-07-15 13:45:06 -05:00
Brues 8176b906df Re-anchor tradeskill merge bar to real craft starts
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.
2026-07-13 12:34:45 -05:00
Brues 721ecce59d Remove RegisterNewModule call for loothistory 2026-07-13 00:32:21 -05:00
Brues 73b409fb88 Route slash registration through pfUI.api.RegisterSlashCommand
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.
2026-07-13 00:06:20 -05:00
Brues 0665764610 cleanup 2026-07-12 15:22:57 -05:00
Brues 3faf06141f Skin Arena Frame 2026-07-12 14:49:17 -05:00
Brues 0c06401ec4 Simplify GUI toggle logic 2026-07-12 14:14:38 -05:00
Brues c68e48111d Remove libtooltip 2026-07-12 14:01:25 -05:00
Brues 6936fd894b Replace unusable tooltip scan with C_PlayerInfo.CanUseItem
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.
2026-07-12 13:48:36 -05:00
Brues b5277ea457 These modules aren't new anymore 2026-07-11 16:04:58 -05:00
Brues 63f0dcbf7a Use GetInventoryItemID instead of parsing link 2026-07-11 13:18:48 -05:00
Brues 0344bff471 cleanup 2026-07-11 12:59:33 -05:00
Brues 1510969105 Unused strings 2026-07-11 12:57:07 -05:00
Brues 6e7361c543 Removed redundant compare.basestats setting
Just disable the module if you don't want stat comparison
2026-07-11 12:43:15 -05:00
Brues 2e20a03cbd Restore Inspect UI frame skin
removed during the tbc purge
2026-07-11 12:36:03 -05:00
Brues 7389c241a4 using GetSpellInfo with just a spell id is dangerous
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
2026-07-11 09:47:46 -05:00
Brues fb5230828c Add Loot History module
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).
2026-07-11 00:02:31 -05:00
Brues 7aee348a70 Bump min version to 1.6.4 2026-07-10 20:27:16 -05:00
Brues 2a5f480839 Show addon dependencies in tooltip
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.
2026-07-10 20:26:05 -05:00
Brues 23d1ab840c Show sell value in tooltip when merchant hidden
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.
2026-07-10 13:23:52 -05:00
Brues 13a08b0ea3 Restore hooksecurefunc 2026-07-09 23:01:52 -05:00
Brues a2177fbf49 Restore HookScript 2026-07-09 22:55:26 -05:00
Brues 9cd83e90ad Added GetNoNameObject debugging 2026-07-09 22:31:58 -05:00
Brues 9bcc11e64f removed tbc logic 2026-07-09 22:26:32 -05:00
112 changed files with 3105 additions and 3215 deletions
+16 -14
View File
@@ -2,11 +2,11 @@
[![Octo WoW](https://img.shields.io/badge/Octo%20WoW-1.18.1-brightgreen.svg)](https://octowow.st/)
[![ClassicAPI](https://img.shields.io/badge/ClassicAPI-Required-purple.svg)](https://github.com/brues-code/ClassicAPI)
[![SuperWoW](https://img.shields.io/badge/SuperWoW-Required-purple.svg)](https://github.com/balakethelock/SuperWoW)
[![Nampower](https://img.shields.io/badge/Nampower-Required-purple.svg)](https://github.com/brues-code/nampower)
[![UnitXP](https://img.shields.io/badge/UnitXP__SP3-Optional-yellow.svg)](https://codeberg.org/konaka/UnitXP_SP3)
[![SuperWoW](https://img.shields.io/badge/SuperWoW-Optional-yellow.svg)](https://github.com/balakethelock/SuperWoW)
[![UnitXP](https://img.shields.io/badge/UnitXP__SP3-Optional-yellow.svg)](https://github.com/brues-code/UnitXP_SP3)
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/) which requires Nampower and SuperWoW with optional UnitXP_SP3 DLL integration.**
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/)**
This version includes significant performance improvements and DLL-enhanced features.
@@ -16,7 +16,7 @@ This version includes significant performance improvements and DLL-enhanced feat
4. Copy "pfUI" into Wow-Directory\Interface\AddOns
5. Restart Wow
## Optional DLL Enhancements
## DLL Enhancements
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality. These DLLs are permitted on Octo WoW:
@@ -36,23 +36,24 @@ Provides:
- Faster, safer profile sharing - Engine-side serialize/compress/base64 via C_EncodingUtil
- C_Timer - After / NewTicker replacing hand-rolled OnUpdate throttles
- Feign death, shapeshift and quest-item detection via real API calls
- Mouseover Unit Frames
- Click-casting
- Loot Roll History (/loothistory)
- New item highlighting
- Plenty other functions
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
Provides:
- UnitPosition for distance calculations
- SetMouseoverUnit for improved targeting
### [Nampower](https://github.com/brues-code/nampower)
Provides:
- Spell queue indicator
- GCD indicator
- Reactive spell detection
- Enhanced cast information
### [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3)
### [SuperWoW](https://github.com/balakethelock/SuperWoW)
Provides:
- Tracks party/raid units on the minimap
### [UnitXP_SP3](https://github.com/brues-code/UnitXP_SP3)
Provides:
- Line of Sight detection
@@ -79,6 +80,7 @@ Use `/pfdll` in-game to check which DLLs are detected.
/swapfocus Toggle Focus and Target-Frame
/pftest Toggle pfUI Unitframe Test Mode
/abp Addon Button Panel
/loothistory Show Loot Roll History
## Languages
pfUI supports and contains language specific code for the following gameclients.
@@ -91,7 +93,7 @@ pfUI supports and contains language specific code for the following gameclients.
* Russian (ruRU)
## Recommended Addons
* [pfQuest](https://shagu.org/pfQuest) A simple database and quest helper
* [pfQuest](https://github.com/brues-code/pfQuest) A simple database and quest helper
* [SuperCleveRoidMacros](https://github.com/brues-code/SuperCleveRoidMacros) Supports modern macro formats
## Plugins
+108 -57
View File
@@ -64,6 +64,10 @@ end
-- Requires UnitXP_SP3
function pfUI.api.UnitInLineOfSight(unit1, unit2)
if not pfUI.api.HasUnitXP() then return nil end
if not unit2 then
unit2 = unit1
unit1 = "player"
end
local success, inSight = pcall(UnitXP, "inSight", unit1, unit2)
if success then return inSight end
return nil
@@ -74,6 +78,10 @@ end
-- Requires UnitXP_SP3
function pfUI.api.UnitIsBehind(unit1, unit2)
if not pfUI.api.HasUnitXP() then return nil end
if not unit2 then
unit2 = unit1
unit1 = "player"
end
local success, behind = pcall(UnitXP, "behind", unit1, unit2)
if success then return behind end
return nil
@@ -84,16 +92,24 @@ gfind = string.gmatch or string.gfind
mod = math.mod or mod
-- [ strsplit ]
-- Splits a string using a delimiter.
-- Splits a string using a delimiter. Self-contained on purpose: it does NOT
-- delegate to the global strsplit / string.split, because third-party addons
-- clobber those (e.g. BigWigs' SpellRequests redefines string:split to return
-- a table, which would make this return a single table instead of r,g,b,a and
-- break color/version parsing depending on load order). Delimiter chars are
-- treated as a set (any one char splits), and empty fields are preserved
-- ("a,,b" -> "a", "", "b"), matching real strsplit semantics.
-- 'delimiter' [string] characters that will be interpreted as delimiter
-- characters (bytes) in the string.
-- 'subject' [string] String to split.
-- return: [list] a list of strings.
local format, sgsub = string.format, string.gsub
function pfUI.api.strsplit(delimiter, subject)
if not subject then return nil end
local delimiter, fields = delimiter or ":", {}
local pattern = string.format("([^%s]+)", delimiter)
string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
local fields = {}
delimiter = delimiter or ":"
local pattern = format("([^%s]+)", delimiter)
sgsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end)
return unpack(fields)
end
@@ -102,11 +118,7 @@ end
-- 'tbl' [table] the table that shall be checked
-- return: [boolean] result of the check.
function pfUI.api.isempty(tbl)
if not tbl then return true end
for k, v in pairs(tbl) do
return false
end
return true
return next(tbl or {}) == nil
end
-- [ checkversion ]
@@ -135,7 +147,6 @@ end
-- It takes care of the rangecheck module if existing.
-- unit [string] A unit to query (string, unitID)
-- return: [bool] "1" if in range otherwise "nil"
local RangeCache = {}
function pfUI.api.UnitInRange(unit)
if not UnitExists(unit) or not UnitIsVisible(unit) then
return nil
@@ -143,6 +154,11 @@ function pfUI.api.UnitInRange(unit)
return 1
end
-- master switch: with the 40y check off, a visible unit beyond interact
-- range counts as in range (nothing fades). Invisible units already
-- returned nil above, matching the pre-collapse behavior.
if C.unitframes.rangecheck == "0" then return 1 end
-- UnitXP precise mode: skip librange entirely, use direct distance check
if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then
local threshold = tonumber(C.unitframes.rangecheck_distance) or 40
@@ -215,7 +231,7 @@ end
-- unit [string] the unitstring
-- return: [table] string, r, g, b
function pfUI.api.GetUnitColor(unitstr)
local _, class = UnitClass(unitstr)
local class = UnitClassBase(unitstr)
local classColor = PFUI_CLASS_COLORS[class]
return classColor:GenerateHexColorMarkup(), classColor:GetRGB()
end
@@ -266,11 +282,7 @@ end
-- 'f' [float] the number to breakdown.
-- returns: [int],[float] whole and fractional part.
function pfUI.api.modf(f)
if modf then return modf(f) end
if f > 0 then
return math.floor(f), mod(f,1)
end
return math.ceil(f), mod(f,1)
return math.modf(f)
end
-- [ GetServerEpoch ]
@@ -365,22 +377,20 @@ end
-- 'str' [string] input string that should be matched
-- 'pat' [string] unformatted pattern
-- returns: [strings] matched string in capture order
local a, b, c, d, e
local _, va, vb, vc, vd, ve
local ra, rb, rc, rd, re
function pfUI.api.cmatch(str, pat)
-- read capture indexes
a, b, c, d, e = GetCaptures(pat)
_, _, va, vb, vc, vd, ve = string.find(str, pfUI.api.SanitizePattern(pat))
-- idx* = the logical %N index of each physical capture slot (nil when the
-- pattern has no %N$ markers); val* = the matched values in physical order.
local idx1, idx2, idx3, idx4, idx5 = GetCaptures(pat)
local _, _, val1, val2, val3, val4, val5 = string.find(str, pfUI.api.SanitizePattern(pat))
-- put entries into the proper return values
ra = e == 1 and ve or d == 1 and vd or c == 1 and vc or b == 1 and vb or va
rb = e == 2 and ve or d == 2 and vd or c == 2 and vc or a == 2 and va or vb
rc = e == 3 and ve or d == 3 and vd or a == 3 and va or b == 3 and vb or vc
rd = e == 4 and ve or a == 4 and va or c == 4 and vc or b == 4 and vb or vd
re = a == 5 and va or d == 5 and vd or c == 5 and vc or b == 5 and vb or ve
-- reorder the physical matches into logical (%1..%5) order
local out1 = idx5 == 1 and val5 or idx4 == 1 and val4 or idx3 == 1 and val3 or idx2 == 1 and val2 or val1
local out2 = idx5 == 2 and val5 or idx4 == 2 and val4 or idx3 == 2 and val3 or idx1 == 2 and val1 or val2
local out3 = idx5 == 3 and val5 or idx4 == 3 and val4 or idx1 == 3 and val1 or idx2 == 3 and val2 or val3
local out4 = idx5 == 4 and val5 or idx1 == 4 and val1 or idx3 == 4 and val3 or idx2 == 4 and val2 or val4
local out5 = idx1 == 5 and val1 or idx4 == 5 and val4 or idx3 == 5 and val3 or idx2 == 5 and val2 or val5
return ra, rb, rc, rd, re
return out1, out2, out3, out4, out5
end
-- [ GetItemLinkByName ]
@@ -389,10 +399,10 @@ end
-- returns: [string] entire itemLink for the given item
function pfUI.api.GetItemLinkByName(name)
for itemID = 1, 25818 do
local itemName, hyperLink, itemQuality = GetItemInfo(itemID)
if (itemName and itemName == name) then
local _, _, _, hex = GetItemQualityColor(tonumber(itemQuality))
return hex.. "|H"..hyperLink.."|h["..itemName.."]|h|r"
local itemName = C_Item.GetItemNameByID(itemID)
if itemName and itemName == name then
local _, itemLink = C_Item.GetItemInfo(itemID)
return itemLink
end
end
end
@@ -542,6 +552,21 @@ do -- create a scope so we don't have to worry about upvalue collisions
end
end
-- [ HookScript ]
-- Securely post-hooks a script handler.
-- 'f' [frame] the frame which needs a hook
-- 'script' [string] the handler to hook
-- 'func' [function] the function that should be added
function HookScript(f, script, func)
f:HookScript(script, func)
end
function hooksecurefunc(tbl, name, func)
if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
if not tbl or type(tbl[name]) ~= "function" then return end
return _G.hooksecurefunc(tbl, name, func)
end
-- [ HookAddonOrVariable ]
-- Sets a function to be called automatically once an addon gets loaded
-- 'addon' [string] addon or variable name
@@ -704,24 +729,15 @@ function pfUI.api.CopyTable(src)
end
-- [ Wipe Table ]
-- Empties a table and returns it
-- Empties a table and returns it.
-- 'src' [table] the table that should be emptied.
-- return: [table] the emptied table.
-- Delegates to ClassicAPI's table.wipe, which also resets the Lua 5.0 getn
-- length (luaL_setn(t,0)) so table.insert on a wiped table resumes at [1].
-- Append to wiped arrays with table.insert -- NOT the t[table.getn(t)+1]=v
-- idiom, which needs an unmanaged length counter and won't work here.
function pfUI.api.wipe(src)
-- notes: table.insert, table.remove will have undefined behavior
-- when used on tables emptied this way because Lua removes nil
-- entries from tables after an indeterminate time.
-- Instead of table.insert(t,v) use t[table.getn(t)+1]=v as table.getn collapses nil entries.
-- There are no issues with hash tables, t[k]=v where k is not a number behaves as expected.
local mt = getmetatable(src) or {}
if mt.__mode == nil or mt.__mode ~= "kv" then
mt.__mode = "kv"
src=setmetatable(src,mt)
end
for k in pairs(src) do
src[k] = nil
end
return src
return table.wipe(src)
end
-- [ Load Movable ]
@@ -979,16 +995,34 @@ end
-- [ GetStringColor ]
-- Queries the pfUI setting strings and extract its color codes
-- returns r,g,b,a
local color_cache = {}
function pfUI.api.GetStringColor(colorstr)
if not color_cache[colorstr] then
local r, g, b, a = pfUI.api.strsplit(",", colorstr)
color_cache[colorstr] = { r, g, b, a }
-- returns r,g,b,a as strings
local color_cache = setmetatable({}, {
__index = function(t, k)
local color = { pfUI.api.strsplit(",", k) }
rawset(t, k, color)
return color
end
})
function pfUI.api.GetStringColor(colorstr)
return unpack(color_cache[colorstr])
end
-- [ GetStringColorObject ]
-- Like GetStringColor, but returns a cached ColorMixin instead of raw values.
-- The object is a shared per-string singleton, so treat it as read-only.
-- returns a ColorMixin
local color_object_cache = setmetatable({}, {
__index = function(t, k)
local r,g,b,a = pfUI.api.GetStringColor(k)
local color = CreateColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
rawset(t, k, color)
return color
end
})
function pfUI.api.GetStringColorObject(colorstr)
return color_object_cache[colorstr]
end
-- [ rgbhex ]
-- Returns color format from color info
-- 'r' [table | number] color table or r color component
@@ -996,12 +1030,13 @@ end
-- 'b' [number] optional b color component
-- 'a' [number] optional alpha component
-- returns color string in the form of '|caarrggbb'
local _r, _g, _b, _a
local rgbhex_cache = {}
function pfUI.api.rgbhex(r, g, b, a)
local _r, _g, _b, _a
if type(r) == "table" then
if r.r then
_r, _g, _b, _a = r.r, r.g, r.b, (r.a or 1)
elseif table.getn(r) >= 3 then
elseif r[3] ~= nil then
_r, _g, _b, _a = r[1], r[2], r[3], (r[4] or 1)
end
elseif tonumber(r) then
@@ -1009,7 +1044,13 @@ function pfUI.api.rgbhex(r, g, b, a)
end
if _r and _g and _b and _a then
return CreateColor(_r, _g, _b, _a):GenerateHexColorMarkup()
local key = ((Round(_r*255)*256 + Round(_g*255))*256 + Round(_b*255))*256 + Round(_a*255)
local hex = rgbhex_cache[key]
if not hex then
hex = "|c" .. C_ColorUtil.GenerateTextColorCode({ r = _r, g = _g, b = _b, a = _a })
rgbhex_cache[key] = hex
end
return hex
end
return ""
@@ -1562,6 +1603,16 @@ end
-- 'arg1' [string]
-- return object
function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2)
-- A nil/non-frame parent otherwise dies on frame:GetRegions()/:GetChildren()
-- below, and the traceback stops here — useless, since all callers share this
-- line. pfUI shadows Lua's `error` (dropping the level arg and not throwing),
-- so fold the caller frame in via debugstack to name the offending skin, then
-- bail so we don't fall through and crash on frame:GetRegions() anyway.
if type(frame) ~= "table" or not frame.GetRegions then
error("GetNoNameObject: invalid parent frame\n" .. debugstack(2, 3, 0))
return
end
local arg1 = arg1 and gsub(arg1, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
local arg2 = arg2 and gsub(arg2, "([%+%-%*%(%)%?%[%]%^])", "%%%1")
+33 -9
View File
@@ -155,6 +155,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "cd", "dynamicsize", "1")
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("appearance", "infight", "screen", "0")
pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
@@ -162,6 +163,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "infight", "intensity", "16")
pfUI:UpdateConfig("appearance", "bags", "unusable", "1")
pfUI:UpdateConfig("appearance", "bags", "unusable_color", ".9,.2,.2,1")
pfUI:UpdateConfig("appearance", "bags", "newitem", "1")
pfUI:UpdateConfig("appearance", "bags", "newitem_color", "1,1,1,1")
pfUI:UpdateConfig("appearance", "bags", "borderlimit", "1")
pfUI:UpdateConfig("appearance", "bags", "borderonlygear", "0")
pfUI:UpdateConfig("appearance", "bags", "fulltext", "1")
@@ -172,6 +175,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "bags", "bagrowlength", "10")
pfUI:UpdateConfig("appearance", "bags", "bankrowlength", "10")
pfUI:UpdateConfig("appearance", "bags", "autoSortOnOpen", "0")
pfUI:UpdateConfig("appearance", "bags", "sortreverse", "0")
pfUI:UpdateConfig("appearance", "bags", "sortprioreverse", "0")
pfUI:UpdateConfig("appearance", "minimap", "size", "140")
pfUI:UpdateConfig("appearance", "minimap", "arrowscale", "1")
pfUI:UpdateConfig("appearance", "minimap", "zonetext", "off")
@@ -196,6 +201,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("loot", nil, "rollannounce", "0")
pfUI:UpdateConfig("loot", nil, "raritytimer", "1")
pfUI:UpdateConfig("loothistory", nil, "autoshow", "0")
pfUI:UpdateConfig("unitframes", nil, "disable", "0")
pfUI:UpdateConfig("unitframes", nil, "pastel", "1")
pfUI:UpdateConfig("unitframes", nil, "custom", "0")
@@ -227,8 +234,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", nil, "druidmanaoffy", "0")
pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3")
pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar")
pfUI:UpdateConfig("unitframes", nil, "druidmanatext", "1")
pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4")
pfUI:UpdateConfig("unitframes", nil, "combowidth", "6")
pfUI:UpdateConfig("unitframes", nil, "comboheight", "6")
pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200")
@@ -379,6 +386,27 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "grouppet", "glowcombat", "0")
pfUI:UpdateConfig("unitframes", "grouppet", "txthpright", "healthperc")
pfUI:UpdateConfig("unitframes", "raidpet", "portrait", "off")
pfUI:UpdateConfig("unitframes", "raidpet", "width", "50")
pfUI:UpdateConfig("unitframes", "raidpet", "height", "14")
pfUI:UpdateConfig("unitframes", "raidpet", "pheight", "0")
pfUI:UpdateConfig("unitframes", "raidpet", "buffs", "off")
pfUI:UpdateConfig("unitframes", "raidpet", "buffsize", "16")
pfUI:UpdateConfig("unitframes", "raidpet", "debuffs", "off")
pfUI:UpdateConfig("unitframes", "raidpet", "debuffsize", "16")
pfUI:UpdateConfig("unitframes", "raidpet", "faderange", "1")
pfUI:UpdateConfig("unitframes", "raidpet", "glowcombat", "0")
pfUI:UpdateConfig("unitframes", "raidpet", "txthpright", "healthperc")
-- off by default; mirrors the raid grid layout when enabled
pfUI:UpdateConfig("unitframes", "raidpet", "visible", "0")
-- collapse: pack only pets that exist (from the roster snapshot) instead
-- of mirroring every raid slot
pfUI:UpdateConfig("unitframes", "raidpet", "collapse", "1")
-- pet block has its own layout, independent of the raid grid
pfUI:UpdateConfig("unitframes", "raidpet", "raidlayout", "8x5")
pfUI:UpdateConfig("unitframes", "raidpet", "raidpadding", "3")
pfUI:UpdateConfig("unitframes", "raidpet", "raidfill", "VERTICAL")
pfUI:UpdateConfig("unitframes", "raid", "portrait", "off")
pfUI:UpdateConfig("unitframes", "raid", "width", "50")
pfUI:UpdateConfig("unitframes", "raid", "height", "26")
@@ -397,6 +425,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "raid", "raidlayout", "8x5")
pfUI:UpdateConfig("unitframes", "raid", "raidpadding", "3")
pfUI:UpdateConfig("unitframes", "raid", "raidfill", "VERTICAL")
pfUI:UpdateConfig("unitframes", "raid", "collapse", "0")
pfUI:UpdateConfig("unitframes", "raid", "raidgrouplabel", "0")
pfUI:UpdateConfig("unitframes", "raid", "grouplabelxoff", "0")
pfUI:UpdateConfig("unitframes", "raid", "grouplabelyoff", "8")
@@ -452,7 +481,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "ptarget", "txthpright", "none")
pfUI:UpdateConfig("unitframes", "ptarget", "overhealperc", "10")
local ufs = { "player", "target", "focus", "focustarget", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback", "tttarget" }
local ufs = { "player", "target", "focus", "focustarget", "group", "grouptarget", "grouppet", "raid", "raidpet", "ttarget", "pet", "ptarget", "fallback", "tttarget" }
for _, unit in pairs(ufs) do
pfUI:UpdateConfig("unitframes", unit, "selfdebuff", "0")
pfUI:UpdateConfig("unitframes", unit, "visible", "1")
@@ -624,6 +653,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "pagemastershift", "0")
pfUI:UpdateConfig("bars", nil, "pagemasterctrl", "0")
pfUI:UpdateConfig("bars", nil, "druidstealth", "0")
pfUI:UpdateConfig("bars", nil, "priestshadow", "0")
pfUI:UpdateConfig("bars", nil, "showcastable", "1")
pfUI:UpdateConfig("bars", nil, "glowrange", "1")
pfUI:UpdateConfig("bars", nil, "rangecolor", "1,0.1,0.1,1")
@@ -755,6 +785,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
pfUI:UpdateConfig("tooltip", nil, "itemid", "0")
pfUI:UpdateConfig("tooltip", nil, "movespeed", "0")
pfUI:UpdateConfig("tooltip", nil, "aurasource", "0")
pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8")
pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0")
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
@@ -1105,13 +1136,6 @@ function pfUI:MigrateConfig()
end
end
-- migrating rangecheck interval (> 3.2.2)
if checkversion(3, 2, 2) then
if tonumber(pfUI_config.unitframes.rangechecki) <= 1 then
pfUI_config.unitframes.rangechecki = "2"
end
end
-- migrating legacy buff/debuff naming (> 3.5.0)
if checkversion(3, 5, 0) then
local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" }
+270 -59
View File
@@ -21,8 +21,7 @@ do -- statusbars
local handlers = {
["DisplayValue"] = function(self, val)
val = val > self.max and self.max or val
val = val < self.min and self.min or val
val = Clamp(val, self.min, self.max)
-- remove animation queue
if val == self.val_ then
@@ -38,8 +37,7 @@ do -- statusbars
point = height / (self.max - self.min) * (val - self.min)
-- keep values in limits
point = math.min(height, point)
point = math.max(0, point)
point = Clamp(point, 0, height)
-- set point to zero if value and max is zero
if val == 0 then point = 0 end
@@ -57,8 +55,7 @@ do -- statusbars
point = width / (self.max - self.min) * (val - self.min)
-- keep values in limits
point = math.min(width, point)
point = math.max(0, point)
point = Clamp(point, 0, width)
-- set point to zero if value and max is zero
if val == 0 then point = 0 end
@@ -138,15 +135,8 @@ do -- statusbars
end
do -- dropdown
local _, class = UnitClass("player")
local color = PFUI_CLASS_COLORS[class]
local function ListEntryOnShow()
if this.parent.id == this.id then
this.icon:Show()
else
this.icon:Hide()
end
this.icon:SetShown(this.parent.id == this.id)
end
local function ListEntryOnClick()
@@ -288,8 +278,7 @@ do -- dropdown
frame.icon = frame:CreateTexture(nil, "OVERLAY")
frame.icon:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
frame.icon:SetHeight(16)
frame.icon:SetWidth(16)
frame.icon:SetSize(16, 16)
frame.icon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
frame.text = frame:CreateFontString(nil, "OVERLAY")
@@ -329,8 +318,7 @@ do -- dropdown
local button = CreateFrame("Button", nil, frame)
button:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
button:SetWidth(16)
button:SetHeight(16)
button:SetSize(16, 16)
button:SetScript("OnClick", ListButtonOnClick)
SkinArrowButton(button, "down")
button.icon:SetVertexColor(1,.9,.1)
@@ -380,8 +368,7 @@ function pfUI.api.CreateTabChild(self, title, bwidth, bheight, bottom, static)
end
-- set dimensions
b:SetHeight(button_height)
b:SetWidth(button_width)
b:SetSize(button_width, button_height)
b:SetID(childcount)
if not self.align or self.align == "LEFT" then
@@ -516,13 +503,7 @@ function pfUI.api.CreateScrollFrame(name, parent)
local max = f:GetVerticalScrollRange()
local new = current - step
if new >= max then
f:SetVerticalScroll(max)
elseif new <= 0 then
f:SetVerticalScroll(0)
else
f:SetVerticalScroll(new)
end
f:SetVerticalScroll(Clamp(new, 0, max))
f:UpdateScrollState()
end
@@ -539,8 +520,7 @@ function pfUI.api.CreateScrollChild(name, parent)
local f = CreateFrame("Frame", name, parent)
-- dummy values required
f:SetWidth(1)
f:SetHeight(1)
f:SetSize(1, 1)
f:SetAllPoints(parent)
parent:SetScrollChild(f)
@@ -607,8 +587,7 @@ end
function pfUI.api.SetHighlight(frame, cr, cg, cb)
if not frame then return end
if not cr or not cg or not cb then
local _, class = UnitClass("player")
cr, cg, cb = GetClassColor(class)
cr, cg, cb = GetClassColor(UnitClassBase('player'))
end
frame.cr, frame.cg, frame.cb = cr, cg, cb, ca
@@ -653,8 +632,7 @@ function pfUI.api.SkinButton(button, cr, cg, cb, icon, disableHighlight)
if not b then b = button end
if not b then return end
if not cr or not cg or not cb then
local _, class = UnitClass("player")
cr, cg, cb = GetClassColor(class)
cr, cg, cb = GetClassColor(UnitClassBase('player'))
end
pfUI.api.CreateBackdrop(b, nil, true)
b:SetNormalTexture("")
@@ -702,8 +680,7 @@ function pfUI.api.SkinCollapseButton(button, all)
b.icon = _G[name] or CreateFrame("Button", name, b)
if all then size = 14 end
b.icon:SetWidth(size)
b.icon:SetHeight(size)
b.icon:SetSize(size, size)
b.icon:SetPoint("LEFT", 2, 2)
CreateBackdrop(b.icon)
b.icon.text = b.icon:CreateFontString(nil, "OVERLAY")
@@ -732,12 +709,10 @@ end
function pfUI.api.SkinRotateButton(button)
pfUI.api.CreateBackdrop(button)
local _, class = UnitClass("player")
local color = PFUI_CLASS_COLORS[class]
local cr, cg, cb = color.r , color.g, color.b
local cr, cg, cb = GetClassColor(UnitClassBase('player'))
button:SetWidth(button:GetWidth() - 18)
button:SetHeight(button:GetHeight() - 18)
local btnW, btnH = button:GetSize()
button:SetSize(btnW - 18, btnH - 18)
button:GetNormalTexture():SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)
button:GetPushedTexture():SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)
@@ -759,8 +734,7 @@ function pfUI.api.SkinCloseButton(button, parentFrame, offsetX, offsetY)
SkinButton(button, 1, .25, .25)
button:SetWidth(15)
button:SetHeight(15)
button:SetSize(15, 15)
if parentFrame then
button:ClearAllPoints()
@@ -787,8 +761,7 @@ function pfUI.api.SkinArrowButton(button, dir, size)
button:SetDisabledTexture(nil)
if size then
button:SetWidth(size)
button:SetHeight(size)
button:SetSize(size, size)
end
if not button.icon then
@@ -894,8 +867,7 @@ function pfUI.api.SkinCheckbox(frame, size)
frame:SetPushedTexture("")
frame:SetHighlightTexture("")
if size then
frame:SetWidth(size)
frame:SetHeight(size)
frame:SetSize(size, size)
end
CreateBackdrop(frame)
SetAllPointsOffset(frame.backdrop, frame, 4)
@@ -923,8 +895,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
CreateBackdrop(button)
button.backdrop:ClearAllPoints()
button.backdrop:SetWidth(18)
button.backdrop:SetHeight(18)
button.backdrop:SetSize(18, 18)
button.backdrop:SetPoint("RIGHT", frame.backdrop, "RIGHT", -2, 0)
if not button.icon then
@@ -936,9 +907,7 @@ function pfUI.api.SkinDropDown(frame, cr, cg, cb, useSmall)
end
if not cr or not cg or not cb then
local _, class = UnitClass("player")
local color = PFUI_CLASS_COLORS[class]
cr, cg, cb = color.r , color.g, color.b
cr, cg, cb = GetClassColor(UnitClassBase('player'))
end
SetHighlight(button, cr, cg, cb)
@@ -1108,8 +1077,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
-- buttons
question.yes = CreateFrame("Button", "pfQuestionDialogYes", question, "UIPanelButtonTemplate")
pfUI.api.SkinButton(question.yes)
question.yes:SetWidth(100)
question.yes:SetHeight(22)
question.yes:SetSize(100, 22)
question.yes:SetText(yescap)
question.yes:SetScript("OnClick", function()
if yes then yes() end
@@ -1124,8 +1092,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
question.no = CreateFrame("Button", "pfQuestionDialogNo", question, "UIPanelButtonTemplate")
pfUI.api.SkinButton(question.no)
question.no:SetWidth(100)
question.no:SetHeight(22)
question.no:SetSize(100, 22)
question.no:SetText(nocap)
question.no:SetScript("OnClick", function()
if no then no() end
@@ -1141,8 +1108,7 @@ function pfUI.api.CreateQuestionDialog(text, yes, no, editbox, onclose)
question.close = CreateFrame("Button", "pfQuestionDialogClose", question)
question.close:SetPoint("TOPRIGHT", -border, -border)
pfUI.api.CreateBackdrop(question.close)
question.close:SetHeight(10)
question.close:SetWidth(10)
question.close:SetSize(10, 10)
question.close.texture = question.close:CreateTexture("pfQuestionDialogCloseTex")
question.close.texture:SetTexture(pfUI.media["img:close"])
question.close.texture:ClearAllPoints()
@@ -1244,9 +1210,8 @@ function pfUI.api.CreateInfoBox(text, time, parent, height)
infobox.duration = time
infobox.lastshow = GetTime()
infobox:SetWidth(infobox.text:GetStringWidth() + 50)
infobox:SetSize(infobox.text:GetStringWidth() + 50, height)
infobox:SetParent(parent)
infobox:SetHeight(height)
infobox:SetFrameStrata("FULLSCREEN_DIALOG")
infobox:Show()
@@ -1281,3 +1246,249 @@ function pfUI.api.SkinMoneyInputFrame(frame)
copperIcon:ClearAllPoints()
copperIcon:SetPoint("LEFT", copper_editbox, "RIGHT", 2, 0)
end
-- Shared icon picker widget. Builds a name field, a "currently selected"
-- preview, and a 10x8 icon grid backed by IconDataProviderMixin with an
-- All/Spells/Items filter and a name search. Used by the macro icon picker
-- and the equipment manager's name/icon popup. The widgets attach to
-- `parent`; the caller owns the surrounding frame, its OK/Cancel buttons,
-- and the save flow. Selection is tracked by texture PATH (not index) so it
-- survives filter changes -- a spell icon you picked still saves after you
-- switch the filter to "Items" and it drops out of the visible grid.
--
-- name global-name prefix for the scroll frame and editboxes
-- parent the popup frame to attach the widgets to
-- extraType IconDataProviderExtraType.* seed (Spellbook / Equipment)
-- nameLabelText header shown above the name field
--
-- Returns a handle:
-- .editbox name EditBox (caller wires enter/escape)
-- .search search EditBox
-- .GetIcon() current selected texture path
-- .SetIcon(path) set selection (nil -> question mark)
-- .Refresh() ensure provider, redraw grid + preview
-- .SetIconAreaShown(shown) show/hide the grid, filter, search, and labels
function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
local ICON_GRID_COLS = 10
local ICON_GRID_ROWS = 8
local ICON_BTN_SIZE = 36
local ICON_BTN_PAD = 6
local provider = nil
local selectedIconPath = QUESTION_MARK
local currentFilter = "all"
local searchText = ""
local filtered = nil -- provider indices matching the search, or nil when empty
local RefreshIconGrid, RebuildFilter
local function EnsureProvider()
if not provider then
provider = CreateAndInitFromMixin(IconDataProviderMixin, extraType)
end
end
-- Normalize a texture to an uppercase basename so matching works across
-- sources: GetMacroInfo / stored set icons return "Interface\Icons\<name>"
-- while provider paths are uppercase-prefixed for base icons and
-- mixed-case for spellbook extras.
local function IconKey(path)
if type(path) ~= "string" then return path end
return string.gsub(string.upper(path), "^.*[\\/]", "")
end
parent.nameLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.nameLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -20)
parent.nameLabel:SetText(nameLabelText)
parent.editbox = CreateFrame("EditBox", name.."Name", parent, "InputBoxTemplate")
parent.editbox:SetSize(280, 20)
parent.editbox:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -38)
parent.editbox:SetAutoFocus(false)
parent.editbox:SetMaxLetters(16)
CreateBackdrop(parent.editbox)
parent.selectedLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.selectedLabel:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -14)
parent.selectedLabel:SetText(ICON_SELECTION_TITLE_CURRENT)
parent.selectedLabel:SetTextColor(1, 0.82, 0)
parent.selectedPreview = CreateFrame("Frame", nil, parent)
parent.selectedPreview:SetSize(42, 42)
parent.selectedPreview:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -30)
CreateBackdrop(parent.selectedPreview)
parent.selectedPreview.tex = parent.selectedPreview:CreateTexture(nil, "ARTWORK")
parent.selectedPreview.tex:SetAllPoints(parent.selectedPreview)
parent.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
parent.iconLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.iconLabel:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -86)
parent.iconLabel:SetText(MACRO_POPUP_CHOOSE_ICON)
-- Icon grid + scroll
local iconScroll = CreateFrame("ScrollFrame", name.."Scroll", parent, "FauxScrollFrameTemplate")
iconScroll:SetPoint("TOPLEFT", parent, "TOPLEFT", 14, -116)
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
local scrollbar = _G[name.."ScrollScrollBar"]
scrollbar:ClearAllPoints()
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
SkinScrollbar(scrollbar)
-- Filter dropdown: All / Spells / Items
local filterDropdown = CreateFrame("Frame", name.."Filter", parent, "UIDropDownMenuTemplate")
filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -80)
local function ApplyFilter(value)
currentFilter = value
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
if provider then
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
else provider:SetIconTypes(nil) end
RebuildFilter()
RefreshIconGrid()
end
end
UIDropDownMenu_Initialize(filterDropdown, function()
local info
info = {}; info.text = ICON_FILTER_ALL; info.value = "all"
info.func = function() ApplyFilter("all") end
info.checked = currentFilter == "all"
UIDropDownMenu_AddButton(info)
info = {}; info.text = ICON_FILTER_SPELL; info.value = "spells"
info.func = function() ApplyFilter("spells") end
info.checked = currentFilter == "spells"
UIDropDownMenu_AddButton(info)
info = {}; info.text = ICON_FILTER_ITEM; info.value = "items"
info.func = function() ApplyFilter("items") end
info.checked = currentFilter == "items"
UIDropDownMenu_AddButton(info)
end)
UIDropDownMenu_SetWidth(120, filterDropdown)
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
SkinDropDown(filterDropdown)
local iconButtons = {}
for r = 1, ICON_GRID_ROWS do
for c = 1, ICON_GRID_COLS do
local i = (r - 1) * ICON_GRID_COLS + c
local btn = CreateFrame("Button", nil, parent)
btn:SetSize(ICON_BTN_SIZE, ICON_BTN_SIZE)
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
CreateBackdrop(btn)
btn.texture = btn:CreateTexture(nil, "ARTWORK")
btn.texture:SetAllPoints(btn)
btn.texture:SetTexCoord(.08, .92, .08, .92)
btn:SetScript("OnClick", function()
if this.iconIndex and provider then
local path = provider:GetIconByIndex(this.iconIndex)
if path then selectedIconPath = path end
RefreshIconGrid()
end
end)
btn:SetScript("OnEnter", function()
if not this.iconIndex or not provider then return end
local path = provider:GetIconByIndex(this.iconIndex)
if type(path) == "string" then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(IconKey(path), 1, 1, 1)
GameTooltip:Show()
end
end)
btn:SetScript("OnLeave", GameTooltip_Hide)
iconButtons[i] = btn
end
end
-- Rebuild the search-filtered index list (provider indices whose icon
-- basename contains the search text); nil when the search box is empty.
function RebuildFilter()
EnsureProvider()
if searchText == "" then
filtered = nil
return
end
filtered = {}
for i = 1, provider:GetNumIcons() do
local path = provider:GetIconByIndex(i)
if type(path) == "string" and string.find(IconKey(path), searchText, 1, true) then
table.insert(filtered, i)
end
end
end
function RefreshIconGrid()
EnsureProvider()
local numIcons = filtered and table.getn(filtered) or provider:GetNumIcons()
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
local offset = FauxScrollFrame_GetOffset(iconScroll)
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
local listIdx = i + offset * ICON_GRID_COLS
local btn = iconButtons[i]
if listIdx <= numIcons then
btn:Show()
local providerIdx = filtered and filtered[listIdx] or listIdx
btn.iconIndex = providerIdx
local path = provider:GetIconByIndex(providerIdx)
btn.texture:SetTexture(path)
if IconKey(path) == IconKey(selectedIconPath) then
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
else
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
end
else
btn:Hide()
btn.iconIndex = nil
end
end
parent.selectedPreview.tex:SetTexture(selectedIconPath)
end
iconScroll:SetScript("OnVerticalScroll", function()
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() RefreshIconGrid() end)
end)
-- Search box (bottom-left) filters the grid by icon name.
parent.searchLabel = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
parent.searchLabel:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 16, 18)
parent.searchLabel:SetText(SEARCH)
parent.search = CreateFrame("EditBox", name.."Search", parent, "InputBoxTemplate")
parent.search:SetSize(180, 20)
parent.search:SetPoint("LEFT", parent.searchLabel, "RIGHT", 10, 0)
parent.search:SetAutoFocus(false)
CreateBackdrop(parent.search)
parent.search:SetScript("OnEscapePressed", function() this:ClearFocus() end)
parent.search:SetScript("OnTextChanged", function()
searchText = strupper(strtrim(this:GetText()))
RebuildFilter()
scrollbar:SetValue(0)
RefreshIconGrid()
end)
-- Release the provider when the popup closes so its icon cache GCs.
parent:HookScript("OnHide", function()
if provider then provider:Release(); provider = nil end
end)
local handle = { editbox = parent.editbox, search = parent.search }
function handle.GetIcon() return selectedIconPath end
function handle.SetIcon(path) selectedIconPath = path or QUESTION_MARK end
function handle.Refresh() RefreshIconGrid() end
function handle.SetIconAreaShown(shown)
local method = shown and "Show" or "Hide"
iconScroll[method](iconScroll)
for _, b in ipairs(iconButtons) do b[method](b) end
parent.iconLabel[method](parent.iconLabel)
parent.selectedLabel[method](parent.selectedLabel)
parent.selectedPreview[method](parent.selectedPreview)
filterDropdown[method](filterDropdown)
parent.searchLabel[method](parent.searchLabel)
parent.search[method](parent.search)
end
return handle
end
+256 -655
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -25,29 +25,8 @@ PLAYER_BUFF_START_ID = -1
ACTIONBAR_SECURE_TEMPLATE_BAR = nil
ACTIONBAR_SECURE_TEMPLATE_BUTTON = nil
UNITFRAME_SECURE_TEMPLATE = nil
--[[ Vanilla API Extensions ]]--
-- Safe post-hook helper. The global `hooksecurefunc` belongs to ClassicAPI
-- (its C implementation); this wrapper only adds pfUI's missing-target guard:
-- ClassicAPI errors when target[name] isn't a function, whereas a lot of our
-- call sites hook optional/late-loaded frames and rely on a silent no-op.
-- Normalizes the string form, skips when the target is absent, then delegates
-- to the C version (uncapped args, callback-pcall, taint parity).
function pfUI.hooksecurefunc(tbl, name, func)
if type(tbl) == "string" then tbl, name, func = _G, tbl, name end
if not tbl or type(tbl[name]) ~= "function" then return end
return _G.hooksecurefunc(tbl, name, func)
end
do -- GetItemInfo
local name, link, rarity, minlevel, itype, isubtype, stack
function GetItemInfo(item)
if not item then return end
name, link, rarity, minlevel, itype, isubtype, stack = _G.GetItemInfo(item)
return name, link, rarity, nil, minlevel, itype, isubtype, stack
end
end
do -- RunMacroText
local obj = { ["GetText"] = function(self) return self.text end }
-17
View File
@@ -1,20 +1,3 @@
-- this table is added in later expansions
CLASS_SORT_ORDER = CLASS_SORT_ORDER or {
"WARRIOR",
"DEATHKNIGHT",
"PALADIN",
"MONK",
"PRIEST",
"SHAMAN",
"DRUID",
"ROGUE",
"MAGE",
"WARLOCK",
"HUNTER",
"DEMONHUNTER",
"UNKNOWN",
}
CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or {
["WARRIOR"] = {0, 0.25, 0, 0.25},
["MAGE"] = {0.25, 0.49609375, 0, 0.25},
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["deDE"] = {
["Always Show Item Comparison"] = nil,
["Always Show On Target Units"] = nil,
["Always Show On Units With Missing HP"] = nil,
["Always Show Self In Raid Frames"] = nil,
["Always Use 2D Portraits"] = nil,
["Ammo Counter"] = nil,
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["deDE"] = {
["Click Casting"] = nil,
["Clock"] = nil,
["Close"] = nil,
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = nil,
["Color Debuff Stacks"] = nil,
@@ -138,7 +138,6 @@ pfUI_translation["deDE"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
@@ -569,6 +568,7 @@ pfUI_translation["deDE"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = nil,
["Paging Actionbar"] = nil,
["Panel"] = nil,
@@ -616,12 +616,13 @@ pfUI_translation["deDE"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = nil,
["Raid-Pet"] = nil,
["Random"] = nil,
["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -703,6 +704,7 @@ pfUI_translation["deDE"] = {
["Show Description"] = nil,
["Show Dispel Indicators"] = nil,
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = nil,
["Show Empty Buttons"] = nil,
["Show FPS and Latency Colors"] = nil,
@@ -739,6 +741,7 @@ pfUI_translation["deDE"] = {
["Show Required Questitem Count"] = nil,
["Show Resting"] = nil,
["Show Self In Group Frames"] = nil,
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
@@ -783,6 +786,7 @@ pfUI_translation["deDE"] = {
["Target Castbar"] = nil,
["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil,
["Target-Target-Target"] = nil,
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["deDE"] = {
["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil,
+17 -4
View File
@@ -19,6 +19,7 @@ pfUI_translation["enUS"] = {
["Align Chat Windows"] = nil,
["Aligned Position"] = nil,
["All messages will be forwarded to:"] = nil,
["All players passed"] = nil,
["Alt-Click Action"] = nil,
["Always Allow Drag Via Shift Key"] = nil,
["Always Show"] = nil,
@@ -27,7 +28,6 @@ pfUI_translation["enUS"] = {
["Always Show Item Comparison"] = nil,
["Always Show On Target Units"] = nil,
["Always Show On Units With Missing HP"] = nil,
["Always Show Self In Raid Frames"] = nil,
["Always Use 2D Portraits"] = nil,
["Ammo Counter"] = nil,
["Anchor Bags Above Chat"] = nil,
@@ -119,11 +119,13 @@ pfUI_translation["enUS"] = {
["Chat Bubble Transparency"] = nil,
["Chat Default Brackets"] = nil,
["Class"] = nil,
["Clear"] = nil,
["Clear Rolls"] = nil,
["Click Action"] = nil,
["Click Casting"] = nil,
["Clock"] = nil,
["Close"] = nil,
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = nil,
["Color Debuff Stacks"] = nil,
@@ -138,7 +140,6 @@ pfUI_translation["enUS"] = {
["Combat Timer"] = nil,
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = nil,
@@ -195,6 +196,7 @@ pfUI_translation["enUS"] = {
["Deficit"] = nil,
["Delete profile"] = nil,
["Delete / Reset"] = nil,
["Dependencies"] = nil,
["Descending"] = nil,
["Description Font"] = nil,
["Description Font Size"] = nil,
@@ -409,6 +411,7 @@ pfUI_translation["enUS"] = {
["Highlight Equipped Items"] = nil,
["Highlight Not Usable Spells"] = nil,
["Highlight Out Of Mana Spells"] = nil,
["Highlight New Items"] = nil,
["Highlight Out Of Range Spells"] = nil,
["Highlight Settings That Require Reload"] = nil,
["Highlight Unusable Items"] = nil,
@@ -477,6 +480,7 @@ pfUI_translation["enUS"] = {
["Look & Feel"] = nil,
["Loot"] = nil,
["Loot & Spam"] = nil,
["Loot History"] = nil,
["Macro Text Color"] = nil,
["Macro Text Size"] = nil,
["Main Actionbar"] = nil,
@@ -499,6 +503,7 @@ pfUI_translation["enUS"] = {
["Menu Font Size"] = nil,
["Messages are no longer forwarded to:"] = nil,
["Middle Mouse Button"] = nil,
["Missing"] = nil,
["Minimap"] = nil,
["Minimap Panel"] = nil,
["Minimap Size (|cffffaaaaExperimental|r)"] = nil,
@@ -525,6 +530,7 @@ pfUI_translation["enUS"] = {
["Network Latency"] = nil,
["Network Up"] = nil,
["New entry:"] = nil,
["New Item Color"] = nil,
["NEW TIMER"] = nil,
["Next"] = nil,
["Next Memory Cleanup"] = nil,
@@ -552,6 +558,7 @@ pfUI_translation["enUS"] = {
["Only Show Own Debuffs (|cffffaaaaExperimental|r)"] = nil,
["Only Show Target Castbar"] = nil,
["On State Change"] = nil,
["Optional Dependencies"] = nil,
["Options"] = nil,
["Orientation"] = nil,
["Other Panel: Minimap"] = nil,
@@ -569,6 +576,7 @@ pfUI_translation["enUS"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = nil,
["Paging Actionbar"] = nil,
["Panel"] = nil,
@@ -616,12 +624,13 @@ pfUI_translation["enUS"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = nil,
["Raid-Pet"] = nil,
["Random"] = nil,
["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -645,6 +654,7 @@ pfUI_translation["enUS"] = {
["Resting"] = nil,
["Reveal Unexplored Areas"] = nil,
["Switch to current zone"] = nil,
["Retrieving item information..."] = nil,
["Right"] = nil,
["Right Actionbar"] = nil,
["Right Anchor"] = nil,
@@ -703,6 +713,7 @@ pfUI_translation["enUS"] = {
["Show Description"] = nil,
["Show Dispel Indicators"] = nil,
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = nil,
["Show Empty Buttons"] = nil,
["Show FPS and Latency Colors"] = nil,
@@ -739,6 +750,7 @@ pfUI_translation["enUS"] = {
["Show Required Questitem Count"] = nil,
["Show Resting"] = nil,
["Show Self In Group Frames"] = nil,
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
@@ -780,12 +792,14 @@ pfUI_translation["enUS"] = {
["Switch Pages On Alt Key Press"] = nil,
["Switch Pages On Ctrl Key Press"] = nil,
["Switch Pages On Druid Stealth"] = nil,
["Switch Pages On Priest Shadowform"] = nil,
["Switch Pages On Shift Key Press"] = nil,
["Systeminfo"] = nil,
["Target"] = nil,
["Target Castbar"] = nil,
["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = nil,
["Target-Target-Target"] = nil,
["Text"] = nil,
@@ -883,7 +897,6 @@ pfUI_translation["enUS"] = {
["XP Percentage"] = nil,
["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil,
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["esES"] = {
["Always Show Item Comparison"] = "Mostrar siempre la comparativa entre objetos",
["Always Show On Target Units"] = "Mostrar siempre en el objectivo",
["Always Show On Units With Missing HP"] = "Mostrar siempre en unidades con falta de salud",
["Always Show Self In Raid Frames"] = "Mostrarse siempre a sí mismo en los marcos de banda",
["Always Use 2D Portraits"] = "Usar siempre retratos 2D",
["Ammo Counter"] = "Contador de munición",
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["esES"] = {
["Click Casting"] = "Lanzamiento al hacer click",
["Clock"] = "Reloj",
["Close"] = "Cerrar",
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = "Colorear pilas de beneficios",
["Color Debuff Stacks"] = "Colorear pilas de perjuicios",
@@ -138,7 +138,6 @@ pfUI_translation["esES"] = {
["Combat Timer"] = "Temporizador de combate",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Comparar estadísticas bases de objetos",
["Components"] = "Componentes",
["Config UI Settings"] = nil,
["Configuration"] = "Configuración",
@@ -569,6 +568,7 @@ pfUI_translation["esES"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = "Paginable",
["Paging Actionbar"] = "Paginando la barra de acción",
["Panel"] = "Panel",
@@ -616,12 +616,13 @@ pfUI_translation["esES"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = "Espaciado del marco de banda",
["Raid-Pet"] = nil,
["Random"] = "Aleatorio",
["Random Roll Announcement Rarity"] = "Anuncio para tirar los dados aleatoriamente",
["Random Rolling"] = "Tirar los dados aleatoriamente",
["Range Based Hunter Paging"] = "Paginado de alcance para cazadores",
["Range Check Interval"] = "Intervalo de comprobación de alcance",
["Rank"] = "Rango",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Borde rojo en unidades enemigas",
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -703,6 +704,7 @@ pfUI_translation["esES"] = {
["Show Description"] = "Mostrar descripción",
["Show Dispel Indicators"] = "Mostrar indicadores para disipar",
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = "Mostrar la duración dentro del beneficios",
["Show Empty Buttons"] = "Mostrar botones vacíos",
["Show FPS and Latency Colors"] = "Mostrar FPS y colores de latencia",
@@ -739,6 +741,7 @@ pfUI_translation["esES"] = {
["Show Required Questitem Count"] = "Muestra el número requerido de objetos de misión",
["Show Resting"] = "Mostrar descanso",
["Show Self In Group Frames"] = "Mostrar a sí mismo en marcos de grupo",
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = "Mostrar icono de hechizo",
["Show Spell Name"] = nil,
["Show Stacks"] = "Mostrar pilas",
@@ -783,6 +786,7 @@ pfUI_translation["esES"] = {
["Target Castbar"] = "Barra de lanzamiento del objetivo",
["Target Debuff Bar"] = "Barra de perjuicios del objetivo",
["Target Nameplate Zoom Factor"] = "Factor de zoom de la placa de nombre del objetivo",
["Target Totem"] = nil,
["Target-Target"] = "Objetivo-Objetivo",
["Target-Target-Target"] = "Objetivo-Objetivo-Objetivo",
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["esES"] = {
["XP Percentage"] = "Porcentaje de exp.",
["Yellow Border On Neutral Units"] = "Borde amarillo en unidades neutrales",
["Yes"] = "",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Obtienes",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Ahora su interfaz está configurada.\n\nPara la configuración avanzada, abra la configuración |cff33ffccpf|rUI por el menú de escape o escriba \"|cffffffaa/pfui|r\" en el chat.",
["Your items have been repaired for"] = "Tus objetos se han reparado por",
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["frFR"] = {
["Always Show Item Comparison"] = "Toujours afficher la comparaison d'objet",
["Always Show On Target Units"] = "Toujours montrer sur la cible",
["Always Show On Units With Missing HP"] = "Toujours montrer sur les cibles avec de la vie manquante",
["Always Show Self In Raid Frames"] = "Toujours montrer soi-même dans le cadre de raid",
["Always Use 2D Portraits"] = "Toujours utiliser les portraits 2D",
["Ammo Counter"] = "Compteur de munitions",
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["frFR"] = {
["Click Casting"] = "Clique sur le lancement de sort",
["Clock"] = "Horloge",
["Close"] = "Fermer",
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = "Couleur des l'empilements des Améliorations",
["Color Debuff Stacks"] = "Couleur de l'empilements de Affaiblissements",
@@ -138,7 +138,6 @@ pfUI_translation["frFR"] = {
["Combat Timer"] = "Chronomètre de combat",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Compare les stats de base d'un objet",
["Components"] = "Composants",
["Config UI Settings"] = nil,
["Configuration"] = "Configuration",
@@ -569,6 +568,7 @@ pfUI_translation["frFR"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = "Pageable",
["Paging Actionbar"] = "Barre d'action de pagination",
["Panel"] = "Panneau",
@@ -616,12 +616,13 @@ pfUI_translation["frFR"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = "Remplissage du Raid",
["Raid-Pet"] = nil,
["Random"] = "Aléatoire",
["Random Roll Announcement Rarity"] = "Rareté des annonces des jets de dés aléatoires",
["Random Rolling"] = "Lancer de dés aléatoires",
["Range Based Hunter Paging"] = "Pagination de distance basée sur le chasseur",
["Range Check Interval"] = "Intervalle de vérification de la distance",
["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Bordure rouge sur les unités ennemies",
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -703,6 +704,7 @@ pfUI_translation["frFR"] = {
["Show Description"] = "Afficher les descriptions",
["Show Dispel Indicators"] = "Afficher les indicateurs de dissipation",
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = "Afficher la durée à l'intérieur des améliorations",
["Show Empty Buttons"] = "Afficher les boutons vides",
["Show FPS and Latency Colors"] = nil,
@@ -739,6 +741,7 @@ pfUI_translation["frFR"] = {
["Show Required Questitem Count"] = nil,
["Show Resting"] = "Afficher au repos",
["Show Self In Group Frames"] = "S'afficher dans les cadres de groupe",
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = "Afficher l'icone des sorts",
["Show Spell Name"] = nil,
["Show Stacks"] = "Afficher les empilements",
@@ -783,6 +786,7 @@ pfUI_translation["frFR"] = {
["Target Castbar"] = "Barre d'incantation de la cible",
["Target Debuff Bar"] = "Barre des affaiblissements de la cible",
["Target Nameplate Zoom Factor"] = "Facteur de zoom du Nameplate de la cible",
["Target Totem"] = nil,
["Target-Target"] = "Cible de la cible",
["Target-Target-Target"] = "Cible de la cible de la cible",
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["frFR"] = {
["XP Percentage"] = "Pourcentage de la barre d'expérience",
["Yellow Border On Neutral Units"] = "Bordure jaune sur les unités neutres",
["Yes"] = "Oui",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Vous avez",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = "Vos objets ont été réparés pour",
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["koKR"] = {
["Always Show Item Comparison"] = "항상 착용 장비와 비교 표시",
["Always Show On Target Units"] = nil,
["Always Show On Units With Missing HP"] = nil,
["Always Show Self In Raid Frames"] = nil,
["Always Use 2D Portraits"] = "항상 2D초상화 사용",
["Ammo Counter"] = "탄약 갯수",
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["koKR"] = {
["Click Casting"] = nil,
["Clock"] = "시계",
["Close"] = "닫기",
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = nil,
["Color Debuff Stacks"] = nil,
@@ -138,7 +138,6 @@ pfUI_translation["koKR"] = {
["Combat Timer"] = "전투 타이머",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = nil,
["Components"] = nil,
["Config UI Settings"] = nil,
["Configuration"] = "구성",
@@ -569,6 +568,7 @@ pfUI_translation["koKR"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = nil,
["Paging Actionbar"] = nil,
["Panel"] = "패널",
@@ -616,12 +616,13 @@ pfUI_translation["koKR"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = nil,
["Raid-Pet"] = nil,
["Random"] = nil,
["Random Roll Announcement Rarity"] = nil,
["Random Rolling"] = nil,
["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = nil,
["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -703,6 +704,7 @@ pfUI_translation["koKR"] = {
["Show Description"] = nil,
["Show Dispel Indicators"] = nil,
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = nil,
["Show Empty Buttons"] = nil,
["Show FPS and Latency Colors"] = nil,
@@ -739,6 +741,7 @@ pfUI_translation["koKR"] = {
["Show Required Questitem Count"] = nil,
["Show Resting"] = nil,
["Show Self In Group Frames"] = nil,
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
@@ -783,6 +786,7 @@ pfUI_translation["koKR"] = {
["Target Castbar"] = nil,
["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "대상-대상",
["Target-Target-Target"] = nil,
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["koKR"] = {
["XP Percentage"] = "경험치 퍼센트",
["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = nil,
["Your items have been repaired for"] = nil,
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["ruRU"] = {
["Always Show Item Comparison"] = "Всегда показывать сравнение предметов",
["Always Show On Target Units"] = "Всегда показывать над выбранной целью",
["Always Show On Units With Missing HP"] = "Всегда показывать над целями с неполным здоровьем",
["Always Show Self In Raid Frames"] = "Всегда показывать себя в рейде",
["Always Use 2D Portraits"] = "Всегда использовать 2D-портреты",
["Ammo Counter"] = "Счетчик боеприпасов",
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["ruRU"] = {
["Click Casting"] = "Каст по нажатию",
["Clock"] = "Часы",
["Close"] = "Закрыть",
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = "Цвет стаков баффа",
["Color Debuff Stacks"] = "Цвет стаков дебаффа",
@@ -138,7 +138,6 @@ pfUI_translation["ruRU"] = {
["Combat Timer"] = "Таймер боя",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "Сравнивать базовые характеристики предмета",
["Components"] = "Компоненты",
["Config UI Settings"] = "Настройка параметров пользовательского интерфейса",
["Configuration"] = "Конфигурация",
@@ -569,6 +568,7 @@ pfUI_translation["ruRU"] = {
["Overwrite If Unit Is Attacking Others"] = nil,
["Overwrite If Unit Is Attacking You"] = nil,
["Overwrite If Unit Is Casting"] = nil,
["Owner Name"] = nil,
["Pageable"] = "Прокрутка страниц",
["Paging Actionbar"] = "Прокрутка панелей",
["Panel"] = "Панель",
@@ -616,12 +616,13 @@ pfUI_translation["ruRU"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = "Отступ рейда",
["Raid-Pet"] = nil,
["Random"] = "Случайно",
["Random Roll Announcement Rarity"] = "Оповещение случайного броска для качества предмета",
["Random Rolling"] = "Случайный бросок костей для",
["Range Based Hunter Paging"] = "[|cffA9D271Охотник|r] Переключение страниц на основе диапазона",
["Range Check Interval"] = "Интервал проверки диапазона",
["Rank"] = "Ранг",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "Красные границы вражеских юнитов",
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = "Региональные настройки",
@@ -703,6 +704,7 @@ pfUI_translation["ruRU"] = {
["Show Description"] = "Показать описание",
["Show Dispel Indicators"] = "Показать индикаторы рассеивания",
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = "Показать продолжительность внутри баффа",
["Show Empty Buttons"] = "Показать пустые кнопки",
["Show FPS and Latency Colors"] = "Показать частоту кадров и задержку в цвете",
@@ -739,6 +741,7 @@ pfUI_translation["ruRU"] = {
["Show Required Questitem Count"] = "Показать необходимое количество предметов для задания",
["Show Resting"] = "Показать иконку отдыха",
["Show Self In Group Frames"] = "Показать себя в окне группы",
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = "Показать иконку заклинания",
["Show Spell Name"] = "Показать название заклинания",
["Show Stacks"] = "Показать стаки",
@@ -783,6 +786,7 @@ pfUI_translation["ruRU"] = {
["Target Castbar"] = "Панель применения цели",
["Target Debuff Bar"] = "Панель дебаффов цели",
["Target Nameplate Zoom Factor"] = "Коэффициент увеличения индикатора здоровья цели",
["Target Totem"] = nil,
["Target-Target"] = "Цель цели",
["Target-Target-Target"] = "Цель цели цели",
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["ruRU"] = {
["XP Percentage"] = "Процент опыта",
["Yellow Border On Neutral Units"] = "Желтые границы на нейтральных юнитах",
["Yes"] = "Да",
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = "Вы получили",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "Теперь ваш интерфейс настроен.\n\nДля расширенной настройки откройте \"Настройки |cff33ffccpf|rUI\" с помощью escape меню или введите \"|cffffffaa/pfui|r\" в чат.\n\nЖелаю хорошего путешествия!\n\n|cffaaaaaa- Shagu",
["Your items have been repaired for"] = "Ваши предметы были отремонтированы за",
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["zhCN"] = {
["Always Show Item Comparison"] = "始终显示装备比较(或按SHIFT键)",
["Always Show On Target Units"] = "始终在目标单位上显示",
["Always Show On Units With Missing HP"] = "始终在未满血单位上显示",
["Always Show Self In Raid Frames"] = "始终在团队框架中显示自己",
["Always Use 2D Portraits"] = "始终使用2D头像",
["Ammo Counter"] = "弹药数量",
["Anchor Bags Above Chat"] = "将背包锚定在聊天框上方",
@@ -124,6 +123,7 @@ pfUI_translation["zhCN"] = {
["Click Casting"] = "点击施法",
["Clock"] = "时间",
["Close"] = "关闭",
["Collapse Empty Slots"] = nil,
["Color"] = "颜色",
["Color Buff Stacks"] = "Buff堆叠颜色",
["Color Debuff Stacks"] = "Debuff堆叠颜色",
@@ -138,7 +138,6 @@ pfUI_translation["zhCN"] = {
["Combat Timer"] = "战斗计时器",
["Combopoint Height"] = "连击点高度",
["Combopoint Width"] = "连击点宽度",
["Compare Item Base Stats"] = "基于属性的装备对比",
["Components"] = "组件",
["Config UI Settings"] = "界面设置",
["Configuration"] = "配置",
@@ -569,6 +568,7 @@ pfUI_translation["zhCN"] = {
["Overwrite If Unit Is Attacking Others"] = "如果单位正在攻击其它单位则变色",
["Overwrite If Unit Is Attacking You"] = "如果单位正在攻击你则变色",
["Overwrite If Unit Is Casting"] = "如果单位正在施法则变色",
["Owner Name"] = nil,
["Pageable"] = "可分页",
["Paging Actionbar"] = "分页动作条",
["Panel"] = "面板",
@@ -616,12 +616,13 @@ pfUI_translation["zhCN"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = "团队填充",
["Raid-Pet"] = nil,
["Random"] = "给随机玩家",
["Random Roll Announcement Rarity"] = "随机Roll点稀有度",
["Random Rolling"] = "随机Roll点 物品:",
["Range Based Hunter Paging"] = "启用基于范围的自动分页[|cff7fff7f猎人|r]",
["Range Check Interval"] = "范围检查间隔",
["Rank"] = "军衔",
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = "显示敌方单位红色边框",
["Red Name Text On Infight Units"] = "进战斗的单位显示红色姓名",
["Regional Settings"] = "区域设置",
@@ -703,6 +704,7 @@ pfUI_translation["zhCN"] = {
["Show Description"] = "显示描述",
["Show Dispel Indicators"] = "显示驱散指示器",
["Show Druid Mana Bar"] = "显示德鲁伊法力条",
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = "显示持续时间在Buff里面",
["Show Empty Buttons"] = "显示空按钮",
["Show FPS and Latency Colors"] = "显示帧数以及延迟颜色",
@@ -740,6 +742,7 @@ pfUI_translation["zhCN"] = {
["Show Required Questitem Count"] = "显示所需的任务物品计数",
["Show Resting"] = "显示休息图标",
["Show Self In Group Frames"] = "在队伍框架中显示自己",
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = "显示技能图标",
["Show Spell Name"] = "显示技能名称",
["Show Stacks"] = "显示堆叠",
@@ -784,6 +787,7 @@ pfUI_translation["zhCN"] = {
["Target Castbar"] = "目标施法条",
["Target Debuff Bar"] = "目标Debuffs条",
["Target Nameplate Zoom Factor"] = "目标姓名板缩放系数",
["Target Totem"] = nil,
["Target-Target"] = "目标的目标",
["Target-Target-Target"] = "目标的目标的目标",
["Text"] = "文本",
@@ -878,7 +882,6 @@ pfUI_translation["zhCN"] = {
["XP Percentage"] = "经验百分比",
["Yellow Border On Neutral Units"] = "显示中立单位黄色边框",
["Yes"] = "",
["You gain (.+) Mana from Totemic Recall"] = "你从图腾召回获得了(.+)点法力值",
["You got"] = "你已得到",
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "你的界面现在已经完成设置.高级设置请点击游戏菜单或者输入命令/pfui进行设置.祝您游戏愉快",
["Your items have been repaired for"] = "你的物品已经修好了",
+7 -4
View File
@@ -27,7 +27,6 @@ pfUI_translation["zhTW"] = {
["Always Show Item Comparison"] = "始終顯示裝備比較",
["Always Show On Target Units"] = nil,
["Always Show On Units With Missing HP"] = nil,
["Always Show Self In Raid Frames"] = nil,
["Always Use 2D Portraits"] = "始終使用2D頭像",
["Ammo Counter"] = "彈藥數量",
["Anchor Bags Above Chat"] = nil,
@@ -124,6 +123,7 @@ pfUI_translation["zhTW"] = {
["Click Casting"] = nil,
["Clock"] = "時間",
["Close"] = "關閉",
["Collapse Empty Slots"] = nil,
["Color"] = nil,
["Color Buff Stacks"] = nil,
["Color Debuff Stacks"] = nil,
@@ -138,7 +138,6 @@ pfUI_translation["zhTW"] = {
["Combat Timer"] = "戰鬥計時器",
["Combopoint Height"] = nil,
["Combopoint Width"] = nil,
["Compare Item Base Stats"] = "基於屬性的裝備對比",
["Components"] = "組件",
["Config UI Settings"] = nil,
["Configuration"] = "配置",
@@ -569,6 +568,7 @@ pfUI_translation["zhTW"] = {
["Overwrite If Unit Is Attacking Others"] = "如果单位正在攻擊其它单位則覆蓋",
["Overwrite If Unit Is Attacking You"] = "如果单位正在攻擊你則覆蓋",
["Overwrite If Unit Is Casting"] = "如果单位正在施法則覆蓋",
["Owner Name"] = nil,
["Pageable"] = nil,
["Paging Actionbar"] = nil,
["Panel"] = "面板",
@@ -616,12 +616,13 @@ pfUI_translation["zhTW"] = {
["Raid Mark X-Offset"] = nil,
["Raid Mark Y-Offset"] = nil,
["Raid Padding"] = nil,
["Raid-Pet"] = nil,
["Random"] = "給隨機玩家",
["Random Roll Announcement Rarity"] = "隨機Roll點公示稀有度",
["Random Rolling"] = "隨機Roll點 物品:",
["Range Based Hunter Paging"] = nil,
["Range Check Interval"] = "範圍檢查間隔",
["Rank"] = nil,
["Recast Totem"] = nil,
["Red Border On Enemy Units"] = nil,
["Red Name Text On Infight Units"] = nil,
["Regional Settings"] = nil,
@@ -703,6 +704,7 @@ pfUI_translation["zhTW"] = {
["Show Description"] = nil,
["Show Dispel Indicators"] = nil,
["Show Druid Mana Bar"] = nil,
["Show Druid Mana Bar Text"] = nil,
["Show Duration Inside Buff"] = "顯示持續時間在Buff裏面",
["Show Empty Buttons"] = nil,
["Show FPS and Latency Colors"] = nil,
@@ -739,6 +741,7 @@ pfUI_translation["zhTW"] = {
["Show Required Questitem Count"] = nil,
["Show Resting"] = nil,
["Show Self In Group Frames"] = nil,
["Show Self In Raid Frames When Solo"] = nil,
["Show Spell Icon"] = nil,
["Show Spell Name"] = nil,
["Show Stacks"] = nil,
@@ -783,6 +786,7 @@ pfUI_translation["zhTW"] = {
["Target Castbar"] = nil,
["Target Debuff Bar"] = nil,
["Target Nameplate Zoom Factor"] = nil,
["Target Totem"] = nil,
["Target-Target"] = "目標的目標",
["Target-Target-Target"] = nil,
["Text"] = nil,
@@ -877,7 +881,6 @@ pfUI_translation["zhTW"] = {
["XP Percentage"] = "經驗百分比",
["Yellow Border On Neutral Units"] = nil,
["Yes"] = nil,
["You gain (.+) Mana from Totemic Recall"] = nil,
["You got"] = nil,
["Your interface is now set up.\n\nFor advanced configuration, just open the |cff33ffccpf|rUI settings via the escape menu or type \"|cffffffaa/pfui|r\" into the chat.\n\n Have a nice trip!\n\n|cffaaaaaa- Shagu"] = "您的UI已經設置完畢.使用遊戲菜單或在聊天窗口輸入\"|cffffffaa/pfui|r\"開啟高級設置.祝您遊戲愉快!\n\n|cffaaaaaa- Shagu",
["Your items have been repaired for"] = "你的物品已經修好了",
-2
View File
@@ -4,9 +4,7 @@
<Include file="..\libs\libdebuff.lua"/>
<Include file="..\libs\librange.lua"/>
<Include file="..\libs\libunitscan.lua"/>
<Include file="..\libs\libtooltip.lua"/>
<Include file="..\libs\libhealth.lua"/>
<Include file="..\libs\libtotem.lua"/>
<Include file="..\libs\libthrottle.lua"/>
<Include file="..\libs\libpredict.lua"/>
<Include file="..\libs\libbagsort.lua"/>
+3
View File
@@ -71,6 +71,7 @@
<Include file="..\modules\energytick.lua"/>
<Include file="..\modules\totems.lua"/>
<Include file="..\modules\macrotweak.lua"/>
<Include file="..\modules\macroicons.lua"/>
<Include file="..\modules\turtle-wow.lua"/>
<Include file="..\modules\superwow.lua"/>
<Include file="..\modules\innervatecall.lua"/>
@@ -78,4 +79,6 @@
<Include file="..\modules\unitxp.lua"/>
<Include file="..\modules\bgscore.lua"/>
<Include file="..\modules\equipmentmanager.lua"/>
<Include file="..\modules\loothistory.lua"/>
<Include file="..\modules\newitem.lua"/>
</Ui>
+1
View File
@@ -1,5 +1,6 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Include file="..\skins\blizzard\character.lua"/>
<Include file="..\skins\blizzard\inspect.lua"/>
<Include file="..\skins\blizzard\spellbook.lua"/>
<Include file="..\skins\blizzard\friends.lua"/>
<Include file="..\skins\blizzard\talents.lua"/>
+123 -63
View File
@@ -11,26 +11,27 @@ pfUI.api.libbagsort = libbagsort
libbagsort.itemGrid = {}
libbagsort.bagList = nil
local HEARTHSTONE_ITEM_ID = 6948
local ItemClass = Enum.ItemClass
local ItemQuality = Enum.ItemQuality
-- Lower prefix = sorted earlier in the bag.
local function SortCategoryPrefix(itemId, itemType, itemSubType, quality)
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
if quality == 0 then return "13" end -- Poor (gray) always last
if itemType == "Weapon" or itemType == "Armor" then
if quality and quality >= 4 then return "01" end -- Epic+ gear
if quality == 3 then return "02" end -- Rare gear
if quality == 2 then return "03" end -- Uncommon gear
return "04" -- Common/poor gear
local function SortCategoryPrefix(itemId, classID, quality)
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
if quality == ItemQuality.Poor then return "13" end -- gray always last
if classID == ItemClass.Weapon or classID == ItemClass.Armor then
if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
if quality == ItemQuality.Rare then return "02" end -- Rare gear
if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
return "04" -- Common/poor gear
end
if itemType == "Consumable" then return "05" end
if itemType == "Reagent" then return "06" end
if itemType == "Trade Goods" then return "07" end
if itemType == "Quest" then return "08" end
if classID == ItemClass.Consumable then return "05" end
if classID == ItemClass.Reagent then return "06" end
if classID == ItemClass.Tradegoods then return "07" end
if classID == ItemClass.Questitem then return "08" end
-- Non-gear items without a specific type, sorted by quality
if quality and quality >= 4 then return "09" end
if quality == 3 then return "10" end
if quality == 2 then return "11" end
if quality and quality >= ItemQuality.Epic then return "09" end
if quality == ItemQuality.Rare then return "10" end
if quality == ItemQuality.Uncommon then return "11" end
return "12"
end
@@ -41,18 +42,31 @@ local function SortCountSuffix(count)
return string.sub(s, -6)
end
local function SortKey(itemId, name, itype, subtype, quality, count)
return SortCategoryPrefix(itemId, itype, subtype, quality)
.. (itype or "") .. "|" .. (subtype or "") .. "|" .. (name or "zzz") .. "|" .. SortCountSuffix(count)
local function SortKey(itemId, name, classID, subClassID, quality, count)
-- Zero-pad the class/subclass so the secondary grouping sorts numerically
-- (as a string, "10" would otherwise precede "2").
return SortCategoryPrefix(itemId, classID, quality)
.. string.format("%02d|%02d|", classID or 99, subClassID or 99)
.. (name or "zzz") .. "|" .. SortCountSuffix(count)
end
local function ClearSortData()
libbagsort.itemGrid = {}
libbagsort.bagList = nil
libbagsort.opts = nil
libbagsort:UnregisterEvent("BAG_UPDATE_DELAYED")
libbagsort:SetScript("OnEvent", nil)
end
local function ReverseArray(t)
local i, j = 1, table.getn(t)
while i < j do
t[i], t[j] = t[j], t[i]
i = i + 1
j = j - 1
end
end
-- Two-pointer consolidation: sorts stacks largest-first, then merges from
-- both ends toward the middle. n is set explicitly so table.getn / table.sort
-- work correctly in Lua 5.0.
@@ -112,36 +126,61 @@ local function BuildConsolidateOps(bagList)
return ops
end
-- Bag family bitmask (1 << (familyID-1)); 0 = general-purpose (holds
-- anything). Backpack (0) and bank (-1) are always general. A specialty
-- bag's family comes from the equipped bag item -- ClassicAPI derives it
-- from the container subclass when the raw field is empty (Turtle leaves
-- bags' m_bagFamily at 0), so quivers/soul/profession bags report properly.
local function BagFamily(bag)
if bag == 0 or bag == -1 then return 0 end
local id = GetInventoryItemID("player", ContainerIDToInventoryID(bag))
return id and C_Item.GetItemFamily(id) or 0
end
local function BuildSortGrid()
local bagList = libbagsort.bagList
libbagsort.itemGrid = {}
local normalItems = {}
local poorItems = {}
local bagCount = 0
local bagSlots = {}
-- Destination cells, split by the family they can accept. A specialty
-- bag's slots only take items of its own family; general slots take
-- anything. Cells are collected in forward order (bag order, slot 1..n).
local generalCells = {} -- { {bag=,slot=}, ... }
local specialtyCells = {} -- family -> { {bag=,slot=}, ... }
for _, bag in ipairs(bagList) do
bagCount = bagCount + 1
local fam = BagFamily(bag)
local numSlots = GetContainerNumSlots(bag)
bagSlots[bagCount] = numSlots
if numSlots > 0 then
libbagsort.itemGrid[bag] = {}
for slot = 1, numSlots do
if fam == 0 then
tinsert(generalCells, {bag=bag, slot=slot})
else
specialtyCells[fam] = specialtyCells[fam] or {}
tinsert(specialtyCells[fam], {bag=bag, slot=slot})
end
local itemId = C_Container.GetContainerItemID(bag, slot)
if itemId then
-- pfUI's compat layer shims GetItemInfo to the modern 10-field
-- signature (inserts nil for itemLevel between quality and
-- minlevel) — so itype/subtype sit at positions 6/7, not 5/6.
local name, _, quality, _, _, itype, subtype = GetItemInfo(itemId)
-- C_Item.GetItemInfo is the full 18-field tuple; classID/subClassID
-- sit at positions 12/13. We categorize on those numeric class IDs
-- rather than the localized itemType/itemSubType strings. (pfUI's
-- shimmed global GetItemInfo is only 10 fields and lacks them.)
local name, _, quality, _, _, _, _, _, _, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
local _, count = GetContainerItemInfo(bag, slot)
local item = {
key = SortKey(itemId, name, itype, subtype, quality, count),
key = SortKey(itemId, name, classID, subClassID, quality, count),
-- vanilla items carry at most one family bit, so equality
-- against a bag family suffices (no bit.band needed).
family = C_Item.GetItemFamily(itemId) or 0,
srcBag = bag,
srcSlot = slot,
curBag = bag,
curSlot = slot,
}
if quality == 0 then
if quality == ItemQuality.Poor then
tinsert(poorItems, item)
else
tinsert(normalItems, item)
@@ -152,46 +191,61 @@ local function BuildSortGrid()
end
end
table.sort(normalItems, function(a, b) return a.key < b.key end)
-- Sort poor items descending so they read in ascending order when placed
-- back-to-front (last poor item lands on the last slot).
table.sort(poorItems, function(a, b) return a.key > b.key end)
local opts = libbagsort.opts or {}
local reverse = opts.reverse
local reversePrio = opts.reversePrio
-- Forward pass: assign normal items from slot 1 of bag 1 onward
local bagIdx, destSlot = 1, 1
while bagIdx <= bagCount and bagSlots[bagIdx] == 0 do
bagIdx = bagIdx + 1
if reverse then
ReverseArray(generalCells)
for _, cells in pairs(specialtyCells) do
ReverseArray(cells)
end
end
if reversePrio then
table.sort(normalItems, function(a, b) return a.key > b.key end)
else
table.sort(normalItems, function(a, b) return a.key < b.key end)
end
if reverse then
table.sort(poorItems, function(a, b) return a.key < b.key end)
else
table.sort(poorItems, function(a, b) return a.key > b.key end)
end
-- Forward pass: route each normal item into the next free cell that
-- accepts it -- a matching specialty bag first, overflowing to general.
local genIdx = 1
local specIdx = {} -- family -> next free index into specialtyCells[family]
for _, item in ipairs(normalItems) do
while bagIdx <= bagCount do
if destSlot <= bagSlots[bagIdx] then break end
bagIdx = bagIdx + 1
destSlot = 1
local cell
local fam = item.family
if fam ~= 0 and specialtyCells[fam] then
local i = specIdx[fam] or 1
if i <= table.getn(specialtyCells[fam]) then
cell = specialtyCells[fam][i]
specIdx[fam] = i + 1
end
end
if bagIdx > bagCount then break end
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
grid.destBag = bagList[bagIdx]
grid.destSlot = destSlot
destSlot = destSlot + 1
if not cell and genIdx <= table.getn(generalCells) then
cell = generalCells[genIdx]
genIdx = genIdx + 1
end
if not cell then break end
item.destBag = cell.bag
item.destSlot = cell.slot
end
-- Reverse pass: assign poor items from the last slot of the last bag backward
local rBagIdx = bagCount
local rDestSlot = 0
while rBagIdx >= 1 do
if bagSlots[rBagIdx] > 0 then rDestSlot = bagSlots[rBagIdx]; break end
rBagIdx = rBagIdx - 1
end
-- Reverse pass: poor items are general; fill remaining general cells from
-- the back, stopping before the ones the forward pass already claimed.
local genBack = table.getn(generalCells)
for _, item in ipairs(poorItems) do
while rBagIdx >= 1 and rDestSlot < 1 do
rBagIdx = rBagIdx - 1
rDestSlot = rBagIdx >= 1 and bagSlots[rBagIdx] or 0
end
if rBagIdx < 1 then break end
local grid = libbagsort.itemGrid[item.srcBag][item.srcSlot]
grid.destBag = bagList[rBagIdx]
grid.destSlot = rDestSlot
rDestSlot = rDestSlot - 1
if genBack < genIdx then break end
local cell = generalCells[genBack]
genBack = genBack - 1
item.destBag = cell.bag
item.destSlot = cell.slot
end
end
@@ -248,9 +302,15 @@ end
-- BAG_UPDATE_DELAYED cycle), then place items by category/name/quality.
-- e.g. `libbagsort:Sort({0, 1, 2, 3, 4})` for the main bags;
-- `{-1, 5, 6, 7, 8, 9, 10}` for the bank.
function libbagsort:Sort(bagList)
--
-- opts (optional): { reverse = bool, reversePrio = bool }
-- 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).
function libbagsort:Sort(bagList, opts)
ClearSortData()
self.bagList = bagList
self.opts = opts
-- Phase 1: fire every consolidation op in a single batch.
local ops = BuildConsolidateOps(bagList)
+59 -9
View File
@@ -11,12 +11,14 @@ setfenv(1, pfUI:GetEnvironment())
-- This eliminates ~400 lines of error-prone shift logic while maintaining full
-- multi-caster tracking support.
--
-- The public per-aura readers (UnitDebuff, UnitOwnDebuff) were retired in favor
-- of ClassicAPI's C_UnitAuras (which now provides sourceUnit/sourceGUID and
-- non-player expirationTime). What remains in libdebuff is the cast-event
-- bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking) and the
-- libdebuff_*_hooks broadcast surface (subscribers in actionbar / swingtimer
-- / libtotem react to SPELL_GO and SPELL_FAILED).
-- The internal debuff plumbing now runs on ClassicAPI's C_UnitAuras (which
-- provides sourceUnit/sourceGUID and non-player expirationTime). The public
-- per-aura readers (UnitDebuff, UnitOwnDebuff) survive only as thin adapters
-- over C_UnitAuras for third-party addons (e.g. pfUI-WeakIcons) that still
-- expect the legacy multi-return signature. The rest of libdebuff is the
-- cast-event bookkeeping consumed by GetBestAuraCast (libpredict HoT tracking)
-- and the libdebuff_*_hooks broadcast surface (subscribers in actionbar /
-- swingtimer react to SPELL_GO and SPELL_FAILED).
-- return instantly when another libdebuff is already active
if pfUI.api.libdebuff then return end
@@ -27,8 +29,7 @@ if GetLocale() == "ruRU" then
end
local libdebuff = CreateFrame("Frame", "pfdebuffsScanner", UIParent)
local _, class = UnitClass("player")
local lastspell
local class = UnitClassBase("player")
-- Nampower Support
local hasNampower = false
@@ -762,6 +763,55 @@ function libdebuff:GetBestAuraCast(guid, spellName)
return nil
end
-- ============================================================================
-- API: UnitDebuff / UnitOwnDebuff (C_UnitAuras adapters)
-- ============================================================================
-- Thin readers kept for third-party addons (e.g. pfUI-WeakIcons) that still
-- expect libdebuff's legacy multi-return signature:
-- effect, rank, texture, stacks, dtype, duration, timeleft, caster
-- ClassicAPI's C_UnitAuras already resolves source and expiration, so these
-- just remap its AuraData onto that tuple -- no cast-tracking tables
-- (ownDebuffs/allAuraCasts) or GetUnitField slot mapping involved.
local function AuraToLegacy(aura)
if not aura then return nil end
local duration = aura.duration or 0
local timeleft = -1
-- Only report a timer for genuinely timed auras. ClassicAPI can leave a
-- stale expirationTime on permanent (duration 0) auras, so gate on duration.
if duration > 0 and aura.expirationTime and aura.expirationTime > 0 then
timeleft = aura.expirationTime - GetTime()
if timeleft < 0 then timeleft = 0 end
end
-- Aura spellId is the specific cast rank, so its subtext is the active rank.
local rank
local subtext = aura.spellId and C_Spell.GetSpellSubtext(aura.spellId)
if subtext and subtext ~= "" then
rank = tonumber((string.gsub(subtext, "Rank ", "")))
end
local dtype = aura.dispelName
if dtype == "" then dtype = nil end
local caster = aura.isFromPlayerOrPlayerPet and "player" or "other"
return aura.name, rank, aura.icon, aura.applications or 0, dtype, duration, timeleft, caster
end
-- id is a 1-based harmful-aura index, matching C_UnitAuras / Blizzard's
-- compacted debuff slots.
function libdebuff:UnitDebuff(unit, id)
return AuraToLegacy(C_UnitAuras.GetAuraDataByIndex(unit, id, "HARMFUL"))
end
-- Player-cast harmful auras only, via the PLAYER filter -- no manual
-- caster-GUID bookkeeping needed.
function libdebuff:UnitOwnDebuff(unit, id)
return AuraToLegacy(C_UnitAuras.GetAuraDataByIndex(unit, id, "HARMFUL|PLAYER"))
end
-- ============================================================================
-- NAMPOWER EVENT HANDLING
-- ============================================================================
@@ -1125,7 +1175,7 @@ if hasNampower then
-- Rank aus spellId ermitteln
local rankNum = 0
local rankString = GetSpellRecField(spellId, "rank")
local rankString = C_Spell.GetSpellSubtext(spellId)
if rankString and rankString ~= "" then
rankNum = tonumber((string.gsub(rankString, "Rank ", ""))) or 0
end
+9 -10
View File
@@ -291,7 +291,7 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
local targetName = resolveNameFromGuid(targetGuid)
if not targetName then return end
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or ""
local rankStr = C_Spell.GetSpellSubtext(spellId) or ""
local spellKey = spellName .. (rankStr or "")
local amount = foreignCache[casterName] and foreignCache[casterName][spellKey]
@@ -355,11 +355,9 @@ pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4,
elseif hotType == "Renew" then duration = renewDuration or 15
end
local rank = 0
if GetSpellRecField then
local rankStr = GetSpellRecField(spellId, "rank")
if rankStr and rankStr ~= "" then
rank = tonumber((string.gsub(rankStr, "Rank ", ""))) or 0
end
local rankSub = C_Spell.GetSpellSubtext(spellId)
if rankSub and rankSub ~= "" then
rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0
end
local playerName = UnitName("player")
libpredict:Hot(playerName, targetName, hotType, duration, nil, "SPELL_GO_SELF", rank)
@@ -897,7 +895,7 @@ local INSTANT_HOT_COOLDOWN = 1.0 -- 1 Sekunde Cooldown (GCD ist 1.5s)
local pendingHots = {}
-- Gather Data by User Actions
pfUI.hooksecurefunc("CastSpell", function(id, bookType)
hooksecurefunc("CastSpell", function(id, bookType)
if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(id, bookType)
if not effect then return end
@@ -950,7 +948,7 @@ pfUI.hooksecurefunc("CastSpell", function(id, bookType)
end
end)
pfUI.hooksecurefunc("CastSpellByName", function(effect, target)
hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(effect)
if not effect then return end
@@ -1014,13 +1012,14 @@ pfUI.hooksecurefunc("CastSpellByName", function(effect, target)
end
end)
pfUI.hooksecurefunc("UseAction", function(slot, target, selfcast)
hooksecurefunc("UseAction", function(slot, target, selfcast)
if not libpredict.sender.enabled then return end
if not IsCurrentAction(slot) then return end
local kind, id = GetActionInfo(slot)
local effect, rank
if kind == "spell" then
effect, rank = GetSpellInfo(id)
local spellInfo = C_Spell.GetSpellInfo(id)
effect, rank = spellInfo.name, spellInfo.rank
elseif kind == "macro" then
effect, rank = GetMacroSpell(id)
end
+14 -164
View File
@@ -2,178 +2,28 @@
setfenv(1, pfUI:GetEnvironment())
--[[ librange ]]--
-- A pfUI library that detects and caches distance to units.
-- A thin wrapper over ClassicAPI's UnitInRange: a fixed 40y healing-range
-- check computed C-side from unit positions, valid for any unit. There is
-- no cache or scan loop -- the check is cheap enough to run per query,
-- which also sidesteps the staleness a cached scan hit on zone changes and
-- roster re-indexing.
--
-- librange:UnitInSpellRange(unit)
-- Returns `1` if the unit is within range, `nil` otherwise.
--
-- Requires SuperWoW's UnitPosition for the friendly scan path. Target
-- range still works via IsActionInRange (vanilla-native) for any class
-- with a known 40y healing spell on the action bar.
if pfUI.api.librange then return end
local _, class = UnitClass("player")
local librange = CreateFrame("Frame", "pfRangecheck", UIParent)
-- 40y spells per class. Only consulted to find an action-bar slot for the
-- IsActionInRange target-range path; the party/raid scan uses UnitPosition.
local spells = {
["PALADIN"] = {
"Interface\\Icons\\Spell_Holy_FlashHeal",
"Interface\\Icons\\Spell_Holy_HolyBolt",
},
["PRIEST"] = {
"Interface\\Icons\\Spell_Holy_FlashHeal",
"Interface\\Icons\\Spell_Holy_LesserHeal",
"Interface\\Icons\\Spell_Holy_Heal",
"Interface\\Icons\\Spell_Holy_GreaterHeal",
"Interface\\Icons\\Spell_Holy_Renew",
},
["DRUID"] = {
"Interface\\Icons\\Spell_Nature_HealingTouch",
"Interface\\Icons\\Spell_Nature_ResistNature",
"Interface\\Icons\\Spell_Nature_Rejuvenation",
},
["SHAMAN"] = {
"Interface\\Icons\\Spell_Nature_MagicImmunity",
"Interface\\Icons\\Spell_Nature_HealingWaveLesser",
"Interface\\Icons\\Spell_Nature_HealingWaveGreater",
},
}
-- friendly units the scan loop iterates
local units = {}
table.insert(units, "pet")
for i=1,4 do table.insert(units, "party" .. i) end
for i=1,4 do table.insert(units, "partypet" .. i) end
for i=1,40 do table.insert(units, "raid" .. i) end
for i=1,40 do table.insert(units, "raidpet" .. i) end
local numunits = table.getn(units)
local unitcache = {}
local unitdata = {}
local librange_isLoggingOut = false
librange.id = 1
librange:Hide()
librange:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
librange:RegisterEvent("PLAYER_ENTERING_WORLD")
librange:RegisterEvent("PLAYER_LOGOUT")
librange:RegisterEvent("PLAYER_LEAVING_WORLD")
librange:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" or event == "PLAYER_LEAVING_WORLD" then
librange_isLoggingOut = true
this:SetScript("OnUpdate", nil)
this:Hide()
return
end
if pfUI_config.unitframes.rangecheck == "0" then
this:Hide()
return
end
this.interval = tonumber(C.unitframes.rangechecki)/numunits
if event == "ACTIONBAR_SLOT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then
librange.slot = this:GetRangeSlot()
if UnitPosition then this:Show() end
end
end)
librange:SetScript("OnUpdate", function()
if librange_isLoggingOut then return end
if (this.tick or 1) > GetTime() then return end
this.tick = GetTime() + this.interval
while not this:NeedRangeScan(units[this.id]) and this.id <= numunits do
this.id = this.id + 1
end
if this.id <= numunits then
local unit = units[this.id]
if not UnitIsUnit("target", unit) then
local x1, y1, z1 = UnitPosition("player")
local x2, y2, z2 = UnitPosition(unit)
if x1 and x2 then
local distance = ((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)^.5
unitdata[unit] = distance < 45 and 1 or 0
end
end
this.id = this.id + 1
else
this.id = 1
end
end)
function librange:NeedRangeScan(unit)
if not UnitExists(unit) then return nil end
if not UnitIsVisible(unit) then return nil end
if CheckInteractDistance(unit, 4) then return nil end
return true
end
function librange:GetRealUnit(unit)
if unitdata[unit] then return unit end
if unitcache[unit] and UnitIsUnit(unitcache[unit], unit) then
return unitcache[unit]
end
for id, realunit in pairs(units) do
if UnitIsUnit(realunit, unit) then
unitcache[unit] = realunit
return realunit
end
end
return unit
end
function librange:GetRangeSlot()
if not spells[class] then return nil end
for i=1,120 do
-- Resolve the slot to a spellID for both spell and macro actions; the old
-- `not GetActionText` macro-filter missed macros that cast a 40y heal but
-- displayed a non-spell icon. C_Spell.GetSpellTexture(spellID) gives the
-- spell's *intrinsic* icon, which is what we match against.
local kind, id = GetActionInfo(i)
local spellID
if kind == "spell" then
spellID = id
elseif kind == "macro" then
local _, _, sid = GetMacroSpell(id)
spellID = sid
end
if spellID then
local texture = C_Spell.GetSpellTexture(spellID)
if texture then
for _, check in pairs(spells[class]) do
if check == texture then return i end
end
end
end
end
return nil
end
local librange = {}
function librange:UnitInSpellRange(unit)
if UnitIsUnit("target", unit) then
if not librange.slot then return nil end
return IsActionInRange(librange.slot) == 1 and 1 or nil
end
local unit = librange:GetRealUnit(unit)
if unitdata[unit] and unitdata[unit] == 1 then
return 1
elseif not unitdata[unit] then
return 1
else
return nil
end
-- _G-qualified: bare `UnitInRange` resolves to pfUI.api.UnitInRange inside
-- the pfUI environment (which calls us), so this must reach ClassicAPI's
-- global directly or it recurses.
local inRange, checked = _G.UnitInRange(unit)
-- position miss (e.g. a unit outside the client's sync range): we can't
-- tell, so default to in-range -- matches the old cache's nil behavior.
if not checked then return 1 end
return inRange and 1 or nil
end
-- add librange to pfUI API
+3 -1
View File
@@ -118,7 +118,9 @@ end
local resetcache = CreateFrame("Frame")
resetcache:RegisterEvent("LEARNED_SPELL_IN_TAB")
resetcache:SetScript("OnEvent", function()
spellmaxrank, spellindex, spellinfo = {}, {}, {}
table.wipe(spellmaxrank)
table.wipe(spellindex)
table.wipe(spellinfo)
end)
-- add libspell to pfUI API
+2 -2
View File
@@ -35,7 +35,7 @@ libthrottle.defaults = {
nameplates_target = "custom",
nameplates_castbar = "custom",
nameplates_mass = "custom",
tooltip_cursor = "custom",
tooltip_cursor = "fastest",
chat_tab = "custom",
swingtimer = "custom",
}
@@ -160,7 +160,7 @@ libthrottle:SetScript("OnEvent", function()
-- Set defaults for custom fields if missing
if not _G.pfUI_throttle.nameplates_target_custom then _G.pfUI_throttle.nameplates_target_custom = "50" end
if not _G.pfUI_throttle.nameplates_custom then _G.pfUI_throttle.nameplates_custom = "10" end
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "50" end
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
if not _G.pfUI_throttle.nameplates_mass_custom then _G.pfUI_throttle.nameplates_mass_custom = "7" end
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
-59
View File
@@ -1,59 +0,0 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
--[[ libtooltip ]]--
-- A pfUI library that provides additional GameTooltip information.
--
-- libtooltip:GetItemID()
-- returns the itemID of the current GameTooltip
-- `nil` when no item is displayed
--
-- libtooltip:GetItemLink()
-- returns the itemLink of the current GameTooltip
-- `nil` when no item is displayed
--
-- libtooltip:GetItemCount()
-- returns the item count (bags) of the current GameTooltip
-- `nil` when no item is displayed
-- return instantly when another libtooltip is already active
if pfUI.api.libtooltip then return end
local libtooltip = CreateFrame("Frame" , "pfLibTooltip", GameTooltip)
libtooltip:SetScript("OnShow", function()
if this:GetParent():HasItem() then
libtooltip.itemName, libtooltip.itemLink, libtooltip.itemID = this:GetParent():GetItem()
end
end)
libtooltip:SetScript("OnHide", function()
this.itemID = nil
this.itemLink = nil
this.itemCount = nil
this.itemName = nil
end)
-- core functions
libtooltip.GetItemID = function(self)
if not libtooltip.itemLink then return end
if not libtooltip.itemID then
libtooltip.itemID = C_Item.GetItemInfoInstant(libtooltip.itemLink)
end
return libtooltip.itemID
end
libtooltip.GetItemLink = function(self)
return libtooltip.itemLink
end
libtooltip.GetItemCount = function(self)
return libtooltip.itemCount
end
pfUI.api.libtooltip = libtooltip
pfUI.hooksecurefunc(GameTooltip, "SetBagItem", function(self, container, slot)
_, libtooltip.itemCount = GetContainerItemInfo(container, slot)
end)
-274
View File
@@ -1,274 +0,0 @@
-- load pfUI environment
setfenv(1, pfUI:GetEnvironment())
--[[ libtotem ]]--
-- A pfUI library that tries to emulate the TotemAPI that was introduced in Patch 2.4.
-- It detects and saves all current totems of the player and returns information based
-- on the totem slot ID. The function GetTotemInfo is supposed to work as it would
-- on later expansions.
--
-- GetTotemInfo(id)
-- Returns totem informations on the givent totem slot
-- active, name, start, duration, icon
-- return instantly when another libtotem is already active
if pfUI.api.libtotem then return end
MAX_TOTEMS = MAX_TOTEMS or 4
FIRE_TOTEM_SLOT = FIRE_TOTEM_SLOT or 1
EARTH_TOTEM_SLOT = EARTH_TOTEM_SLOT or 2
WATER_TOTEM_SLOT = WATER_TOTEM_SLOT or 3
AIR_TOTEM_SLOT = AIR_TOTEM_SLOT or 4
local _, class = UnitClass("player")
local libtotem
local active = { [1] = {}, [2] = {}, [3] = {}, [4] = {} }
-- SpellID -> { slot, duration } mapping
-- rank-specific durations are handled via spellId directly
local spellids = {
-- FIRE (slot 1)
[1535] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R1
[8498] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R2
[8499] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R3
[11314] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R4
[11315] = { slot = FIRE_TOTEM_SLOT, duration = 5 }, -- Fire Nova Totem R5
[8227] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R1
[8249] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R2
[10526] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R3
[16387] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Flametongue Totem R4
[8184] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R1
[10478] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R2
[10479] = { slot = FIRE_TOTEM_SLOT, duration = 120 }, -- Frost Resistance Totem R3
[8190] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R1
[10585] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R2
[10586] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R3
[10587] = { slot = FIRE_TOTEM_SLOT, duration = 20 }, -- Magma Totem R4
[3599] = { slot = FIRE_TOTEM_SLOT, duration = 30 }, -- Searing Totem R1
[6363] = { slot = FIRE_TOTEM_SLOT, duration = 35 }, -- Searing Totem R2
[6364] = { slot = FIRE_TOTEM_SLOT, duration = 40 }, -- Searing Totem R3
[6365] = { slot = FIRE_TOTEM_SLOT, duration = 45 }, -- Searing Totem R4
[10437] = { slot = FIRE_TOTEM_SLOT, duration = 50 }, -- Searing Totem R5
[10438] = { slot = FIRE_TOTEM_SLOT, duration = 55 }, -- Searing Totem R6
-- EARTH (slot 2)
[2484] = { slot = EARTH_TOTEM_SLOT, duration = 45 }, -- Earthbind Totem
[5730] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R1
[6390] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R2
[6391] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R3
[6392] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R4
[10427] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R5
[10428] = { slot = EARTH_TOTEM_SLOT, duration = 15 }, -- Stoneclaw Totem R6
[8071] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R1
[8154] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R2
[8155] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R3
[10406] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R4
[10407] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R5
[10408] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Stoneskin Totem R6
[8075] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R1
[8160] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R2
[8161] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R3
[10442] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R4
[25361] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Strength of Earth Totem R5
[8143] = { slot = EARTH_TOTEM_SLOT, duration = 120 }, -- Tremor Totem
-- WATER (slot 3)
[8170] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Disease Cleansing Totem
[8185] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R1
[10537] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R2
[10538] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Fire Resistance Totem R3
[5394] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R1
[6375] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R2
[6377] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R3
[10462] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R4
[10463] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Healing Stream Totem R5
[5675] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R1
[10495] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R2
[10496] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R3
[10497] = { slot = WATER_TOTEM_SLOT, duration = 60 }, -- Mana Spring Totem R4
[16190] = { slot = WATER_TOTEM_SLOT, duration = 12 }, -- Mana Tide Totem
[8166] = { slot = WATER_TOTEM_SLOT, duration = 120 }, -- Poison Cleansing Totem
-- AIR (slot 4)
[8835] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R1
[10627] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Grace of Air Totem R2
[8177] = { slot = AIR_TOTEM_SLOT, duration = 45 }, -- Grounding Totem
[10595] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R1
[10600] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R2
[10601] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Nature Resistance Totem R3
[25359] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Tranquil Air Totem
[8512] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R1
[10613] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R2
[10614] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windfury Totem R3
[15107] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R1
[15421] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R2
[15422] = { slot = AIR_TOTEM_SLOT, duration = 120 }, -- Windwall Totem R3
}
-- icon-based fallback table (used by CastSpell/UseAction hooks that don't have spellId)
local totems = {
[FIRE_TOTEM_SLOT] = {
["Spell_Fire_SealOfFire"] = {[-1] = 5},
["Spell_Nature_GuardianWard"] = {[-1] = 120},
["Spell_FrostResistanceTotem_01"] = {[-1] = 120},
["Spell_Fire_SelfDestruct"] = {[-1] = 20},
["Spell_Fire_SearingTotem"] = {[-1] = 55,[1] = 30,[2] = 35,[3] = 40,[4] = 45,[5] = 50,[6] = 55},
},
[EARTH_TOTEM_SLOT] = {
["Spell_Nature_StrengthOfEarthTotem02"] = {[-1] = 45},
["Spell_Nature_StoneClawTotem"] = {[-1] = 15},
["Spell_Nature_StoneSkinTotem"] = {[-1] = 120},
["Spell_Nature_EarthBindTotem"] = {[-1] = 120},
["Spell_Nature_TremorTotem"] = {[-1] = 120},
},
[WATER_TOTEM_SLOT] = {
["Spell_Nature_DiseaseCleansingTotem"] = {[-1] = 120},
["Spell_FireResistanceTotem_01"] = {[-1] = 120},
["INV_Spear_04"] = {[-1] = 60},
["Spell_Nature_ManaRegenTotem"] = {[-1] = 60},
["Spell_Frost_SummonWaterElemental"] = {[-1] = 12},
["Spell_Nature_PoisonCleansingTotem"] = {[-1] = 120},
},
[AIR_TOTEM_SLOT] = {
["Spell_Nature_InvisibilityTotem"] = {[-1] = 120},
["Spell_Nature_GroundingTotem"] = {[-1] = 45},
["Spell_Nature_NatureResistanceTotem"] = {[-1] = 120},
["Spell_Nature_Brilliance"] = {[-1] = 120},
["Spell_Nature_Windfury"] = {[-1] = 120},
["Spell_Nature_EarthBind"] = {[-1] = 120},
},
}
GetTotemInfo = function(id)
if not active[id] or not active[id].name then return end
if active[id].start + active[id].duration - GetTime() < 0 then
libtotem:Clean(id)
return nil
end
return 1, active[id].name, active[id].start, active[id].duration, active[id].icon
end
if class ~= "SHAMAN" then return end
libtotem = CreateFrame("Frame")
libtotem:RegisterEvent("PLAYER_DEAD")
libtotem:SetScript("OnEvent", function()
if event == "PLAYER_DEAD" then
for i = 1, 4 do libtotem:Clean(i) end
end
end)
libtotem.totems = totems
libtotem.Clean = function(self, slot)
active[slot].name = nil
active[slot].start = nil
active[slot].duration = nil
active[slot].icon = nil
end
-- Direct SpellID commit (Nampower SPELL_GO_SELF, most accurate)
libtotem.CommitBySpellId = function(spellId, icon)
local data = spellids[spellId]
if not data then return false end
local slot = data.slot
active[slot].name = active[slot].pending_name or active[slot].name
active[slot].duration = data.duration
active[slot].icon = icon or active[slot].pending_icon
active[slot].start = GetTime()
active[slot].pending_name = nil
active[slot].pending_icon = nil
return true
end
-- Fallback: icon-based lookup (for CastSpell/UseAction without spellId)
libtotem.CheckAddQueue = function(self, name, rank, icon, spellId)
-- if we have a spellId, just store the name/icon as pending for SPELL_GO
if spellId and spellids[spellId] then
local slot = spellids[spellId].slot
active[slot].pending_name = name
active[slot].pending_icon = icon
return true
end
-- icon-based fallback
for slot = 1, 4 do
for texture, data in pairs(totems[slot]) do
if string.find(icon, texture, 1) then
if rank then
_, _, rank = string.find(rank, "%s(%d+)")
end
local duration
if rank and tonumber(rank) and data[tonumber(rank)] then
duration = data[tonumber(rank)]
else
duration = data[-1]
end
active[slot].pending_name = name
active[slot].pending_icon = icon
active[slot].pending_duration = duration
return true
end
end
end
return nil
end
-- assign library to global space
pfUI.api.libtotem = libtotem
-- SPELL_GO_SELF from libdebuff: commit directly by SpellID, no queue needed
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
pfUI.libdebuff_spell_go_hooks["libtotem"] = function(spellId)
if not spellId then return end
local data = spellids[spellId]
if not data then return end
local slot = data.slot
-- use pending name/icon if available (set by CastSpellByName hook), else GetSpellInfo
local name = active[slot].pending_name
local icon = active[slot].pending_icon
if not name and GetSpellInfo then
name = GetSpellInfo(spellId)
end
active[slot].name = name
active[slot].duration = data.duration
active[slot].icon = icon
active[slot].start = GetTime()
active[slot].pending_name = nil
active[slot].pending_icon = nil
active[slot].pending_duration = nil
end
-- Hook CastSpellByName to store pending name/icon per slot
pfUI.hooksecurefunc("CastSpellByName", function(effect, target)
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(effect)
if not name then return end
libtotem:CheckAddQueue(name, rank, icon, spellId)
end)
-- Hook CastSpell to store pending name/icon per slot
pfUI.hooksecurefunc("CastSpell", function(id, bookType)
if not id or not bookType then return end
if bookType ~= BOOKTYPE_SPELL and bookType ~= BOOKTYPE_PET then return end
local name, rank, icon, _, _, _, spellId = libspell.GetSpellInfo(id, bookType)
if not name then return end
libtotem:CheckAddQueue(name, rank, icon, spellId)
end)
-- Hook UseAction. GetActionInfo + GetMacroSpell give us the spellID
-- directly for both spell-action and macro-action slots, so the
-- tooltip-scan fallback (and the "no spellId available" caveat) goes away.
pfUI.hooksecurefunc("UseAction", function(slot, target, selfcast)
if not IsCurrentAction(slot) then return end
local kind, id = GetActionInfo(slot)
local name, rank, spellID
if kind == "spell" then
spellID = id
name, rank = GetSpellInfo(id)
elseif kind == "macro" then
name, rank, spellID = GetMacroSpell(id)
end
if not name then return end
libtotem:CheckAddQueue(name, rank, GetActionTexture(slot), spellID)
end)
+22 -20
View File
@@ -89,7 +89,7 @@ libunitscan:SetScript("OnEvent", function()
-- update own character details
local name = UnitName("player")
local _, class = UnitClass("player")
local class = UnitClassBase("player")
local level = UnitLevel("player")
local guild = GetGuildInfo("player")
AddData("players", name, class, level, nil, guild)
@@ -128,7 +128,7 @@ libunitscan:SetScript("OnEvent", function()
local name, class, level, unit, _, guild
for i = 1, GetNumPartyMembers() do
unit = "party" .. i
_, class = UnitClass(unit)
class = UnitClassBase(unit)
name = UnitName(unit)
level = UnitLevel(unit)
guild = GetGuildInfo(unit)
@@ -149,24 +149,26 @@ libunitscan:SetScript("OnEvent", function()
or event == "NAME_PLATE_UNIT_ADDED" and arg1
or "mouseover"
local name, class, level, elite, guild, _
if UnitIsPlayer(scan) then
_, class = UnitClass(scan)
level = UnitLevel(scan)
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
level = level > 0 and level or nil
name = UnitName(scan)
guild = GetGuildInfo(scan)
AddData("players", name, class, level, nil, guild)
RememberByUnit(scan, name, class)
else
_, class = UnitClass(scan)
elite = UnitClassification(scan)
level = UnitLevel(scan)
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
level = level > 0 and level or nil
name = UnitName(scan)
guild = UnitSubName(scan)
AddData("mobs", name, class, level, elite, guild)
if UnitExists(scan) then
if UnitIsPlayer(scan) then
class = UnitClassBase(scan)
level = UnitLevel(scan)
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
level = level > 0 and level or nil
name = UnitName(scan)
guild = GetGuildInfo(scan)
AddData("players", name, class, level, nil, guild)
RememberByUnit(scan, name, class)
else
class = UnitClassBase(scan)
elite = UnitClassification(scan)
level = UnitLevel(scan)
-- UnitLevel returns -1 for unknown levels, don't overwrite known values
level = level > 0 and level or nil
name = UnitName(scan)
guild = UnitSubName(scan)
AddData("mobs", name, class, level, elite, guild)
end
end
end
end)
+71 -79
View File
@@ -1,5 +1,5 @@
pfUI:RegisterModule("actionbar", function ()
local _, class = UnitClass("player")
local class = UnitClassBase("player")
local _, cr, cg, cb = GetUnitColor('player')
local er, eg, eb, ea = GetStringColor(pfUI_config.appearance.border.color)
@@ -168,8 +168,7 @@ pfUI:RegisterModule("actionbar", function ()
["zoomfade"] = function()
if this.active == 0 then
-- init animation
this:SetWidth(this.parent:GetWidth())
this:SetHeight(this.parent:GetHeight())
this:SetSize(this.parent:GetSize())
this:SetScale(this.parent:GetScale())
this.tex:SetTexture(this.parent.icon:GetTexture())
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
@@ -191,8 +190,7 @@ pfUI:RegisterModule("actionbar", function ()
["shrinkreturn"] = function()
if this.active == 0 then
-- init animation
this:SetWidth(this.parent:GetWidth())
this:SetHeight(this.parent:GetHeight())
this:SetSize(this.parent:GetSize())
this:SetScale(this.parent:GetScale())
this.tex:SetTexture(this.parent.icon:GetTexture())
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
@@ -218,8 +216,7 @@ pfUI:RegisterModule("actionbar", function ()
["elasticzoom"] = function()
if this.active == 0 then
-- init animation
this:SetWidth(this.parent:GetWidth())
this:SetHeight(this.parent:GetHeight())
this:SetSize(this.parent:GetSize())
this:SetScale(this.parent:GetScale())
this.tex:SetTexture(this.parent.icon:GetTexture())
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
@@ -245,8 +242,7 @@ pfUI:RegisterModule("actionbar", function ()
["wobblezoom"] = function()
if this.active == 0 then
-- init animation
this:SetWidth(this.parent:GetWidth())
this:SetHeight(this.parent:GetHeight())
this:SetSize(this.parent:GetSize())
this:SetScale(this.parent:GetScale())
this.tex:SetTexture(this.parent.icon:GetTexture())
this.tex:SetVertexColor(this.parent.icon:GetVertexColor())
@@ -604,17 +600,17 @@ pfUI:RegisterModule("actionbar", function ()
-- update usable [out-of-range = 1, oom = 2, not-usable = 3, default = 0]
if self.outofrange and C.bars.glowrange == "1" then
if self.vertexstate ~= 1 then
self.icon:SetVertexColor(self.rangeColor[1], self.rangeColor[2], self.rangeColor[3], self.rangeColor[4])
self.icon:SetVertexColor(self.rangeColor:GetRGBA())
self.vertexstate = 1
end
elseif oom and C.bars.showoom == "1" then
if self.vertexstate ~= 2 then
self.icon:SetVertexColor(self.oomColor[1], self.oomColor[2], self.oomColor[3], self.oomColor[4])
self.icon:SetVertexColor(self.oomColor:GetRGBA())
self.vertexstate = 2
end
elseif not usable and C.bars.showna == "1" then
if self.vertexstate ~= 3 then
self.icon:SetVertexColor(self.naColor[1], self.naColor[2], self.naColor[3], self.naColor[4])
self.icon:SetVertexColor(self.naColor:GetRGBA())
self.vertexstate = 3
end
else
@@ -853,16 +849,20 @@ pfUI:RegisterModule("actionbar", function ()
end
end
local cat, stealth
local inCatForm = nil
local prowlActive = nil
-- Cat Form ID per vanilla 1.12 SpellShapeshiftForm.dbc.
-- Form IDs per vanilla 1.12 SpellShapeshiftForm.dbc.
local CAT_FORM = 1
local SHADOWFORM = 28
local function HasCatForm()
return GetShapeshiftFormID() == CAT_FORM and true or nil
end
local function InShadowform()
return GetShapeshiftFormID() == SHADOWFORM and true or nil
end
local function FullScan()
if class ~= "DRUID" then return nil end
inCatForm = HasCatForm()
@@ -872,7 +872,7 @@ pfUI:RegisterModule("actionbar", function ()
-- pagemaster / meta page switch
do
local prowl, shift, ctrl, alt, default = 8, 6, 5, 3, 1
local formpage, shift, ctrl, alt, default = 8, 6, 5, 3, 1
-- set temporary pagemaster bindings keybinds
if C.bars.pagemaster == "1" then
@@ -890,8 +890,10 @@ pfUI:RegisterModule("actionbar", function ()
end)
end
-- setup page switch frame
local prowling = nil
-- setup page switch frame. `formpaging` is the shared "auto-page is
-- active" flag; druid prowl and priest shadowform each drive it, and
-- no character is ever both, so one flag covers both features.
local formpaging = nil
local pageswitch = CreateFrame("Frame", "pfActionBarPageSwitch", UIParent)
pageswitch:RegisterEvent("PLAYER_ENTERING_WORLD")
pageswitch:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
@@ -905,10 +907,17 @@ pfUI:RegisterModule("actionbar", function ()
return
end
if class == "PRIEST" then
if event == "UPDATE_SHAPESHIFT_FORM" or event == "PLAYER_ENTERING_WORLD" then
formpaging = InShadowform()
end
return
end
if class ~= "DRUID" then return end
if event == "PLAYER_ENTERING_WORLD" then
prowling = FullScan()
formpaging = FullScan()
return
end
@@ -917,7 +926,7 @@ pfUI:RegisterModule("actionbar", function ()
inCatForm = HasCatForm()
if not inCatForm then
prowlActive = nil
prowling = nil
formpaging = nil
end
return
end
@@ -927,10 +936,10 @@ pfUI:RegisterModule("actionbar", function ()
if inCatForm then
if IsStealthed() then
prowlActive = true
prowling = true
formpaging = true
else
prowlActive = nil
prowling = nil
formpaging = nil
end
end
end)
@@ -946,7 +955,7 @@ pfUI:RegisterModule("actionbar", function ()
if PROWL_IDS[spellId] then
inCatForm = true
prowlActive = true
prowling = true
formpaging = true
end
end
pageswitch:SetScript("OnUpdate", function()
@@ -964,11 +973,12 @@ pfUI:RegisterModule("actionbar", function ()
SwitchBar(default)
end
-- switch actionbar page if druid stealth is detected
if C.bars.druidstealth == "1" then
if prowling and _G.CURRENT_ACTIONBAR_PAGE == 1 then
SwitchBar(prowl)
elseif not prowling and _G.CURRENT_ACTIONBAR_PAGE == 8 then
-- switch actionbar page while druid stealth / priest shadowform is active
if (class == "DRUID" and C.bars.druidstealth == "1")
or (class == "PRIEST" and C.bars.priestshadow == "1") then
if formpaging and _G.CURRENT_ACTIONBAR_PAGE == 1 then
SwitchBar(formpage)
elseif not formpaging and _G.CURRENT_ACTIONBAR_PAGE == 8 then
SwitchBar(default)
end
end
@@ -982,13 +992,13 @@ pfUI:RegisterModule("actionbar", function ()
local font_offset = tonumber(C.bars.font_offset)
local macro_size = tonumber(C.bars.macro_size)
local macro_color = { strsplit(",", C.bars.macro_color) }
local macro_color = { GetStringColor(C.bars.macro_color) }
local count_size = tonumber(C.bars.count_size)
local count_color = { strsplit(",", C.bars.count_color) }
local count_color = { GetStringColor(C.bars.count_color) }
local bind_size = tonumber(C.bars.bind_size)
local bind_color = { strsplit(",", C.bars.bind_color) }
local bind_color = { GetStringColor(C.bars.bind_color) }
local cd_size = tonumber(C.bars.cd_size)
@@ -1147,23 +1157,22 @@ pfUI:RegisterModule("actionbar", function ()
end
-- range glow color
f.rangeColor = { strsplit(",", C.bars.rangecolor) }
f.rangeColor = GetStringColorObject(C.bars.rangecolor)
-- out of mana color
f.oomColor = { strsplit(",", C.bars.oomcolor) }
f.oomColor = GetStringColorObject(C.bars.oomcolor)
-- not usable color
f.naColor = { strsplit(",", C.bars.nacolor) }
f.naColor = GetStringColorObject(C.bars.nacolor)
-- equipped color
if f.equipped then
f.equipped:SetTexture(strsplit(",", C.bars.eqcolor))
f.equipped:SetTexture(GetStringColor(C.bars.eqcolor))
end
-- general appearance
f.showempty = showempty == "1" and true or nil
f:SetHeight(size)
f:SetWidth(size)
f:SetSize(size, size)
CreateBackdrop(f, border)
return f
@@ -1335,8 +1344,7 @@ pfUI:RegisterModule("actionbar", function ()
-- adjust actionbar size
BarLayoutSize(bars[i], buttons, formfactor, size, border, spacing, uneven, fillmode)
bars[i]:SetWidth(bars[i]._size[1])
bars[i]:SetHeight(bars[i]._size[2])
bars[i]:SetSize(bars[i]._size[1], bars[i]._size[2])
bars[i]:ClearAllPoints()
if i == 1 then -- main
bars[i]:SetPoint("BOTTOM", 0, 2*border)
@@ -1538,7 +1546,7 @@ pfUI:RegisterModule("actionbar", function ()
-- via GetActionInfo + C_Spell.GetSpellReagents. Macro actions and
-- bag-item actions are skipped (their reagent resolution would need a
-- macro-body parse / item-effect lookup that we don't bother with).
local UpdateSlot = function(slot)
local function UpdateSlot(slot)
local newID = nil
if HasAction(slot) then
local kind, spellID = GetActionInfo(slot)
@@ -1553,60 +1561,44 @@ pfUI:RegisterModule("actionbar", function ()
if reagent_slots[slot] ~= newID then
reagent_slots[slot] = newID
if newID then
reagent_counts[newID] = reagent_counts[newID] or 0
reagent_counts[newID] = reagent_counts[newID] or C_Item.GetItemCount(newID)
end
updatecache[slot] = true
end
end
-- Recount every tracked reagent and flag its buttons for a refresh.
local RecountReagents = function()
for itemID in pairs(reagent_counts) do
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end
for slot in pairs(reagent_slots) do
updatecache[slot] = true
end
end
local reagentcounter = CreateFrame("Frame", "pfReagentCounter", UIParent)
reagentcounter:RegisterEvent("PLAYER_ENTERING_WORLD")
reagentcounter:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
reagentcounter:RegisterEvent("BAG_UPDATE_DELAYED")
reagentcounter:SetScript("OnEvent", function()
if event == "BAG_UPDATE_DELAYED" then
this.event = true
if event == "ACTIONBAR_SLOT_CHANGED" then
-- arg1 is the changed slot; 0 (or nil) means "all slots"
if arg1 and arg1 > 0 then
UpdateSlot(arg1)
else
for slot = 1, 120 do UpdateSlot(slot) end
end
elseif event == "BAG_UPDATE_DELAYED" then
-- inventory changed: refresh the counts we already track
RecountReagents()
else
this.scan = 1
-- PLAYER_ENTERING_WORLD: seed the full reagent map (UpdateSlot seeds
-- each new reagent's count; a BAG_UPDATE_DELAYED follows during login)
for slot = 1, 120 do UpdateSlot(slot) end
end
end)
-- Reagent counter update with throttle for performance optimization
reagentcounter:SetScript("OnUpdate", function()
-- Throttle entire function to 10 FPS for smooth scanning
if (this.tick_update or 0) > GetTime() then return end
this.tick_update = GetTime() + 0.1
-- scan one action slot per update
if this.scan and this.scan <= 120 then
UpdateSlot(this.scan)
this.scan = this.scan + 1
end
-- trigger reagent count updates after action scans
if this.scan and this.scan >= 120 then
this.event = true
this.scan = nil
end
-- queue events to fire only once per second
if not this.event then return end
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
-- scan for all reagent item counts
for itemID in pairs(reagent_counts) do
reagent_counts[itemID] = C_Item.GetItemCount(itemID)
end
-- update all actionbar buttons
for slot in pairs(reagent_slots) do
updatecache[slot] = true
end
-- remove event trigger
this.event = nil
end)
function IsReagentAction(slot)
return reagent_slots[slot] and true or nil
end
+27 -33
View File
@@ -37,15 +37,10 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons.minimapbutton = CreateFrame("Button", "pfMinimapButton", pfUI.minimap or UIParent)
pfUI.addonbuttons.minimapbutton:SetFrameLevel(24)
pfUI.addonbuttons.minimapbutton:SetWidth(12)
pfUI.addonbuttons.minimapbutton:SetHeight(12)
pfUI.addonbuttons.minimapbutton:SetSize(12, 12)
pfUI.addonbuttons.minimapbutton:SetScript("OnClick", function()
if pfUI.addonbuttons:IsShown() then
pfUI.addonbuttons:Hide()
else
pfUI.addonbuttons:Show()
end
pfUI.addonbuttons:SetShown(not pfUI.addonbuttons:IsShown())
end)
pfUI.addonbuttons.buttons = {}
@@ -163,27 +158,25 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons:SetScale(pfUI.minimap:GetScale())
pfUI.addonbuttons.minimapbutton:ClearAllPoints()
local mbtnWidth, mbtnHeight = pfUI.minimap:GetSize()
local dynamicSize = ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing)
if C.abuttons.position == "bottom" then
pfUI.addonbuttons:SetWidth(pfUI.minimap:GetWidth())
pfUI.addonbuttons:SetHeight(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
pfUI.addonbuttons:SetSize(mbtnWidth, dynamicSize)
pfUI.addonbuttons:SetPoint("TOP", pfUI.minimap, "BOTTOM", 0 , -default_border * 3)
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "down")
pfUI.addonbuttons.minimapbutton:SetPoint("BOTTOM", pfUI.minimap, "BOTTOM", 0, 4)
elseif C.abuttons.position == "left" then
pfUI.addonbuttons:SetWidth(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
pfUI.addonbuttons:SetHeight(pfUI.minimap:GetHeight())
pfUI.addonbuttons:SetSize(dynamicSize, mbtnHeight)
pfUI.addonbuttons:SetPoint("TOPRIGHT", pfUI.minimap, "TOPLEFT", -default_border * 3, 0)
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "left")
pfUI.addonbuttons.minimapbutton:SetPoint("LEFT", pfUI.minimap, "LEFT", 4, 0)
elseif C.abuttons.position == "top" then
pfUI.addonbuttons:SetWidth(pfUI.minimap:GetWidth())
pfUI.addonbuttons:SetHeight(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
pfUI.addonbuttons:SetSize(mbtnWidth, dynamicSize)
pfUI.addonbuttons:SetPoint("BOTTOM", pfUI.minimap, "TOP", 0 , default_border * 3)
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "up")
pfUI.addonbuttons.minimapbutton:SetPoint("TOP", pfUI.minimap, "TOP", 0, -4)
elseif C.abuttons.position == "right" then
pfUI.addonbuttons:SetWidth(ceil((GetNumButtons() > 0 and GetNumButtons() or 1) / tonumber(C.abuttons.rowsize)) * GetStringSize() + tonumber(C.abuttons.spacing))
pfUI.addonbuttons:SetHeight(pfUI.minimap:GetHeight())
pfUI.addonbuttons:SetSize(dynamicSize, mbtnHeight)
pfUI.addonbuttons:SetPoint("TOPLEFT", pfUI.minimap, "TOPRIGHT", default_border * 3, 0)
SkinArrowButton(pfUI.addonbuttons.minimapbutton, "right")
pfUI.addonbuttons.minimapbutton:SetPoint("RIGHT", pfUI.minimap, "RIGHT", -4, 0)
@@ -231,7 +224,7 @@ pfUI:RegisterModule("addonbuttons", function ()
frame.backup.is_clamped_to_screen = frame:IsClampedToScreen()
frame.backup.is_movable = frame:IsMovable()
frame.backup.point = {frame:GetPoint()}
frame.backup.size = {frame:GetHeight(), frame:GetWidth()}
frame.backup.size = {frame:GetSize()}
frame.backup.scale = frame:GetScale()
if frame:HasScript("OnDragStart") then
frame.backup.on_drag_start = frame:GetScript("OnDragStart")
@@ -256,8 +249,7 @@ pfUI:RegisterModule("addonbuttons", function ()
frame:SetClampedToScreen(frame.backup.is_clamped_to_screen)
frame:SetMovable(frame.backup.is_movable)
frame:SetScale(frame.backup.scale)
frame:SetHeight(frame.backup.size[1])
frame:SetWidth(frame.backup.size[2])
frame:SetSize(frame.backup.size[2], frame.backup.size[1])
frame:ClearAllPoints()
frame:SetPoint(frame.backup.point[1], frame.backup.point[2], frame.backup.point[3], frame.backup.point[4], frame.backup.point[5])
if frame.backup.on_drag_start ~= nil then
@@ -405,22 +397,25 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons:ProcessButtons()
end)
pfUI.addonbuttons:SetScript("OnUpdate", function()
-- Initial setup on the next frame, once other addons' minimap buttons exist
RunNextFrame(function()
-- check if the panel should be shown by default
if not this.initialized then
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
pfUI.addonbuttons:Show()
else
pfUI.addonbuttons:Hide()
end
-- update all buttons
pfUI.addonbuttons:ProcessButtons()
this.initialized = true
if C.abuttons.showdefault == "1" and GetNumButtons() > 0 then
pfUI.addonbuttons:Show()
else
pfUI.addonbuttons:Hide()
end
-- throttle updates to once per 5 seconds
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 5 end
-- update all buttons and apply workarounds
pfUI.addonbuttons:ProcessButtons()
for k, v in pairs(pfUI.addonbuttons.overrides) do
_G[k] = v
end
end)
-- Rescan minimap buttons every 5 seconds while the panel is shown
C_Timer.NewTicker(5, function()
if not pfUI.addonbuttons:IsShown() then return end
-- reload/rescan minimap buttons
pfUI.addonbuttons:ProcessButtons()
@@ -440,6 +435,5 @@ pfUI:RegisterModule("addonbuttons", function ()
pfUI.addonbuttons:UpdateConfig()
_G.SLASH_PFABP1, _G.SLASH_PFABP2 = "/abp", "/pfabp"
_G.SlashCmdList.PFABP = ManualAddOrRemove
pfUI.api.RegisterSlashCommand("PFABP", { "/abp", "/pfabp" }, ManualAddOrRemove, true)
end)
+26 -14
View File
@@ -18,8 +18,7 @@ pfUI:RegisterModule("addons", function ()
-- addon window
pfUI.addons = CreateFrame("Frame", "pfAddons", UIParent)
pfUI.addons:SetFrameStrata("DIALOG")
pfUI.addons:SetHeight(490)
pfUI.addons:SetWidth(380)
pfUI.addons:SetSize(380, 490)
pfUI.addons:SetPoint("CENTER", 0,0)
pfUI.addons:EnableMouseWheel(1)
pfUI.addons:SetMovable(true)
@@ -51,8 +50,7 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.close = CreateFrame("Button", "pfBagClose", pfUI.addons)
pfUI.addons.close:SetPoint("TOPRIGHT", -border*2,-border*2 )
CreateBackdrop(pfUI.addons.close)
pfUI.addons.close:SetHeight(15)
pfUI.addons.close:SetWidth(15)
pfUI.addons.close:SetSize(15, 15)
pfUI.addons.close.texture = pfUI.addons.close:CreateTexture("pfBagClose")
pfUI.addons.close.texture:SetTexture(pfUI.media["img:close"])
pfUI.addons.close.texture:ClearAllPoints()
@@ -125,8 +123,7 @@ pfUI:RegisterModule("addons", function ()
end)
pfUI.addons.profile:SetPoint("TOP", pfUI.addons, "TOP", 0, -30)
pfUI.addons.profile:SetHeight(30)
pfUI.addons.profile:SetWidth(370)
pfUI.addons.profile:SetSize(370, 30)
CreateBackdrop(pfUI.addons.profile, nil, true)
-- addon profile: title
@@ -139,8 +136,7 @@ pfUI:RegisterModule("addons", function ()
-- addon profile: delete
pfUI.addons.profile.del = CreateFrame("Button", nil, pfUI.addons.profile, "UIPanelButtonTemplate")
SkinButton(pfUI.addons.profile.del)
pfUI.addons.profile.del:SetWidth(16)
pfUI.addons.profile.del:SetHeight(16)
pfUI.addons.profile.del:SetSize(16, 16)
pfUI.addons.profile.del:SetPoint("RIGHT", -10, 0)
pfUI.addons.profile.del:GetFontString():SetPoint("CENTER", 1, 0)
pfUI.addons.profile.del:SetText("-")
@@ -157,8 +153,7 @@ pfUI:RegisterModule("addons", function ()
-- addon profile: create
pfUI.addons.profile.add = CreateFrame("Button", nil, pfUI.addons.profile, "UIPanelButtonTemplate")
SkinButton(pfUI.addons.profile.add)
pfUI.addons.profile.add:SetWidth(16)
pfUI.addons.profile.add:SetHeight(16)
pfUI.addons.profile.add:SetSize(16, 16)
pfUI.addons.profile.add:SetPoint("RIGHT", pfUI.addons.profile.del, "LEFT", -4, 0)
pfUI.addons.profile.add:GetFontString():SetPoint("CENTER", 1, 0)
pfUI.addons.profile.add:SetText("+")
@@ -201,8 +196,7 @@ pfUI:RegisterModule("addons", function ()
-- addon list: scroll frame
pfUI.addons.scroll = CreateScrollFrame("pfAddonListScroll", pfUI.addons)
pfUI.addons.scroll:SetWidth(360)
pfUI.addons.scroll:SetHeight(410)
pfUI.addons.scroll:SetSize(360, 410)
pfUI.addons.scroll:SetPoint("BOTTOM", 0, 10)
pfUI.addons.scroll.backdrop = CreateFrame("Frame", nil, pfUI.addons.scroll)
@@ -217,6 +211,21 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.list:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.addons.list:SetHeight(GetNumAddOns() * 25 + 26)
local function AddDependencyLines(header, deps)
if not deps or table.getn(deps) == 0 then return end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(header .. ":", .2, 1, .8)
for _, dep in ipairs(deps) do
if IsAddOnLoaded(dep) then
GameTooltip:AddLine(" " .. dep, .5, 1, .5)
elseif C_AddOns.DoesAddOnExist(dep) then
GameTooltip:AddLine(" " .. dep, 1, .82, 0)
else
GameTooltip:AddLine(" " .. dep .. " (" .. T["Missing"] .. ")", 1, .4, .4)
end
end
end
local function AddonOnEnter()
this:SetBackdropBorderColor(1,1,1,.08)
@@ -231,6 +240,8 @@ pfUI:RegisterModule("addons", function ()
end
GameTooltip:AddLine(this.anote, .75,.75,.75,1)
AddDependencyLines(T["Dependencies"], this.adeps)
AddDependencyLines(T["Optional Dependencies"], this.aoptdeps)
GameTooltip:SetWidth(180)
GameTooltip:Show()
end
@@ -272,9 +283,10 @@ pfUI:RegisterModule("addons", function ()
frame.anote = anote
frame.aauthor = aauthor
frame.aversion = aversion
frame.adeps = { GetAddOnDependencies(i) } -- required (.toc Dependencies)
frame.aoptdeps = { C_AddOns.GetAddOnOptionalDependencies(i) } -- optional (.toc OptionalDeps)
frame:SetWidth(340)
frame:SetHeight(25)
frame:SetSize(340, 25)
frame:SetPoint("TOPLEFT", 5, i * -25 + 20)
frame:SetBackdrop(pfUI.backdrop_hover)
+1 -2
View File
@@ -48,8 +48,7 @@ pfUI:RegisterModule("afkcam", function ()
local chat = CreateFrame("ScrollingMessageFrame", "pfAFKCamChat", overlay)
chat:EnableMouse(false)
chat:EnableMouseWheel(true)
chat:SetHeight(150)
chat:SetWidth(500)
chat:SetSize(500, 150)
chat:SetPoint("BOTTOMLEFT",overlay,"BOTTOMLEFT", 10, 10)
chat:SetTimeVisible(1800.0)
chat:SetMaxLines(500)
+1 -2
View File
@@ -56,8 +56,7 @@ pfUI:RegisterModule("autovendor", function ()
-- Setup Autosell button
autovendor.button = CreateFrame("Button", "pfMerchantAutoVendorButton", MerchantFrame)
autovendor.button:SetWidth(36)
autovendor.button:SetHeight(36)
autovendor.button:SetSize(36, 36)
autovendor.button.icon = autovendor.button:CreateTexture("ARTWORK")
autovendor.button.icon:SetTexture("Interface\\Icons\\Spell_Shadow_SacrificialShield")
autovendor.button:SetScript("OnEnter", function()
+31 -37
View File
@@ -8,6 +8,13 @@ pfUI:RegisterModule("bags", function ()
local scanner = libtipscan:GetScanner("input_search")
local function BagSortOpts()
return {
reverse = C.appearance.bags.sortreverse == "1",
reversePrio = C.appearance.bags.sortprioreverse == "1",
}
end
-- function to detect openable items in inventory
local openable = { bag = nil, slot = nil, icon = nil }
local function GetNextOpenable()
@@ -319,8 +326,7 @@ pfUI:RegisterModule("bags", function ()
default_border + x*(frame.button_size+default_border*3),
-default_border*2 - y*(frame.button_size+default_border*3) - topspace)
pfUI.bags[bag].slots[slot].frame:SetHeight(frame.button_size)
pfUI.bags[bag].slots[slot].frame:SetWidth(frame.button_size)
pfUI.bags[bag].slots[slot].frame:SetSize(frame.button_size, frame.button_size)
if x >= rowlength - 1 then
y = y + 1
@@ -337,12 +343,13 @@ pfUI:RegisterModule("bags", function ()
local chat = pfUI.chat and ( object == "bank" and pfUI.chat.left or pfUI.chat.right) or nil
frame:SetScript("OnShow", function()
frame.opened = true
if C.appearance.bags.hidechat == "1" and chat and chat:IsVisible() then
frame.chatWasOpen = true
chat:Hide()
end
if C.appearance.bags.autoSortOnOpen == "1" then
libbagsort:Sort({0, 1, 2, 3, 4})
libbagsort:Sort({0, 1, 2, 3, 4}, BagSortOpts())
end
pfUI.bag:CreateBags(object)
PlaySound("INTERFACESOUND_BACKPACKOPEN")
@@ -355,6 +362,10 @@ pfUI:RegisterModule("bags", function ()
end
pfUI.bag:CreateBags(object)
PlaySound("INTERFACESOUND_BACKPACKCLOSE")
if frame.opened then
frame.opened = nil
pfUI.events:TriggerEvent("bag:closed", object)
end
end)
end
@@ -443,7 +454,7 @@ pfUI:RegisterModule("bags", function ()
end
end
local _, _, q, _, _, _, itype = GetItemInfo(itemID)
local _, _, q, _, _, _, itype = C_Item.GetItemInfo(itemID)
-- running advanced item color scan
if C.appearance.bags.borderonlygear == "0" and texture and quality and quality < 1 then
@@ -533,8 +544,7 @@ pfUI:RegisterModule("bags", function ()
local width = (frame.button_size/5*4 + default_border*2) * (max-min+1+extra)
local height = default_border + (frame.button_size/5*4 + default_border)
frame.bagslots:SetWidth(width)
frame.bagslots:SetHeight(height)
frame.bagslots:SetSize(width, height)
for slot=min, max do
if not frame.bagslots.slots[slot] then
frame.bagslots.slots[slot] = {}
@@ -594,8 +604,7 @@ pfUI:RegisterModule("bags", function ()
frame.bagslots.slots[slot].frame:ClearAllPoints()
frame.bagslots.slots[slot].frame:SetPoint("TOPLEFT", frame.bagslots, "TOPLEFT", left, top)
frame.bagslots.slots[slot].frame:SetHeight(frame.button_size/5*4)
frame.bagslots.slots[slot].frame:SetWidth(frame.button_size/5*4)
frame.bagslots.slots[slot].frame:SetSize(frame.button_size/5*4, frame.button_size/5*4)
CreateBackdrop(frame.bagslots.slots[slot].frame, default_border)
frame.bagslots.slots[slot].frame:Show()
@@ -614,8 +623,7 @@ pfUI:RegisterModule("bags", function ()
end
frame.bagslots.buy:SetPoint("RIGHT", frame.bagslots, "RIGHT", -default_border, 0)
CreateBackdrop(frame.bagslots.buy, default_border)
frame.bagslots.buy:SetHeight(frame.button_size/5*4)
frame.bagslots.buy:SetWidth(frame.button_size/5*4)
frame.bagslots.buy:SetSize(frame.button_size/5*4, frame.button_size/5*4)
frame.bagslots.buy:SetText("+")
frame.bagslots.buy:SetTextColor(.5,.5,1,1)
frame.bagslots.buy:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
@@ -707,8 +715,7 @@ pfUI:RegisterModule("bags", function ()
frame.close = CreateFrame("Button", "pfBagClose", frame)
frame.close:SetPoint("TOPRIGHT", -default_border*1,-default_border )
CreateBackdrop(frame.close, default_border)
frame.close:SetHeight(12)
frame.close:SetWidth(12)
frame.close:SetSize(12, 12)
frame.close.texture = frame.close:CreateTexture("pfBagClose")
frame.close.texture:SetTexture(pfUI.media["img:close"])
frame.close.texture:ClearAllPoints()
@@ -733,8 +740,7 @@ pfUI:RegisterModule("bags", function ()
frame.bags = CreateFrame("Button", "pfBagSlotShow", frame)
frame.bags:SetPoint("TOPRIGHT", frame.close, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.bags, default_border)
frame.bags:SetHeight(12)
frame.bags:SetWidth(12)
frame.bags:SetSize(12, 12)
frame.bags:SetTextColor(1,1,.25,1)
frame.bags:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.bags.texture = frame.bags:CreateTexture("pfBagArrowUp")
@@ -761,11 +767,7 @@ pfUI:RegisterModule("bags", function ()
end)
frame.bags:SetScript("OnClick", function()
if pfUI.bag.right.bagslots:IsShown() then
pfUI.bag.right.bagslots:Hide()
else
pfUI.bag.right.bagslots:Show()
end
pfUI.bag.right.bagslots:SetShown(not pfUI.bag.right.bagslots:IsShown())
end)
end
@@ -774,8 +776,7 @@ pfUI:RegisterModule("bags", function ()
frame.open = CreateFrame("Button", "pfBagSlotOpen", frame)
frame.open:SetPoint("TOPRIGHT", frame.bags, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.open, default_border)
frame.open:SetHeight(12)
frame.open:SetWidth(12)
frame.open:SetSize(12, 12)
frame.open:SetTextColor(1,1,.25,1)
frame.open:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.open.texture = frame.open:CreateTexture("pfBagOpenContainer")
@@ -833,8 +834,7 @@ pfUI:RegisterModule("bags", function ()
frame.disenchant = CreateFrame("Button", "pfBagSlotDisenchant", frame)
frame.disenchant:SetPoint("TOPRIGHT", frame.open, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.disenchant, default_border)
frame.disenchant:SetHeight(12)
frame.disenchant:SetWidth(12)
frame.disenchant:SetSize(12, 12)
frame.disenchant:SetTextColor(1,1,.25,1)
frame.disenchant:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.disenchant.texture = frame.disenchant:CreateTexture("pfBagDisenchant")
@@ -876,8 +876,7 @@ pfUI:RegisterModule("bags", function ()
frame.picklock = CreateFrame("Button", "pfBagSlotPicklock", frame)
frame.picklock:SetPoint("TOPRIGHT", frame.disenchant, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.picklock, default_border)
frame.picklock:SetHeight(12)
frame.picklock:SetWidth(12)
frame.picklock:SetSize(12, 12)
frame.picklock:SetTextColor(1,1,.25,1)
frame.picklock:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.picklock.texture = frame.picklock:CreateTexture("pfBagPicklock")
@@ -919,8 +918,7 @@ pfUI:RegisterModule("bags", function ()
frame.keys = CreateFrame("Button", "pfBagSlotShow", frame)
frame.keys:SetPoint("TOPRIGHT", frame.picklock, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.keys, default_border)
frame.keys:SetHeight(12)
frame.keys:SetWidth(12)
frame.keys:SetSize(12, 12)
frame.keys:SetTextColor(1,1,.25,1)
frame.keys:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.keys.texture = frame.keys:CreateTexture("pfBagArrowUp")
@@ -962,8 +960,7 @@ pfUI:RegisterModule("bags", function ()
frame.sort = CreateFrame("Button", "pfBagSort", frame)
frame.sort:SetPoint("TOPRIGHT", frame.keys, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.sort, default_border)
frame.sort:SetHeight(12)
frame.sort:SetWidth(12)
frame.sort:SetSize(12, 12)
frame.sort.texture = frame.sort:CreateTexture("pfBagSortIcon")
frame.sort.texture:SetTexture(pfUI.media["img:sort"])
frame.sort.texture:ClearAllPoints()
@@ -988,7 +985,7 @@ pfUI:RegisterModule("bags", function ()
end)
frame.sort:SetScript("OnClick", function()
libbagsort:Sort({0, 1, 2, 3, 4})
libbagsort:Sort({0, 1, 2, 3, 4}, BagSortOpts())
end)
end
@@ -996,8 +993,7 @@ pfUI:RegisterModule("bags", function ()
if not frame.gold and (C.appearance.bags.movable == "1" or not pfUI.panel) then
frame.gold = CreateFrame("Frame", "pfBagGoldString", frame)
frame.gold:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 1)
frame.gold:SetWidth(200)
frame.gold:SetHeight(18)
frame.gold:SetSize(200, 18)
frame.gold:RegisterEvent("PLAYER_ENTERING_WORLD")
frame.gold:RegisterEvent("PLAYER_MONEY")
frame.gold:SetScript("OnEvent", function()
@@ -1133,8 +1129,7 @@ pfUI:RegisterModule("bags", function ()
frame.close = CreateFrame("Button", "pfBagClose", frame)
frame.close:SetPoint("TOPRIGHT", -default_border*1,-default_border )
CreateBackdrop(frame.close, default_border)
frame.close:SetHeight(12)
frame.close:SetWidth(12)
frame.close:SetSize(12, 12)
frame.close.texture = frame.close:CreateTexture("pfBagClose")
frame.close.texture:SetTexture(pfUI.media["img:close"])
frame.close.texture:ClearAllPoints()
@@ -1159,8 +1154,7 @@ pfUI:RegisterModule("bags", function ()
frame.bags = CreateFrame("Button", "pfBagSlotShow", frame)
frame.bags:SetPoint("TOPRIGHT", frame.close, "TOPLEFT", -default_border*3, 0)
CreateBackdrop(frame.bags, default_border)
frame.bags:SetHeight(12)
frame.bags:SetWidth(12)
frame.bags:SetSize(12, 12)
frame.bags:SetTextColor(1,1,.25,1)
frame.bags:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
frame.bags.texture = frame.bags:CreateTexture("pfBagArrowUp")
@@ -1226,7 +1220,7 @@ pfUI:RegisterModule("bags", function ()
end)
frame.sort:SetScript("OnClick", function()
libbagsort:Sort({-1, 5, 6, 7, 8, 9, 10})
libbagsort:Sort({-1, 5, 6, 7, 8, 9, 10}, BagSortOpts())
end)
end
end
+2 -4
View File
@@ -2,14 +2,12 @@ pfUI:RegisterModule("bgscore", function ()
local bgframe = WorldStateAlwaysUpFrame
if not bgframe then
bgframe = CreateFrame("Frame", "WorldStateAlwaysUpFrame", UIParent)
bgframe:SetWidth(200)
bgframe:SetHeight(25)
bgframe:SetSize(200, 25)
bgframe:SetPoint("TOP", UIParent, "TOP", 0, -100)
end
local mover = CreateFrame("Frame", "pfUIBGScoreMover", UIParent)
mover:SetWidth(220)
mover:SetHeight(30)
mover:SetSize(220, 30)
mover:SetPoint("TOP", UIParent, "TOP", 0, -100)
mover:SetFrameStrata("DIALOG")
mover:SetMovable(true)
+2 -9
View File
@@ -17,12 +17,6 @@ pfUI:RegisterModule("bubbles", function ()
RunNextFrame(function() pfUI.bubbles:ScanBubbles() end)
end)
function pfUI.bubbles:IsBubble(f)
if f:GetName() then return end
if not f:GetRegions() then return end
return f:GetRegions().GetTexture and f:GetRegions():GetTexture() == "Interface\\Tooltips\\ChatBubble-Background"
end
function pfUI.bubbles:ProcessBubble(f)
f.text:Hide()
f.text:SetFont(pfUI.font_default, tonumber(C.global.font_size) * UIParent:GetScale(), "OUTLINE")
@@ -34,9 +28,8 @@ pfUI:RegisterModule("bubbles", function ()
end
function pfUI.bubbles:ScanBubbles()
local childs = { WorldFrame:GetChildren() }
for _, f in pairs(childs) do
if not f.frame and pfUI.bubbles:IsBubble(f) then
for _, f in ipairs(C_ChatBubbles.GetAllChatBubbles()) do
if not f.frame then
local textures = {f:GetRegions()}
for _, object in pairs(textures) do
if object:GetObjectType() == "Texture" then
+23 -24
View File
@@ -138,16 +138,10 @@ pfUI:RegisterModule("buff", function ()
end
end)
buff:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
buff:SetScript("OnLeave", GameTooltip_Hide)
buff:SetScript("OnClick", function()
if CancelItemTempEnchantment and this.mode and this.mode == "MAINHAND" then
CancelItemTempEnchantment(1)
elseif CancelItemTempEnchantment and this.mode and this.mode == "OFFHAND" then
CancelItemTempEnchantment(2)
elseif this.spellId then
if this.spellId then
C_Spell.CancelSpellByID(this.spellId)
end
end)
@@ -277,28 +271,27 @@ pfUI:RegisterModule("buff", function ()
-- config loading
function pfUI.buff:UpdateConfigBuffButton(buff)
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
local buffSize, buffSpacing = tonumber(C.buffs.size), tonumber(C.buffs.spacing)
local rowcount, relFrame, offsetX, offsetY
if buff.btype == "HELPFUL" then
if buff.weapon == 1 and C.buffs.separateweapons == "1" then
rowcount = floor((buff.gid-1) / tonumber(C.buffs.wepbuffrowsize))
relFrame = pfUI.buff.wepbuffs
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.wepbuffrowsize))*(buffSize+2*buffSpacing)
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
else
rowcount = floor((buff.gid-1) / tonumber(C.buffs.buffrowsize))
relFrame = pfUI.buff.buffs
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.buffrowsize))*(buffSize+2*buffSpacing)
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
end
else
rowcount = floor((buff.gid-1) / tonumber(C.buffs.debuffrowsize))
relFrame = pfUI.buff.debuffs
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing))
offsetX = -(buff.gid-1-rowcount*tonumber(C.buffs.debuffrowsize))*(buffSize+2*buffSpacing)
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
end
buff:SetWidth(tonumber(C.buffs.size))
buff:SetHeight(tonumber(C.buffs.size))
buff:SetSize(buffSize, buffSize)
buff:ClearAllPoints()
buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY)
@@ -318,20 +311,26 @@ pfUI:RegisterModule("buff", function ()
function pfUI.buff:UpdateConfig()
local fontsize = C.buffs.fontsize == "-1" and C.global.font_size or C.buffs.fontsize
pfUI.buff.buffs:SetWidth(tonumber(C.buffs.buffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
pfUI.buff.buffs:SetHeight(ceil(32/tonumber(C.buffs.buffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4*tonumber(C.buffs.spacing), 0)
local spacing = tonumber(C.buffs.spacing)
local cell = tonumber(C.buffs.size) + 2 * spacing
local rowextra = C.buffs.textinside == "1" and 0 or (fontsize * 1.5)
local function SizeBuffFrame(frame, rowsize, count)
rowsize = tonumber(rowsize)
frame:SetSize(rowsize * cell, ceil(count / rowsize) * (rowextra + cell))
end
SizeBuffFrame(pfUI.buff.buffs, C.buffs.buffrowsize, 32)
pfUI.buff.buffs:SetPoint("TOPRIGHT", pfUI.minimap or UIParent, "TOPLEFT", -4 * spacing, 0)
UpdateMovable(pfUI.buff.buffs)
pfUI.buff.debuffs:SetWidth(tonumber(C.buffs.debuffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
pfUI.buff.debuffs:SetHeight(ceil(16/tonumber(C.buffs.debuffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
SizeBuffFrame(pfUI.buff.debuffs, C.buffs.debuffrowsize, 16)
pfUI.buff.debuffs:SetPoint("TOPRIGHT", pfUI.buff.buffs, "BOTTOMRIGHT", 0, 0)
UpdateMovable(pfUI.buff.debuffs)
if C.buffs.separateweapons == "1" then
pfUI.buff.wepbuffs:ClearAllPoints()
pfUI.buff.wepbuffs:SetWidth(tonumber(C.buffs.wepbuffrowsize) * (tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
pfUI.buff.wepbuffs:SetHeight(ceil(2/tonumber(C.buffs.wepbuffrowsize)) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+tonumber(C.buffs.size)+2*tonumber(C.buffs.spacing)))
SizeBuffFrame(pfUI.buff.wepbuffs, C.buffs.wepbuffrowsize, 2)
pfUI.buff.wepbuffs:SetPoint("TOPRIGHT", pfUI.buff.debuffs, "BOTTOMRIGHT", 0, 0)
pfUI.buff.wepbuffs:Show()
UpdateMovable(pfUI.buff.wepbuffs)
+13 -25
View File
@@ -162,8 +162,7 @@ pfUI:RegisterModule("buffwatch", function ()
local color = parent.color
local bordercolor = parent.bordercolor
local textcolor = parent.textcolor
local width = parent:GetWidth()
local height = parent:GetHeight()
local width, height = parent:GetSize()
local framename = "pf" .. parent.unit .. ( parent.type == "HARMFUL" and "Debuff" or "Buff" ) .. "Bar" .. bar
local font = parent.config.use_unitfonts == "1" and pfUI.font_unit or pfUI.font_default
@@ -172,8 +171,7 @@ pfUI:RegisterModule("buffwatch", function ()
frame:EnableMouse(1)
frame:Hide()
frame:SetPoint("BOTTOM", 0, (bar-1)*(height+2*border+1))
frame:SetWidth(width)
frame:SetHeight(height)
frame:SetSize(width, height)
frame.bar = CreateFrame("StatusBar", "pfBuffBar" .. bar, frame)
frame.bar:SetPoint("TOPLEFT", frame, "TOPLEFT", height+1, 0)
@@ -202,8 +200,7 @@ pfUI:RegisterModule("buffwatch", function ()
frame.time:SetJustifyH("RIGHT")
frame.icon = frame:CreateTexture(nil, "OVERLAY")
frame.icon:SetWidth(height)
frame.icon:SetHeight(height)
frame.icon:SetSize(height, height)
frame.icon:SetPoint("LEFT", frame, "LEFT", 0, 0)
frame.icon:SetTexCoord(.07,.93,.07,.93)
@@ -225,7 +222,7 @@ pfUI:RegisterModule("buffwatch", function ()
CreateBackdrop(frame)
CreateBackdropShadow(frame)
if bordercolor.r ~= "0" and bordercolor.g ~= "0" and bordercolor.b ~= "0" and bordercolor.a ~= "0" then
if bordercolor.r ~= 0 and bordercolor.g ~= 0 and bordercolor.b ~= 0 and bordercolor.a ~= 0 then
frame.backdrop:SetBackdropBorderColor(bordercolor.r,bordercolor.g,bordercolor.b,1)
end
@@ -422,18 +419,15 @@ pfUI:RegisterModule("buffwatch", function ()
if pfUI.uf.player and C.buffbar.pbuff.enable == "1" then
pfUI.uf.player.buffbar = CreateBuffBarFrame("Player", "HELPFUL")
local config = C.buffbar.pbuff
local r, g, b, a = strsplit(",", config.color)
local br, bg, bb, ba = strsplit(",", config.bordercolor)
local tr, tg, tb, ta = strsplit(",", config.textcolor)
pfUI.uf.player.buffbar:SetWidth(config.width == "-1" and pfUI.uf.player:GetWidth() or config.width)
pfUI.uf.player.buffbar:SetHeight(config.height)
pfUI.uf.player.buffbar.threshold = tonumber(config.threshold)
pfUI.uf.player.buffbar.config = config
pfUI.uf.player.buffbar.buffcmp = config.sort == "asc" and asc or desc
pfUI.uf.player.buffbar.color = { r = r, g = g, b = b, a = a }
pfUI.uf.player.buffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
pfUI.uf.player.buffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
pfUI.uf.player.buffbar.color = GetStringColorObject(config.color)
pfUI.uf.player.buffbar.bordercolor = GetStringColorObject(config.bordercolor)
pfUI.uf.player.buffbar.textcolor = GetStringColorObject(config.textcolor)
pfUI.uf.player.buffbar.anchors = {
pfUI.uf.player,
pfUI.uf.player and pfUI.uf.player.debuffs,
@@ -448,9 +442,6 @@ pfUI:RegisterModule("buffwatch", function ()
-- create player debuffbars
if pfUI.uf.player and C.buffbar.pdebuff.enable == "1" then
local config = C.buffbar.pdebuff
local r, g, b, a = strsplit(",", config.color)
local br, bg, bb, ba = strsplit(",", config.bordercolor)
local tr, tg, tb, ta = strsplit(",", config.textcolor)
pfUI.uf.player.debuffbar = CreateBuffBarFrame("Player", "HARMFUL")
pfUI.uf.player.debuffbar:SetWidth(config.width == "-1" and pfUI.uf.player:GetWidth() or config.width)
@@ -458,9 +449,9 @@ pfUI:RegisterModule("buffwatch", function ()
pfUI.uf.player.debuffbar.threshold = tonumber(config.threshold)
pfUI.uf.player.debuffbar.config = config
pfUI.uf.player.debuffbar.buffcmp = config.sort == "asc" and asc or desc
pfUI.uf.player.debuffbar.color = { r = r, g = g, b = b, a = a }
pfUI.uf.player.debuffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
pfUI.uf.player.debuffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
pfUI.uf.player.debuffbar.color = GetStringColorObject(config.color)
pfUI.uf.player.debuffbar.bordercolor = GetStringColorObject(config.bordercolor)
pfUI.uf.player.debuffbar.textcolor = GetStringColorObject(config.textcolor)
pfUI.uf.player.debuffbar.anchors = {
pfUI.uf.player,
pfUI.uf.player and pfUI.uf.player.buffbar and pfUI.uf.player.buffbar.bars,
@@ -476,18 +467,15 @@ pfUI:RegisterModule("buffwatch", function ()
-- create target debuffbars
if pfUI.uf.target and C.buffbar.tdebuff.enable == "1" then
local config = C.buffbar.tdebuff
local r, g, b, a = strsplit(",", config.color)
local br, bg, bb, ba = strsplit(",", config.bordercolor)
local tr, tg, tb, ta = strsplit(",", config.textcolor)
pfUI.uf.target.debuffbar = CreateBuffBarFrame("Target", "HARMFUL")
pfUI.uf.target.debuffbar:SetWidth(config.width == "-1" and pfUI.uf.target:GetWidth() or config.width)
pfUI.uf.target.debuffbar:SetHeight(config.height)
pfUI.uf.target.debuffbar.config = config
pfUI.uf.target.debuffbar.buffcmp = config.sort == "asc" and asc or desc
pfUI.uf.target.debuffbar.color = { r = r, g = g, b = b, a = a }
pfUI.uf.target.debuffbar.bordercolor = { r = br, g = bg, b = bb, a = ba }
pfUI.uf.target.debuffbar.textcolor = { r = tr, g = tg, b = tb, a = ta }
pfUI.uf.target.debuffbar.color = GetStringColorObject(config.color)
pfUI.uf.target.debuffbar.bordercolor = GetStringColorObject(config.bordercolor)
pfUI.uf.target.debuffbar.textcolor = GetStringColorObject(config.textcolor)
pfUI.uf.target.debuffbar.threshold = tonumber(config.threshold)
pfUI.uf.target.debuffbar.anchors = {
pfUI.uf.target,
+122 -144
View File
@@ -16,9 +16,13 @@ pfUI:RegisterModule("castbar", function ()
end
end
-- Clear cast state on the bar. Shows the bar full for one frame, then
-- OnUpdate fades it out.
local function ClearBar(cb)
-- Clear cast state on the bar and start the fade-out. On a normal end the bar
-- is left at its current fill (a completed cast is already ~full; nothing
-- snaps to full, which used to flash for a frame when the next cast stamped
-- the bar). When `failed` is set for a cast that was actually in progress, the
-- bar flashes full red before fading — the cancelled-cast indicator.
local function ClearBar(cb, failed)
local wasActive = cb.endTime ~= nil
cb.startTime, cb.endTime, cb.isChannel = nil, nil, nil
cb.activeName, cb.spellID = nil, nil
cb.isTradeskill = nil
@@ -26,8 +30,11 @@ pfUI:RegisterModule("castbar", function ()
cb.tradeskillSingleMs, cb.currentCraftStart = nil, nil
cb.lastMax = nil
cb.delay = 0
cb.bar:SetMinMaxValues(1, 100)
cb.bar:SetValue(100)
if failed and wasActive then
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar.failcolor))
cb.bar:SetMinMaxValues(0, 1)
cb.bar:SetValue(1)
end
if cb.bar.spark then cb.bar.spark:Hide() end
cb.fadeout = 1
end
@@ -65,16 +72,21 @@ pfUI:RegisterModule("castbar", function ()
end
-- Mark the start of craft 2..N within an active merge (called from the
-- SPELLCAST_START / SPELL_START_SELF handlers). Resets the per-craft
-- spark to the left edge of the bar.
-- UNIT_SPELLCAST_START handler). Resets the per-craft spark to the left
-- edge of the bar.
local function StartTradeskillCraft(cb)
cb.currentCraftStart = GetTime() * 1000
local remaining = cb.tradeskillTotal - (cb.tradeskillCompleted or 0)
cb.endTime = cb.currentCraftStart + cb.tradeskillSingleMs * remaining
local duration = (cb.endTime - cb.startTime) / 1000
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
UpdateTradeskillLabel(cb)
end
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
-- then animates the fill from this state without touching C_Spell.
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill)
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank)
cb.startTime = startMs
cb.endTime = endMs
cb.isChannel = isChannel
@@ -85,12 +97,15 @@ pfUI:RegisterModule("castbar", function ()
cb:SetAlpha(1)
cb.fadeout = nil
cb.bar:SetStatusBarColor(strsplit(",", C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
local rank = ""
if spellID and GetSpellRecField then
rank = GetSpellRecField(spellID, "rank") or ""
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so
-- it falls back to a lookup.
if not rank and spellID then
rank = C_Spell.GetSpellSubtext(spellID) or ""
end
rank = rank or ""
local spellname = (cb.showname and name) and (name .. " ") or ""
local rankstr = (cb.showrank and rank ~= "") and string.format("|cffaaffcc[%s]|r", rank) or ""
cb.bar.left:SetText(spellname .. rankstr)
@@ -98,8 +113,7 @@ pfUI:RegisterModule("castbar", function ()
if tex and cb.showicon then
local size = cb:GetHeight()
cb.icon:Show()
cb.icon:SetHeight(size)
cb.icon:SetWidth(size)
cb.icon:SetSize(size, size)
cb.icon.texture:SetTexture(tex)
cb.bar:SetPoint("TOPLEFT", cb.icon, "TOPRIGHT", cb.spacing, 0)
else
@@ -118,11 +132,21 @@ pfUI:RegisterModule("castbar", function ()
cb.bar:SetMinMaxValues(0, duration)
cb.lastMax = duration
-- Prime the fill on this frame. StampBar otherwise leaves the previous
-- value in place (ClearBar, run on the prior cast's STOP, leaves it full),
-- so the bar would flash full for the frame between here and the next
-- OnUpdate tick. Reset the throttle too so the timer text updates promptly.
local nowSec = GetTime()
local cur = isChannel and (endMs / 1000 - nowSec) or (nowSec - startMs / 1000)
if cur < 0 then cur = 0 elseif cur > duration then cur = duration end
cb.bar:SetValue(cur)
cb.tick = 0
end
-- One-shot poll: read C_Spell for the bar's unit, stamp or clear. Called
-- from event handlers (cast start, target/focus change), never per-frame.
local function RefreshBar(cb)
local function RefreshBar(cb, rank)
local query = cb.unitstr ~= "" and cb.unitstr or cb.unitname
if not query or (cb.unitstr ~= "" and not UnitExists(cb.unitstr)) then
ClearBar(cb)
@@ -145,7 +169,7 @@ pfUI:RegisterModule("castbar", function ()
end
end
if name and startMs and endMs then
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill)
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank)
else
ClearBar(cb)
end
@@ -164,8 +188,7 @@ pfUI:RegisterModule("castbar", function ()
-- icon
cb.icon = CreateFrame("Frame", nil, cb)
cb.icon:SetPoint("TOPLEFT", 0, 0)
cb.icon:SetHeight(16)
cb.icon:SetWidth(16)
cb.icon:SetSize(16, 16)
cb.icon.texture = cb.icon:CreateTexture(nil, "OVERLAY")
cb.icon.texture:SetAllPoints()
@@ -291,35 +314,27 @@ pfUI:RegisterModule("castbar", function ()
end
end)
-- Cast lifecycle events. Player bars react to vanilla SPELLCAST_*; non-
-- player bars also react to Nampower SPELL_*_OTHER (gated by the
-- NP_EnableSpell{Start,Go}Events CVars, enabled by libdebuff) plus the
-- retarget event. Player events also feed non-player bars for the
-- target=self case.
cb:RegisterEvent("SPELLCAST_START")
cb:RegisterEvent("SPELLCAST_STOP")
cb:RegisterEvent("SPELLCAST_FAILED")
cb:RegisterEvent("SPELLCAST_INTERRUPTED")
cb:RegisterEvent("SPELLCAST_CHANNEL_START")
cb:RegisterEvent("SPELLCAST_CHANNEL_STOP")
cb:RegisterEvent("SPELLCAST_CHANNEL_UPDATE")
cb:RegisterEvent("SPELL_DELAYED_SELF")
-- Chained same-spell recasts never run the client cast path (the 1.12
-- engine short-circuits at spellID == current-cast), so vanilla
-- SPELLCAST_START never fires for them. nampower's SPELL_START_SELF
-- (server-driven) is the only signal that shows them.
cb:RegisterEvent("SPELL_START_SELF")
if unitstr == "player" then
cb:RegisterEvent("SPELL_GO_SELF")
end
if unitstr ~= "player" and unitstr ~= "" then
cb:RegisterEvent("SPELL_START_OTHER")
cb:RegisterEvent("SPELL_FAILED_OTHER")
if unitstr == "target" then
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
elseif unitstr == "focus" then
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
end
-- Cast lifecycle, entirely on ClassicAPI's UNIT_SPELLCAST_* events. They
-- fire per unit token: arg1=="player" for the player's own casts, and the
-- remote token(s) ("target", "focus", ...) for other units -- so one set of
-- events drives every bar with no Nampower dependency. The handler routes an
-- event to this bar when arg1 matches its unit, or -- since the player's own
-- casts only ever fire arg1=="player" -- when the bar's unit resolves to the
-- player (target=self). PLAYER_TARGET/FOCUS_CHANGED re-polls so a unit
-- already mid-cast when it becomes the target/focus still shows.
cb:RegisterEvent("UNIT_SPELLCAST_START")
cb:RegisterEvent("UNIT_SPELLCAST_STOP")
cb:RegisterEvent("UNIT_SPELLCAST_FAILED")
cb:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
cb:RegisterEvent("UNIT_SPELLCAST_DELAYED")
cb:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
cb:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
if unitstr == "target" then
cb:RegisterEvent("PLAYER_TARGET_CHANGED")
elseif unitstr == "focus" then
cb:RegisterEvent("PLAYER_FOCUS_CHANGED")
end
cb:SetScript("OnEvent", function()
@@ -330,83 +345,33 @@ pfUI:RegisterModule("castbar", function ()
return
end
if event == "SPELL_START_OTHER" then
-- arg3=casterGuid. Defer one frame so ClassicAPI's UnitChannelInfo
-- can see the engine's +0x228 broadcast for remote-unit channels
-- (the cohook+packet handler runs in the same frame; the broadcast
-- propagates after).
if arg3 == UnitGUID(unit) then
local target = this
RunNextFrame(function() RefreshBar(target) end)
end
-- UNIT_SPELLCAST_* fire per unit token (arg1). Handle an event when it's
-- for this bar's unit, or -- since the player's own casts only ever fire
-- arg1=="player" -- when this bar's unit currently resolves to the player
-- (target=self / focus=self).
-- Args: arg1=unit, arg2=castGUID, arg3=spellID, arg4=name, arg5=rank.
if arg1 ~= unit and not (arg1 == "player" and UnitIsUnit(unit, "player")) then
return
end
if event == "SPELL_FAILED_OTHER" then
if arg1 == UnitGUID(unit) then ClearBar(this) end
return
end
-- Vanilla SPELLCAST_* + SPELL_DELAYED_SELF fire only for the local
-- player. Non-player bars handle them only when their unit currently
-- resolves to the player (target=self / focus=self).
if not UnitIsUnit(unit, 'player') then return end
if event == "SPELLCAST_START" or event == "SPELLCAST_CHANNEL_START" then
if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- START also fires per craft in a same-spell chain, so an active merge
-- just resyncs the current craft's spark/label instead of restamping.
if this.tradeskillTotal then
-- Mid-chain craft N+1 of N. Keep the merged bar; resync the spark
-- to the new craft's start and refresh the "(N)" count label.
StartTradeskillCraft(this)
else
RefreshBar(this)
RefreshBar(this, arg5)
if this.isTradeskill and (this.pendingTradeskillCount or 0) > 1
and C.castbar.player.mergetradeskill == "1" then
EnterTradeskillMerge(this, this.startTime, this.endTime, this.pendingTradeskillCount)
end
this.pendingTradeskillCount = nil
end
elseif event == "SPELL_START_SELF" then
-- Catches chained same-spell recasts (no SPELLCAST_START fires) —
-- including tradeskill chaining where craft 2..N reuse one spell.
-- Defer one frame so ClassicAPI's SMSG_SPELL_START co-hook has
-- stamped g_cast before we poll, regardless of co-hook order.
if this.tradeskillTotal then
StartTradeskillCraft(this)
else
local target = this
RunNextFrame(function()
-- Re-check: SPELL_START_SELF (nampower co-hook) fires before vanilla
-- SPELLCAST_START on the same packet, so SPELLCAST_START may have
-- entered merge in this same frame. Don't restamp over it.
if target.tradeskillTotal then
StartTradeskillCraft(target)
return
end
RefreshBar(target)
if target.unitstr == "player" and target.isTradeskill
and (target.pendingTradeskillCount or 0) > 1
and C.castbar.player.mergetradeskill == "1" then
EnterTradeskillMerge(target, target.startTime, target.endTime, target.pendingTradeskillCount)
end
target.pendingTradeskillCount = nil
end)
end
elseif event == "SPELLCAST_CHANNEL_STOP" then
-- A channel's stop can arrive after a following cast already claimed
-- the bar (channel->cast transition); only clear if a channel is
-- actually being shown, so it doesn't wipe an active cast bar.
if this.isChannel then ClearBar(this) end
elseif event == "SPELLCAST_STOP" then
-- During a tradeskill chain, SPELL_GO_SELF already counted this craft
-- and either cleared the bar (chain done) or kept it running. Only a
-- non-merge cast clears here.
if not this.tradeskillTotal then ClearBar(this) end
elseif event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" then
ClearBar(this)
elseif event == "SPELL_GO_SELF" then
-- arg2 = spellId. In a tradeskill merge, count each successful craft
-- and clear when the chain is done.
if this.tradeskillTotal and arg2 == this.tradeskillSpellID then
elseif event == "UNIT_SPELLCAST_SUCCEEDED" then
-- Per-craft completion during a tradeskill merge (arg3 = spellID):
-- count and clear when the chain is done. A no-op for normal casts,
-- which are cleared by UNIT_SPELLCAST_STOP.
if this.tradeskillTotal and arg3 == this.tradeskillSpellID then
this.tradeskillCompleted = (this.tradeskillCompleted or 0) + 1
if this.tradeskillCompleted >= this.tradeskillTotal then
ClearBar(this)
@@ -414,33 +379,46 @@ pfUI:RegisterModule("castbar", function ()
UpdateTradeskillLabel(this)
end
end
elseif event == "SPELL_DELAYED_SELF" then
-- Cast pushback. nampower's event carries the delay (arg2); apply it
-- locally rather than re-polling, so the bar doesn't depend on
-- ClassicAPI's SMSG_SPELL_DELAYED co-hook having bumped g_cast before
-- this fires (co-hook order vs nampower is not guaranteed).
if not this.endTime or not arg2 then return end
local delayMs = tonumber(arg2) or 0
if delayMs > 0 then
this.delay = (this.delay or 0) + delayMs / 1000
this.endTime = this.endTime + delayMs
local newDuration = (this.endTime - this.startTime) / 1000
this.bar:SetMinMaxValues(0, newDuration)
this.lastMax = newDuration
end
elseif event == "SPELLCAST_CHANNEL_UPDATE" then
-- Channel pushback. ClassicAPI doesn't track channel delay in
-- g_channel, so we adjust endTime + delay locally and resize the
-- bar so OnUpdate animates against the new total.
if not this.endTime or not arg1 then return end
local newEndMs = GetTime() * 1000 + arg1
local diff = this.endTime - newEndMs
if diff > 50 then
this.delay = (this.delay or 0) + diff / 1000
this.endTime = newEndMs
local newDuration = (this.endTime - this.startTime) / 1000
this.bar:SetMinMaxValues(0, newDuration)
this.lastMax = newDuration
elseif event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- A channel's stop can arrive after a following cast already claimed
-- the bar (channel->cast transition); only clear if a channel is
-- actually being shown, so it doesn't wipe an active cast bar.
if this.isChannel then ClearBar(this) end
elseif event == "UNIT_SPELLCAST_STOP" then
-- STOP fires between crafts in a merge too (each craft is a new cast);
-- UNIT_SPELLCAST_SUCCEEDED owns the count, so only a non-merge cast
-- clears here.
if not this.tradeskillTotal then ClearBar(this) end
elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then
ClearBar(this, true)
elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
-- Pushback: a cast delayed later or a channel shortened. Following
-- Quartz, re-poll just the times and accumulate the shift into a running
-- this.delay for the +/- indicator, rather than a full restamp (which
-- would reset that total and re-render icon/text). ClassicAPI moves
-- endMs (SpellDelayed_h bumps g_cast.endMs; the MSG_CHANNEL_UPDATE
-- co-hook rewrites g_channel.endMs) while startMs stays put, so we diff
-- endMs. delay is a positive magnitude; OnUpdate signs it "+" for casts
-- and "-" for channels. Skipped during a tradeskill merge.
if this.endTime and not this.tradeskillTotal then
local query = this.unitstr ~= "" and this.unitstr or this.unitname
local startMs, endMs
if this.isChannel then
local _, _, _, s, e = C_Spell.UnitChannelInfo(query)
startMs, endMs = s, e
else
local _, _, _, s, e = C_Spell.UnitCastingInfo(query)
startMs, endMs = s, e
end
if startMs and endMs then
local shift = this.isChannel and (this.endTime - endMs) or (endMs - this.endTime)
this.delay = (this.delay or 0) + shift / 1000
this.startTime = startMs
this.endTime = endMs
local newDuration = (endMs - startMs) / 1000
this.bar:SetMinMaxValues(0, newDuration)
this.lastMax = newDuration
end
end
end
end)
@@ -486,11 +464,11 @@ pfUI:RegisterModule("castbar", function ()
UpdateMovable(pfUI.castbar.player)
-- Tradeskill merge: hook DoTradeSkill so the player castbar knows the
-- requested count before the first SPELLCAST_START fires. Always-on hook
-- (the config knob is read at event time so toggling takes effect on the
-- next craft without a /reload). DoTradeSkill is synchronous; the server
-- roundtrip to SPELLCAST_START gives us plenty of time after this hook.
pfUI.hooksecurefunc("DoTradeSkill", function(index, num)
-- requested count before the first UNIT_SPELLCAST_START fires. Always-on
-- hook (the config knob is read at event time so toggling takes effect on
-- the next craft without a /reload). DoTradeSkill is synchronous; the
-- server roundtrip to UNIT_SPELLCAST_START gives us plenty of time.
hooksecurefunc("DoTradeSkill", function(index, num)
if pfUI.castbar.player then
pfUI.castbar.player.pendingTradeskillCount = tonumber(num) or 1
end
+10 -10
View File
@@ -24,7 +24,7 @@ pfUI:RegisterModule("chat", function ()
end
end
pfUI.hooksecurefunc("UnitPopup_OnClick", function(self)
hooksecurefunc("UnitPopup_OnClick", function(self)
if this.value == "IGNORE_PLAYER" then
AddIgnore(_G[UIDROPDOWNMENU_INIT_MENU].name)
end
@@ -82,10 +82,10 @@ pfUI:RegisterModule("chat", function ()
end
if C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
pfUI.chat.left.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
pfUI.chat.left.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -257,10 +257,10 @@ pfUI:RegisterModule("chat", function ()
end
if C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
pfUI.chat.right.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
pfUI.chat.right.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -429,7 +429,7 @@ pfUI:RegisterModule("chat", function ()
_G["ChatFrame" .. i .. "TabFlash"].Show = function() return end
end
local _, class = UnitClass("player")
local class = UnitClassBase("player")
local classColor = PFUI_CLASS_COLORS[class]
_G["ChatFrame" .. i .. "TabText"]:SetTextColor((classColor.r + .3) * .5, (classColor.g + .3) * .5, (classColor.b + .3) * .5, 1)
_G["ChatFrame" .. i .. "TabText"]:SetFont(panelfont,panelfont_size, "OUTLINE")
@@ -449,7 +449,7 @@ pfUI:RegisterModule("chat", function ()
end
end
pfUI.hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
hooksecurefunc("FCF_SaveDock", pfUI.chat.RefreshChat)
if C.chat.global.tabmouse == "1" then
pfUI.chat.mouseovertab = CreateFrame("Frame")
@@ -675,7 +675,7 @@ pfUI:RegisterModule("chat", function ()
end
-- read and parse whisper color settings
local cr, cg, cb, ca = strsplit(",", C.chat.global.whisper)
local cr, cg, cb, ca = GetStringColor(C.chat.global.whisper)
cr, cg, cb = tonumber(cr), tonumber(cg), tonumber(cb)
local wcol = rgbhex(cr, cg, cb)
@@ -706,10 +706,10 @@ pfUI:RegisterModule("chat", function ()
_G.CHAT_WHISPER_INFORM_GET = '[W]' .. default
end
local r,g,b,a = strsplit(",", C.chat.text.timecolor)
local r,g,b,a = GetStringColor(C.chat.text.timecolor)
local timecolorhex = rgbhex(r,g,b,a)
local r,g,b = strsplit(",", C.chat.text.unknowncolor)
local r,g,b = GetStringColor(C.chat.text.unknowncolor)
local unknowncolorhex = rgbhex(r,g,b)
-- Suppress FriendsFrame's WHO_LIST_UPDATE handling for our DLL-issued
+1 -2
View File
@@ -96,8 +96,7 @@ pfUI:RegisterModule("chatcopy", function ()
scroll:Hide()
local editbox = CreateFrame("EditBox", "pfChatCopyBox" .. i, scroll)
editbox:SetHeight(frame:GetHeight())
editbox:SetWidth(frame:GetWidth())
editbox:SetSize(frame:GetSize())
editbox:SetAllPoints(scroll)
editbox:SetTextColor(1,1,1,1)
editbox:SetFontObject(ChatFontNormal)
+1 -1
View File
@@ -5,7 +5,7 @@ pfUI:RegisterModule("combopoints", function ()
ComboFrame:Hide()
ComboFrame:UnregisterAllEvents()
local _, class = UnitClass("player")
local class = UnitClassBase("player")
local combo_width = C["unitframes"]["combowidth"]
local combo_height = C["unitframes"]["comboheight"]
pfUI.combopoints = {}
+6 -6
View File
@@ -1,10 +1,10 @@
pfUI:RegisterModule("cooldown", function ()
-- cache values
local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
-- local lowcolor = {strsplit(",", C.appearance.cd.lowcolor)}
-- local normalcolor = {strsplit(",", C.appearance.cd.normalcolor)}
-- local minutecolor = {strsplit(",", C.appearance.cd.minutecolor)}
-- local hourcolor = {strsplit(",", C.appearance.cd.hourcolor)}
-- local daycolor = {strsplit(",", C.appearance.cd.daycolor)}
local parent, parent_name
local function pfCooldownOnUpdate()
@@ -136,5 +136,5 @@ pfUI:RegisterModule("cooldown", function ()
-- vanilla does not have a cooldown frame type, so we hook the
-- regular SetTimer function that each one is calling.
pfUI.hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
hooksecurefunc("CooldownFrame_SetTimer", SetCooldown)
end)
+2 -4
View File
@@ -123,8 +123,7 @@ pfUI:RegisterModule("easteregg", function ()
local f = GetExplosion()
f:ClearAllPoints()
f:SetPoint("CENTER", fireworks, "TOPLEFT", x, y)
f:SetWidth(25)
f:SetHeight(25)
f:SetSize(25, 25)
f.tex:SetTexture(1,1,1,.5)
f:SetAlpha(1)
f:Show()
@@ -134,8 +133,7 @@ pfUI:RegisterModule("easteregg", function ()
local f = GetExplosion()
f:ClearAllPoints()
f:SetPoint("CENTER", fireworks, "TOPLEFT", x+(math.random(0,100)-50), y+(math.random(0,100)-50))
f:SetWidth(2)
f:SetHeight(2)
f:SetSize(2, 2)
f.tex:SetTexture(math.random(),math.random(),math.random(),1)
f:SetAlpha(1)
f:Show()
+7 -7
View File
@@ -2,7 +2,7 @@ local function getAdjustedTickTimer()
local adjustedEnergyTick = 2
-- Check rogue talents and compute energy tick timing reduction for Combat spec (1.18.0 Blade Rush Talent)
if UnitClass("player") == "Rogue" then
if UnitClassBase("player") == "ROGUE" then
local _, _, _, _, currRank = GetTalentInfo(2, 16)
local bladeRushRank = currRank or 0
@@ -32,10 +32,10 @@ pfUI:RegisterModule("energytick", function()
energytick:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS")
energytick:SetScript("OnEvent", function()
if UnitPowerType("player") == 0 and C.unitframes.player.manatick == "1" then
if UnitPowerType("player") == Enum.PowerType.Mana and C.unitframes.player.manatick == "1" then
this.mode = "MANA"
this:Show()
elseif UnitPowerType("player") == 3 and C.unitframes.player.energy == "1" then
elseif UnitPowerType("player") == Enum.PowerType.Energy and C.unitframes.player.energy == "1" then
this.mode = "ENERGY"
this:Show()
else
@@ -51,11 +51,11 @@ pfUI:RegisterModule("energytick", function()
end
if event == "PLAYER_ENTERING_WORLD" then
this.lastMana = UnitMana("player")
this.lastMana = UnitPower("player")
end
if (event == "UNIT_MANA" or event == "UNIT_ENERGY") and arg1 == "player" then
this.currentMana = UnitMana("player")
this.currentMana = UnitPower("player")
local diff = 0
if this.lastMana then
diff = this.currentMana - this.lastMana
@@ -64,7 +64,7 @@ pfUI:RegisterModule("energytick", function()
if this.mode == "MANA" and diff < 0 then
this.target = 5
elseif this.mode == "MANA" and diff > 0 then
if UnitMana("player") >= UnitManaMax("player") then
if UnitPower("player") >= UnitPowerMax("player") then
this.start = nil
this.spark:SetAlpha(0)
this:Hide()
@@ -105,7 +105,7 @@ pfUI:RegisterModule("energytick", function()
if this.current > this.max then
-- Don't restart tick timer if mana is full
if this.mode == "MANA" and UnitMana("player") >= UnitManaMax("player") then
if this.mode == "MANA" and UnitPower("player") >= UnitPowerMax("player") then
this.start = nil
this.spark:SetAlpha(0)
return
+4 -6
View File
@@ -88,7 +88,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevMerchant = ShoppingTooltip1.SetMerchantCompareItem
local function SetMerchantCompareItem(self, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
if compareItem == 1 then
ShowCompareItem(nil, GetMerchantItemLink(index), 1)
return false
end
@@ -97,7 +97,7 @@ pfUI:RegisterModule("eqcompare", function ()
local prevAuction = ShoppingTooltip1.SetAuctionCompareItem
local function SetAuctionCompareItem(self, type, index, compareItem)
if C.tooltip.compare.basestats == "1" and compareItem == 1 then
if compareItem == 1 then
ShowCompareItem(nil, GetAuctionItemLink(type, index), 1)
return false
end
@@ -132,15 +132,13 @@ pfUI:RegisterModule("eqcompare", function ()
local function makeHook(getter)
return function(tooltip, arg1, arg2, arg3)
if C.tooltip.compare.basestats == "1" then
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
end
ShowCompareItem(tooltip, getter(arg1, arg2, arg3))
end
end
local function HookTooltip(tooltip)
for setter, getter in pairs(TooltipHooks) do
pfUI.hooksecurefunc(tooltip, setter, makeHook(getter))
hooksecurefunc(tooltip, setter, makeHook(getter))
end
end
+70 -268
View File
@@ -1,11 +1,10 @@
-- Equipment Manager module
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's
-- C_EquipmentSet.* API. Adds a 6th tab to CharacterFrame.
-- Backport of the 4.3.4 GearManagerDialog UI on top of ClassicAPI's C_EquipmentSet.* API.
pfUI:RegisterModule("equipmentmanager", function()
if not C_EquipmentSet or not C_EquipmentSet.CanUseEquipmentSets() then return end
pfUI.equipmentmanager = pfUI.equipmentmanager or {}
pfUI.equipmentmanager = {}
local SET_ROW_HEIGHT = 36
@@ -55,7 +54,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local function EquipSet(setID)
if not setID then return end
if C_EquipmentSet.EquipmentSetContainsLockedItems(setID) then
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT or "Locked items in set", 1, .1, .1, 1)
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1, .1, .1, 1)
return
end
ClearCursor()
@@ -68,8 +67,7 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================
local frame = CreateFrame("Frame", "pfEquipmentManagerFrame", CharacterFrame)
frame:SetWidth(220)
frame:SetHeight(350)
frame:SetSize(220, 350)
frame:SetFrameStrata("HIGH")
frame:SetScript("OnShow", function()
this:ClearAllPoints()
@@ -86,7 +84,7 @@ pfUI:RegisterModule("equipmentmanager", function()
frame.title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
frame.title:SetPoint("TOP", frame, "TOP", 0, -10)
frame.title:SetText(T["Equipment Manager"] or "Equipment Manager")
frame.title:SetText(EQUIPMENT_MANAGER)
local closeBtn = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
@@ -98,15 +96,14 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================
local toggleBtn = CreateFrame("Button", "pfEqMgrToggleButton", PaperDollFrame)
toggleBtn:SetWidth(28)
toggleBtn:SetHeight(28)
toggleBtn:SetSize(28, 28)
toggleBtn:SetPoint("BOTTOM", CharacterHandsSlot, "TOP", 0, 4)
toggleBtn:SetNormalTexture(pfUI.path.."\\img\\UI-GearManager-Button")
toggleBtn:SetPushedTexture(pfUI.path.."\\img\\UI-GearManager-Button-Pushed")
toggleBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
toggleBtn:SetScript("OnEnter", function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(T["Equipment Manager"] or "Equipment Manager")
GameTooltip:SetText(PAPERDOLL_EQUIPMENTMANAGER)
GameTooltip:Show()
end)
toggleBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
@@ -126,13 +123,13 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================
local btnEquip = CreateFrame("Button", "pfEqMgrEquip", frame, "UIPanelButtonTemplate")
btnEquip:SetWidth(86); btnEquip:SetHeight(22); btnEquip:SetText(T["Equip"] or "Equip")
btnEquip:SetSize(86, 22); btnEquip:SetText(EQUIPSET_EQUIP)
btnEquip:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -30)
SkinButton(btnEquip)
btnEquip:SetScript("OnClick", function() EquipSet(selectedSetID) end)
local btnSave = CreateFrame("Button", "pfEqMgrSave", frame, "UIPanelButtonTemplate")
btnSave:SetWidth(86); btnSave:SetHeight(22); btnSave:SetText(T["Save"] or "Save")
btnSave:SetSize(86, 22); btnSave:SetText(SAVE)
btnSave:SetPoint("LEFT", btnEquip, "RIGHT", 6, 0)
SkinButton(btnSave)
btnSave:SetScript("OnClick", function()
@@ -141,7 +138,7 @@ pfUI:RegisterModule("equipmentmanager", function()
if not name then return end
local targetID = selectedSetID
StaticPopupDialogs["PFUI_EQMGR_SAVE_CONFIRM"] = {
text = string.format(T["Would you like to save the equipment set '%s'?"] or "Would you like to save the equipment set '%s'?", name),
text = string.format(CONFIRM_SAVE_EQUIPMENT_SET, name),
button1 = YES, button2 = NO,
OnAccept = function()
C_EquipmentSet.ClearIgnoredSlotsForSave()
@@ -166,7 +163,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent)
rowMenu:SetFrameStrata("DIALOG")
rowMenu:SetWidth(140); rowMenu:SetHeight(50)
rowMenu:SetSize(140, 50);
rowMenu:Hide()
CreateBackdrop(rowMenu, nil, nil, .95)
CreateBackdropShadow(rowMenu)
@@ -185,9 +182,9 @@ pfUI:RegisterModule("equipmentmanager", function()
tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
rowMenu.changeBtn:SetWidth(130); rowMenu.changeBtn:SetHeight(20)
rowMenu.changeBtn:SetSize(130, 20);
rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3)
rowMenu.changeBtn:SetText(T["Change Name/Icon"] or "Change Name/Icon")
rowMenu.changeBtn:SetText(EQUIPMENT_SET_EDIT)
SkinButton(rowMenu.changeBtn)
rowMenu.changeBtn:SetScript("OnClick", function()
rowMenu:Hide()
@@ -198,9 +195,9 @@ pfUI:RegisterModule("equipmentmanager", function()
end)
rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
rowMenu.deleteBtn:SetWidth(130); rowMenu.deleteBtn:SetHeight(20)
rowMenu.deleteBtn:SetSize(130, 20);
rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2)
rowMenu.deleteBtn:SetText(T["Delete"] or "Delete")
rowMenu.deleteBtn:SetText(DELETE)
SkinButton(rowMenu.deleteBtn)
rowMenu.deleteBtn:SetScript("OnClick", function()
rowMenu:Hide()
@@ -208,7 +205,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local targetID = rowMenu.targetSetID
local name = C_EquipmentSet.GetEquipmentSetInfo(targetID)
StaticPopupDialogs["PFUI_EQMGR_DELETE"] = {
text = string.format(T["Delete equipment set '%s'?"] or "Delete equipment set '%s'?", name or "?"),
text = string.format(CONFIRM_DELETE_EQUIPMENT_SET, name or "?"),
button1 = YES, button2 = NO,
OnAccept = function()
pendingIgnoredToggles[targetID] = nil
@@ -229,9 +226,8 @@ pfUI:RegisterModule("equipmentmanager", function()
local LIST_ROW_STRIDE = SET_ROW_HEIGHT + 2
local listFrame = CreateFrame("Frame", nil, frame)
listFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -58)
listFrame:SetWidth(180)
-- Height fits N rows + (N-1) inter-row gaps + 3px top/bottom padding.
listFrame:SetHeight(LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
listFrame:SetSize(180, LIST_VISIBLE_ROWS * LIST_ROW_STRIDE + 4)
CreateBackdrop(listFrame, nil, nil, .75)
-- Mouse-wheel scroll: list of [sets..., newSetRow] is virtualized
@@ -247,15 +243,13 @@ pfUI:RegisterModule("equipmentmanager", function()
local setRows = {}
local function CreateSetRow()
local row = CreateFrame("Button", nil, listFrame)
row:SetWidth(170)
row:SetHeight(SET_ROW_HEIGHT)
row:SetSize(170, SET_ROW_HEIGHT)
-- Position is set per-Refresh based on the row's visible slot;
-- start anchored to avoid uninitialized geometry before first Refresh.
row:SetPoint("TOPLEFT", listFrame, "TOPLEFT", 5, -3)
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetWidth(30)
row.icon:SetHeight(30)
row.icon:SetSize(30, 30)
row.icon:SetPoint("LEFT", row, "LEFT", 2, 0)
row.icon:SetTexCoord(.08, .92, .08, .92)
@@ -270,7 +264,7 @@ pfUI:RegisterModule("equipmentmanager", function()
row.highlight:Hide()
row.gear = CreateFrame("Button", nil, row)
row.gear:SetWidth(16); row.gear:SetHeight(16)
row.gear:SetSize(16, 16);
row.gear:SetPoint("RIGHT", row, "RIGHT", -4, 0)
row.gear.tex = row.gear:CreateTexture(nil, "ARTWORK")
row.gear.tex:SetAllPoints(row.gear)
@@ -306,27 +300,27 @@ pfUI:RegisterModule("equipmentmanager", function()
pfUI.equipmentmanager.Refresh()
end)
-- Hover handling: OnLeave fires when the cursor moves onto a child
-- (the gear button becomes the topmost mouse target). Use an
-- OnUpdate poll so the gear stays shown while the cursor is over
-- the row OR the gear itself.
-- Show the gear and tooltip while the cursor is over the row or its
-- gear child. The gear sits inside the row's rectangle, so a single
-- MouseIsOver(row) check covers both. OnLeave on the row and the gear
-- catches every exit path, so no OnUpdate poll is needed.
local function HideRowHover()
if MouseIsOver(row) then return end
GameTooltip:Hide()
row.gear:Hide()
end
row:SetScript("OnEnter", function()
if not this.setID then return end
local name = C_EquipmentSet.GetEquipmentSetInfo(this.setID)
if not row.setID then return end
local name = C_EquipmentSet.GetEquipmentSetInfo(row.setID)
if name then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetOwner(row, "ANCHOR_RIGHT")
GameTooltip:SetEquipmentSet(name)
GameTooltip:Show()
end
row.gear:Show()
this:SetScript("OnUpdate", function()
if not MouseIsOver(this) and not MouseIsOver(row.gear) then
this:SetScript("OnUpdate", nil)
GameTooltip:Hide()
row.gear:Hide()
end
end)
end)
row:SetScript("OnLeave", HideRowHover)
row.gear:SetScript("OnLeave", HideRowHover)
return row
end
@@ -338,10 +332,10 @@ pfUI:RegisterModule("equipmentmanager", function()
-- specific set index (row[i] always shows ids[i]).
local newSetRow = CreateFrame("Button", nil, listFrame)
newSetRow:SetWidth(170); newSetRow:SetHeight(SET_ROW_HEIGHT)
newSetRow:SetSize(170, SET_ROW_HEIGHT);
newSetRow.icon = newSetRow:CreateTexture(nil, "ARTWORK")
newSetRow.icon:SetWidth(24); newSetRow.icon:SetHeight(24)
newSetRow.icon:SetSize(24, 24);
newSetRow.icon:SetPoint("LEFT", newSetRow, "LEFT", 5, 0)
newSetRow.icon:SetTexture(pfUI.path.."\\img\\Character-Plus")
@@ -349,7 +343,7 @@ pfUI:RegisterModule("equipmentmanager", function()
newSetRow.text:SetPoint("LEFT", newSetRow.icon, "RIGHT", 8, 0)
newSetRow.text:SetPoint("RIGHT", newSetRow, "RIGHT", -4, 0)
newSetRow.text:SetJustifyH("LEFT")
newSetRow.text:SetText(T["New Set"] or "New Set")
newSetRow.text:SetText(PAPERDOLL_NEWEQUIPMENTSET)
newSetRow.text:SetTextColor(0.2, 1, 0.2)
newSetRow.highlight = newSetRow:CreateTexture(nil, "BACKGROUND")
@@ -362,8 +356,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local function MakeButton(name, label, parent, anchor, ax, ay, width)
local b = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
b:SetWidth(width or 70)
b:SetHeight(22)
b:SetSize(width or 70, 22)
b:SetText(label)
b:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", ax, ay)
SkinButton(b)
@@ -376,8 +369,7 @@ pfUI:RegisterModule("equipmentmanager", function()
local namePopup = CreateFrame("Frame", "pfEqMgrNamePopup", UIParent)
namePopup:SetFrameStrata("DIALOG")
namePopup:SetWidth(472)
namePopup:SetHeight(498)
namePopup:SetSize(472, 484)
namePopup:SetPoint("CENTER", UIParent, "CENTER")
namePopup:Hide()
CreateBackdrop(namePopup, nil, nil, .9)
@@ -388,200 +380,28 @@ pfUI:RegisterModule("equipmentmanager", function()
namePopup:SetScript("OnDragStart", function() this:StartMoving() end)
namePopup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
namePopup.title = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.title:SetPoint("TOP", namePopup, "TOP", 0, -10)
namePopup.title:SetText(T["Save Set"] or "Save Set")
-- Equipment seed leads the grid with gear-relevant item icons. The widget
-- owns the name field, preview, grid, filter, and search; this module keeps
-- the OK/Cancel buttons and the create/save/rename flow below.
local iconPicker = CreateIconPicker("pfEqMgrIcon", namePopup,
IconDataProviderExtraType.Equipment, GEARSETS_POPUP_TEXT)
namePopup.nameLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.nameLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -34)
namePopup.nameLabel:SetText(T["Enter Set Name (Max 16 Characters):"] or "Enter Set Name (Max 16 Characters):")
local btnPopupOK = MakeButton("pfEqMgrPopupOK", OKAY, namePopup, namePopup, 14, -340, 80)
namePopup.editbox = CreateFrame("EditBox", "pfEqMgrNameEdit", namePopup, "InputBoxTemplate")
namePopup.editbox:SetWidth(280)
namePopup.editbox:SetHeight(20)
namePopup.editbox:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -52)
namePopup.editbox:SetAutoFocus(false)
namePopup.editbox:SetMaxLetters(16)
CreateBackdrop(namePopup.editbox)
namePopup.selectedLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.selectedLabel:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -28)
namePopup.selectedLabel:SetText(T["Currently Selected"] or "Currently Selected")
namePopup.selectedLabel:SetTextColor(1, 0.82, 0)
namePopup.selectedPreview = CreateFrame("Frame", nil, namePopup)
namePopup.selectedPreview:SetWidth(42)
namePopup.selectedPreview:SetHeight(42)
namePopup.selectedPreview:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", -14, -44)
CreateBackdrop(namePopup.selectedPreview)
namePopup.selectedPreview.tex = namePopup.selectedPreview:CreateTexture(nil, "ARTWORK")
namePopup.selectedPreview.tex:SetAllPoints(namePopup.selectedPreview)
namePopup.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
namePopup.iconLabel = namePopup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
namePopup.iconLabel:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -100)
namePopup.iconLabel:SetText(T["Choose an Icon:"] or "Choose an Icon:")
-- Icon picker: 10×8 grid of buttons + scroll
local ICON_GRID_COLS = 10
local ICON_GRID_ROWS = 8
local ICON_BTN_SIZE = 36
local ICON_BTN_PAD = 6
local iconScroll = CreateFrame("ScrollFrame", "pfEqMgrIconScroll", namePopup, "FauxScrollFrameTemplate")
iconScroll:SetPoint("TOPLEFT", namePopup, "TOPLEFT", 14, -130)
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
-- IconDataProviderMixin owns the icon DB, dedup, and lazy load.
-- Init on first picker open; release on hide so the cache GCs.
local provider = nil
-- Selection is tracked by PATH (not index) so it survives filter
-- changes: a spell icon you picked still saves correctly even after
-- you switch the filter to "Items" and it's no longer in the visible list.
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
local selectedIconPath = QUESTION_MARK
local function EnsureProvider()
if not provider then
provider = CreateAndInitFromMixin(IconDataProviderMixin,
IconDataProviderExtraType.Equipment)
end
end
-- Anchor scrollbar to iconScroll's right edge so its position tracks
-- the icon grid rather than the popup. -16/+16 vertical insets are the
-- standard up/down arrow spacing for UIPanelScrollBarTemplate.
local scrollbar = _G["pfEqMgrIconScrollScrollBar"]
if scrollbar then
scrollbar:ClearAllPoints()
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
SkinScrollbar(scrollbar)
end
-- Filter dropdown: "All Icons" / "Spells" / "Items" (top right of icon area).
local filterDropdown = CreateFrame("Frame", "pfEqMgrIconFilter", namePopup, "UIDropDownMenuTemplate")
filterDropdown:SetPoint("TOPRIGHT", namePopup, "TOPRIGHT", 0, -94)
local currentFilter = "all"
local function ApplyFilter(value)
currentFilter = value
UIDropDownMenu_SetSelectedValue(filterDropdown, value)
if provider then
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
else provider:SetIconTypes(nil) end
-- Don't reset selection — selectedIconPath persists. If the
-- selected icon isn't in the new filter, no grid entry will be
-- highlighted but Save will still write the chosen icon.
pfUI.equipmentmanager.RefreshIconGrid()
end
end
UIDropDownMenu_Initialize(filterDropdown, function()
local info
info = {}; info.text = T["All Icons"] or "All Icons"; info.value = "all"
info.func = function() ApplyFilter("all") end
info.checked = currentFilter == "all"
UIDropDownMenu_AddButton(info)
info = {}; info.text = T["Spells"] or "Spells"; info.value = "spells"
info.func = function() ApplyFilter("spells") end
info.checked = currentFilter == "spells"
UIDropDownMenu_AddButton(info)
info = {}; info.text = T["Items"] or "Items"; info.value = "items"
info.func = function() ApplyFilter("items") end
info.checked = currentFilter == "items"
UIDropDownMenu_AddButton(info)
end)
UIDropDownMenu_SetWidth(120, filterDropdown)
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
SkinDropDown(filterDropdown)
local iconButtons = {}
for r = 1, ICON_GRID_ROWS do
for c = 1, ICON_GRID_COLS do
local i = (r - 1) * ICON_GRID_COLS + c
local btn = CreateFrame("Button", nil, namePopup)
btn:SetWidth(ICON_BTN_SIZE)
btn:SetHeight(ICON_BTN_SIZE)
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
CreateBackdrop(btn)
btn.texture = btn:CreateTexture(nil, "ARTWORK")
btn.texture:SetAllPoints(btn)
btn.texture:SetTexCoord(.08, .92, .08, .92)
btn.gridIndex = i
btn:SetScript("OnClick", function()
if this.iconIndex and provider then
local path = provider:GetIconByIndex(this.iconIndex)
if path then selectedIconPath = path end
pfUI.equipmentmanager.RefreshIconGrid()
end
end)
btn:SetScript("OnEnter", function()
if not this.iconIndex or not provider then return end
local path = provider:GetIconByIndex(this.iconIndex)
if type(path) == "string" then
local name = string.gsub(path, "^.-INTERFACE\\\\ICONS\\\\", "")
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(name)
GameTooltip:Show()
end
end)
btn:SetScript("OnLeave", GameTooltip_Hide)
iconButtons[i] = btn
end
end
function pfUI.equipmentmanager.RefreshIconGrid()
EnsureProvider()
local numIcons = provider:GetNumIcons()
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
local offset = FauxScrollFrame_GetOffset(iconScroll)
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
local listIdx = i + offset * ICON_GRID_COLS
local btn = iconButtons[i]
if listIdx <= numIcons then
btn:Show()
btn.iconIndex = listIdx
local path = provider:GetIconByIndex(listIdx)
btn.texture:SetTexture(path)
if path == selectedIconPath then
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
else
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
end
else
btn:Hide()
btn.iconIndex = nil
end
end
-- Sync the "Currently Selected" preview from the path directly so it
-- still shows the chosen icon when filtered out of the grid.
namePopup.selectedPreview.tex:SetTexture(selectedIconPath)
end
iconScroll:SetScript("OnVerticalScroll", function()
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() pfUI.equipmentmanager.RefreshIconGrid() end)
end)
namePopup:SetScript("OnHide", function()
if provider then provider:Release(); provider = nil end
end)
local btnPopupOK = MakeButton("pfEqMgrPopupOK", T["OK"] or "OK", namePopup, namePopup, 14, -340, 80)
btnPopupOK:ClearAllPoints()
btnPopupOK:SetPoint("BOTTOMLEFT", namePopup, "BOTTOMLEFT", 14, 12)
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", T["Cancel"] or "Cancel", namePopup, namePopup, 0, 0, 80)
local btnPopupCancel = MakeButton("pfEqMgrPopupCancel", CANCEL, namePopup, namePopup, 0, 0, 80)
btnPopupCancel:ClearAllPoints()
btnPopupCancel:SetPoint("BOTTOMRIGHT", namePopup, "BOTTOMRIGHT", -14, 12)
btnPopupCancel:SetScript("OnClick", function() namePopup:Hide() end)
-- Okay | Cancel, both anchored to the bottom-right corner.
btnPopupOK:ClearAllPoints()
btnPopupOK:SetPoint("BOTTOMRIGHT", btnPopupCancel, "BOTTOMLEFT", -6, 0)
btnPopupOK:SetScript("OnClick", function()
local name = namePopup.editbox:GetText()
if not name or name == "" then return end
-- Strip the prefix to match ClassicAPI's persisted short-form basenames.
local iconForSave = string.gsub(selectedIconPath, "INTERFACE\\ICONS\\", "")
local iconForSave = string.gsub(iconPicker.GetIcon(), "INTERFACE\\ICONS\\", "")
if pendingAction == "new" then
C_EquipmentSet.CreateEquipmentSet(name, iconForSave)
C_EquipmentSet.ClearIgnoredSlotsForSave()
@@ -610,31 +430,20 @@ pfUI:RegisterModule("equipmentmanager", function()
function OpenNamePopup(action, prefillName, prefillIcon)
pendingAction = action
namePopup.editbox:SetText(prefillName or "")
EnsureProvider()
if prefillIcon then
local short = string.gsub(prefillIcon, "INTERFACE\\ICONS\\", "")
selectedIconPath = "INTERFACE\\ICONS\\" .. strupper(short)
-- Stored icons come back either as a full path or a bare basename
-- (see the row rendering in Refresh). Only prepend the prefix when
-- there's no path separator, so a full path isn't double-prefixed.
iconPicker.SetIcon(string.find(prefillIcon, "\\") and prefillIcon
or ("INTERFACE\\ICONS\\" .. prefillIcon))
else
selectedIconPath = QUESTION_MARK
end
if action == "rename" then
namePopup.title:SetText(T["Rename Set"] or "Rename Set")
iconScroll:Hide()
for _, b in ipairs(iconButtons) do b:Hide() end
namePopup.iconLabel:Hide()
namePopup.selectedLabel:Hide()
namePopup.selectedPreview:Hide()
filterDropdown:Hide()
else
namePopup.title:SetText(action == "new" and (T["Name Set"] or "Name Set") or (T["Save Set"] or "Save Set"))
iconScroll:Show()
namePopup.iconLabel:Show()
namePopup.selectedLabel:Show()
namePopup.selectedPreview:Show()
filterDropdown:Show()
iconPicker.SetIcon(nil)
end
namePopup.search:SetText("")
-- Rename only changes the name, so hide the whole icon-picking area.
iconPicker.SetIconAreaShown(action ~= "rename")
namePopup:Show()
if action ~= "rename" then pfUI.equipmentmanager.RefreshIconGrid() end
if action ~= "rename" then iconPicker.Refresh() end
namePopup.editbox:SetFocus()
end
@@ -643,9 +452,13 @@ pfUI:RegisterModule("equipmentmanager", function()
-- ============================================================
local flyout = CreateFrame("Frame", "pfEqMgrFlyout", UIParent)
-- Everything tied to the EM sidecar closes with it: the popout arrows,
-- the per-slot flyout, and the name/icon popup. (OnShow re-shows the
-- popout arrows.)
frame:SetScript("OnHide", function()
for _, b in ipairs(popoutButtons) do b:Hide() end
flyout:Hide()
namePopup:Hide()
end)
flyout:SetFrameStrata("DIALOG")
flyout:Hide()
@@ -676,7 +489,7 @@ pfUI:RegisterModule("equipmentmanager", function()
end
end
ClearCursor()
UIErrorsFrame:AddMessage(EQUIPMENT_MANAGER_BAGS_FULL or "Your bags are full.", 1, .1, .1, 1)
UIErrorsFrame:AddMessage(ERR_EQUIPMENT_MANAGER_BAGS_FULL, 1, .1, .1, 1)
end
local function MakeFlyoutButton(i)
@@ -692,11 +505,11 @@ pfUI:RegisterModule("equipmentmanager", function()
b:SetScript("OnEnter", function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
if this.specialAction == "placeInBags" then
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS or "Place in Bags", 1, 1, 1)
GameTooltip:SetText(EQUIPMENT_MANAGER_PLACE_IN_BAGS, 1, 1, 1)
elseif this.specialAction == "ignore" then
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT or "Ignore this slot", 1, 1, 1)
GameTooltip:SetText(EQUIPMENT_MANAGER_IGNORE_SLOT, 1, 1, 1)
elseif this.specialAction == "unignore" then
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT or "Stop ignoring this slot", 1, 1, 1)
GameTooltip:SetText(EQUIPMENT_MANAGER_UNIGNORE_SLOT, 1, 1, 1)
elseif this.bag then
GameTooltip:SetBagItem(this.bag, this.slot)
elseif this.invSlot then
@@ -704,7 +517,7 @@ pfUI:RegisterModule("equipmentmanager", function()
end
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:SetScript("OnLeave", GameTooltip_Hide)
b:SetScript("OnClick", function()
if this.specialAction == "placeInBags" then
UnequipToBags(flyout.targetInvSlot)
@@ -809,7 +622,6 @@ pfUI:RegisterModule("equipmentmanager", function()
if num == 0 then
flyout:Hide()
UIErrorsFrame:AddMessage(T["No matching items in bags"] or "No matching items in bags", 1, 1, 0, 1)
return
end
@@ -938,16 +750,6 @@ pfUI:RegisterModule("equipmentmanager", function()
end
end
-- Tie popout visibility to the EM sidecar. They appear when the
-- sidecar opens, hide when it closes, and the flyout closes too.
frame:HookScript("OnShow", function()
for _, b in ipairs(popoutButtons) do b:Show() end
end)
frame:HookScript("OnHide", function()
for _, b in ipairs(popoutButtons) do b:Hide() end
if flyout then flyout:Hide() end
end)
-- ============================================================
-- Refresh
-- ============================================================
+4 -4
View File
@@ -25,8 +25,7 @@ pfUI:RegisterModule("farmmode", function ()
Minimap_ZoomOut()
end
_G.SLASH_PFFARMMAP1, _G.SLASH_PFFARMMAP2 = "/farm", "/farmmode"
_G.SlashCmdList.PFFARMMAP = ToggleFarmMode
pfUI.api.RegisterSlashCommand("PFFARMMAP", { "/farm", "/farmmode" }, ToggleFarmMode, true)
pfUI.farmmap = CreateFrame("Minimap", "pfFarmMap", UIParent)
pfUI.farmmap:Hide()
@@ -42,8 +41,9 @@ pfUI:RegisterModule("farmmode", function ()
pfUI.farmmap:RegisterForDrag("LeftButton")
pfUI.farmmap:SetScript("OnMouseWheel", function()
if IsControlKeyDown() then
this:SetWidth(this:GetWidth() + (arg1 > 0 and 10 or -10))
this:SetHeight(this:GetHeight() + (arg1 > 0 and 10 or -10))
local adjust = (arg1 > 0 and 10 or -10)
local width, height = this:GetSize()
this:SetSize(width + adjust, height + adjust)
Minimap_ZoomIn()
Minimap_ZoomOut()
elseif IsShiftKeyDown() then
+2 -2
View File
@@ -1,6 +1,6 @@
pfUI:RegisterModule("feigndeath", function ()
local oldUnitHealth = UnitHealth
function UnitHealth(unit)
local oldUnitHealth = _G.UnitHealth
_G.UnitHealth = function(unit)
if UnitIsFeignDeath(unit) then
local hp = GetUnitField(unit, "health")
if hp and hp > 0 then return hp end
+11 -12
View File
@@ -2,20 +2,22 @@ pfUI:RegisterModule("focus", function ()
-- do not go further on disabled UFs
if C.unitframes.disable == "1" then return end
pfUI.uf.focus = pfUI.uf:CreateUnitFrame("Focus", nil, C.unitframes.focus, .2)
pfUI.uf.focus = pfUI.uf:CreateUnitFrame("Focus", nil, C.unitframes.focus)
pfUI.uf.focus:UpdateFrameSize()
pfUI.uf.focus:SetPoint("BOTTOMLEFT", UIParent, "BOTTOM", 220, 220)
UpdateMovable(pfUI.uf.focus)
pfUI.uf.focus:Hide()
pfUI.uf.focustarget = pfUI.uf:CreateUnitFrame("FocusTarget", nil, C.unitframes.focustarget, .2)
pfUI.uf.focustarget = pfUI.uf:CreateUnitFrame("FocusTarget", nil, C.unitframes.focustarget)
pfUI.uf.focustarget:UpdateFrameSize()
pfUI.uf.focustarget:SetPoint("BOTTOMLEFT", pfUI.uf.focus, "TOP", 0, 10)
UpdateMovable(pfUI.uf.focustarget)
pfUI.uf.focustarget:Hide()
-- PLAYER_FOCUS_CHANGED drives immediate refresh on focus assign / clear.
-- The frame's 0.2s tick keeps health/power/aura data fresh between events.
-- Between events, ClassicAPI fires UNIT_* (health/mana/aura/...) with
-- arg1 == "focus" and arg1 == "focustarget", so both frames update
-- event-driven like target and need no polling tick.
local refresher = CreateFrame("Frame")
refresher:RegisterEvent("PLAYER_FOCUS_CHANGED")
refresher:SetScript("OnEvent", function()
@@ -30,8 +32,7 @@ end)
-- /focusname is pfUI-specific because the engine has no name→GUID
-- lookup for off-screen units — we resolve via a short target-swap.
SLASH_PFFOCUSNAME1, SLASH_PFFOCUSNAME2 = '/focusname', '/pffocusname'
function SlashCmdList.PFFOCUSNAME(msg)
pfUI.api.RegisterSlashCommand("PFFOCUSNAME", { '/focusname', '/pffocusname' }, function(msg)
if msg == "" then return end
local prevGUID = UnitGUID("target")
@@ -61,10 +62,9 @@ function SlashCmdList.PFFOCUSNAME(msg)
else
ClearTarget()
end
end
end, true)
SLASH_PFCASTFOCUS1, SLASH_PFCASTFOCUS2 = '/castfocus', '/pfcastfocus'
function SlashCmdList.PFCASTFOCUS(msg)
pfUI.api.RegisterSlashCommand("PFCASTFOCUS", { '/castfocus', '/pfcastfocus' }, function(msg)
local focusGUID = UnitGUID("focus")
if not focusGUID or focusGUID == "0x0000000000000000" then
UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0)
@@ -105,10 +105,9 @@ function SlashCmdList.PFCASTFOCUS(msg)
else
TargetLastTarget()
end
end
end, true)
SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus'
function SlashCmdList.PFSWAPFOCUS(msg)
pfUI.api.RegisterSlashCommand("PFSWAPFOCUS", { '/swapfocus', '/pfswapfocus' }, function(msg)
local targetGUID = UnitGUID("target")
local oldFocusGUID = UnitGUID("focus")
@@ -118,4 +117,4 @@ function SlashCmdList.PFSWAPFOCUS(msg)
TargetUnit(oldFocusGUID)
end
end
end
end, true)
+2 -2
View File
@@ -150,7 +150,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
pfUI.hooksecurefunc("WhoList_Update", function()
hooksecurefunc("WhoList_Update", function()
for i = 1, WHOS_TO_DISPLAY do
local level = _G["WhoFrameButton"..i.."Level"]
level:ClearAllPoints()
@@ -231,7 +231,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
pfUI.hooksecurefunc("GuildStatus_Update", function()
hooksecurefunc("GuildStatus_Update", function()
for i = 1, GUILDMEMBERS_TO_DISPLAY do
local level = _G["GuildFrameButton"..i.."Level"]
level:ClearAllPoints()
+1 -1
View File
@@ -86,7 +86,7 @@ pfUI:RegisterModule("gm", function ()
-- pet dropdown
-- table.insert(UnitPopupMenus["PET"], "GM_HEADER")
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
hooksecurefunc("UnitPopup_OnClick", function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
local button = this.value
local unit = dropdownFrame.unit
+21 -20
View File
@@ -961,7 +961,7 @@ pfUI:RegisterModule("gui", function ()
"16:" .. T["Very Slow"],
},
["uf_rangecheck_mode"] = {
"vanilla:" .. T["Vanilla (Spellbook)"],
"vanilla:" .. T["ClassicAPI (UnitInRange)"],
"unitxp:" .. T["UnitXP (Precise)"],
},
["uf_raidlayout"] = {
@@ -1068,6 +1068,7 @@ pfUI:RegisterModule("gui", function ()
"unitrev:" .. T["Unit String (Reverse)"],
"name:" .. T["Name"],
"nameshort:" .. T["Name (Short)"],
"ownername:" .. T["Owner Name"],
"level:" .. T["Level"],
"class:" .. T["Class"],
"namehealth:" .. T["Name | Health Missing"],
@@ -2119,10 +2120,9 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable 40y-Range Check"], C.unitframes, "rangecheck", "checkbox", nil, nil, nil, nil)
CreateConfig(nil, T["Range Check Mode"], C.unitframes, "rangecheck_mode", "dropdown", pfUI.gui.dropdowns.uf_rangecheck_mode, nil, nil, nil)
CreateConfig(nil, T["UnitXP Range Threshold (yards)"], C.unitframes, "rangecheck_distance", nil, nil, nil, nil, nil)
CreateConfig(nil, T["Range Check Interval"], C.unitframes, "rangechecki", "dropdown", pfUI.gui.dropdowns.uf_rangecheckinterval, nil, nil, nil)
CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox")
CreateConfig(nil, T["Always Show Self In Raid Frames"], C.unitframes, "selfinraid", "checkbox")
CreateConfig(nil, T["Show Self In Raid Frames When Solo"], C.unitframes, "selfinraid", "checkbox")
CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox")
CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox")
CreateConfig(nil, T["Hide Group Frames While In Raid"], C.unitframes.group, "hide_in_raid", "checkbox")
CreateConfig(nil, T["Max Amount Of Raid Frames"], C.unitframes, "maxraid", "dropdown", pfUI.gui.dropdowns.maxraid)
@@ -2143,6 +2143,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Druid Settings"], nil, nil, "header")
CreateConfig(nil, T["Show Druid Mana Bar"], C.unitframes, "druidmanabar", "checkbox", nil, nil, nil, nil)
CreateConfig(nil, T["Show Druid Mana Bar Text"], C.unitframes, "druidmanatext", "checkbox", nil, nil, nil, nil)
CreateConfig(nil, T["Druid Mana Bar Height"], C.unitframes, "druidmanaheight", nil, nil, nil, nil, nil)
CreateConfig(nil, T["Druid Mana Bar Width (-1 = auto)"], C.unitframes, "druidmanawidth", nil, nil, nil, nil, nil)
CreateConfig(nil, T["Druid Mana Bar X-Offset"], C.unitframes, "druidmanaoffx", nil, nil, nil, nil, nil)
@@ -2185,6 +2186,7 @@ pfUI:RegisterModule("gui", function ()
[10] = { "grouptarget", T["Group-Target"]},
[11] = { "grouppet", T["Group-Pet"] },
[12] = { "raid", T["Raid"] },
[13] = { "raidpet", T["Raid-Pet"] },
}
CreateGUIEntry(T["Unit Frames"], T["Click Casting"], function()
@@ -2234,6 +2236,7 @@ pfUI:RegisterModule("gui", function ()
U.ptarget = U["pettarget"]
U.grouptarget = U["group"]
U.grouppet = U["group"]
U.raidpet = U["raid"]
-- build config entries
CreateConfig(U[c], T["Display Frame"] .. ": " .. t, C.unitframes[c], "visible", "checkbox")
@@ -2278,6 +2281,13 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
elseif c == "raidpet" then
CreateConfig(U[c], T["Layout"], nil, nil, "header")
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
end
CreateConfig(U[c], T["Healthbar"], nil, nil, "header")
@@ -2413,6 +2423,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable Item Quality Color For Equipment Only"], C.appearance.bags, "borderonlygear", "checkbox")
CreateConfig(nil, T["Highlight Unusable Items"], C.appearance.bags, "unusable", "checkbox")
CreateConfig(nil, T["Unusable Item Color"], C.appearance.bags, "unusable_color", "color")
CreateConfig(nil, T["Highlight New Items"], C.appearance.bags, "newitem", "checkbox")
CreateConfig(nil, T["New Item Color"], C.appearance.bags, "newitem_color", "color")
CreateConfig(nil, T["Enable Movable Bags"], C.appearance.bags, "movable", "checkbox")
CreateConfig(nil, T["Anchor Bags Above Chat"], C.appearance.bags, "abovechat", "checkbox")
CreateConfig(nil, T["Hide Chat When Bags Are Opened"], C.appearance.bags, "hidechat", "checkbox")
@@ -2423,6 +2435,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Auto Sell Grey Items"], C.global, "autosell", "checkbox")
CreateConfig(nil, T["Auto Repair Items"], C.global, "autorepair", "checkbox")
CreateConfig(nil, T["Auto Sort When Opening Bags"], C.appearance.bags, "autoSortOnOpen", "checkbox")
CreateConfig(nil, T["Reverse Sort Direction (Last Bag First)"], C.appearance.bags, "sortreverse", "checkbox")
CreateConfig(nil, T["Reverse Sort Priority (Hearthstone Last)"], C.appearance.bags, "sortprioreverse", "checkbox")
end)
CreateGUIEntry(T["Loot"], nil, function()
@@ -2573,6 +2587,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Switch Pages On Shift Key Press"], C.bars, "pagemastershift", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Ctrl Key Press"], C.bars, "pagemasterctrl", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Druid Stealth"], C.bars, "druidstealth", "checkbox")
CreateConfig(U["bars"], T["Switch Pages On Priest Shadowform"], C.bars, "priestshadow", "checkbox")
CreateConfig(nil, T["Range Based Hunter Paging"], C.bars, "hunterbar", "checkbox", nil, nil, nil, nil)
end)
@@ -2757,24 +2772,10 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Show Aura Caster"], C.tooltip, "aurasource", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
local showAlways = CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
local function gate()
local on = C.tooltip.compare.basestats == "1"
if on then
showAlways.input:Enable()
showAlways.caption:SetTextColor(1, 1, 1)
else
showAlways.input:Disable()
showAlways.caption:SetTextColor(0.5, 0.5, 0.5)
end
end
gate()
pfUI.events:RegisterCallback("config:changed", function(_, cat, key)
if cat == C.tooltip.compare and key == "basestats" then gate() end
end, "eqcompare-showalways-gate")
CreateConfig(nil, T["Always Show Item Comparison"], C.tooltip.compare, "showalways", "checkbox")
CreateConfig(nil, T["Always Show Extended Vendor Values"], C.tooltip.vendor, "showalways", "checkbox")
CreateConfig(U["questitem"], T["Show Related Quest On Questitems"], C.tooltip.questitem, "showquest", "checkbox")
CreateConfig(U["questitem"], T["Show Required Questitem Count"], C.tooltip.questitem, "showcount", "checkbox")
+1 -1
View File
@@ -1,5 +1,5 @@
pfUI:RegisterModule("hunterbar", function ()
local _,class = UnitClass("player")
local class = UnitClassBase("player")
if class ~= "HUNTER" or C.bars.hunterbar == "0" then return end
-- Wing Clip (any rank) and Arcane Shot (any rank) spell IDs.
+1 -3
View File
@@ -2,14 +2,12 @@
-- Announces Innervate casts via raid/party/battleground chat
-- Registers AURA_CAST events directly - zero polling, pure event-driven
pfUI:RegisterNewModule("innervatecall", "Innervate Callout", "DRUID")
pfUI:RegisterModule("innervatecall", function ()
-- Requires Nampower for AURA_CAST events
if not GetNampowerVersion then return end
-- Only load for druids
local _, playerClass = UnitClass("player")
if playerClass ~= "DRUID" then return end
if UnitClassBase("player") ~= "DRUID" then return end
local INNERVATE_SPELLID = 29166
+1 -1
View File
@@ -41,7 +41,7 @@ pfUI:RegisterModule("itemcount", function ()
end
end)
pfUI.hooksecurefunc("SetItemRef", function()
hooksecurefunc("SetItemRef", function()
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
if id then AddCounts(ItemRefTooltip, id) end
+4 -4
View File
@@ -236,7 +236,7 @@ pfUI:RegisterModule("loot", function ()
if (candidate) then
index_to_name[i] = candidate
name_to_index[candidate] = i
randoms[table.getn(randoms)+1]=i
table.insert(randoms, i)
if candidate == pfUI.loot.me then
pfUI.loot.my_index = i
end
@@ -392,7 +392,7 @@ pfUI:RegisterModule("loot", function ()
end
pfUI.loot:RemoveMasterlootMenus() -- remove then add to ensure no duplicate menus
pfUI.loot:AddMasterLootMenus()
pfUI.hooksecurefunc("UnitPopup_OnClick",function()
hooksecurefunc("UnitPopup_OnClick",function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
if not dropdownFrame then return end
local button = this.value
@@ -405,7 +405,7 @@ pfUI:RegisterModule("loot", function ()
end
end
end)
pfUI.hooksecurefunc("UnitPopup_HideButtons",function()
hooksecurefunc("UnitPopup_HideButtons",function()
local dropdownFrame = _G[UIDROPDOWNMENU_INIT_MENU]
local unit = dropdownFrame.unit
local name = dropdownFrame.name
@@ -693,7 +693,7 @@ pfUI:RegisterModule("loot", function ()
else -- not an eligible candidate for that item
pfUI.loot.rollers[who] = {roll=tonumber(roll),value="disabled"}
end
pfUI.loot.rollers_sorted[table.getn(pfUI.loot.rollers_sorted)+1]={who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value}
table.insert(pfUI.loot.rollers_sorted, {who=who,roll=tonumber(roll),value=pfUI.loot.rollers[who].value})
end
end
table.sort(pfUI.loot.rollers_sorted,function(a,b)
+388
View File
@@ -0,0 +1,388 @@
pfUI:RegisterModule("loothistory", function ()
local rawborder, border = GetBorderSize()
-- Layout
local ITEM_H, PLAYER_H = 24, 18
local ITEM_W, PLAYER_W = 350, 330
-- rollType constants returned by C_LootHistory.GetPlayerInfo (0/1/2; vanilla
-- has no disenchant roll).
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
local ROLL_TEX = {
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
}
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
local function ClassColor(class)
local c = class and RAID_CLASS_COLORS[class]
if c then return c.r, c.g, c.b end
return 1, 1, 1
end
-- Paint an item row's icon/name/quality from a loaded Item mixin.
local function RenderItemVisual(f, item)
local r, g, b = 1, 1, 1
local qc = item:GetItemQualityColor()
if qc then r, g, b = qc.r, qc.g, qc.b end
f.icon:SetTexture(item:GetItemIcon())
f.iconbg:SetBackdropBorderColor(r, g, b, 1)
f.name:SetText(item:GetItemName() or UNKNOWN)
f.name:SetTextColor(r, g, b)
end
local function ShowRetrieving(f)
f.icon:SetTexture(QUESTIONMARK)
f.iconbg:SetBackdropBorderColor(1, .3, .3, 1)
f.name:SetText(T["Retrieving item information..."])
f.name:SetTextColor(1, .3, .3)
end
-- expansion state keyed on the stable rollID (survives ring-index shifts)
local expanded = {}
-- ==========================================================================
-- Window
-- ==========================================================================
pfUI.loothistory = CreateFrame("Frame", "pfLootHistory", UIParent)
pfUI.loothistory:SetFrameStrata("DIALOG")
pfUI.loothistory:SetSize(380, 490)
pfUI.loothistory:SetPoint("CENTER", 0, 0)
pfUI.loothistory:SetMovable(true)
pfUI.loothistory:EnableMouse(true)
pfUI.loothistory:RegisterForDrag("LeftButton")
pfUI.loothistory:SetScript("OnDragStart", function() this:StartMoving() end)
pfUI.loothistory:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
pfUI.loothistory:Hide()
CreateBackdrop(pfUI.loothistory, nil, true, .75)
CreateBackdropShadow(pfUI.loothistory)
tinsert(UISpecialFrames, "pfLootHistory")
pfUI.loothistory.caption = pfUI.loothistory:CreateFontString("Status", "LOW", "GameFontNormal")
pfUI.loothistory.caption:SetFont(pfUI.font_default, C.global.font_size + 4, "OUTLINE")
pfUI.loothistory.caption:SetTextColor(.2, 1, .8, 1)
pfUI.loothistory.caption:SetPoint("TOP", 0, -10)
pfUI.loothistory.caption:SetText(T["Loot History"])
-- close button
pfUI.loothistory.close = CreateFrame("Button", nil, pfUI.loothistory)
pfUI.loothistory.close:SetPoint("TOPRIGHT", -border*2, -border*2)
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close:SetSize(15, 15)
pfUI.loothistory.close.texture = pfUI.loothistory.close:CreateTexture("pfLootHistoryClose")
pfUI.loothistory.close.texture:SetTexture(pfUI.media["img:close"])
pfUI.loothistory.close.texture:SetPoint("TOPLEFT", pfUI.loothistory.close, "TOPLEFT", 4, -4)
pfUI.loothistory.close.texture:SetPoint("BOTTOMRIGHT", pfUI.loothistory.close, "BOTTOMRIGHT", -4, 4)
pfUI.loothistory.close.texture:SetVertexColor(1, .25, .25, 1)
pfUI.loothistory.close:SetScript("OnEnter", function()
CreateBackdrop(pfUI.loothistory.close)
pfUI.loothistory.close.backdrop:SetBackdropBorderColor(1, .25, .25, 1)
end)
pfUI.loothistory.close:SetScript("OnLeave", function() CreateBackdrop(pfUI.loothistory.close) end)
pfUI.loothistory.close:SetScript("OnClick", function() pfUI.loothistory:Hide() end)
-- clear button
pfUI.loothistory.clear = CreateFrame("Button", nil, pfUI.loothistory, "UIPanelButtonTemplate")
SkinButton(pfUI.loothistory.clear)
pfUI.loothistory.clear:SetSize(60, 16)
pfUI.loothistory.clear:SetPoint("TOPLEFT", 10, -8)
pfUI.loothistory.clear:SetText(T["Clear"])
pfUI.loothistory.clear:SetScript("OnClick", function() C_LootHistory.Clear() end)
-- scroll frame
pfUI.loothistory.scroll = CreateScrollFrame("pfLootHistoryScroll", pfUI.loothistory)
pfUI.loothistory.scroll:SetSize(360, 440)
pfUI.loothistory.scroll:SetPoint("BOTTOM", 0, 10)
pfUI.loothistory.scroll.backdrop = CreateFrame("Frame", nil, pfUI.loothistory.scroll)
pfUI.loothistory.scroll.backdrop:SetFrameLevel(1)
pfUI.loothistory.scroll.backdrop:SetPoint("TOPLEFT", pfUI.loothistory.scroll, "TOPLEFT", -5, 5)
pfUI.loothistory.scroll.backdrop:SetPoint("BOTTOMRIGHT", pfUI.loothistory.scroll, "BOTTOMRIGHT", 5, -5)
CreateBackdrop(pfUI.loothistory.scroll.backdrop, nil, true)
local list = CreateScrollChild("pfLootHistoryList", pfUI.loothistory.scroll)
pfUI.loothistory.list = list
-- ==========================================================================
-- Frame pools
-- ==========================================================================
local itemFrames = {}
local usedPlayers, freePlayers = {}, {}
local FullUpdate -- forward declaration (toggle handlers call it)
local function CreateItemFrame()
local f = CreateFrame("Button", nil, list)
f:SetSize(ITEM_W, ITEM_H)
f:SetBackdrop(pfUI.backdrop_hover)
f:SetBackdropBorderColor(1, 1, 1, .04)
f:EnableMouse(1)
-- expand / collapse toggle
f.toggle = CreateFrame("Button", nil, f)
f.toggle:SetSize(14, 14)
f.toggle:SetPoint("LEFT", 4, 0)
f.toggle:SetScript("OnClick", function()
local id = f.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
-- icon + quality-colored border
f.iconbg = CreateFrame("Frame", nil, f)
f.iconbg:SetSize(ITEM_H - 8, ITEM_H - 8)
f.iconbg:SetPoint("LEFT", f.toggle, "RIGHT", 4, 0)
CreateBackdrop(f.iconbg, nil, true)
f.icon = f.iconbg:CreateTexture(nil, "ARTWORK")
f.icon:SetPoint("TOPLEFT", f.iconbg, "TOPLEFT", 2, -2)
f.icon:SetPoint("BOTTOMRIGHT", f.iconbg, "BOTTOMRIGHT", -2, 2)
f.icon:SetTexCoord(.08, .92, .08, .92)
-- winner block (right side, shown for decided rolls)
f.winicon = f:CreateTexture(nil, "OVERLAY")
f.winicon:SetSize(14, 14)
f.winicon:SetPoint("RIGHT", f, "RIGHT", -6, 0)
f.winroll = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winroll:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winroll:SetPoint("RIGHT", f.winicon, "LEFT", -2, 0)
f.winroll:SetTextColor(1, 1, 1, 1)
f.winname = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.winname:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.winname:SetPoint("RIGHT", f.winroll, "LEFT", -4, 0)
f.winname:SetJustifyH("RIGHT")
-- item name (leaves room on the right for the winner block)
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", f.iconbg, "RIGHT", 5, 0)
f.name:SetPoint("RIGHT", f, "RIGHT", -100, 0)
f.name:SetJustifyH("LEFT")
f:SetScript("OnEnter", function()
this:SetBackdropBorderColor(1, 1, 1, .08)
if this.itemLink then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetHyperlink(this.itemLink)
GameTooltip:Show()
end
end)
f:SetScript("OnLeave", function()
this:SetBackdropBorderColor(1, 1, 1, .04)
GameTooltip:Hide()
end)
f:SetScript("OnClick", function()
local id = this.rollID
if id then expanded[id] = not expanded[id]; FullUpdate() end
end)
return f
end
local function CreatePlayerFrame()
local f = CreateFrame("Frame", nil, list)
f:SetSize(PLAYER_W, PLAYER_H)
-- name is indented to leave room for the winner checkmark on its left
f.name = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.name:SetPoint("LEFT", 20, 0)
f.name:SetJustifyH("LEFT")
f.rollicon = f:CreateTexture(nil, "OVERLAY")
f.rollicon:SetSize(16, 16)
f.rollicon:SetPoint("RIGHT", -4, 0)
f.rolltext = f:CreateFontString("Status", "OVERLAY", "GameFontNormal")
f.rolltext:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
f.rolltext:SetPoint("RIGHT", f.rollicon, "LEFT", -3, 0)
f.rolltext:SetTextColor(1, 1, 1, 1)
-- winner checkmark, just left of the player name (matches reference)
f.winmark = f:CreateTexture(nil, "OVERLAY")
f.winmark:SetSize(16, 16)
f.winmark:SetTexture(WINMARK)
f.winmark:SetPoint("RIGHT", f.name, "LEFT", -1, 0)
return f
end
local function RecycleAllPlayers()
for i = 1, table.getn(usedPlayers) do
local pf = usedPlayers[i]
pf:Hide()
table.insert(freePlayers, pf)
end
usedPlayers = {}
end
local function GetPlayerFrame()
local pf = table.remove(freePlayers) or CreatePlayerFrame()
table.insert(usedPlayers, pf)
return pf
end
local function SetToggleTexture(toggle, isExpanded)
if isExpanded then
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
else
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
end
end
-- ==========================================================================
-- Rendering
-- ==========================================================================
local function UpdateItemFrame(f, i)
local rollID, itemLink, numPlayers, isDone, winnerIdx = C_LootHistory.GetItem(i)
f.rollID = rollID
f.itemIdx = i
f.itemLink = itemLink
f.numPlayers = numPlayers or 0
f.isDone = isDone
local isExpanded = rollID and expanded[rollID]
SetToggleTexture(f.toggle, isExpanded)
-- Item icon/name/quality via the ClassicAPI Item mixin. When the item
-- isn't cached yet, show a placeholder and re-paint this row from the
-- ContinueOnItemLoad callback (guarded on rollID: rows are pooled, so the
-- callback must no-op if the row has since been reused for another roll).
local item = itemLink and Item:CreateFromItemLink(itemLink)
if item and not item:IsItemEmpty() then
if item:IsItemDataCached() then
RenderItemVisual(f, item)
else
ShowRetrieving(f)
local pending = rollID
item:ContinueOnItemLoad(function()
if f.rollID == pending then RenderItemVisual(f, item) end
end)
end
else
ShowRetrieving(f)
end
-- winner summary only on a decided, collapsed row
if isDone and not isExpanded then
if winnerIdx then
local wname, wclass, wrollType, wroll = C_LootHistory.GetPlayerInfo(i, winnerIdx)
f.winicon:SetTexture(ROLL_TEX[wrollType] or ROLL_TEX[ROLL_NEED])
f.winicon:Show()
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
f.winroll:Show()
f.winname:SetText(wname or UNKNOWN)
f.winname:SetTextColor(ClassColor(wclass))
f.winname:Show()
else
-- nobody won: everyone passed
f.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
f.winicon:Show()
f.winroll:SetText("")
f.winroll:Show()
f.winname:SetText(T["All players passed"])
f.winname:SetTextColor(1, .4, .4)
f.winname:Show()
end
else
f.winicon:Hide()
f.winroll:Hide()
f.winname:Hide()
end
end
local function RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf.name:SetText(name or UNKNOWN)
pf.name:SetTextColor(ClassColor(class))
pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS])
if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end
if isWinner then pf.winmark:Show() else pf.winmark:Hide() end
end
-- A player row is worth showing while the roll is undecided (see everyone),
-- or afterwards only if they actually rolled or it's you (hide the passers'
-- noise on a decided roll) — mirrors Blizzard's ShouldDisplayPlayer.
local function ShouldDisplayPlayer(isDone, roll, isMe)
return isMe or (roll and roll > 0) or not isDone
end
function FullUpdate()
if not pfUI.loothistory:IsShown() then return end
RecycleAllPlayers()
local num = C_LootHistory.GetNumItems()
local y = -2
for i = 1, num do
local f = itemFrames[i] or CreateItemFrame()
itemFrames[i] = f
UpdateItemFrame(f, i)
f:ClearAllPoints()
f:SetPoint("TOPLEFT", list, "TOPLEFT", 4, y)
f:Show()
y = y - ITEM_H - 2
if f.rollID and expanded[f.rollID] then
for p = 1, f.numPlayers do
local name, class, rollType, roll, isWinner, isMe = C_LootHistory.GetPlayerInfo(i, p)
if ShouldDisplayPlayer(f.isDone, roll, isMe) then
local pf = GetPlayerFrame()
RenderPlayerFrame(pf, name, class, rollType, roll, isWinner)
pf:ClearAllPoints()
pf:SetPoint("TOPLEFT", list, "TOPLEFT", 22, y)
pf:Show()
y = y - PLAYER_H
end
end
y = y - 2
end
end
for i = num + 1, table.getn(itemFrames) do
itemFrames[i]:Hide()
end
-- Resize the scroll child and re-attach it: SetHeight alone on a
-- SetAllPoints'd child doesn't make the ScrollFrame recompute its scroll
-- range, so a dynamically-grown list wouldn't scroll until a /reload.
list:SetHeight(math.max(1, -y + 2))
pfUI.loothistory.scroll:SetScrollChild(list)
pfUI.loothistory.scroll:UpdateScrollState()
end
pfUI.loothistory:SetScript("OnShow", function() FullUpdate() end)
-- ==========================================================================
-- Events
-- ==========================================================================
local events = CreateFrame("Frame")
events:RegisterEvent("LOOT_HISTORY_FULL_UPDATE")
events:RegisterEvent("LOOT_HISTORY_ROLL_CHANGED")
events:RegisterEvent("LOOT_HISTORY_ROLL_COMPLETE")
events:SetScript("OnEvent", function()
-- auto-show on a new roll opening / completing, if enabled
if C.loothistory.autoshow == "1"
and (event == "LOOT_HISTORY_FULL_UPDATE" or event == "LOOT_HISTORY_ROLL_COMPLETE")
and not pfUI.loothistory:IsShown() then
pfUI.loothistory:Show() -- OnShow runs FullUpdate
return
end
FullUpdate() -- no-op while hidden
end)
-- ==========================================================================
-- Slash command
-- ==========================================================================
local function Toggle()
pfUI.loothistory:SetShown(not pfUI.loothistory:IsShown())
end
pfUI.api.RegisterSlashCommand("PFLOOTHISTORY", { "/loothistory", "/pfloothistory" }, Toggle, true)
end)
+128
View File
@@ -0,0 +1,128 @@
-- Macro icon picker
-- Replaces Blizzard's MacroPopupFrame (the "name + icon" dialog opened by the
-- New / Change-Icon buttons) with a pfUI-native picker driven by
-- IconDataProviderMixin, so the full spell + item + loose icon set is
-- available instead of the stock spell-only list. Saving goes 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
-- on both the modern (Turtle/Octo) and stock-vanilla macro UIs. The surrounding
-- MacroFrame (list + body editor) is Blizzard's, already skinned elsewhere.
pfUI:RegisterModule("macroicons", function ()
HookAddonOrVariable("Blizzard_MacroUI", function()
-- ============================================================
-- Popup frame
-- ============================================================
local picker = CreateFrame("Frame", "pfMacroIconPicker", UIParent)
picker:SetFrameStrata("DIALOG")
picker:SetSize(472, 484)
-- Starts anchored to the right of the macro panel (re-anchored at open
-- once the skin's backdrop exists); dragging pins it in place after.
picker:SetPoint("BOTTOMLEFT", MacroFrame, "BOTTOMRIGHT", 8, 0)
picker:Hide()
CreateBackdrop(picker, nil, nil, .9)
CreateBackdropShadow(picker)
picker:EnableMouse(true)
picker:SetMovable(true)
picker:RegisterForDrag("LeftButton")
picker:SetScript("OnDragStart", function() this:StartMoving() end)
picker:SetScript("OnDragStop", function()
this:StopMovingOrSizing()
this.userMoved = true
end)
-- Spellbook seed leads the grid with class-relevant spell/talent icons.
local iconPicker = CreateIconPicker("pfMacroIcon", picker,
IconDataProviderExtraType.Spellbook, MACRO_POPUP_TEXT)
-- ============================================================
-- Open / save
-- ============================================================
-- Blizzard's popup disables these while it is open; mirror that so the
-- underlying frame can't be double-driven, then let MacroFrame_Update
-- restore the correct states on close.
local function SetMacroButtons(enabled)
local m = enabled and "Enable" or "Disable"
MacroNewButton[m](MacroNewButton)
MacroEditButton[m](MacroEditButton)
MacroDeleteButton[m](MacroDeleteButton)
end
local function OpenMacroPopup(mode)
if mode == "edit" and MacroFrame.selectedMacro then
picker.mode = "edit"
local name, texture = GetMacroInfo(MacroFrame.selectedMacro)
picker.editbox:SetText(name or "")
iconPicker.SetIcon(texture)
else
picker.mode = "new"
picker.editbox:SetText("")
iconPicker.SetIcon(nil)
end
picker.search:SetText("")
if not picker.userMoved then
picker:ClearAllPoints()
picker:SetPoint("BOTTOMLEFT", MacroFrame.backdrop or MacroFrame, "BOTTOMRIGHT", 8, 0)
end
SetMacroButtons(false)
picker:Show()
picker.editbox:SetFocus()
iconPicker.Refresh()
end
local function SaveMacroPopup()
local text = strtrim(picker.editbox:GetText())
if text == "" then return end
local icon = iconPicker.GetIcon()
if picker.mode == "edit" and MacroFrame.selectedMacro then
C_Macro.EditMacro(MacroFrame.selectedMacro, text, icon, nil)
MacroFrame_SelectMacro(MacroFrame.selectedMacro)
else
local idx = C_Macro.CreateMacro(text, icon, nil, (MacroFrame.macroBase or 0) > 0)
if idx then MacroFrame_SelectMacro(idx) end
end
picker:Hide()
end
picker:SetScript("OnHide", function() MacroFrame_Update() end)
picker.editbox:SetScript("OnEnterPressed", SaveMacroPopup)
picker.editbox:SetScript("OnEscapePressed", function() picker:Hide() end)
-- OK / Cancel
local okay = CreateFrame("Button", "pfMacroIconPickerOkay", picker, "UIPanelButtonTemplate")
okay:SetSize(80, 22)
okay:SetText(OKAY)
okay:SetScript("OnClick", SaveMacroPopup)
SkinButton(okay)
local cancel = CreateFrame("Button", "pfMacroIconPickerCancel", picker, "UIPanelButtonTemplate")
cancel:SetSize(80, 22)
cancel:SetText(CANCEL)
cancel:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 12)
cancel:SetScript("OnClick", function() picker:Hide() end)
SkinButton(cancel)
-- Okay | Cancel, both anchored to the bottom-right corner.
okay:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -6, 0)
-- ============================================================
-- Take over the New / Change-Icon buttons; retire Blizzard's popup
-- ============================================================
MacroNewButton:SetScript("OnClick", function()
MacroFrame_SaveMacro()
OpenMacroPopup("new")
end)
MacroEditButton:SetScript("OnClick", function()
MacroFrame_SaveMacro()
OpenMacroPopup("edit")
end)
-- Closing the macro window takes the picker with it.
MacroFrame:HookScript("OnHide", function() picker:Hide() end)
end)
end)
+6 -10
View File
@@ -2,15 +2,14 @@ pfUI:RegisterModule("macrotweak", function ()
local conflictAddons = { "Supermacro", "SuperCleveRoidMacros", "UltimaMacros" }
local disabled = false
local function CheckConflicts()
for _, name in pairs(conflictAddons) do
if IsAddOnLoaded(name) then
for _, addon in pairs(conflictAddons) do
local name = addon
EventUtil.ContinueOnAddOnLoaded(name, function()
if not disabled then
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: " .. name .. " found, macrotweak disabled.")
disabled = true
return true
end
end
return false
disabled = true
end)
end
-- do not write macro calls into chat input history
@@ -64,7 +63,4 @@ pfUI:RegisterModule("macrotweak", function ()
UseInventoryItem(slot)
end
end)
-- Check conflicts after one tick so all addons have finished loading
RunNextFrame(CheckConflicts)
end)
+5 -11
View File
@@ -25,11 +25,7 @@ pfUI:RegisterModule("map", function ()
pfUI.map = { UpdateConfig = UpdateTooltipScale }
function _G.ToggleWorldMap()
if WorldMapFrame:IsShown() then
WorldMapFrame:Hide()
else
WorldMapFrame:Show()
end
WorldMapFrame:SetShown(not WorldMapFrame:IsShown())
end
C.position["WorldMapFrame"] = C.position["WorldMapFrame"] or { alpha = 1.0, scale = 0.7 }
@@ -107,8 +103,8 @@ pfUI:RegisterModule("map", function ()
WorldMapFrame:ClearAllPoints()
WorldMapFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
WorldMapFrame:SetWidth(WorldMapButton:GetWidth() + 15)
WorldMapFrame:SetHeight(WorldMapButton:GetHeight() + 55)
local wmWidth, wmHeight = WorldMapButton:GetSize()
WorldMapFrame:SetSize(wmWidth + 15, wmHeight + 55)
LoadMovable(WorldMapFrame)
-- skin
@@ -130,8 +126,7 @@ pfUI:RegisterModule("map", function ()
btn:SetHighlightTexture("")
btn.text = _G["pfUI_map_autozoneswitchText"]
CreateBackdrop(btn, nil, true)
btn:SetWidth(14)
btn:SetHeight(14)
btn:SetSize(14, 14)
btn:SetPoint("RIGHT", WorldMapContinentDropDown, "LEFT", -8, 2)
btn.text:ClearAllPoints()
btn.text:SetPoint("RIGHT", btn, "LEFT", -4, 1)
@@ -169,8 +164,7 @@ pfUI:RegisterModule("map", function ()
WorldMapButton.coords.text:SetJustifyH("RIGHT")
WorldMapButton.coords:SetScript("OnUpdate", function()
local width = WorldMapButton:GetWidth()
local height = WorldMapButton:GetHeight()
local width, height = WorldMapButton:GetSize()
local mx, my = WorldMapButton:GetCenter()
local scale = WorldMapButton:GetEffectiveScale()
local x, y = GetCursorPosition()
+5 -5
View File
@@ -85,7 +85,7 @@ pfUI:RegisterModule("mapcolors", function ()
end
local function ColorizeName(frame)
local _, class = UnitClass(frame.unit)
local class = UnitClassBase(frame.unit)
local color = PFUI_CLASS_COLORS[class]
frame.name = frame.name or UnitName(frame.unit)
frame.name = '|c'..color.colorStr..frame.name..'|r'
@@ -125,13 +125,13 @@ pfUI:RegisterModule("mapcolors", function ()
-- WorldMap
Initialize('WorldMap')
pfUI.hooksecurefunc('WorldMapButton_OnUpdate', function()
hooksecurefunc('WorldMapButton_OnUpdate', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitFrames('WorldMap')
end)
if C.appearance.worldmap.colornames == "1" then
pfUI.hooksecurefunc('WorldMapUnit_OnEnter', function()
hooksecurefunc('WorldMapUnit_OnEnter', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitColors('WorldMap', WorldMapTooltip)
end)
@@ -141,13 +141,13 @@ pfUI:RegisterModule("mapcolors", function ()
HookAddonOrVariable("Blizzard_BattlefieldMinimap", function()
Initialize('BattlefieldMinimap')
pfUI.hooksecurefunc('BattlefieldMinimap_OnUpdate', function()
hooksecurefunc('BattlefieldMinimap_OnUpdate', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitFrames('BattlefieldMinimap')
end)
if C.appearance.worldmap.colornames == "1" then
pfUI.hooksecurefunc('BattlefieldMinimapUnit_OnEnter', function()
hooksecurefunc('BattlefieldMinimapUnit_OnEnter', function()
if ( this.tick or .5) > GetTime() then return else this.tick = GetTime() + .5 end
UpdateUnitColors('BattlefieldMinimap', GameTooltip)
end)
+54 -60
View File
@@ -1,58 +1,32 @@
pfUI:RegisterNewModule("marktracking", "Mark Tracker")
pfUI:RegisterModule("marktracking", function ()
-- Requires mark1-mark8 unit tokens (Turtle WoW / Nampower)
if not UnitExists("mark1") and not UnitExists("mark8") then
if not pcall(function() UnitExists("mark1") end) then return end
end
local rawborder, border = GetBorderSize()
-- Parse color strings "r,g,b,a" into components
local function ParseColor(str, dr, dg, db, da)
if not str or str == "" then return dr, dg, db, da end
local _, _, r, g, b, a = string.find(str, "([%d%.]+),([%d%.]+),([%d%.]+),([%d%.]+)")
if r then
return tonumber(r) or dr, tonumber(g) or dg, tonumber(b) or db, tonumber(a) or da
end
return dr, dg, db, da
end
local markerOrder = { 8, 7, 6, 5, 4, 3, 2, 1 } -- skull, cross, square, moon, triangle, diamond, circle, star
local markerTokens = {}
for i = 1, 8 do markerTokens[i] = "mark" .. i end
-- Default colors per marker
local defaultColors = {
[1] = { 1.0, 0.9, 0.0, 1 }, -- star: yellow
[2] = { 1.0, 0.5, 0.0, 1 }, -- circle: orange
[3] = { 0.8, 0.0, 0.8, 1 }, -- diamond: purple
[4] = { 0.0, 0.8, 0.0, 1 }, -- triangle: green
[5] = { 0.7, 0.7, 0.7, 1 }, -- moon: silver
[6] = { 0.0, 0.4, 0.9, 1 }, -- square: blue
[7] = { 0.9, 0.0, 0.0, 1 }, -- cross: red
[8] = { 1.0, 1.0, 1.0, 1 }, -- skull: white
}
local markerConfigKeys = {
[1] = "raidmarkercolor_star",
[2] = "raidmarkercolor_circle",
[3] = "raidmarkercolor_diamond",
[4] = "raidmarkercolor_triangle",
[5] = "raidmarkercolor_moon",
[6] = "raidmarkercolor_square",
[7] = "raidmarkercolor_cross",
[8] = "raidmarkercolor_skull",
"raidmarkercolor_star",
"raidmarkercolor_circle",
"raidmarkercolor_diamond",
"raidmarkercolor_triangle",
"raidmarkercolor_moon",
"raidmarkercolor_square",
"raidmarkercolor_cross",
"raidmarkercolor_skull",
}
local markerColors = {}
for i = 1, 8 do
local d = defaultColors[i]
local r, g, b, a = ParseColor(C.unitframes[markerConfigKeys[i]], d[1], d[2], d[3], d[4])
markerColors[i] = { r, g, b, a }
for i, markKey in ipairs(markerConfigKeys) do
markerTokens[i] = "mark" .. i
local r, g, b, a = GetStringColor(C.unitframes[markKey])
markerColors[i] = { tonumber(r), tonumber(g), tonumber(b), tonumber(a) }
end
local FALLBACK_INTERVAL = 1.0 -- safety net for units that come into range after marker was set
local elapsed = 0
local isUnlocked = false
local ROW_HEIGHT = tonumber(C.unitframes.raidmarkerheight) or 14
local BAR_WIDTH = tonumber(C.unitframes.raidmarkerwidth) or 80
@@ -126,19 +100,16 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
local i = markerOrder[idx]
local row = CreateFrame("Button", nil, pfUI.marktracking)
row:SetWidth(TOTAL_ROW_WIDTH)
row:SetHeight(ROW_HEIGHT)
row:SetSize(TOTAL_ROW_WIDTH, ROW_HEIGHT)
row:Hide()
row:RegisterForClicks("LeftButtonUp")
row:SetScript("OnClick", function()
TargetUnit(markerTokens[this.markerIndex])
end)
row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
row:SetAttribute("type1", "target")
row:SetAttribute("type2", "menu")
-- raid icon
row.icon = row:CreateTexture(nil, "ARTWORK")
row.icon:SetWidth(ROW_HEIGHT)
row.icon:SetHeight(ROW_HEIGHT)
row.icon:SetSize(ROW_HEIGHT, ROW_HEIGHT)
row.icon:SetPoint("LEFT", row, "LEFT", 1, 0)
local markTex = C.unitframes.blizzard_raidicons == "1" and "Interface\\TargetingFrame\\UI-RaidTargetingIcons" or pfUI.media["img:raidicons"]
row.icon:SetTexture(markTex)
@@ -146,8 +117,7 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
-- portrait (right side)
row.portrait = row:CreateTexture(nil, "ARTWORK")
row.portrait:SetWidth(PORTRAIT_SIZE)
row.portrait:SetHeight(PORTRAIT_SIZE)
row.portrait:SetSize(PORTRAIT_SIZE, PORTRAIT_SIZE)
row.portrait:SetPoint("RIGHT", row, "RIGHT", -1, 0)
row.portrait:SetTexCoord(.1, .9, .1, .9)
if not rm_showportrait then row.portrait:Hide() end
@@ -205,8 +175,8 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
local token = markerTokens[i]
if UnitExists(token) and not UnitIsDead(token) then
local hp = UnitHealth(token)
local maxhp = UnitHealthMax(token)
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
row:SetAttribute('unit', token)
if hp and maxhp and maxhp > 0 and hp > 0 then
local pct = hp / maxhp
@@ -268,6 +238,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
end
end
-- Fast per-row refresh for a single mark's UNIT_HEALTH/UNIT_MAXHEALTH: moves
-- just the bar + hp text. If the mark's visibility flips (comes into range,
-- dies, hp crosses 0) the visible rows re-pack, so hand off to UpdateDisplay.
local function UpdateRow(i)
if isUnlocked then return end
local row = pfUI.marktracking.rows[i]
if not row then return end
local token = markerTokens[i]
local hp, maxhp = UnitHealth(token), UnitHealthMax(token)
local shouldShow = UnitExists(token) and not UnitIsDead(token)
and hp and maxhp and maxhp > 0 and hp > 0 and true or false
if shouldShow ~= (row:IsShown() and true or false) then
UpdateDisplay()
return
end
if not shouldShow then return end
local pct = hp / maxhp
row.health:SetValue(pct)
if rm_showpct then row.hptext:SetText(math.ceil(pct * 100) .. "%") end
end
-- Unlock mode: show fixed 1-row placeholder so positioning works correctly
if pfUI.unlock then
local origShow = pfUI.unlock:GetScript("OnShow")
@@ -293,24 +284,27 @@ pfUI.marktracking = CreateFrame("Frame", "pfMarkTracking", UIParent)
-- Event-driven scanner frame
local scanner = CreateFrame("Frame")
-- RAID_TARGET_UPDATE: fires when a raid marker is set/cleared
-- PLAYER_ENTERING_WORLD: fires on login, reload, and zone transitions
-- UNIT_HEALTH/UNIT_MAXHEALTH: fires on HP changes for real-time bar updates
-- RAID_TARGET_UPDATE: a raid marker was set/cleared -> full refresh
-- PLAYER_ENTERING_WORLD: login/reload/zone -> full refresh
-- UNIT_HEALTH/UNIT_MAXHEALTH: ClassicAPI fires these per token; with the mark
-- tokens observed they arrive as arg1 == "markN", so we refresh just that
-- one row (UpdateRow) instead of rescanning all eight.
scanner:RegisterEvent("RAID_TARGET_UPDATE")
scanner:RegisterEvent("PLAYER_ENTERING_WORLD")
scanner:RegisterEvent("UNIT_HEALTH")
scanner:RegisterEvent("UNIT_MAXHEALTH")
scanner:SetScript("OnEvent", function()
if event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
-- arg1 is the token; "markN" -> N, nil for any non-mark token.
local i = arg1 and tonumber(string.match(arg1, "^mark(%d)"))
if i then UpdateRow(i) end
return
end
UpdateDisplay()
end)
-- Fallback poll at 1s: catches units that come into range AFTER a marker was set
-- Fallback poll: catches units that come into range AFTER a marker was set
-- (no event fires for that case, so we need this safety net)
scanner:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed < FALLBACK_INTERVAL then return end
elapsed = 0
UpdateDisplay()
end)
C_Timer.NewTicker(FALLBACK_INTERVAL, UpdateDisplay)
end)
+17 -36
View File
@@ -33,11 +33,9 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap.UpdateConfig = function(self)
size = tonumber(C.appearance.minimap.size) or 140
pfUI.minimap:SetWidth(size)
pfUI.minimap:SetHeight(size)
pfUI.minimap:SetSize(size, size)
Minimap:SetWidth(size)
Minimap:SetHeight(size)
Minimap:SetSize(size, size)
-- vanilla+tbc: do the best to detect the minimap arrow
local arrowscale = tonumber(C.appearance.minimap.arrowscale)
@@ -58,7 +56,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap:UpdateConfig()
pfUI.hooksecurefunc("ToggleMinimap", function()
hooksecurefunc("ToggleMinimap", function()
if pfUI.farmmap and pfUI.farmmap:IsShown() then
Minimap:Hide()
return
@@ -133,18 +131,16 @@ pfUI:RegisterModule("minimap", function ()
-- Create coordinates text frame with location configurable
pfUI.minimapCoordinates = CreateFrame("Frame", "pfMinimapCoord", pfUI.minimap)
pfUI.minimapCoordinates:SetScript("OnUpdate", function()
-- Throttle to update coords every 0.1 seconds
if ( this.tick or 0) > GetTime() then return end
this.tick = GetTime() + .1
-- Update coords every 0.1 seconds
C_Timer.NewTicker(0.1, function()
if C.appearance.minimap.coordstext == "off" then return end
this.posX, this.posY = GetPlayerMapPosition("player")
if this.posX ~= 0 and this.posY ~= 0 then
this.text:SetText(string.format("%.1f, %.1f", round(this.posX * 100, 1), round(this.posY * 100, 1)))
local coord = pfUI.minimapCoordinates
coord.posX, coord.posY = GetPlayerMapPosition("player")
if coord.posX ~= 0 and coord.posY ~= 0 then
coord.text:SetText(string.format("%.1f, %.1f", round(coord.posX * 100, 1), round(coord.posY * 100, 1)))
else
this.text:SetText("|cffffaaaaN/A")
coord.text:SetText("|cffffaaaaN/A")
end
end)
@@ -158,8 +154,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimapCoordinates:SetPoint("BOTTOMLEFT", 3, 3)
end
pfUI.minimapCoordinates:SetHeight(C.global.font_size)
pfUI.minimapCoordinates:SetWidth(Minimap:GetWidth())
pfUI.minimapCoordinates:SetSize(Minimap:GetWidth(), C.global.font_size)
pfUI.minimapCoordinates.text = pfUI.minimapCoordinates:CreateFontString("MinimapCoordinatesText", "LOW", "GameFontNormal")
pfUI.minimapCoordinates.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
pfUI.minimapCoordinates.text:SetTextColor(1,1,1,1)
@@ -171,19 +166,14 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimapCoordinates.text:SetJustifyH("LEFT")
end
if C.appearance.minimap.coordstext ~= "on" then
pfUI.minimapCoordinates:Hide()
else
pfUI.minimapCoordinates:Show()
end
pfUI.minimapCoordinates:SetShown(C.appearance.minimap.coordstext == "on")
-- Create zone text frame in top center of minimap
pfUI.minimapZone = CreateFrame("Frame", "pfMinimapZone", pfUI.minimap)
pfUI.minimapZone:RegisterEvent("MINIMAP_ZONE_CHANGED")
pfUI.minimapZone:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.minimapZone:SetPoint("TOP", 0, -3)
pfUI.minimapZone:SetHeight(C.global.font_size + 2)
pfUI.minimapZone:SetWidth(Minimap:GetWidth())
pfUI.minimapZone:SetSize(Minimap:GetWidth(), C.global.font_size + 2)
pfUI.minimapZone.text = pfUI.minimapZone:CreateFontString("minimapZoneText", "LOW", "GameFontNormal")
pfUI.minimapZone.text:SetFont(pfUI.font_default, C.global.font_size + 2, "OUTLINE")
pfUI.minimapZone.text:SetAllPoints(pfUI.minimapZone)
@@ -205,17 +195,13 @@ pfUI:RegisterModule("minimap", function ()
elseif pvp == "contested" then
pfUI.minimapZone.text:SetTextColor(1.0, 0.7, 0)
else
pfUI.minimapZone.text:SetTextColor(1, 1, 1, 1)
pfUI.minimapZone.text:SetTextColor(WHITE_FONT_COLOR:GetRGBA())
end
pfUI.minimapZone.text:SetText(GetMinimapZoneText())
end
end)
if C.appearance.minimap.zonetext ~= "on" then
pfUI.minimapZone:Hide()
else
pfUI.minimapZone:Show()
end
pfUI.minimapZone:SetShown(C.appearance.minimap.zonetext == "on")
-- Minimap hover event
-- Update and toggle showing of coordinates and zone text on mouse enter/leave
@@ -241,8 +227,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap.pvpicon:RegisterEvent("UPDATE_FACTION")
pfUI.minimap.pvpicon:RegisterEvent("UNIT_FACTION")
pfUI.minimap.pvpicon:SetFrameStrata("HIGH")
pfUI.minimap.pvpicon:SetWidth(16)
pfUI.minimap.pvpicon:SetHeight(16)
pfUI.minimap.pvpicon:SetSize(16, 16)
pfUI.minimap.pvpicon:SetAlpha(.5)
pfUI.minimap.pvpicon:SetParent(pfUI.minimap)
pfUI.minimap.pvpicon:SetPoint("BOTTOMRIGHT", pfUI.minimap, "BOTTOMRIGHT", -5, 5)
@@ -251,11 +236,7 @@ pfUI:RegisterModule("minimap", function ()
pfUI.minimap.pvpicon.texture:SetAllPoints(pfUI.minimap.pvpicon)
pfUI.minimap.pvpicon:SetScript("OnEvent", function()
if C.unitframes.player.showPVPMinimap == "1" and UnitIsPVP("player") then
pfUI.minimap.pvpicon:Show()
else
pfUI.minimap.pvpicon:Hide()
end
pfUI.minimap.pvpicon:SetShown(C.unitframes.player.showPVPMinimap == "1" and UnitIsPVP("player"))
end)
end)
+2 -3
View File
@@ -1,6 +1,5 @@
pfUI:RegisterModule("mouseover", function ()
_G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse"
function SlashCmdList.PFCAST(msg)
pfUI.api.RegisterSlashCommand("PFCAST", { "/pfcast", "/pfmouse" }, function(msg)
local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg)
local unit = "mouseover"
@@ -30,5 +29,5 @@ pfUI:RegisterModule("mouseover", function ()
if restore_target then TargetUnit(unit) end
func()
if restore_target then TargetLastTarget() end
end
end, true)
end)
+192 -163
View File
@@ -4,7 +4,6 @@ pfUI:RegisterModule("nameplates", function ()
-- Local function references for performance
local GetTime = GetTime
local UnitExists = UnitExists
local UnitName = UnitName
local UnitClass = UnitClass
local UnitLevel = UnitLevel
@@ -15,8 +14,6 @@ pfUI:RegisterModule("nameplates", function ()
local UnitCanAssist = UnitCanAssist
local UnitHealth = UnitHealth
local UnitHealthMax = UnitHealthMax
local UnitMana = UnitMana
local UnitManaMax = UnitManaMax
local pairs = pairs
local tonumber = tonumber
local strlower = strlower
@@ -58,18 +55,30 @@ pfUI:RegisterModule("nameplates", function ()
local savedHostileState = nil
local savedFriendlyState = nil
local inFriendlyZone = false
local myGuild = nil
local platecount = 0
local registry = {}
-- Subset of registry that currently has a unit assigned (between
-- NAME_PLATE_UNIT_ADDED and _REMOVED). The central loop iterates this instead
-- of the full pool so hidden pool slots aren't touched every tick.
local visiblePlates = {}
local raidGuidCache = {} -- guid -> name (rebuilt on RAID_ROSTER_UPDATE/PARTY_MEMBERS_CHANGED)
-- Resolve a plate GUID to its cast/channel info via C_Spell. Returns a
-- compact struct (spellName / icon / startTime / endTime / duration /
-- isChannel) or nil when the unit isn't casting / the GUID can't map to a
-- live token.
local function GetCastInfo(guid)
if not guid then return nil end
local unit = UnitTokenFromGUID(guid)
-- Per-GUID cast state, populated from ClassicAPI's UNIT_SPELLCAST_* events
-- (which now fire for nameplate tokens) and cleared on STOP / plate removal /
-- expiry. This replaces the old per-tick C_Spell poll on every visible plate:
-- cast detection is event driven, and GetCastInfo just reads this cache.
local castState = {}
-- guid -> nameplate, maintained on NAME_PLATE_UNIT_ADDED/_REMOVED so a cast
-- event can find its plate in O(1) and only cache casts we actually show.
local plateByGuid = {}
-- One-shot C_Spell poll. Builds the cast struct for a UNIT_SPELLCAST_* event
-- (the payload carries no timing) and seeds a plate that spawns while its unit
-- is already mid-cast (its START fired before the plate existed). Picks cast
-- vs channel itself. Never called per frame.
local function PollCastInfo(unit)
if not unit then return nil end
local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit)
local isChannel
@@ -88,14 +97,27 @@ pfUI:RegisterModule("nameplates", function ()
isChannel = isChannel,
}
end
-- Read a unit's current cast from the event-driven cache (keyed by GUID).
-- Returns the cached struct while the cast is still active, else nil (and
-- prunes the expired entry). Same struct shape and callers as before, minus
-- the per-tick poll.
local function GetCastInfo(unit)
if not unit then return nil end
local guid = UnitGUID(unit)
if not guid then return nil end
local info = castState[guid]
if info and info.endTime > GetTime() then return info end
if info then castState[guid] = nil end
return nil
end
local guidTargetTokenCache = {} -- guid -> "<guid>target" interned string
local debuffCache = {} -- guid -> { [spellID] = { start, duration } }
-- Reusable per-plate debuff display buffer (avoid GC churn from per-call table creation)
local debuffDisplayBuf = {} -- [i] = { effect, texture, stacks, dtype, duration, timeleft }
for i = 1, 16 do debuffDisplayBuf[i] = {} end
local threatMemory = {} -- guid -> true if mob had player targeted
local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
-- local debuffSeen = {} -- reusable table for debuff tracking (avoid GC churn)
-- PERF: visiblePlateCount maintained event-driven (NAME_PLATE_UNIT_ADDED/_REMOVED)
local visiblePlateCount = 0
@@ -159,20 +181,13 @@ pfUI:RegisterModule("nameplates", function ()
-- ============================================================================
local frameState = {
now = 0,
hasTarget = false,
targetGuid = nil,
hasMouseover = false,
mouseoverGuid = nil,
}
-- cache default border color
local er, eg, eb, ea = GetStringColor(pfUI_config.appearance.border.color)
-- Vanilla Lua 5.0 bitwise check: math.mod(math.floor(value / flag), 2) ~= 0
local function HasFlag(flags, flag)
return math.mod(math.floor(flags / flag), 2) ~= 0
end
local UNIT_FLAG_IN_COMBAT = 524288 -- 0x00080000
local NULL_GUID = "0x0000000000000000"
local function RebuildRaidGuidCache()
@@ -191,10 +206,11 @@ pfUI:RegisterModule("nameplates", function ()
local combatColorCache = {} -- guid -> { color, expires }
local function GetCombatStateColor(guid)
local function GetCombatStateColor(guid, token)
-- PERF: Quick exit if player not in combat
if not UnitAffectingCombat("player") then return false end
if UnitCanAssist("player", guid) then return false end
if not token then return false end
if UnitCanAssist("player", token) then return false end
-- PERF: 0.2s throttle per guid - color changes are not time-critical
local now = frameState.now
@@ -203,24 +219,17 @@ pfUI:RegisterModule("nameplates", function ()
return cached.color
end
local flags = GetUnitField and GetUnitField(guid, "flags")
if not flags then return false end
if not HasFlag(flags, UNIT_FLAG_IN_COMBAT) then return false end
if not UnitAffectingCombat(token) then return false end
local mobTargetGuid = GetUnitField and GetUnitField(guid, "target")
-- The mob's current target via the nameplate token chain (ClassicAPI):
-- "nameplateNtarget" resolves to whatever this plate's unit is targeting,
-- so no GetUnitField("target") or SuperWoW "<guid>target" token needed.
local target = token .. "target"
local mobTargetGuid = UnitGUID(target)
local hasTarget = mobTargetGuid and mobTargetGuid ~= NULL_GUID
-- PERF: cache the SuperWoW-style "<guid>target" unit token. The concat
-- intern-hits Lua's string pool every call; caching once per guid
-- saves the hash+lookup. Cleared in NAME_PLATE_UNIT_REMOVED.
local target = guidTargetTokenCache[guid]
if not target then
target = guid .. "target"
guidTargetTokenCache[guid] = target
end
local color = false
local castInfo = GetCastInfo(guid)
local castInfo = GetCastInfo(token)
local isCasting = castInfo and castInfo.endTime and now < castInfo.endTime
local targetingPlayer = hasTarget and UnitIsUnit(target, "player")
@@ -442,8 +451,7 @@ pfUI:RegisterModule("nameplates", function ()
nameplate.debuffs[i]:SetPoint(aligna, nameplate.debuffs[i-limit], alignb, 0, space)
end
nameplate.debuffs[i]:SetWidth(tonumber(C.nameplates.debuffsize))
nameplate.debuffs[i]:SetHeight(tonumber(C.nameplates.debuffsize))
nameplate.debuffs[i]:SetSize(debuffsize, debuffsize)
-- Update cooldown display settings
if nameplate.debuffs[i].cd then
@@ -468,10 +476,14 @@ nameplates:RegisterEvent("PARTY_MEMBERS_CHANGED")
nameplates:RegisterEvent("NAME_PLATE_CREATED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_ADDED")
nameplates:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
if GetUnitField then
nameplates:RegisterEvent("UNIT_FLAGS_GUID")
nameplates:RegisterEvent("UNIT_AURA_GUID")
end
nameplates:RegisterEvent("UNIT_AURA")
nameplates:RegisterEvent("UNIT_FLAGS")
nameplates:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
nameplates:RegisterEvent("UNIT_SPELLCAST_START")
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
nameplates:RegisterEvent("UNIT_SPELLCAST_STOP")
nameplates:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
nameplates:SetScript("OnEvent", function()
-- Stop event handling during logout to prevent crash 132
@@ -483,12 +495,17 @@ end
nameplates.mouselook:SetScript("OnUpdate", nil)
end
return
elseif event == "PLAYER_GUILD_UPDATE" and arg1 == 'player' then
myGuild = GetGuildInfo("player")
elseif event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
if event == "PLAYER_ENTERING_WORLD" then
CacheConfig()
this:SetGameVariables()
RebuildRaidGuidCache()
frameState.targetGuid = UnitGUID("target")
myGuild = GetGuildInfo("player")
end
-- Handle friendly zone nameplate disable feature
@@ -544,10 +561,21 @@ end
end
elseif event == "NAME_PLATE_UNIT_ADDED" then
-- arg1 = "nameplateN" unit token; resolve to GUID for cache keys
-- arg1 = "nameplateN" unit token. Cache the GUID for cache keys and the
-- token itself for token-based UnitX reads (stable per plate lifetime).
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.cachedGuid = UnitGUID(arg1)
visiblePlates[plate] = plate
local guid = UnitGUID(arg1)
plate.nameplate.cachedGuid = guid
plate.nameplate.unit = arg1
if guid then
plateByGuid[guid] = plate.nameplate
-- Seed: the unit may already be mid-cast (its UNIT_SPELLCAST_START
-- fired before this plate existed). One poll here catches that;
-- ongoing casts arrive via the event.
castState[guid] = PollCastInfo(arg1)
end
nameplates.OnShow(plate)
end
visiblePlateCount = visiblePlateCount + 1
@@ -556,57 +584,97 @@ end
visiblePlateCount = visiblePlateCount > 0 and visiblePlateCount - 1 or 0
-- arg1 = "nameplateN" unit token; UnitGUID still resolves inside the
-- handler (the slot is freed after dispatch returns)
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate then visiblePlates[plate] = nil end
local guid = UnitGUID(arg1)
if guid then
if debuffCache[guid] then debuffCache[guid] = nil end
if threatMemory[guid] then threatMemory[guid] = nil end
if guidTargetTokenCache[guid] then guidTargetTokenCache[guid] = nil end
if combatColorCache[guid] then combatColorCache[guid] = nil end
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if castState[guid] then castState[guid] = nil end
if plateByGuid[guid] then plateByGuid[guid] = nil end
if plate and plate.nameplate and plate.nameplate.cachedGuid == guid then
plate.nameplate.cachedGuid = nil
plate.nameplate.unit = nil
end
end
elseif event == "UNIT_FLAGS_GUID" then
-- Nampower: fires instantly when any unit's flags change (e.g. stun, combat enter/leave)
-- arg1 = guid — directly flag that nameplate for immediate update, bypassing throttle
local plate = C_NamePlate.GetNamePlateForGUID(arg1)
if plate and plate.nameplate then
plate.nameplate.eventcache = true
elseif event == "UNIT_FLAGS" then
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.eventcache = true
end
end
elseif event == "UNIT_AURA_GUID" then
-- Nampower: fires when a unit's aura set changes (add/remove/modify).
-- arg1 = guid. Flag the matching plate so OnUpdate triggers a fresh
-- C_UnitAuras read on the next tick instead of waiting on the 0.5s
-- throttle — covers expirations, dispels, refreshes, and stack changes
-- in one event.
local plate = C_NamePlate.GetNamePlateForGUID(arg1)
if plate and plate.nameplate then
plate.nameplate.auraUpdate = true
elseif event == "UPDATE_MOUSEOVER_UNIT" then
local new = UnitGUID("mouseover")
local old = frameState.mouseoverGuid
if new ~= old then
frameState.mouseoverGuid = new
local po = old and plateByGuid[old]
if po then po.eventcache = true end
local pn = new and plateByGuid[new]
if pn then pn.eventcache = true end
end
elseif event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then
-- ClassicAPI fires UNIT_SPELLCAST_* per unit token, including the caster's
-- "nameplateN". The payload has no timing, so poll it (PollCastInfo picks
-- cast vs channel) and cache -- only for a unit we have a plate for, so
-- the table stays bounded to on-screen casters.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
local plate = guid and plateByGuid[guid]
if plate then
castState[guid] = PollCastInfo(arg1)
if castState[guid] then
plate.castUpdate = true -- bypass the throttle so the bar shows now
end
end
end
elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then
-- Cast/channel ended (natural, interrupted, or cancelled -- the poll fires
-- STOP for all three). Clear the cached cast and refresh its plate.
if arg1 and strfind(arg1, "^nameplate") then
local guid = UnitGUID(arg1)
if guid and castState[guid] then
castState[guid] = nil
local plate = plateByGuid[guid]
if plate then plate.castUpdate = true end
end
end
elseif event == "UNIT_AURA" then
-- ClassicAPI: fires with arg1 == "nameplateN" when a unit's aura set
-- changes (add/remove/modify). Flag the matching plate so OnUpdate does a
-- fresh C_UnitAuras read next tick instead of waiting on the 0.5s
-- throttle -- covers expirations, dispels, refreshes, and stack changes
-- in one event. Guard on the token prefix (UNIT_AURA also fires for
-- target/party/raid).
if arg1 and strfind(arg1, "^nameplate") then
local plate = C_NamePlate.GetNamePlateForUnit(arg1)
if plate and plate.nameplate then
plate.nameplate.auraUpdate = true
end
end
elseif event == "PLAYER_TARGET_CHANGED" then
-- Flag target plate for update via GUID registry
local targetGuid = UnitGUID("target")
if targetGuid then
local plate = C_NamePlate.GetNamePlateForGUID(targetGuid)
if plate and plate.nameplate then
plate.nameplate.targetUpdate = true
end
frameState.targetGuid = UnitGUID('target')
-- Flag the target's plate for update
local plate = C_NamePlate.GetNamePlateForUnit("target")
if plate and plate.nameplate then
plate.nameplate.targetUpdate = true
end
-- Also propagate to all plates for alpha/strata updates
this.eventcache = true
elseif event == "PLAYER_COMBO_POINTS" or event == "UNIT_COMBO_POINTS" then
-- Only flag the target plate for combo point update
local targetGuid = UnitGUID("target")
if targetGuid then
local plate = C_NamePlate.GetNamePlateForGUID(targetGuid)
if plate and plate.nameplate then
plate.nameplate.comboUpdate = true
end
-- Only flag the target's plate for combo point update
local plate = C_NamePlate.GetNamePlateForUnit("target")
if plate and plate.nameplate then
plate.nameplate.comboUpdate = true
end
else
this.eventcache = true
@@ -622,21 +690,15 @@ end
-- PERF: Cache GetTime() once per frame
frameState.now = now
frameState.hasTarget, frameState.targetGuid = UnitExists("target")
frameState.hasMouseover = UnitExists("mouseover")
-- propagate events to all nameplates
if this.eventcache then
this.eventcache = nil
for plate in pairs(registry) do
plate.eventcache = true
for plate in pairs(visiblePlates) do
plate.nameplate.eventcache = true
end
end
-- visiblePlateCount is maintained event-driven via NAME_PLATE_UNIT_ADDED/_REMOVED.
-- Central OnUpdate for all visible plates
for plate in pairs(registry) do
for plate in pairs(visiblePlates) do
if plate:IsVisible() then
nameplates.OnUpdate(plate, frameState)
end
@@ -669,7 +731,7 @@ end
nameplates.OnCreate = function(frame)
local parent = frame or this
platecount = platecount + 1
platename = "pfNamePlate" .. platecount
local platename = "pfNamePlate" .. platecount
-- create pfUI nameplate overlay
local nameplate = CreateFrame("Button", platename, parent)
@@ -734,8 +796,7 @@ end
nameplate.totem = CreateFrame("Frame", nil, nameplate)
nameplate.totem:SetPoint("CENTER", nameplate, "CENTER", 0, 0)
nameplate.totem:SetHeight(32)
nameplate.totem:SetWidth(32)
nameplate.totem:SetSize(32, 32)
nameplate.totem.icon = nameplate.totem:CreateTexture(nil, "OVERLAY")
nameplate.totem.icon:SetTexCoord(.078, .92, .079, .937)
nameplate.totem.icon:SetAllPoints()
@@ -823,11 +884,11 @@ end
local plate_width = C.nameplates.width + 50
local plate_height = C.nameplates.heighthealth + font_size + 5
local plate_height_cast = C.nameplates.heighthealth + font_size + 5 + C.nameplates.heightcast + 5
-- local plate_height_cast = C.nameplates.heighthealth + font_size + 5 + C.nameplates.heightcast + 5
local combo_size = 5
local width = tonumber(C.nameplates.width)
local debuffsize = tonumber(C.nameplates.debuffsize)
-- local width = tonumber(C.nameplates.width)
-- local debuffsize = tonumber(C.nameplates.debuffsize)
local healthoffset = tonumber(C.nameplates.health.offset)
local orientation = C.nameplates.verticalhealth == "1" and "VERTICAL" or "HORIZONTAL"
@@ -840,8 +901,7 @@ end
RebuildOfftanks()
nameplate:SetWidth(plate_width)
nameplate:SetHeight(plate_height)
nameplate:SetSize(plate_width, plate_height)
nameplate:SetPoint("TOP", parent, "TOP", 0, 0)
nameplate.name:SetFont(font, font_size, font_style)
@@ -870,23 +930,20 @@ end
nameplate.guild:SetFont(font, font_size, font_style)
nameplate.glow:SetWidth(C.nameplates.width + 60)
nameplate.glow:SetHeight(C.nameplates.heighthealth + 30)
nameplate.glow:SetSize(C.nameplates.width + 60, C.nameplates.heighthealth + 30)
nameplate.glow:SetVertexColor(glowr, glowg, glowb, glowa)
nameplate.raidicon:ClearAllPoints()
nameplate.raidicon:SetPoint("BOTTOM", nameplate.health, "TOP", C.nameplates.raidiconoffx, C.nameplates.raidiconoffy)
nameplate.level:SetFont(font, font_size, font_style)
nameplate.raidicon:SetWidth(C.nameplates.raidiconsize)
nameplate.raidicon:SetHeight(C.nameplates.raidiconsize)
nameplate.raidicon:SetSize(C.nameplates.raidiconsize, C.nameplates.raidiconsize)
for i=1,16 do
UpdateDebuffConfig(nameplate, i)
end
for i=1,5 do
nameplate.combopoints[i]:SetWidth(combo_size)
nameplate.combopoints[i]:SetHeight(combo_size)
nameplate.combopoints[i]:SetSize(combo_size, combo_size)
nameplate.combopoints[i]:SetPoint("TOPRIGHT", nameplate.health, "BOTTOMRIGHT", -(i-1)*(combo_size+default_border*3), -default_border*3)
CreateBackdrop(nameplate.combopoints[i], default_border)
end
@@ -897,7 +954,7 @@ end
-- Use the same castbar texture and color as the unit frame castbar (castbar.lua)
local cbtexture = pfUI.media[C.appearance.castbar.texture]
nameplate.castbar:SetStatusBarTexture(cbtexture or hptexture)
local cbr, cbg, cbb, cba = strsplit(",", C.appearance.castbar.castbarcolor)
local cbr, cbg, cbb, cba = GetStringColor(C.appearance.castbar.castbarcolor)
nameplate.castbar:SetStatusBarColor(cbr, cbg, cbb, cba)
-- reset endTime cache so color/texture refresh takes effect on next cast
nameplate.castbar.lastEndTime = nil
@@ -944,7 +1001,7 @@ end
end
local target = plate.istarget
local mouseover = UnitExists("mouseover") and plate.original.glow:IsShown() or nil
local mouseover = plate.cachedGuid and plate.cachedGuid == frameState.mouseoverGuid or nil
local unitstr = target and "target" or mouseover and "mouseover" or plate.cachedGuid or nil
-- resolve player vs npc from plate's own unit so libunitscan can't return
@@ -988,16 +1045,14 @@ end
-- always make sure to keep plate visible
plate:Show()
if target and cfg.targetglow then
plate.glow:Show() else plate.glow:Hide()
end
plate.glow:SetShown(target and cfg.targetglow)
-- target indicator
if cfg.outcombatstate then
local guid = plate.cachedGuid or ""
-- determine color based on combat state
local color = GetCombatStateColor(guid)
local color = GetCombatStateColor(guid, plate.unit)
if not color then color = combatstate.NONE end
-- set border color
@@ -1072,7 +1127,7 @@ end
if guild and C.nameplates.showguildname == "1" then
plate.guild:SetText(guild)
if guild == GetGuildInfo("player") then
if guild == myGuild then
plate.guild:SetTextColor(0, 0.9, 0, 1)
else
plate.guild:SetTextColor(0.8, 0.8, 0.8, 1)
@@ -1091,15 +1146,15 @@ end
if cfg.showhp then
local rhp, rhpmax, estimated
local guid = plate.cachedGuid
if guid and GetUnitField then
local npHp = GetUnitField(guid, "health")
local npMaxHp = GetUnitField(guid, "maxHealth")
local unit = plate.unit
if unit then
local npHp = UnitHealth(unit)
local npMaxHp = UnitHealthMax(unit)
if npHp and npHp > 0 and npMaxHp and npMaxHp > 0 and npMaxHp ~= 100 then
rhp, rhpmax = npHp, npMaxHp
end
end
-- Fallback to existing methods
if not rhp then
if hpmax > 100 or (round(hpmax/100*hp) ~= hp) then
@@ -1150,7 +1205,7 @@ end
if cfg.barcombatstate then
local guid = plate.cachedGuid or ""
local color = GetCombatStateColor(guid)
local color = GetCombatStateColor(guid, plate.unit)
if color then
r, g, b, a = color.r, color.g, color.b, color.a
@@ -1270,19 +1325,7 @@ end
end
nameplates.OnShow = function(frame)
local frame = frame or this
local nameplate = frame.nameplate
-- cachedGuid is set by NAME_PLATE_UNIT_ADDED before this fires
local guid = nameplate.cachedGuid
if guid and pfUI.api.libunitscan and pfUI.api.libunitscan.ScanGuid then
-- notify libunitscan so it can cache unit data without mouseover
local name = nameplate.original.name:GetText()
local npcFlags = GetUnitField(guid, "npcFlags") or 0
pfUI.api.libunitscan.ScanGuid(guid, name, npcFlags == 0)
end
nameplates:OnDataChanged(nameplate)
nameplates:OnDataChanged((frame or this).nameplate)
end
nameplates.OnUpdate = function(frame, state)
@@ -1296,13 +1339,13 @@ end
-- and immediately correct on de-target (unlike istarget which updates one tick later)
local targetGuid = state and state.targetGuid
local target = (targetGuid and nameplate.cachedGuid and targetGuid == nameplate.cachedGuid) or
(state and state.hasTarget and frame:GetAlpha() >= 0.99) or nil
(state and state.targetGuid and frame:GetAlpha() >= 0.99) or nil
-- Target plate castbar runs on its own dedicated frame (nameplates.castbarFrame).
-- For non-target plates with castbar active, use castbar throttle to ensure
-- smooth animation without overloading the central loop.
local isCastingNonTarget = not target and nameplate.castbar and nameplate.castbar:IsShown()
if not isCastingNonTarget and not target and cfg.showcastbar and nameplate.cachedGuid then
local castInfo = GetCastInfo(nameplate.cachedGuid)
local castInfo = GetCastInfo(nameplate.unit)
if castInfo and castInfo.endTime > now then
isCastingNonTarget = true
end
@@ -1346,7 +1389,7 @@ end
local update
local original = nameplate.original
local name = original.name:GetText()
local mouseover = state and state.hasMouseover and original.glow:IsShown() or nil
local mouseover = nameplate.cachedGuid and nameplate.cachedGuid == frameState.mouseoverGuid or nil
-- trigger queued event update
if hasEventUpdate then
@@ -1374,8 +1417,7 @@ end
if C.nameplates["overlap"] == "1" then
if frame:GetWidth() > 1 then
frame:SetWidth(1)
frame:SetHeight(1)
frame:SetSize(1, 1)
end
else
if not nameplate.dwidth then
@@ -1383,8 +1425,9 @@ end
end
if floor(frame:GetWidth()) ~= nameplate.dwidth then
frame:SetWidth(nameplate:GetWidth() * UIParent:GetScale())
frame:SetHeight(nameplate:GetHeight() * UIParent:GetScale())
local nameW, nameH = nameplate:GetSize()
local uiScale = UIParent:GetScale()
frame:SetSize(nameW * uiScale, nameH * uiScale)
end
end
@@ -1410,7 +1453,7 @@ end
-- Set non-target plate alpha
local configAlpha = cfg.notargalpha or 0.5
local desiredAlpha = (target or not state.hasTarget) and 1 or configAlpha
local desiredAlpha = (target or not state.targetGuid) and 1 or configAlpha
if nameplate.cachedAlpha ~= desiredAlpha then
nameplate:SetAlpha(desiredAlpha)
@@ -1441,13 +1484,7 @@ end
-- trigger update when name color changed (includes combat state check)
local r, g, b = original.name:GetTextColor()
local inCombatWithPlayer = false
if cfg.namefightcolor then
local guid = nameplate.cachedGuid
if guid then
inCombatWithPlayer = UnitAffectingCombat(guid) and UnitAffectingCombat("player")
end
end
local inCombatWithPlayer = cfg.namefightcolor and UnitAffectingCombat(nameplate.unit) and UnitAffectingCombat("player")
if r + g + b ~= nameplate.cache.namecolor or (cfg.namefightcolor and nameplate.cache.inCombat ~= inCombatWithPlayer) then
nameplate.cache.namecolor = r + g + b
@@ -1495,7 +1532,7 @@ end
nameplate.health.targetHeight = hc
end
local w, h = nameplate.health:GetWidth(), nameplate.health:GetHeight()
local w, h = nameplate.health:GetSize()
local wc, hc = nameplate.health.targetWidth, nameplate.health.targetHeight
if wc and hc then
@@ -1507,15 +1544,14 @@ end
nameplate.health.zoomTransition = true
else
if nameplate.health.zoomTransition then
nameplate.health:SetWidth(wc)
nameplate.health:SetHeight(hc)
nameplate.health:SetSize(wc, hc)
nameplate.health.zoomTransition = nil
end
nameplate.health.zoomed = true
end
end
elseif nameplate.health.zoomed or nameplate.health.zoomTransition then
local w, h = nameplate.health:GetWidth(), nameplate.health:GetHeight()
local w, h = nameplate.health:GetSize()
local wc = cfg.width
local hc = cfg.heighthealth
@@ -1524,8 +1560,7 @@ end
elseif h > hc + 0.5 then
nameplate.health:SetHeight(h*0.95)
else
nameplate.health:SetWidth(wc)
nameplate.health:SetHeight(hc)
nameplate.health:SetSize(wc, hc)
nameplate.health.zoomTransition = nil
nameplate.health.zoomed = nil
nameplate.health.targetWidth = nil
@@ -1591,7 +1626,7 @@ end
-- Shared castbar update logic (used by both dedicated frame and central loop)
nameplates.UpdateCastbar = function(nameplate, now)
if not nameplate or not nameplate.castbar then return end
local castInfo = GetCastInfo(nameplate.cachedGuid)
local castInfo = GetCastInfo(nameplate.unit)
if not castInfo or castInfo.endTime < now then
nameplate.castbar.isShown = nil
nameplate.castbar.lastEndTime = nil
@@ -1607,7 +1642,7 @@ end
-- Relative 0..duration range to avoid float precision loss with large
-- absolute timestamps.
nameplate.castbar:SetMinMaxValues(0, duration)
nameplate.castbar:SetStatusBarColor(strsplit(",", C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")]))
nameplate.castbar:SetStatusBarColor(GetStringColor(C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")]))
if castInfo.icon then
nameplate.castbar.icon.tex:SetTexture(castInfo.icon)
nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
@@ -1627,23 +1662,17 @@ end
if not nameplate.castbar.isShown then nameplate.castbar.isShown = true; nameplate.castbar:Show() end
end
-- Dedicated frame that updates ONLY the target plate castbar.
-- Uses nameplates_castbar throttle from libthrottle.
-- Dedicated frame that updates ONLY the target plate castbar. Unthrottled:
-- now that casts are event-driven, this just reads the cache + SetValue, so
-- it animates the fill every frame for the smoothest sweep on the bar the
-- player watches most. (Non-target plates stay throttled via the central
-- loop's nameplates_castbar gate.)
nameplates.castbarFrame = CreateFrame("Frame", nil, UIParent)
nameplates.castbarFrame:SetScript("OnUpdate", function()
if not cfg.showcastbar then return end
local now = GetTime()
local throttle = pfUI.throttle:Get("nameplates_castbar")
if (this.tick or 0) > now then return end
this.tick = now + throttle
local targetGuid = UnitExists("target") and UnitGUID("target")
if not targetGuid then return end
local frame = C_NamePlate.GetNamePlateForGUID(targetGuid)
if not frame or not frame.nameplate then return end
nameplates.UpdateCastbar(frame.nameplate, now)
if not cfg.showcastbar or not frameState.targetGuid then return end
local nameplate = plateByGuid[frameState.targetGuid]
if not nameplate then return end
nameplates.UpdateCastbar(nameplate, GetTime())
end)
-- set nameplate game settings
+19 -220
View File
@@ -15,8 +15,7 @@ pfUI:RegisterModule("nampower", function ()
pfUI.spellqueue = CreateFrame("Frame", "pfSpellQueue", UIParent)
pfUI.spellqueue:SetFrameStrata("HIGH")
pfUI.spellqueue:SetWidth(size)
pfUI.spellqueue:SetHeight(size)
pfUI.spellqueue:SetSize(size, size)
pfUI.spellqueue:Hide()
-- Position near player castbar if available
@@ -53,8 +52,7 @@ pfUI:RegisterModule("nampower", function ()
return
end
local eventCode = arg1
local spellId = arg2
local eventCode, spellId = arg1, arg2
if eventCode == NORMAL_QUEUED or eventCode == NON_GCD_QUEUED or eventCode == ON_SWING_QUEUED then
local texture = C_Spell.GetSpellTexture(spellId)
@@ -74,21 +72,21 @@ pfUI:RegisterModule("nampower", function ()
-- Shows when reactive abilities like Overpower, Revenge, Execute are usable
if C.unitframes.reactive_indicator == "1" then
local size = tonumber(C.unitframes.reactive_size) or 28
local _, class = UnitClass("player")
local class = UnitClassBase("player")
-- Reactive spells by class
local reactiveSpells = {
WARRIOR = {
{ name = "Overpower", texture = "Interface\\Icons\\Ability_MeleeDamage" },
{ name = "Revenge", texture = "Interface\\Icons\\Ability_Warrior_Revenge" },
{ name = "Execute", texture = "Interface\\Icons\\INV_Sword_48" },
7384, -- Overpower
6572, -- Revenge
5283, -- Execute
},
ROGUE = {
{ name = "Riposte", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
76, -- Riposte
},
HUNTER = {
{ name = "Mongoose Bite", texture = "Interface\\Icons\\Ability_Hunter_SwiftStrike" },
{ name = "Counterattack", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
1495, -- Mongoose Bite
19306, -- Counterattack
},
}
@@ -97,21 +95,19 @@ pfUI:RegisterModule("nampower", function ()
pfUI.reactive = CreateFrame("Frame", "pfReactiveIndicator", UIParent)
pfUI.reactive:SetFrameStrata("HIGH")
local spellCount = table.getn(spells)
pfUI.reactive:SetWidth(size * spellCount + 4 * (spellCount - 1))
pfUI.reactive:SetHeight(size)
pfUI.reactive:SetSize(size * spellCount + 4 * (spellCount - 1), size)
pfUI.reactive:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
pfUI.reactive:Hide()
pfUI.reactive.icons = {}
for i, spell in ipairs(spells) do
local icon = CreateFrame("Frame", nil, pfUI.reactive)
icon:SetWidth(size)
icon:SetHeight(size)
icon:SetSize(size, size)
icon:SetPoint("LEFT", pfUI.reactive, "LEFT", (i-1) * (size + 4), 0)
icon.texture = icon:CreateTexture(nil, "ARTWORK")
icon.texture:SetAllPoints(icon)
icon.texture:SetTexture(spell.texture)
icon.texture:SetTexture(C_Spell.GetSpellTexture(spell))
icon.texture:SetTexCoord(.08, .92, .08, .92)
icon.glow = icon:CreateTexture(nil, "OVERLAY")
@@ -122,7 +118,7 @@ pfUI:RegisterModule("nampower", function ()
CreateBackdrop(icon)
icon:Hide()
icon.spellName = spell.name
icon.spellName = C_Spell.GetSpellName(spell)
pfUI.reactive.icons[i] = icon
end
@@ -132,27 +128,17 @@ pfUI:RegisterModule("nampower", function ()
local anyVisible = false
for _, icon in ipairs(this.icons) do
local usable = C_Spell.IsSpellUsable(icon.spellName)
if usable then
icon:Show()
anyVisible = true
else
icon:Hide()
end
end
if anyVisible then
this:Show()
else
this:Hide()
icon:SetShown(usable)
anyVisible = anyVisible or usable
end
this:SetShown(anyVisible)
end)
end
end
-- /disenchantall slash command (DisenchantAll is Nampower-provided)
if DisenchantAll then
_G.SLASH_PFDISENCHANTALL1 = "/disenchantall"
_G.SLASH_PFDISENCHANTALL2 = "/dea"
SlashCmdList["PFDISENCHANTALL"] = function(msg)
pfUI.api.RegisterSlashCommand("PFDISENCHANTALL", { "/disenchantall", "/dea" }, function(msg)
-- DisenchantAll(itemIdOrName | quality, [includeSoulbound]).
-- Quality is a string keyword ("greens", "blues", "purples", or pipe-
-- combined). Numbers are interpreted as item IDs, not quality levels.
@@ -160,195 +146,8 @@ pfUI:RegisterModule("nampower", function ()
local arg = (msg and msg ~= "") and msg or "greens"
local target = tonumber(arg) or arg
DisenchantAll(target)
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")")
end
print("|cff33ffccpfUI|r: DisenchantAll(" .. tostring(target) .. ")")
end, true)
end
-- Druid Secondary Mana Bar
-- Shows base mana when druid is in shapeshift form (Bear/Cat uses Rage/Energy)
-- Uses Nampower's GetUnitField to get base mana values
-- Fully self-contained: uses its own config settings from C.unitframes.druidmana*
if GetUnitField and pfUI.uf and pfUI_config.unitframes.druidmanabar == "1" then
local rawborder, default_border = GetBorderSize("unitframes")
local DC = C.unitframes -- druid mana config lives here as druidmana* keys
-- Shared helper: create a druid mana bar on a unit frame
local function CreateDruidManaBar(parent, unit)
if not parent then return nil end
local parentConfig = parent.config
-- Read own config values
local dmHeight = tonumber(DC.druidmanaheight) or 10
local dmWidth = DC.druidmanawidth or "-1"
local dmOffX = tonumber(DC.druidmanaoffx) or 0
local dmOffY = tonumber(DC.druidmanaoffy) or 0
local dmSpace = tonumber(DC.druidmanaspace) or -3
local dmTexture = DC.druidmanatexture or "Interface\\AddOns\\pfUI\\img\\bar"
local bar = CreateFrame("StatusBar", "pfDruidMana_" .. unit, parent)
bar:SetFrameStrata(parent:GetFrameStrata())
bar:SetFrameLevel(parent:GetFrameLevel() + 5)
bar:SetStatusBarTexture(pfUI.media[dmTexture] or dmTexture)
-- Bar color: use same manacolor logic as the normal power bar
local manacolor = parentConfig.defcolor == "0" and parentConfig.manacolor or C.unitframes.manacolor
local r, g, b, a = pfUI.api.strsplit(",", manacolor)
bar:SetStatusBarColor(tonumber(r) or .25, tonumber(g) or .25, tonumber(b) or 1, tonumber(a) or 1)
-- Size: own width/height, fallback to parent power bar width if -1
local width = dmWidth ~= "-1" and tonumber(dmWidth) or nil
if width then
bar:SetWidth(width)
end
bar:SetHeight(dmHeight)
-- Position below the power bar with own spacing + offsets
local spacing = -2 * default_border - dmSpace
if width then
-- Fixed width: use single point with offset
bar:SetPoint("TOP", parent.power, "BOTTOM", dmOffX, spacing + dmOffY)
else
-- Auto width: anchor to both sides of power bar
bar:SetPoint("TOPLEFT", parent.power, "BOTTOMLEFT", dmOffX, spacing + dmOffY)
bar:SetPoint("TOPRIGHT", parent.power, "BOTTOMRIGHT", dmOffX, spacing + dmOffY)
end
bar:Hide()
CreateBackdrop(bar)
CreateBackdropShadow(bar)
-- Font settings (same logic as power bar)
local fontname = pfUI.font_unit
local fontsize = tonumber(pfUI_config.global.font_unit_size)
local fontstyle = pfUI_config.global.font_unit_style
if parentConfig.customfont == "1" then
fontname = pfUI.media[parentConfig.customfont_name]
fontsize = tonumber(parentConfig.customfont_size)
fontstyle = parentConfig.customfont_style
end
-- Text color (always mana-colored)
local tr, tg, tb = ManaBarColor[0].r, ManaBarColor[0].g, ManaBarColor[0].b
if C.unitframes.pastel == "1" then
tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5
end
-- Single center text showing current/max
bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
bar.text:SetFontObject(GameFontWhite)
bar.text:SetFont(fontname, fontsize, fontstyle)
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 0)
bar.text:SetJustifyH("CENTER")
bar.text:SetTextColor(tr, tg, tb, 1)
return bar
end
-- Shared helper: update druid mana bar values and text
local function UpdateDruidManaBar(bar, unit)
if not UnitExists(unit) then
bar:Hide()
return
end
-- For non-player units, only show if the target is a Druid
if unit ~= "player" then
local _, unitClass = UnitClass(unit)
if unitClass ~= "DRUID" then
bar:Hide()
return
end
end
local powerType = UnitPowerType(unit)
-- Only show when NOT using mana (i.e., in Bear/Cat form)
if powerType == 0 then
bar:Hide()
return
end
-- Get base mana using Nampower's GetUnitField
local baseMana, baseMaxMana
local guid = UnitGUID(unit)
if guid then
baseMana = GetUnitField(guid, "power1")
baseMaxMana = GetUnitField(guid, "maxPower1")
end
-- Round down power values (Nampower can return decimals)
if baseMana then baseMana = math.floor(baseMana) end
if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end
if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then
bar:Hide()
return
end
-- Update bar
bar:SetMinMaxValues(0, baseMaxMana)
bar:SetValue(baseMana)
-- Always show current/max
bar.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana)))
bar:Show()
end
-- ===== Player Druid Mana Bar =====
local _, playerClass = UnitClass("player")
if pfUI.uf.player and playerClass == "DRUID" then
local playerMana = CreateDruidManaBar(pfUI.uf.player, "player")
if playerMana then
playerMana:RegisterEvent("UNIT_MANA")
playerMana:RegisterEvent("UNIT_MAXMANA")
playerMana:RegisterEvent("UNIT_DISPLAYPOWER")
playerMana:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
playerMana:RegisterEvent("PLAYER_LOGOUT")
playerMana:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
return
end
if arg1 == nil or arg1 == "player" then
UpdateDruidManaBar(playerMana, "player")
end
end)
-- Initial update
UpdateDruidManaBar(playerMana, "player")
end
end
-- ===== Target Druid Mana Bar =====
if pfUI.uf.target then
local targetMana = CreateDruidManaBar(pfUI.uf.target, "target")
if targetMana then
targetMana:RegisterEvent("UNIT_MANA")
targetMana:RegisterEvent("UNIT_MAXMANA")
targetMana:RegisterEvent("UNIT_DISPLAYPOWER")
targetMana:RegisterEvent("PLAYER_TARGET_CHANGED")
targetMana:RegisterEvent("PLAYER_LOGOUT")
targetMana:SetScript("OnEvent", function()
if event == "PLAYER_LOGOUT" then
this:UnregisterAllEvents()
this:SetScript("OnEvent", nil)
return
end
if event == "PLAYER_TARGET_CHANGED" or arg1 == nil or arg1 == "target" then
UpdateDruidManaBar(targetMana, "target")
end
end)
-- Initial update
UpdateDruidManaBar(targetMana, "target")
end
end
end
end)
+68
View File
@@ -0,0 +1,68 @@
pfUI:RegisterModule("newitem", function ()
if not pfUI.bag then return end
if C.appearance.bags.newitem ~= "1" then return end
pfUI.newitem = {}
local r, g, b, a = pfUI.api.GetStringColor(C.appearance.bags.newitem_color)
function pfUI.newitem:UpdateSlot(bag, slot)
if bag < 0 or bag > 4 then return end
if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end
local frame = pfUI.bags[bag].slots[slot].frame
if frame.hasItem and C_NewItems.IsNewItem(bag, slot) then
if not frame.newitem then
local glow = frame:CreateTexture(nil, "OVERLAY")
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
glow:SetBlendMode("ADD")
glow:SetVertexColor(r, g, b, a)
glow:SetPoint("CENTER", frame, "CENTER")
glow:Hide()
glow.RefreshSize = function(g)
local w = g:GetParent():GetWidth()
if w > 0 then g:SetSize(w * 1.8, w * 1.8) end
end
frame.newitem = glow
frame:HookScript("OnEnter", function()
C_NewItems.RemoveNewItem(bag, slot)
end)
end
frame.newitem:RefreshSize()
frame.newitem:Show()
elseif frame.newitem and frame.newitem:IsShown() then
frame.newitem:Hide()
end
end
-- The new-item set can change without any slot's contents changing (an item
-- acknowledged, pruned when it leaves the bags, or ClearAll) -- re-evaluate
-- every decorated slot when that happens.
function pfUI.newitem:RefreshAll()
for bag in pairs(pfUI.bags) do
local slots = pfUI.bags[bag] and pfUI.bags[bag].slots
if slots then
for slot in pairs(slots) do
pfUI.newitem:UpdateSlot(bag, slot)
end
end
end
end
-- per-slot: pfUI re-runs UpdateSlot whenever a slot's contents change.
hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
pfUI.newitem:UpdateSlot(bag, slot)
end)
EventRegistry:RegisterFrameEventAndCallback("BAG_NEW_ITEMS_UPDATED", function()
pfUI.newitem:RefreshAll()
end)
pfUI.events:RegisterCallback("bag:closed", function(_, object)
if object then return end
C_NewItems.ClearAll()
end, "newitem")
end)
+17 -36
View File
@@ -37,18 +37,12 @@ pfUI:RegisterModule("panel", function()
return
end
if arg1 == "LeftButton" then
if widget.timerFrame:IsShown() then
widget.timerFrame:Hide()
else
widget.timerFrame:Show()
end
widget.timerFrame:SetShown(not widget.timerFrame:IsShown())
elseif arg1 == "RightButton" then
widget.timerFrame.Snapshot = GetTime()
end
end
widget:SetScript("OnUpdate",function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
C_Timer.NewTicker(1, function()
local secondsenabled = C.panel.seconds == "1"
local fmt
if C.global.twentyfour == "0" then
@@ -62,8 +56,7 @@ pfUI:RegisterModule("panel", function()
widget.timerFrame = CreateFrame("Frame", "pfUITimer", UIParent)
widget.timerFrame:Hide()
widget.timerFrame:SetWidth(120)
widget.timerFrame:SetHeight(35)
widget.timerFrame:SetSize(120, 35)
widget.timerFrame:SetPoint("TOP", 0, -100)
UpdateMovable(widget.timerFrame)
@@ -103,11 +96,9 @@ pfUI:RegisterModule("panel", function()
pfUI.panel:OutputPanel("combat", T["Combat"] .. ": " .. NOT_APPLICABLE)
end
end)
widget.combat:SetScript("OnUpdate", function()
if not this.tick then this.tick = GetTime() end
if GetTime() <= this.tick + 1 then return else this.tick = GetTime() end
if this.combat then
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - this.combat)))
C_Timer.NewTicker(1, function()
if widget.combat.combat then
pfUI.panel:OutputPanel("combat", "|cffffaaaa" .. SecondsToTime(ceil(GetTime() - widget.combat.combat)))
end
end)
end
@@ -153,9 +144,7 @@ pfUI:RegisterModule("panel", function()
pfUI.addons:Show()
end
end
widget:SetScript("OnUpdate",function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + 1 end
C_Timer.NewTicker(1, function()
fps = floor(GetFramerate())
_, _, lag = GetNetStats()
@@ -346,7 +335,7 @@ pfUI:RegisterModule("panel", function()
widget:RegisterEvent("PLAYER_GUILD_UPDATE")
widget.Tooltip = function()
-- skip without guild
if not GetGuildInfo("player") then return end
if not IsInGuild() then return end
local raidparty = {}
for i=1,4 do -- detect people in group
@@ -398,7 +387,7 @@ pfUI:RegisterModule("panel", function()
end
widget.Click = function() ToggleFriendsFrame(3) end
widget:SetScript("OnEvent", function()
if GetGuildInfo("player") then
if IsInGuild() then
local count = 0
for i = 1, GetNumGuildMembers() do
local _, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i)
@@ -411,9 +400,8 @@ pfUI:RegisterModule("panel", function()
end
end)
widget:SetScript("OnUpdate",function()
if ( this.tick or 60) > GetTime() then return else this.tick = GetTime() + 60 end
if GetGuildInfo("player") then GuildRoster() end
C_Timer.NewTicker(60, function()
if IsInGuild() then GuildRoster() end
end)
end
@@ -492,11 +480,7 @@ pfUI:RegisterModule("panel", function()
GameTooltip:Show()
end
widget.Click = function()
if WorldMapFrame:IsShown() then
WorldMapFrame:Hide()
else
WorldMapFrame:Show()
end
WorldMapFrame:SetShown(not WorldMapFrame:IsShown())
end
widget:SetScript("OnEvent", function()
pfUI.panel:OutputPanel("zone", GetMinimapZoneText(), widget.Tooltip, widget.Click)
@@ -645,8 +629,7 @@ pfUI:RegisterModule("panel", function()
local frame = CreateFrame("Button", nil, parent)
frame:SetFrameLevel(0)
frame:ClearAllPoints()
frame:SetWidth(width)
frame:SetHeight(parent:GetHeight())
frame:SetSize(width, parent:GetHeight())
frame:SetPoint(location, 0, 0)
frame.text = frame:CreateFontString("Status", "LOW", "GameFontNormal")
frame.text:ClearAllPoints()
@@ -687,8 +670,7 @@ pfUI:RegisterModule("panel", function()
local imgstring = "img:" .. leftright
parent.texture:SetTexture(pfUI.media[imgstring])
parent.texture:SetPoint("CENTER", 0, 0)
parent.texture:SetWidth(8)
parent.texture:SetHeight(8)
parent.texture:SetSize(8, 8)
parent.texture:SetVertexColor(.25,.25,.25,1)
return parent.texture
end
@@ -710,7 +692,7 @@ pfUI:RegisterModule("panel", function()
UpdateMovable(pfUI.panel.left)
pfUI.panel.left.hide:SetScript("OnClick", function()
if pfUI.chat.left:IsShown() then pfUI.chat.left:Hide() else pfUI.chat.left:Show() end
pfUI.chat.left:SetShown(not pfUI.chat.left:IsShown())
end)
if not pfUI.chat then pfUI.panel.left.hide:Hide() end
@@ -747,7 +729,7 @@ pfUI:RegisterModule("panel", function()
UpdateMovable(pfUI.panel.right)
pfUI.panel.right.hide:SetScript("OnClick", function()
if pfUI.chat.right:IsShown() then pfUI.chat.right:Hide() else pfUI.chat.right:Show() end
pfUI.chat.right:SetShown(not pfUI.chat.right:IsShown())
end)
if not pfUI.chat then pfUI.panel.right.hide:Hide() end
@@ -796,8 +778,7 @@ pfUI:RegisterModule("panel", function()
pfUI.panel.microbutton = CreateFrame("Frame", "pfPanelMicroButton", UIParent)
pfUI.panel.microbutton:SetPoint("TOP", pfUI.panel.minimap, "BOTTOM", 0, -2*default_border)
UpdateMovable(pfUI.panel.microbutton)
pfUI.panel.microbutton:SetHeight(23)
pfUI.panel.microbutton:SetWidth(145)
pfUI.panel.microbutton:SetSize(145, 23)
pfUI.panel.microbutton:SetFrameStrata("MEDIUM")
for i=1,table.getn(MICRO_BUTTONS) do
+35 -40
View File
@@ -34,22 +34,25 @@ pfUI:RegisterModule("player", function ()
playerFrame.infoTopCenterText:SetHeight(14)
end
local _, myclass = UnitClass("player")
local myclass = UnitClassBase("player")
playerFrame.myclass = myclass
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
local function cfgColorToHex(colorStr)
if not colorStr or colorStr == "" then return nil end
local r, g, b = strsplit(",", colorStr)
r, g, b = tonumber(r), tonumber(g), tonumber(b)
if not r or not g or not b then return nil end
return string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
end
local cfgColorToHexTable = setmetatable({}, {
__index = function (t, colorStr)
if not colorStr or colorStr == "" then return nil end
local r, g, b = GetStringColor(colorStr)
if not r or not g or not b then return nil end
local hex = C_ColorUtil.GenerateTextColorCode({ r=r, g = g, b=b})
rawset(t, colorStr, hex)
return hex
end
})
-- SP school colors indexed by GetSpellPower("net") return order
-- SP school colors indexed by GetSpellBonusDamage's 1-based school order
-- (1=phys, 2=holy, 3=fire, 4=nature, 5=frost, 6=shadow, 7=arcane)
local spColors = { "FFFFFF", "FFFF80", "FF8000", "4DFF4D", "80FFFF", "9482C9", "FFFFFF" }
local spColors = { "FFFFFFFF", "FFFFFF80", "FFFF8000", "FF4DFF4D", "FF80FFFF", "FF9482C9", "FFFFFFFF" }
-- Default SP school per class used as tiebreaker when multiple schools are equal
local spDefaultSchool = {
@@ -60,53 +63,45 @@ pfUI:RegisterModule("player", function ()
-- Compute and cache the haste/SP text; called from OnUpdate, throttled to 0.25s
local function UpdateInfoText()
if not GetUnitField then return end -- do nothing for older nampower
local cfg = playerFrame.config
if not cfg then
return
end
-- display_haste: "0"=hidden, "1"=show modCastSpeed (gear haste). Talent-
-- side cast-time reductions show up in the actual cast bar via
-- C_Spell.UnitCastingInfo; double-folding them into this overlay was
-- mixing two different concepts into one number.
local showHaste = cfg.display_haste == "1"
local showSP = cfg.display_spellpower == "1"
-- display_haste: "0"=hidden, "1"=show cast-speed haste (UnitSpellHaste,
-- from UNIT_MOD_CAST_SPEED). Talent/spell-specific cast-time reductions
-- show up in the actual cast bar via C_Spell.UnitCastingInfo; folding them
-- in here too was mixing two different concepts into one number.
local isSpellCaster = playerFrame.isSpellCaster
if (not showHaste or not isSpellCaster) and not showSP then
local showHaste = isSpellCaster and cfg.display_haste == "1"
local showSP = isSpellCaster and cfg.display_spellpower == "1"
if not showHaste and not showSP then
playerFrame.infoTopCenterText:SetText("")
return
end
local haste = GetUnitField("player", "modCastSpeed")
local text = ""
local parts = {}
if showHaste and isSpellCaster and haste then
local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
if showHaste then
local hex = cfgColorToHexTable[cfg.display_haste_color] or "FFFFFFFF"
table.insert(parts, string.format("|c%s%.1f%%|r", hex, UnitSpellHaste("player")))
end
if showSP and isSpellCaster then
local schools = { GetSpellPower("net") }
local defSchool = spDefaultSchool[myclass] or 2
local maxSP = schools[defSchool] or 0
local maxColor = spColors[defSchool]
if showSP then
local school = spDefaultSchool[myclass] or 2
local maxSP = GetSpellBonusDamage(school) or 0
for i = 2, 7 do -- skip physical (1)
local v = schools[i] or 0
if v > maxSP then
maxSP = v
maxColor = spColors[i]
end
local v = GetSpellBonusDamage(i) or 0
if v > maxSP then maxSP, school = v, i end
end
if maxSP > 0 then
local spHex = (cfg.display_sp_color_override == "1" and cfgColorToHex(cfg.display_sp_color)) or maxColor
if text ~= "" then text = text .. " " end
text = text .. string.format("|cff%s+%d SP|r", spHex, maxSP)
local hex = (cfg.display_sp_color_override == "1" and cfgColorToHexTable[cfg.display_sp_color]) or spColors[school]
table.insert(parts, string.format("|c%s+%d SP|r", hex, maxSP))
end
end
playerFrame.infoTopCenterText:SetText(text)
playerFrame.infoTopCenterText:SetText(table.concat(parts, " "))
end
-- Keep a reference to the generic UF UpdateConfig so we can chain it
@@ -141,7 +136,7 @@ pfUI:RegisterModule("player", function ()
end
end
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
hooksecurefunc("UnitPopup_OnClick", function()
local button = this.value
if button == "RESET_INSTANCES_FIX" then
StaticPopup_Show("CONFIRM_RESET_INSTANCES")
+58 -37
View File
@@ -1,8 +1,43 @@
pfUI:RegisterModule("questitem", function ()
-- [itemID] = { index = questLogIndex, count = requiredCount }. Rebuilt
-- on QUEST_LOG_UPDATE from ClassicAPI's per-quest cached requirements.
local requiredItems = {}
local function AddQuest(questID)
local details = C_QuestLog.GetQuestDetails(questID)
if not details then return false end
if details.requirements then
for _, req in ipairs(details.requirements) do
if req.kind == "item" and req.id and req.id > 0 then
requiredItems[req.id] = {
questID = questID,
title = details.title,
level = details.level,
count = req.count,
}
end
end
end
return true
end
local function RemoveQuest(questID)
for itemID, entry in pairs(requiredItems) do
if entry.questID == questID then requiredItems[itemID] = nil end
end
end
local function Seed()
for k in pairs(requiredItems) do requiredItems[k] = nil end
local complete = true
local i = 1
while true do
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
if questID == nil then break end
if questID > 0 and not AddQuest(questID) then complete = false end
i = i + 1
end
return complete
end
local function AddTooltip(frame, itemID)
if not itemID then return end
if C.tooltip.questitem.showquest ~= "1" then return end
@@ -19,7 +54,7 @@ pfUI:RegisterModule("questitem", function ()
if not entry and not replace then return end
local quest, level = UNKNOWN, 255
if entry then quest, level = GetQuestLogTitle(entry.index) end
if entry then quest, level = entry.title, entry.level end
if not quest then return end
local color = GetDifficultyColor(level)
@@ -42,48 +77,37 @@ pfUI:RegisterModule("questitem", function ()
pfUI.questitem = CreateFrame("Frame", "pfQuestItemScanner", UIParent)
pfUI.questitem:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.questitem:RegisterEvent("QUEST_LOG_UPDATE")
pfUI.questitem:RegisterEvent("QUEST_ACCEPTED")
pfUI.questitem:RegisterEvent("QUEST_REMOVED")
pfUI.questitem:SetScript("OnEvent", function()
-- debounce rebuilds — QUEST_LOG_UPDATE fires in bursts
this.run = GetTime() + .5
if C.tooltip.questitem.showquest ~= "1" then return end
if event == "QUEST_ACCEPTED" then -- arg1 = logIndex, arg2 = questID
if not AddQuest(arg2) then -- cache cold (rare) -> reseed
this.seeding = true
this.run = GetTime() + .5
end
elseif event == "QUEST_REMOVED" then -- arg1 = questID
RemoveQuest(arg1)
elseif event == "PLAYER_ENTERING_WORLD" then
this.seeding = true -- seed pre-existing quests
this.run = GetTime() + .5
elseif event == "QUEST_LOG_UPDATE" and this.seeding then
this.run = GetTime() + .5 -- keep retrying while cache warms
end
end)
pfUI.questitem:SetScript("OnUpdate", function()
if C.tooltip.questitem.showquest ~= "1" then return end
if not this.run or GetTime() < this.run then return end
for k in pairs(requiredItems) do requiredItems[k] = nil end
-- GetQuestIDForLogIndex returns nil past the end, 0 for headers, else
-- the questID. GetQuestDetails reads the engine's static-info cache —
-- nil if not yet populated; we'll catch it on the next refresh.
local i = 1
while true do
local questID = C_QuestLog.GetQuestIDForLogIndex(i)
if questID == nil then break end
if questID > 0 then
local details = C_QuestLog.GetQuestDetails(questID)
if details and details.requirements then
for _, req in ipairs(details.requirements) do
if req.kind == "item" and req.id and req.id > 0 then
requiredItems[req.id] = { index = i, count = req.count }
end
end
end
end
i = i + 1
end
this.run = nil
if Seed() then this.seeding = nil end
end)
-- reload quest entries on config change
pfUI.questitem.UpdateConfig = function()
if C.tooltip.questitem.showquest ~= "1" then return end
pfUI.questitem.seeding = true
pfUI.questitem.run = GetTime() + .5
end
-- regular tooltip: catch every Show via a child frame's OnShow, then ask
-- the tooltip directly for the item it's displaying. Replaces a libtooltip
-- indirection that did the same query with extra caching layers.
pfUI.questitem.tooltip = CreateFrame("Frame", "pfQuestItems", GameTooltip)
pfUI.questitem.tooltip:SetScript("OnShow", function()
if GameTooltip:HasItem() then
@@ -92,10 +116,7 @@ pfUI:RegisterModule("questitem", function ()
end
end)
-- itemref tooltip (chat link clicks): hooksecurefunc runs after SetItemRef
-- populates ItemRefTooltip, so we just read the item back out of the tooltip
-- instead of re-parsing the "item:NNN" out of the link string.
pfUI.hooksecurefunc("SetItemRef", function()
hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
+117 -9
View File
@@ -13,11 +13,32 @@ pfUI:RegisterModule("raid", function ()
local rawborder, default_border = GetBorderSize("chat")
local cluster = CreateFrame("Frame", "pfRaidCluster", UIParent)
cluster:SetFrameLevel(20)
cluster:SetWidth(120)
cluster:SetHeight(10)
cluster:SetSize(120, 10)
cluster:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", default_border*2, C.chat.left.height + default_border*5)
UpdateMovable(cluster)
-- Separate, independently-movable block that mirrors the raid grid layout
-- for pet frames (raidpet1..40). Defaults to the right of the raid grid.
local petcluster = CreateFrame("Frame", "pfRaidPetCluster", UIParent)
petcluster:SetFrameLevel(20)
petcluster:SetSize(120, 10)
petcluster:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", default_border*2 + 300, C.chat.left.height + default_border*5)
UpdateMovable(petcluster)
-- flat pool of pet frames, laid out by LayoutPets (mirror or collapsed)
pfUI.uf.raid.pets = {}
-- 1-based grid slot -> (row, col) for the current fill direction, matching
-- the raid grid's own fill loops.
local function SlotToCoord(slot, fill, x, y)
slot = slot - 1
if fill == "VERTICAL" then
return floor(slot / y) + 1, mod(slot, y) + 1
else
return mod(slot, x) + 1, floor(slot / x) + 1
end
end
pfUI.uf.raid.tanksfirst = {
["PF_TANK_TOGGLE"] = { T["Toggle as Tank"], "toggleTank" }
}
@@ -28,6 +49,8 @@ pfUI:RegisterModule("raid", function ()
function pfUI.uf.raid:UpdateConfig()
local rawborder, default_border = GetBorderSize("unitframes")
maxraid = tonumber(C.unitframes.maxraid)
local showpets = C.unitframes.raidpet.visible == "1"
self.showpets = showpets
for i=1,maxraid do
pfUI.uf.raid[i] = pfUI.uf.raid[i] or pfUI.uf:CreateUnitFrame("Raid", i, C.unitframes.raid)
@@ -36,6 +59,18 @@ pfUI:RegisterModule("raid", function ()
pfUI.uf.raid[i]:UpdateConfig()
pfUI.uf.raid[i]:UpdateFrameSize()
if showpets then
self.pets[i] = self.pets[i] or pfUI.uf:CreateUnitFrame("RaidPet", i, C.unitframes.raidpet, 0.5)
self.pets[i]:SetParent(petcluster)
self.pets[i]:SetFrameLevel(5)
self.pets[i]:UpdateConfig()
self.pets[i]:UpdateFrameSize()
elseif self.pets[i] then
self.pets[i]:UpdateConfig()
self.pets[i]:Hide()
RemoveMovable(self.pets[i])
end
end
local i = 1
@@ -47,6 +82,17 @@ pfUI:RegisterModule("raid", function ()
local _, _, x, y = string.find(layout,"(.+)x(.+)")
x, y = tonumber(x), tonumber(y)
if showpets then
local petcfg = C.unitframes.raidpet
local _, _, px, py = string.find(petcfg.raidlayout, "(.+)x(.+)")
self.petgrid = {
fill = petcfg.raidfill, x = tonumber(px), y = tonumber(py),
pad = tonumber(petcfg.raidpadding) * GetPerfectPixel(),
w = self.pets[1]:GetWidth()+2*default_border,
h = self.pets[1]:GetHeight()+2*default_border,
}
end
if fill == "VERTICAL" then
for r=1, x do for g=1, y do
if pfUI.uf.raid[i] then
@@ -66,6 +112,50 @@ pfUI:RegisterModule("raid", function ()
i = i + 1
end end
end
self:LayoutPets()
self:Show()
end
function pfUI.uf.raid:LayoutPets()
if not self.showpets or not self.petgrid then return end
local grid = self.petgrid
local function place(pet, cell, id)
pet.id = id
local r, g = SlotToCoord(cell, grid.fill, grid.x, grid.y)
pet:ClearAllPoints()
pet:SetPoint("BOTTOMLEFT", petcluster, "BOTTOMLEFT", (r-1)*(grid.pad+grid.w), (g-1)*(grid.pad+grid.h))
UpdateMovable(pet, true)
pet:UpdateVisibility()
end
if pfUI.uf.showall then
for id = 1, maxraid do
if self.pets[id] then place(self.pets[id], id, id) end
end
return
end
if C.unitframes.raidpet.collapse == "1" then
-- Pack the pets that exist into the leading cells, no gaps.
local k = 0
for id = 1, maxraid do
if UnitExists("raidpet"..id) and self.pets[k+1] then
k = k + 1
place(self.pets[k], k, id)
end
end
for j = k+1, maxraid do
if self.pets[j] then self.pets[j].id = 0 self.pets[j]:Hide() end
end
else
-- Mirror: cell N always shows raidpet<N> at a fixed position.
for id = 1, maxraid do
if self.pets[id] then place(self.pets[id], id, id) end
end
end
end
pfUI.uf.raid:UpdateConfig()
@@ -76,13 +166,22 @@ pfUI:RegisterModule("raid", function ()
frame:UpdateVisibility()
end
-- add units to the beginning of their groups
-- add units to their groups; collapse packs everyone into the leading slots
function pfUI.uf.raid:AddUnitToGroup(index, group)
for subindex = 1, 5 do
local ids = subindex + 5*(group-1)
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
SetRaidIndex(pfUI.uf.raid[ids], index)
return
if C.unitframes.raid.collapse == "1" then
for ids = 1, maxraid do
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
SetRaidIndex(pfUI.uf.raid[ids], index)
return
end
end
else
for subindex = 1, 5 do
local ids = subindex + 5*(group-1)
if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then
SetRaidIndex(pfUI.uf.raid[ids], index)
return
end
end
end
end
@@ -92,7 +191,14 @@ pfUI:RegisterModule("raid", function ()
pfUI.uf.raid:RegisterEvent("PARTY_MEMBERS_CHANGED")
pfUI.uf.raid:RegisterEvent("PARTY_LEADER_CHANGED")
pfUI.uf.raid:RegisterEvent("VARIABLES_LOADED")
pfUI.uf.raid:RegisterEvent("UNIT_PET")
pfUI.uf.raid:SetScript("OnEvent", function()
if event == "UNIT_PET" then
if this.showpets and C.unitframes.raidpet.collapse == "1" then
this:LayoutPets()
end
return
end
this:Show()
-- Debounce: delay update by 0.5s to batch rapid roster changes (mass swaps)
this.pendingUpdate = GetTime() + 0.5
@@ -127,6 +233,8 @@ pfUI:RegisterModule("raid", function ()
end
end
this:LayoutPets()
-- Smart GUID-based updates: only refresh frames where unit changed
if pfUI.uf.guidTracker then
local tracker = pfUI.uf.guidTracker
@@ -164,7 +272,7 @@ pfUI:RegisterModule("raid", function ()
end
end
pfUI.hooksecurefunc("UnitPopup_OnClick", function()
hooksecurefunc("UnitPopup_OnClick", function()
local dropdownFrame = UIDROPDOWNMENU_INIT_MENU and _G[UIDROPDOWNMENU_INIT_MENU]
if not dropdownFrame then return end
local button = this.value
+2 -2
View File
@@ -42,7 +42,7 @@ pfUI:RegisterModule("roll", function ()
end
local _, _, itemLink = string.find(hyperlink, "(item:%d+:%d+:%d+:%d+)")
local itemName = GetItemInfo(itemLink)
local itemName = C_Item.GetItemInfo(itemLink)
-- delete obsolete tables
if pfUI.roll.cache[itemName] and pfUI.roll.cache[itemName]["TIMESTAMP"] < GetTime() - 60 then
@@ -234,7 +234,7 @@ pfUI:RegisterModule("roll", function ()
f.time.bar:SetAllPoints(f.time)
f.time.bar:SetStatusBarTexture(pfUI.media["img:bar"])
f.time.bar:SetMinMaxValues(0, 100)
local r, g, b, a = strsplit(",", C.appearance.border.color)
local r, g, b, a = GetStringColor(C.appearance.border.color)
f.time.bar:SetStatusBarColor(r, g, b)
f.time.bar:SetValue(20)
f.time.bar:SetScript("OnUpdate", function()
+4 -2
View File
@@ -23,11 +23,13 @@ pfUI:RegisterModule("sellvalue", function ()
frame:AddDoubleLine(T["Buy"] .. ":", CreateGoldString(buy), 1, 1, 1)
end
end
elseif not MerchantFrame:IsShown() and sell > 0 then
SetTooltipMoney(frame, sell * count)
end
frame:Show()
end
pfUI.hooksecurefunc("SetItemRef", function()
hooksecurefunc("SetItemRef", function()
if IsModifierKeyDown() then return end
if ItemRefTooltip:HasItem() then
local _, _, id = ItemRefTooltip:GetItem()
@@ -163,7 +165,7 @@ pfUI:RegisterModule("sellvalue", function ()
local function HookTooltip(tooltip)
for setter, entry in pairs(TooltipHooks) do
pfUI.hooksecurefunc(tooltip, setter, makeHook(entry))
hooksecurefunc(tooltip, setter, makeHook(entry))
end
end
+2 -3
View File
@@ -389,9 +389,8 @@ pfUI:RegisterModule("share", function ()
end)
end
_G.SLASH_PFEXPORT1, _G.SLASH_PFEXPORT2, _G.SLASH_PFEXPORT3 = "/export", "/import", "/share"
function SlashCmdList.PFEXPORT(msg, editbox)
pfUI.api.RegisterSlashCommand("PFEXPORT", { "/export", "/import", "/share" }, function(msg, editbox)
f:Show()
end
end, true)
end
end)
+2 -2
View File
@@ -51,12 +51,12 @@ pfUI:RegisterModule("skin", function ()
DurabilityFrame.SetPoint = function() return end
if C.appearance.cd.blizzard == "1" then
pfUI.hooksecurefunc("PaperDollItemSlotButton_Update", function()
hooksecurefunc("PaperDollItemSlotButton_Update", function()
local cooldown = _G[this:GetName().."Cooldown"]
if cooldown then cooldown.pfCooldownType = "BLIZZARD" end
end)
pfUI.hooksecurefunc("SpellButton_UpdateButton", function()
hooksecurefunc("SpellButton_UpdateButton", function()
local cooldown = _G[this:GetName().."Cooldown"]
if cooldown then cooldown.pfCooldownType = "BLIZZARD" end
end)
+3 -3
View File
@@ -10,7 +10,7 @@ pfUI:RegisterModule("socialmod", function ()
end
end)
do -- add colors to guild list
pfUI.hooksecurefunc("GuildStatus_Update", function()
hooksecurefunc("GuildStatus_Update", function()
local playerzone = GetRealZoneText()
local off = FauxScrollFrame_GetOffset(GuildListScrollFrame)
for i=1, GUILDMEMBERS_TO_DISPLAY, 1 do
@@ -70,7 +70,7 @@ pfUI:RegisterModule("socialmod", function ()
end
do -- add colors to friend list
pfUI.hooksecurefunc("FriendsList_Update", function()
hooksecurefunc("FriendsList_Update", function()
if GetNumFriends() == 0 then return end
local playerzone = GetRealZoneText()
@@ -123,7 +123,7 @@ pfUI:RegisterModule("socialmod", function ()
end
do -- add colors to who list
pfUI.hooksecurefunc("WhoList_Update", function()
hooksecurefunc("WhoList_Update", function()
local num, max = GetNumWhoResults()
local off = FauxScrollFrame_GetOffset(WhoListScrollFrame)
+4 -7
View File
@@ -2,8 +2,7 @@
-- https://github.com/balakethelock/SuperWoW
-- DLL Status Check Command (always available)
SLASH_PFDLLSTATUS1 = "/pfdll"
SlashCmdList["PFDLLSTATUS"] = function()
pfUI.api.RegisterSlashCommand("PFDLLSTATUS", { "/pfdll" }, function()
local chat = DEFAULT_CHAT_FRAME
chat:AddMessage("|cff33ffccpfUI|r: DLL Status Check")
@@ -43,7 +42,7 @@ SlashCmdList["PFDLLSTATUS"] = function()
else
chat:AddMessage(" |cffff0000Target frame|r: NOT found")
end
end
end, true)
pfUI:RegisterModule("superwow", function ()
if SetAutoloot and SpellInfo and not SUPERWOW_VERSION then
@@ -173,12 +172,10 @@ pfUI:RegisterModule("superwow", function ()
end
-- Add slash command for clickthrough toggle
_G.SLASH_PFCLICKTHROUGH1 = "/clickthrough"
_G.SLASH_PFCLICKTHROUGH2 = "/ct"
SlashCmdList["PFCLICKTHROUGH"] = function()
pfUI.api.RegisterSlashCommand("PFCLICKTHROUGH", { "/clickthrough", "/ct" }, function()
local enabled = pfUI.api.ToggleClickthrough()
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: Clickthrough mode " .. (enabled and "|cff00ff00enabled|r" or "|cffff0000disabled|r"))
end
end, true)
end
end)
+3 -4
View File
@@ -1,4 +1,3 @@
pfUI:RegisterNewModule("swingtimer", "Swing Timer")
pfUI:RegisterModule("swingtimer", function ()
local rawborder, border = GetBorderSize()
@@ -83,7 +82,7 @@ pfUI:RegisterModule("swingtimer", function ()
local ohR, ohG, ohB, ohA = ParseColor(C.unitframes.swingtimerohcolor, 0.3, 0.8, 0.3, 1)
local raR, raG, raB, raA = ParseColor(C.unitframes.swingtimerrangedcolor, 0.3, 0.6, 1.0, 1)
local rwR, rwG, rwB, rwA = ParseColor(C.unitframes.swingtimerrangedwarncolor, 0.9, 0.0, 0.0, 1)
local isHunter = UnitClass("player") == "Hunter"
local isHunter = UnitClassBase("player") == "HUNTER"
local mhDefaultR, mhDefaultG, mhDefaultB = mhR, mhG, mhB
@@ -430,7 +429,7 @@ pfUI:RegisterModule("swingtimer", function ()
local kind, id = GetActionInfo(slot)
local name
if kind == "spell" then
name = GetSpellInfo(id)
name = C_Spell.GetSpellName(id)
elseif kind == "macro" then
name = GetMacroSpell(id)
end
@@ -909,7 +908,7 @@ pfUI:RegisterModule("swingtimer", function ()
S.autoAttackActive = false
elseif event == "PLAYER_ENTERING_WORLD" then
local _, class = UnitClass("player")
local class = UnitClassBase("player")
S.isWarrior = (class == "WARRIOR")
S.isDruid = (class == "DRUID")
UpdateWeaponSpeeds()
+11 -11
View File
@@ -100,10 +100,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(KLHTM_Frame)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
KLHTM_Frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
KLHTM_Frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -199,10 +199,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(TWTMain)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
TWTMain.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
TWTMain.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -328,10 +328,10 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
CreateBackdropShadow(SW_BarFrame1)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
SW_BarFrame1.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
SW_BarFrame1.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -530,7 +530,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
end
-- replace wim class colors with pfUI ones
pfUI.hooksecurefunc("WIM_InitClassProps", function()
hooksecurefunc("WIM_InitClassProps", function()
for class in pairs(PFUI_CLASS_COLORS) do
local wimclass = _G[format("WIM_LOCALIZED_%s",class)]
local colorstr = "|c" .. PFUI_CLASS_COLORS[class].colorStr
@@ -547,7 +547,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
btnClose:SetWidth(13)
btnClose:SetHeight(13)
end
pfUI.hooksecurefunc("WIM_Icon_DropDown_Update", function()
hooksecurefunc("WIM_Icon_DropDown_Update", function()
for i=1,_G.WIM_MaxMenuCount do
local btn = _G["WIM_ConversationMenuTellButton"..i]
if i==1 and btn:IsEnabled() == 0 then return end
@@ -729,7 +729,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
SkinScrollbar(WIM_HelpScrollFrameScrollBar)
end
pfUI.hooksecurefunc("WIM_WindowOnShow", function()
hooksecurefunc("WIM_WindowOnShow", function()
if this.backdrop then return end -- already skinned
local windowname = this:GetName()
@@ -904,7 +904,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
end
-- trigger the event whenever SuperMacro got an update
pfUI.hooksecurefunc("SM_UpdateActionSpell", function()
hooksecurefunc("SM_UpdateActionSpell", function()
for slot=1,120 do pfUI.bars.update[slot] = true end
end)
end)
@@ -922,7 +922,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
pfUI.bars.skip_macro = true
-- send clevermacro events to pfUI actionbars
pfUI.hooksecurefunc("ActionButton_OnEvent", function(event)
hooksecurefunc("ActionButton_OnEvent", function(event)
events(this, event)
end)
end)
+4 -4
View File
@@ -244,10 +244,10 @@ pfUI:RegisterModule("thirdparty", function()
end
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
@@ -350,10 +350,10 @@ pfUI:RegisterModule("thirdparty", function()
CreateBackdropShadow(frame)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = strsplit(",", C.chat.global.background)
local r, g, b, a = GetStringColor(C.chat.global.background)
frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = strsplit(",", C.chat.global.border)
local r, g, b, a = GetStringColor(C.chat.global.border)
frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
end
+64 -32
View File
@@ -3,8 +3,7 @@ pfUI:RegisterModule("tooltip", function ()
pfUI.tooltip = CreateFrame('Frame', "pfTooltip", GameTooltip)
pfUI.tooltip.anchorframe = CreateFrame('Frame', "pfTooltipAnchor", UIParent)
pfUI.tooltip.anchorframe:SetWidth(128)
pfUI.tooltip.anchorframe:SetHeight(72)
pfUI.tooltip.anchorframe:SetSize(128, 72)
pfUI.tooltip.anchorframe:SetPoint("TOP", UIParent, "TOP", 0, -50)
pfUI.tooltip.anchorframe:Hide()
UpdateMovable(pfUI.tooltip.anchorframe)
@@ -24,36 +23,54 @@ pfUI:RegisterModule("tooltip", function ()
end
if C.tooltip.position == "cursor" then
-- Cursor mode makes the tooltip follow the mouse. The client has no
-- mouse-move event, so following means polling GetCursorPosition() via an
-- invisible follower frame that the tooltip anchors to. The follower is
-- only shown while a tooltip is visible -- an OnUpdate fires only while its
-- frame is shown, so the poll stops the moment the tooltip hides instead
-- of running forever.
local follower, Reposition
if C.tooltip.cursoralign ~= "native" then
local size = tonumber(C.tooltip.cursoroffset) * 2
follower = CreateFrame("Frame", nil, UIParent)
follower:SetSize(size, size)
follower:Hide()
Reposition = function()
local scale = UIParent:GetScale()
local x, y = GetCursorPosition()
follower:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x/scale, y/scale)
if C.tooltip.cursoralign == "top" then
follower:SetWidth(GameTooltip:GetWidth())
end
end
follower:SetScript("OnUpdate", function()
-- throttle - cursor following doesn't need to be every frame
if (this.tick or 0) > GetTime() then return end
this.tick = GetTime() + (pfUI.throttle and pfUI.throttle:Get("tooltip_cursor") or 0.1)
Reposition()
end)
-- stop polling as soon as the tooltip is gone
pfUI.tooltip:SetScript("OnHide", function() follower:Hide() end)
end
function _G.GameTooltip_SetDefaultAnchor(tooltip, parent)
tooltip:SetOwner(parent, "ANCHOR_CURSOR")
if C.tooltip.cursoralign ~= "native" then
-- create mouse follow frame
if not tooltip.cursor then
tooltip.cursor = CreateFrame("Frame", nil, UIParent)
tooltip.cursor:SetWidth(tonumber(C.tooltip.cursoroffset) * 2)
tooltip.cursor:SetHeight(tonumber(C.tooltip.cursoroffset) * 2)
tooltip.cursor:SetScript("OnUpdate", function()
-- throttle - cursor following doesn't need to be every frame
if (this.tick or 0) > GetTime() then return end
this.tick = GetTime() + (pfUI.throttle and pfUI.throttle:Get("tooltip_cursor") or 0.1)
if not follower then return end
local scale = UIParent:GetScale()
local x, y = GetCursorPosition()
this:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x/scale, y/scale)
if C.tooltip.cursoralign == "top" then
tooltip.cursor:SetWidth(tooltip:GetWidth())
end
end)
end
-- position the follower right away so the tooltip doesn't flash at a
-- stale spot before the first OnUpdate tick
follower:Show()
Reposition()
-- adjust tooltip to mouse frame
if C.tooltip.cursoralign == "top" then
tooltip:SetPoint("BOTTOMLEFT", tooltip.cursor, "TOPLEFT", 0, 0)
elseif C.tooltip.cursoralign == "left" then
tooltip:SetPoint("BOTTOMRIGHT", tooltip.cursor, "LEFT", 0, 0)
elseif C.tooltip.cursoralign == "right" then
tooltip:SetPoint("BOTTOMLEFT", tooltip.cursor, "RIGHT", 0, 0)
end
if C.tooltip.cursoralign == "top" then
tooltip:SetPoint("BOTTOMLEFT", follower, "TOPLEFT", 0, 0)
elseif C.tooltip.cursoralign == "left" then
tooltip:SetPoint("BOTTOMRIGHT", follower, "LEFT", 0, 0)
elseif C.tooltip.cursoralign == "right" then
tooltip:SetPoint("BOTTOMLEFT", follower, "RIGHT", 0, 0)
end
end
end
@@ -172,8 +189,9 @@ pfUI:RegisterModule("tooltip", function ()
local unit = pfUI.tooltip:GetUnit()
if unit == "none" then
-- process item tooltips
if C.tooltip.itemid == "1" and libtooltip:GetItemID() then
GameTooltip:AddLine(T["ItemID"] .. ": " .. libtooltip:GetItemID(), .25,.5,1)
if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
local _, _, itemID = GameTooltip:GetItem()
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
GameTooltip:Show()
end
@@ -183,7 +201,7 @@ pfUI:RegisterModule("tooltip", function ()
local pvpname = UnitPVPName(unit)
local name = UnitName(unit)
local validTarget, target = pcall(UnitName, unit .. 'target')
local _, class = UnitClass(unit)
local class = UnitClassBase(unit)
local guild, rankstr, rankid = GetGuildInfo(unit)
local reaction = UnitReaction(unit, "player")
local pvptitle = gsub(gsub(pvpname or name, name, "", 1), "^%s*(.-)%s*$", "%1")
@@ -218,7 +236,7 @@ pfUI:RegisterModule("tooltip", function ()
end
if validTarget and target then
local _, targetClass = UnitClass(unit .. "target")
local targetClass = UnitClassBase(unit .. "target")
local targetReaction = UnitReaction("player",unit .. "target")
if UnitIsPlayer(unit .. "target") and targetClass then
local color = PFUI_CLASS_COLORS[targetClass]
@@ -248,4 +266,18 @@ pfUI:RegisterModule("tooltip", function ()
end
GameTooltip:Show()
end
if C.tooltip.aurasource == "1" then
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
if not aura then return end
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
if not caster and aura.sourceGUID then
caster = UnitNameFromGUID(aura.sourceGUID)
end
if not caster or caster == "" then return end
self:AddLine(T["Cast by"] .. ": " .. caster, .7, .7, 1)
self:Show()
end)
end
end)
+32 -45
View File
@@ -1,64 +1,53 @@
pfUI:RegisterModule("totems", function ()
local _, class = UnitClass("player")
local slots = {
[FIRE_TOTEM_SLOT] = { r = .5, g = .2, b = .1 },
[EARTH_TOTEM_SLOT] = { r = .2, g = .4, b = .1 },
[WATER_TOTEM_SLOT] = { r = .1, g = .4, b = .6 },
[AIR_TOTEM_SLOT] = { r = .4, g = .1, b = .7 },
local slotColors = {
[FIRE_TOTEM_SLOT] = CreateColor(.5, .2, .1),
[EARTH_TOTEM_SLOT] = CreateColor(.2, .4, .1),
[WATER_TOTEM_SLOT] = CreateColor(.1, .4, .6),
[AIR_TOTEM_SLOT] = CreateColor(.4, .1, .7),
}
local totems = CreateFrame("Frame", "pfTotems", UIParent)
totems:RegisterEvent("PLAYER_TOTEM_UPDATE")
totems:RegisterEvent("PLAYER_ENTERING_WORLD")
totems:SetScript("OnEvent", function(self)
totems:RefreshList()
totems:SetScript("OnEvent", function()
this:RefreshList()
end)
if class == "SHAMAN" then
-- there's no totem event in vanilla using ticks instead
local eventemu = CreateFrame("Frame")
eventemu:SetScript("OnUpdate", function()
if ( this.tick or 1) > GetTime() then return else this.tick = GetTime() + .5 end
totems:RefreshList()
end)
end
totems.OnEnter = function(self)
if not this.id then return end
local active, name, start, duration, icon = GetTotemInfo(this.id)
if not name or not active then return end -- Prüfen ob name gültig ist
local color = slots[this.id]
local id = this:GetID()
local spellID = select(7, GetTotemInfo(id))
if not spellID or spellID == 0 then return end
GameTooltip:SetOwner(this, "ANCHOR_LEFT")
GameTooltip:SetText(name, color.r+.2, color.g+.2, color.b+.2)
GameTooltip:SetSpellByID(spellID)
GameTooltip:AddDoubleLine(T["Left Click"], T["Recast Totem"], nil, nil, nil, WHITE_FONT_COLOR:GetRGB())
GameTooltip:AddDoubleLine(T["Right Click"], T["Target Totem"], nil, nil, nil, WHITE_FONT_COLOR:GetRGB())
GameTooltip:Show()
end
totems.OnLeave = function(self)
GameTooltip:Hide()
end
totems.OnLeave = GameTooltip_Hide
totems.OnClick = function(self)
if this.id and arg1 and arg1 == "LeftButton" then
-- Try to recast totem on left click
local active, name, start, duration, icon = GetTotemInfo(this.id)
if name then CastSpellByName(name) end
local id = this:GetID()
if arg1 == "LeftButton" then
local spellID = select(7, GetTotemInfo(id))
if spellID and spellID > 0 then CastSpell(FindSpellBookSlotByID(spellID)) end
elseif arg1 == "RightButton" then
TargetTotem(id)
end
end
totems.RefreshList = function(self)
local count = 0
for i = 1, MAX_TOTEMS do
local active, name, start, duration, icon = GetTotemInfo(i)
local _, _, start, duration, icon = GetTotemInfo(i)
if active and icon and icon ~= "" then
if start and start > 0 and icon and icon ~= "" then
count = count + 1
local color = slots[i]
self.bar[count]:Show()
self.bar[count]:SetBackdropBorderColor(color.r, color.g, color.b)
self.bar[count]:SetBackdropBorderColor(slotColors[i]:GetRGB())
self.bar[count].icon:SetTexture(icon)
self.bar[count].id = i
self.bar[count]:SetID(i)
CooldownFrame_SetTimer(self.bar[count].cd, start, duration, 1)
end
@@ -77,14 +66,14 @@ end
self:Show()
end
local count = count and count > 0 and count or MAX_TOTEMS
count = count and count > 0 and count or MAX_TOTEMS
local thickness = self.iconsize + self.spacing*2
local length = thickness * count
if pfUI_config.totems.direction == "HORIZONTAL" then
self:SetHeight(self.iconsize + self.spacing*2)
self:SetWidth(self.spacing*2 + self.iconsize + (count-1)*(self.iconsize + self.spacing*2))
self:SetSize(length, thickness)
else
self:SetWidth(self.iconsize + self.spacing*2)
self:SetHeight(self.spacing*2 + self.iconsize + (count-1)*(self.iconsize + self.spacing*2))
self:SetSize(thickness, length)
end
end
@@ -114,8 +103,7 @@ end
end
end
self.bar[i]:SetHeight(self.iconsize)
self.bar[i]:SetWidth(self.iconsize)
self.bar[i]:SetSize(self.iconsize, self.iconsize)
CreateBackdrop(self.bar[i], nil, true)
self.bar[i].icon = self.bar[i].icon or self.bar[i]:CreateTexture(nil, "ARTWORK")
@@ -123,8 +111,7 @@ end
SetAllPointsOffset(self.bar[i].icon, self.bar[i], 2,-2)
self.bar[i].cdbg = self.bar[i].cdbg or CreateFrame("Frame", nil, self.bar[i])
self.bar[i].cdbg:SetHeight(self.iconsize - 3)
self.bar[i].cdbg:SetWidth(self.iconsize - 3)
self.bar[i].cdbg:SetSize(self.iconsize - 3, self.iconsize - 3)
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame(COOLDOWN_FRAME_TYPE, "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1
+1 -1
View File
@@ -128,7 +128,7 @@ pfUI:RegisterModule("tracking", function ()
end)
function pfUI.tracking:RefreshSpells()
local _, playerClass = UnitClass("player")
local playerClass = UnitClassBase("player")
local isCatForm = pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
-- Build set of valid SpellIDs for this class
+61 -42
View File
@@ -65,14 +65,14 @@ pfUI:RegisterModule("turtle-wow", function ()
HookAddonOrVariable("GroupFrame", function()
-- After Turtle's own init, hide frames if pfUI handles them
pfUI.hooksecurefunc("GroupFrame_Toggle", function()
hooksecurefunc("GroupFrame_Toggle", function()
if pfUIHandlesGroupOrRaid() then
DisableTurtleGroupFrames()
end
end)
-- After every group/raid update, re-hide if pfUI handles them
pfUI.hooksecurefunc("GroupFrame_Update", function()
hooksecurefunc("GroupFrame_Update", function()
if pfUIHandlesGroupOrRaid() then
DisableTurtleGroupFrames()
end
@@ -80,24 +80,6 @@ pfUI:RegisterModule("turtle-wow", function ()
end)
end
-- custom debuff durations
L["debuffs"]["Hand of Reckoning"] = {[0]=3.0}
L["debuffs"]['Insect Swarm'] = {[0]=18.0}
L["debuffs"]['Moonfire'] = {[1]=9.0,[2]=18.0,[3]=18.0,[4]=18.0,[5]=18.0,[6]=18.0,[7]=18.0,[8]=18.0,[9]=18.0,[10]=18.0,[0]=18.0}
L["debuffs"]['Deep Wound'] = {[0]=6.0}
-- turtle wow totemic recall clear totem indicators
local _, class = UnitClass("player")
if libtotem and class == "SHAMAN" then
local trecall = CreateFrame("Frame", "pfTotemsRecall", UIParent)
trecall:RegisterEvent("CHAT_MSG_SPELL_SELF_BUFF")
trecall:SetScript("OnEvent", function()
if arg1 and string.find(arg1, T["You gain (.+) Mana from Totemic Recall"]) then
for i = 1, 4 do libtotem:Clean(i) end
end
end)
end
local delay = CreateFrame("Frame")
delay:SetScript("OnUpdate", function()
this:Hide()
@@ -295,40 +277,58 @@ pfUI:RegisterModule("turtle-wow", function ()
local initialized = false
HookAddonOrVariable("Blizzard_InspectUI", function()
pfUI.hooksecurefunc("InspectFrame_Show", function()
hooksecurefunc("InspectFrame_Show", function()
-- break if theres nothing left to do
if initialized then return end
-- adjust ui positions
local _, border = GetBorderSize()
-- adjust the inspect frame's Talents tab
SkinTab(InspectFrameTab3)
InspectFrameTab3:ClearAllPoints()
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", GetBorderSize()*2 + 1, 0)
TWTalentFrameTab1:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, TWTalentFrameTab1:GetHeight() + 4)
InspectFrameTab3:SetPoint("LEFT", InspectFrameTab2, "RIGHT", border*2 + 1, 0)
-- reload text position
InspectFrameTab3:Hide()
InspectFrameTab3:Show()
-- skin inspect window elements
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
for i = 1, 3 do
SkinTab(_G["TWTalentFrameTab"..i])
end
-- the talent tree frame is created lazily; skin it once it exists
if TWTalentFrame then
StripTextures(InspectTalentsFrame)
StripTextures(TWTalentFrame)
StripTextures(TWTalentFrameScrollFrame)
SkinScrollbar(TWTalentFrameScrollFrameScrollBar)
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent" .. i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent" .. i .. "IconTexture"])
_G["TWTalentFrameTalent" .. i .. "Rank"]:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
-- skin + position the talent-tree tabs
for i = 1, 3 do
local tab = _G["TWTalentFrameTab"..i]
if tab then
SkinTab(tab)
tab:ClearAllPoints()
local lastTab = _G["TWTalentFrameTab"..(i-1)]
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", TWTalentFrameScrollFrame, "TOPLEFT", 2, tab:GetHeight() + 4)
end
end
end
end
-- only run once
initialized = true
-- skin each talent button
for i = 1, (MAX_NUM_TALENTS or 100) do
local talent = _G["TWTalentFrameTalent"..i]
if talent then
StripTextures(talent)
SkinButton(talent, nil, nil, nil, _G["TWTalentFrameTalent"..i.."IconTexture"])
local rank = _G["TWTalentFrameTalent"..i.."Rank"]
if rank then
rank:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
end
end
end
-- only run once the talent frame has actually been skinned
initialized = true
end
end)
end)
end
@@ -355,6 +355,25 @@ pfUI:RegisterModule("turtle-wow", function ()
end
end)
if C.disabled["macroicons"] == "1" then
HookAddonOrVariable("Blizzard_MacroUI", function()
if type(UpdateMacroIconFilenames) ~= "function" then return end
function _G.UpdateMacroIconFilenames()
wipe(MACRO_ICON_FILENAMES)
local provider = CreateAndInitFromMixin(IconDataProviderMixin, IconDataProviderExtraType.Spellbook)
local seen = {}
for i = 1, provider:GetNumIcons() do
-- uppercase, prefix-stripped basename, matching Blizzard's original
local icon = string.gsub(string.upper(provider:GetIconByIndex(i)), "INTERFACE\\ICONS\\", "")
if not seen[icon] then
seen[icon] = true
table.insert(MACRO_ICON_FILENAMES, icon)
end
end
provider:Release()
end
end)
end
-- add turtle-wow sell values
pfSellData = {
+2 -23
View File
@@ -331,29 +331,8 @@ pfUI:RegisterModule("unitxp", function ()
return success and found
end
pfUI.api.UnitInLineOfSight = function(unit1, unit2)
if not unit2 then
unit2 = unit1
unit1 = "player"
end
local success, inSight = pcall(UnitXP, "inSight", unit1, unit2)
if success then return inSight end
return nil
end
pfUI.api.UnitIsBehind = function(unit1, unit2)
if not unit2 then
unit2 = unit1
unit1 = "player"
end
local success, behind = pcall(UnitXP, "behind", unit1, unit2)
if success then return behind end
return nil
end
-- Debug command to test UnitXP indicators
_G.SLASH_PFUNITXP1 = "/pfunitxp"
SlashCmdList["PFUNITXP"] = function()
pfUI.api.RegisterSlashCommand("PFUNITXP", { "/pfunitxp" }, function()
local chat = DEFAULT_CHAT_FRAME
chat:AddMessage("|cff33ffccpfUI|r: UnitXP Indicator Debug")
@@ -387,5 +366,5 @@ pfUI:RegisterModule("unitxp", function ()
else
chat:AddMessage(" Target frame: |cffff0000NOT found|r")
end
end
end, true)
end)
+6 -2
View File
@@ -6,6 +6,7 @@ pfUI:RegisterModule("unlock", function ()
-- Name Shift Ctrl
{ "pfCombo", 5 },
{ "pfRaid", 40, 5 },
{ "pfRaidPet", 40, 5 },
{ "pfGroup", 4 },
{ "pfLootRollFrame", 4 },
}
@@ -35,6 +36,8 @@ pfUI:RegisterModule("unlock", function ()
-- groupframes
["Raid%d"] = { T["Group Frames"], T["Raid"] },
["Raid%d%d"] = { T["Group Frames"], T["Raid"] },
["RaidPet%d"] = { T["Group Frames"], T["Raid-Pet"] },
["RaidPet%d%d"] = { T["Group Frames"], T["Raid-Pet"] },
["Group%d"] = { T["Group Frames"], T["Group"] },
["Party%dTarget"] = { T["Group Frames"], T["Group-Target"] },
["PartyPet%d"] = { T["Group Frames"], T["Group-Pet"] },
@@ -98,7 +101,8 @@ pfUI:RegisterModule("unlock", function ()
-- search and add clustered frames
for id, cluster in pairs(clusters) do
local len = strlen(cluster[1])
if strsub(frame:GetName(),0,len) == cluster[1] then
local fid = tonumber(strsub(frame:GetName(),len+1,len+2))
if fid and strsub(frame:GetName(),0,len) == cluster[1] then
if IsShiftKeyDown() and cluster[2] then
for i = 1, cluster[2] do
if _G[cluster[1] .. i] ~= frame then
@@ -106,7 +110,7 @@ pfUI:RegisterModule("unlock", function ()
end
end
elseif IsControlKeyDown() and cluster[3] then
local id = tonumber(strsub(frame:GetName(),len+1,len+2))
local id = fid
local b = 1
for i = cluster[3]+1, cluster[2], cluster[3] do
+10 -25
View File
@@ -4,8 +4,6 @@ pfUI:RegisterModule("unusable", function ()
pfUI.unusable = {}
local scanner = libtipscan:GetScanner("unusable")
local durability = string.gsub(DURABILITY_TEMPLATE, "%%[^%s]+", "(.+)")
local r, g, b, a = strsplit(",", C.appearance.bags.unusable_color)
function pfUI.unusable:UpdateSlot(bag, slot)
@@ -13,40 +11,27 @@ pfUI:RegisterModule("unusable", function ()
if not pfUI.bags[bag] then return end
if not pfUI.bags[bag].slots[slot] then return end
-- add button shortcuts
local frame = pfUI.bags[bag].slots[slot].frame
local name = frame:GetName()
-- return on empty buttons
local frame = pfUI.bags[bag].slots[slot].frame
if not frame.hasItem then return end
-- set the proper tooltip method
if bag == BANK_CONTAINER then
scanner:SetInventoryItem("player", 39+slot)
else
scanner:SetBagItem(bag, slot)
-- C_PlayerInfo.CanUseItem is the "is this red in the tooltip" gate:
-- proficiency, required level, class/race, skill/spell/rep. It checks
-- *requirements* only, so a broken (0-durability) item still reads as
-- usable -- no durability-line exclusion needed like the old scanner.
local itemID = C_Container.GetContainerItemID(bag, slot)
if itemID and not C_PlayerInfo.CanUseItem(itemID) then
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- check for red color in tooltip
local red = scanner:Color(RED_FONT_COLOR)
if not red then return end
-- check for broken items
local left = scanner:Line(red)
local _, _, broken = string.find(left, durability, 1)
if broken then return end
-- update button vertex color
_G.SetItemButtonTextureVertexColor(frame, r, g, b, a)
end
-- update on regular pfUI button updates
pfUI.hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
hooksecurefunc(pfUI.bag, "UpdateSlot", function(self, bag, slot)
pfUI.unusable:UpdateSlot(bag, slot)
end)
-- update on bank frame itemlock updates
pfUI.hooksecurefunc("BankFrameItemButton_UpdateLock", function()
hooksecurefunc("BankFrameItemButton_UpdateLock", function()
pfUI.unusable:UpdateSlot(-1, this:GetID())
end)
end)
+1 -2
View File
@@ -3,8 +3,7 @@ pfUI:RegisterModule("whisperproxy", function ()
local proxy = CreateFrame("Button", "pfWhisperProxy", pfUI.chat.left.panelTop)
proxy:SetPoint("TOPRIGHT", pfUI.chat.left, "TOPRIGHT", -22, -5)
proxy:SetWidth(12)
proxy:SetHeight(12)
proxy:SetSize(12, 12)
proxy.tex = proxy:CreateTexture(nil, "OVERLAY")
proxy.tex:SetAllPoints()
proxy.tex:SetTexture(pfUI.media["img:proxy"])
+2 -10
View File
@@ -183,11 +183,7 @@ end
local self = self or this
if self.text_mouse == "1" then
if MouseIsOver(self) then
self.bar.text:Show()
else
self.bar.text:Hide()
end
self.bar.text:SetShown(MouseIsOver(self))
end
if self.always then return end
@@ -352,11 +348,7 @@ end
b.bar.text:SetJustifyH("CENTER")
b.bar.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
if b.text == "1" then
b.bar.text:Show()
else
b.bar.text:Hide()
end
b.bar.text:SetShown(b.text == "1")
b.restedbar = b.restedbar or CreateFrame("StatusBar", nil, b)
b.restedbar:SetStatusBarTexture(pfUI.media[C.panel.xp.texture])
+25 -14
View File
@@ -5,11 +5,7 @@ end
SLASH_PFUI1 = '/pfui'
function SlashCmdList.PFUI(msg, editbox)
if pfUI.gui:IsShown() then
pfUI.gui:Hide()
else
pfUI.gui:Show()
end
pfUI.gui:SetShown(not pfUI.gui:IsShown())
end
SLASH_GM1, SLASH_GM2 = '/gm', '/support'
@@ -27,7 +23,7 @@ do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10600 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 10903 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
@@ -40,9 +36,16 @@ do
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true
EventUtil.ContinueOnPlayerLogin(function()
local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI\n " .. minVersion .. " or newer.\n\nAll |cff33ffccpf|cffffffffUI|r modules have been disabled.\nInstall ClassicAPI from:",
text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
@@ -51,7 +54,7 @@ do
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = _G[this:GetName().."EditBox"]
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
@@ -61,9 +64,15 @@ do
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
"This fork of |cff33ffccpf|cffffffffUI|r requires ClassicAPI " .. minVersion .. "+. Get it at " .. PFUI_CLASSIC_API_LATEST_URL,
"|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
@@ -101,9 +110,11 @@ pfUI.movables = {}
pfUI.version = {}
pfUI.env = {}
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
if not pfUI.disabled then
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
end
-- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded()
@@ -298,7 +309,7 @@ function pfUI:CheckNewModules()
pfUI_init.seen_modules = pfUI_init.seen_modules or {}
local pending = 0
local _, playerClass = UnitClass("player")
local playerClass = UnitClassBase("player")
for _, entry in pairs(pfUI.new_modules) do
if not pfUI_init.seen_modules[entry.name] and pfUI_init["finalize"] then
if not entry.class or entry.class == playerClass then
+9 -14
View File
@@ -2,17 +2,12 @@ pfUI:RegisterSkin("Auctionhouse", function ()
local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
HookAddonOrVariable("Blizzard_AuctionUI", function()
-- Compatibility
if BrowseResetButton then -- tbc
SkinButton(BrowseResetButton)
else -- vanilla
SkinArrowButton(BidPrevPageButton, "left", 18)
SkinArrowButton(BidNextPageButton, "right", 18)
SkinArrowButton(AuctionsPrevPageButton, "left", 18)
SkinArrowButton(AuctionsNextPageButton, "right", 18)
end
SkinArrowButton(BidPrevPageButton, "left", 18)
SkinArrowButton(BidNextPageButton, "right", 18)
SkinArrowButton(AuctionsPrevPageButton, "left", 18)
SkinArrowButton(AuctionsNextPageButton, "right", 18)
pfUI.hooksecurefunc("AuctionFrame_OnShow", function()
hooksecurefunc("AuctionFrame_OnShow", function()
AuctionFrame:ClearAllPoints()
AuctionFrame:SetPoint("TOPLEFT", 10, -104)
end)
@@ -76,7 +71,7 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:SetPoint("LEFT", 2, 0)
end
end
pfUI.hooksecurefunc("AuctionFrameBrowse_Update", function()
hooksecurefunc("AuctionFrameBrowse_Update", function()
for i = 1, NUM_BROWSE_TO_DISPLAY do
HandleIcon(_G["BrowseButton"..i.."Item"], _G["BrowseButton"..i.."ItemIconTexture"])
end
@@ -145,7 +140,7 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:ClearAllPoints()
item:SetPoint("LEFT", 2, 0)
end
pfUI.hooksecurefunc("AuctionFrameBid_Update", function()
hooksecurefunc("AuctionFrameBid_Update", function()
for i = 1, NUM_BIDS_TO_DISPLAY do
HandleIcon(_G["BidButton"..i.."Item"], _G["BidButton"..i.."ItemIconTexture"])
end
@@ -191,14 +186,14 @@ pfUI:RegisterSkin("Auctionhouse", function ()
item:ClearAllPoints()
item:SetPoint("LEFT", 2, 0)
end
pfUI.hooksecurefunc("AuctionFrameAuctions_Update", function()
hooksecurefunc("AuctionFrameAuctions_Update", function()
for i = 1, NUM_AUCTIONS_TO_DISPLAY do
HandleIcon(_G["AuctionsButton"..i.."Item"], _G["AuctionsButton"..i.."ItemIconTexture"])
end
end)
SkinButton(AuctionsItemButton)
pfUI.hooksecurefunc("AuctionSellItemButton_OnEvent", function()
hooksecurefunc("AuctionSellItemButton_OnEvent", function()
if event ~= "NEW_AUCTION_UPDATE" then return end
HandleIcon(AuctionsItemButton, AuctionsItemButton:GetNormalTexture())
end)
+1 -2
View File
@@ -52,8 +52,7 @@ pfUI:RegisterSkin("Battlefield", function ()
end)
BattlefieldFrame.textbox = CreateFrame("Frame", "BattlefieldFrameTextBox", BattlefieldFrame)
BattlefieldFrame.textbox:SetWidth(320)
BattlefieldFrame.textbox:SetHeight(110)
BattlefieldFrame.textbox:SetSize(320, 110)
CreateBackdrop(BattlefieldFrame.textbox)
BattlefieldFrame.textbox:SetPoint("BOTTOM", BattlefieldFrame.backdrop, "BOTTOM", 0, 36)
BattlefieldFrameZoneDescription:ClearAllPoints()
+2 -3
View File
@@ -6,8 +6,7 @@ pfUI:RegisterSkin("Battlefield Minimap", function ()
CreateBackdrop(BattlefieldMinimap, nil, nil, 0)
CreateBackdropShadow(BattlefieldMinimap)
BattlefieldMinimap:SetWidth(220)
BattlefieldMinimap:SetHeight(146)
BattlefieldMinimap:SetSize(220, 146)
SkinCloseButton(BattlefieldMinimapCloseButton, BattlefieldMinimap, 0, 0)
@@ -19,7 +18,7 @@ pfUI:RegisterSkin("Battlefield Minimap", function ()
BattlefieldMinimapTab:Hide()
end)
pfUI.hooksecurefunc("BattlefieldMinimap_ShowOpacity", function()
hooksecurefunc("BattlefieldMinimap_ShowOpacity", function()
OpacityFrame:ClearAllPoints()
OpacityFrame:SetPoint("TOPRIGHT", "BattlefieldMinimap", "TOPLEFT", -2*border, 0)
end)
+28 -14
View File
@@ -4,7 +4,25 @@ pfUI:RegisterSkin("Character", function ()
-- Honor Tab
StripTextures(HonorFrame)
StripTextures(ArenaFrame)
if ArenaFrame then
StripTextures(ArenaFrame)
for _, frame in pairs({'Arena', 'Honor'}) do
for i = 1, 2 do
local tab = _G[frame.."FrameTab"..i]
local lastTab = _G[frame.."FrameTab"..(i-1)]
if lastTab and lastTab:IsShown() then
tab:ClearAllPoints()
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
end
SkinTab(tab)
end
end
for i = 1, 3 do
local team = _G["ArenaFrameTeam"..i]
StripTextures(team)
CreateBackdrop(team)
end
end
HonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
CreateBackdrop(HonorFrameProgressBar)
@@ -113,7 +131,7 @@ pfUI:RegisterSkin("Character", function ()
if ShaguScore and itemID then
local itemLevel = C_Item.GetCurrentItemLevel({ equipmentSlotIndex = slotId })
local _, _, quality, _, _, _, _, _, itemSlot, _ = GetItemInfo(itemID)
local _, _, quality, _, _, _, _, _, itemSlot, _ = C_Item.GetItemInfo(itemID)
local score = ShaguScore:Calculate(itemSlot, quality, itemLevel)
if score and score > 0 and quality and quality > 0 then
local r,g,b = GetItemQualityColor(quality)
@@ -136,18 +154,18 @@ pfUI:RegisterSkin("Character", function ()
end
end
pfUI.hooksecurefunc("CharacterFrame_OnShow", function()
hooksecurefunc("CharacterFrame_OnShow", function()
RefreshCharacterSlots()
RefreshPetPosition()
end)
pfUI.hooksecurefunc("PaperDollItemSlotButton_Update", function()
hooksecurefunc("PaperDollItemSlotButton_Update", function()
if this:GetParent() == PaperDollFrame then
RefreshCharacterSlot(this)
end
end)
pfUI.hooksecurefunc("PetTab_Update", RefreshPetPosition)
hooksecurefunc("PetTab_Update", RefreshPetPosition)
StripTextures(PaperDollFrame)
StripTextures(CharacterAttributesFrame)
@@ -159,8 +177,7 @@ pfUI:RegisterSkin("Character", function ()
for i,c in pairs(magicResTextureCords) do
local magicResFrame = _G["MagicResFrame"..i]
magicResFrame:SetWidth(26)
magicResFrame:SetHeight(26)
magicResFrame:SetSize(26, 26)
CreateBackdrop(magicResFrame)
SetAllPointsOffset(magicResFrame.backdrop, magicResFrame, 2)
local icon = GetNoNameObject(magicResFrame, "Texture", "BACKGROUND", "ResistanceIcons")
@@ -218,8 +235,7 @@ pfUI:RegisterSkin("Character", function ()
for i,c in pairs(magicResTextureCords) do
local magicResFrame = _G["PetMagicResFrame"..i]
magicResFrame:SetWidth(26)
magicResFrame:SetHeight(26)
magicResFrame:SetSize(26, 26)
CreateBackdrop(magicResFrame)
SetAllPointsOffset(magicResFrame.backdrop, magicResFrame, 2)
local icon = GetNoNameObject(magicResFrame, "Texture", "BACKGROUND", "ResistanceIcons")
@@ -239,8 +255,7 @@ pfUI:RegisterSkin("Character", function ()
local war = _G["ReputationBar"..i.."AtWarCheck"]
StripTextures(war)
war:SetWidth(13)
war:SetHeight(13)
war:SetSize(13, 13)
war:ClearAllPoints()
war:SetPoint("LEFT", bar.backdrop, "RIGHT", 6, 0)
war.icon = war:CreateTexture(nil, "OVERLAY")
@@ -270,7 +285,7 @@ pfUI:RegisterSkin("Character", function ()
-- the FactionStanding text from `bar.standingText` on mouseout, so we
-- stash our augmented text there too — otherwise hovering a bar strips
-- the "(N)" suffix off.
pfUI.hooksecurefunc("ReputationFrame_Update", function()
hooksecurefunc("ReputationFrame_Update", function()
if C.character.reputation.repRequired ~= "1" then return end
local offset = FauxScrollFrame_GetOffset(ReputationListScrollFrame)
for i = 1, NUM_FACTIONS_DISPLAYED do
@@ -338,8 +353,7 @@ pfUI:RegisterSkin("Character", function ()
SkillDetailStatusBar:SetParent(SkillDetailScrollFrame)
StripTextures(SkillDetailStatusBarUnlearnButton)
SkillDetailStatusBarUnlearnButton:SetWidth(20)
SkillDetailStatusBarUnlearnButton:SetHeight(20)
SkillDetailStatusBarUnlearnButton:SetSize(20, 20)
SkillDetailStatusBarUnlearnButton:SetHitRectInsets(0,0,0,0)
SkillDetailStatusBarUnlearnButton:ClearAllPoints()
SkillDetailStatusBarUnlearnButton:SetPoint("LEFT", SkillDetailStatusBar, "RIGHT", 6, 0)
+2 -2
View File
@@ -150,7 +150,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
pfUI.hooksecurefunc("WhoList_Update", function()
hooksecurefunc("WhoList_Update", function()
for i = 1, WHOS_TO_DISPLAY do
local level = _G["WhoFrameButton"..i.."Level"]
level:ClearAllPoints()
@@ -231,7 +231,7 @@ pfUI:RegisterSkin("Friends", function ()
end
-- set positions
pfUI.hooksecurefunc("GuildStatus_Update", function()
hooksecurefunc("GuildStatus_Update", function()
for i = 1, GUILDMEMBERS_TO_DISPLAY do
local level = _G["GuildFrameButton"..i.."Level"]
level:ClearAllPoints()
+2 -2
View File
@@ -3,8 +3,8 @@ pfUI:RegisterSkin("Game Menu", function ()
CreateBackdrop(GameMenuFrame, nil, true, .75)
CreateBackdropShadow(GameMenuFrame)
GameMenuFrame:SetWidth(GameMenuFrame:GetWidth() - 30)
GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + 6)
local menuWidth, menuHeight = GameMenuFrame:GetSize()
GameMenuFrame:SetSize(menuWidth - 30, menuHeight + 6)
local title = GetNoNameObject(GameMenuFrame, "FontString", "ARTWORK", MAIN_MENU)
title:SetTextColor(1,1,1,1)
+2 -2
View File
@@ -24,11 +24,11 @@ pfUI:RegisterSkin("Gossip and Quest", function ()
QuestRewardItemHighlightBG:SetTexture(1,1,1,.2)
QuestRewardItemHighlightBG:SetAllPoints()
pfUI.hooksecurefunc("QuestFrameItems_Update", function()
hooksecurefunc("QuestFrameItems_Update", function()
QuestRewardItemHighlight:Hide()
end)
pfUI.hooksecurefunc("QuestRewardItem_OnClick", function()
hooksecurefunc("QuestRewardItem_OnClick", function()
if this.type == "choice" then
QuestRewardItemHighlight:SetAllPoints(this.backdrop)
QuestRewardItemHighlight:Show()
+193
View File
@@ -0,0 +1,193 @@
local slots = {
"HeadSlot",
"NeckSlot",
"ShoulderSlot",
"BackSlot",
"ChestSlot",
"ShirtSlot",
"TabardSlot",
"WristSlot",
"HandsSlot",
"WaistSlot",
"LegsSlot",
"FeetSlot",
"Finger0Slot",
"Finger1Slot",
"Trinket0Slot",
"Trinket1Slot",
"MainHandSlot",
"SecondaryHandSlot",
"RangedSlot",
}
pfUI:RegisterSkin("Inspect", function ()
local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
HookAddonOrVariable("Blizzard_InspectUI", function()
local cache = {}
CreateBackdrop(InspectFrame, nil, nil, .75)
CreateBackdropShadow(InspectFrame)
InspectFrame.backdrop:SetPoint("TOPLEFT", 10, -10)
InspectFrame.backdrop:SetPoint("BOTTOMRIGHT", -30, 72)
InspectFrame:SetHitRectInsets(10,30,10,72)
EnableMovable("InspectFrame", "Blizzard_InspectUI", INSPECTFRAME_SUBFRAMES)
SkinCloseButton(InspectFrameCloseButton, InspectFrame.backdrop, -6, -6)
InspectFrame:DisableDrawLayer("ARTWORK")
InspectNameText:ClearAllPoints()
InspectNameText:SetPoint("TOP", InspectFrame.backdrop, "TOP", 0, -10)
-- Turtle WoW has up to 4 inspect tabs: Character, Honor, Arena, Talents
for i = 1, 4 do
local tab = _G["InspectFrameTab"..i]
if tab then
local lastTab = _G["InspectFrameTab"..(i-1)]
tab:ClearAllPoints()
if lastTab then
tab:SetPoint("LEFT", lastTab, "RIGHT", border*2 + 1, 0)
else
tab:SetPoint("TOPLEFT", InspectFrame.backdrop, "BOTTOMLEFT", bpad, -(border + (border == 1 and 1 or 2)))
end
SkinTab(tab)
end
end
do -- Character Tab
StripTextures(InspectPaperDollFrame)
EnableClickRotate(InspectModelFrame)
local rotL = InspectModelRotateLeftButton or InspectModelFrameRotateLeftButton
if rotL then rotL:Hide() end
local rotR = InspectModelRotateRightButton or InspectModelFrameRotateRightButton
if rotR then rotR:Hide() end
for _, slot in pairs(slots) do
local frame = _G["Inspect"..slot]
StripTextures(frame)
CreateBackdrop(frame)
SetAllPointsOffset(frame.backdrop, frame, 0)
HandleIcon(frame.backdrop, _G["Inspect"..slot.."IconTexture"])
local funce = frame:GetScript("OnEnter")
frame:SetScript("OnEnter", function()
local bid = this:GetID()
if not GetInventoryItemLink(InspectFrame.unit, this:GetID()) and this.hasItem then
GameTooltip:SetOwner(this, "ANCHOR_TOPRIGHT")
GameTooltip:SetHyperlink("item:"..cache[bid]["id"])
GameTooltip:Show()
else
funce()
end
end)
end
local function ColorSlot(slot, id, itemID, vslot)
local item = Item:CreateFromItemID(itemID)
if item:IsItemEmpty() then return end
item:ContinueOnItemLoad(function()
if not InspectFrame.unit then return end
if GetInventoryItemID(InspectFrame.unit, id) ~= itemID then return end
local quality = item:GetItemQuality()
if not quality then return end
local r, g, b = GetItemQualityColor(quality)
slot.backdrop:SetBackdropBorderColor(r, g, b)
if ShaguScore then
if not slot.scoreText then
slot.scoreText = slot:CreateFontString(nil, "OVERLAY", "GameFontNormal")
slot.scoreText:SetFont(pfUI.font_default, 12, "OUTLINE")
slot.scoreText:SetPoint("TOPRIGHT", 0, 0)
end
local itemLevel = ShaguScore.Database[itemID] or 0
local score = ShaguScore:Calculate(vslot, quality, itemLevel)
if score and score > 0 then
slot.scoreText:SetText(score)
slot.scoreText:SetTextColor(r, g, b)
else
slot.scoreText:SetText("")
end
end
end)
end
local function UpdateSlots()
if not InspectFrame.unit then return end
local guild, title = GetGuildInfo(InspectFrame.unit)
if guild then
InspectGuildText:SetPoint("TOP", InspectLevelText, "BOTTOM", 0, -1)
InspectGuildText:SetText(format(TEXT(GUILD_TITLE_TEMPLATE), title, guild))
InspectGuildText:Show()
else
InspectGuildText:SetText("")
InspectGuildText:Hide()
end
for _, vslot in pairs(slots) do
local id = GetInventorySlotInfo(vslot)
local itemID = GetInventoryItemID(InspectFrame.unit, id)
local slot = _G["Inspect" .. vslot]
if itemID then
ColorSlot(slot, id, itemID, vslot)
elseif not slot.hasItem then
-- genuinely empty slot: reset to a plain backdrop
CreateBackdrop(slot)
SetAllPointsOffset(slot.backdrop, slot, 0)
if slot.scoreText then
slot.scoreText:SetText("")
end
end
end
end
hooksecurefunc("InspectPaperDollItemSlotButton_Update", function(button)
local bid = button:GetID()
local itemID = GetInventoryItemID(InspectFrame.unit, bid)
if itemID then
cache[bid] = cache[bid] or {}
cache[bid]["id"] = itemID
cache[bid]["tex"] = GetInventoryItemTexture(InspectFrame.unit, button:GetID())
cache[bid]["count"] = GetInventoryItemCount(InspectFrame.unit, button:GetID())
cache[bid]["name"] = UnitName(InspectFrame.unit)
elseif cache[bid] and UnitName(InspectFrame.unit) == cache[bid].name then
-- restore cache information
SetItemButtonTexture(button, cache[bid]["tex"])
SetItemButtonCount(button, cache[bid]["count"])
button.hasItem = 1
end
UpdateSlots()
end)
end
do -- Honor Tab
StripTextures(InspectHonorFrame)
if InspectArenaFrame then
StripTextures(InspectArenaFrame)
for i = 1, 3 do
local team = _G["InspectArenaFrameTeam"..i]
StripTextures(team)
CreateBackdrop(team)
end
end
CreateBackdrop(InspectHonorFrameProgressBar)
InspectHonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
InspectHonorFrameProgressBar:SetHeight(24)
end
-- NOTE: Turtle WoW's Talent tab (InspectTalentsFrame / TWTalentFrame) is
-- skinned in modules/turtle-wow.lua, which activates once this skin is
-- registered (it gates on pfUI.skin["Inspect"]).
end)
end)
+2 -4
View File
@@ -167,12 +167,10 @@ pfUI:RegisterSkin("Turtle LFT", function ()
local sep = LFTGroupReadyFrame:CreateTexture(nil, "ARTWORK")
sep:SetTexture("Interface\\FrameXML\\LFT\\images\\ui-lfg-separator")
sep:SetPoint("TOPLEFT", LFTGroupReadyFrame, "TOPLEFT", 10, -125)
sep:SetWidth(288)
sep:SetHeight(16)
sep:SetSize(288, 16)
-- Restore role icon (updated dynamically by LFT_GroupReadyShow)
LFTGroupReadyFrameRoleTexture:SetWidth(56)
LFTGroupReadyFrameRoleTexture:SetHeight(56)
LFTGroupReadyFrameRoleTexture:SetSize(56, 56)
LFTGroupReadyFrameRoleTexture:ClearAllPoints()
LFTGroupReadyFrameRoleTexture:SetPoint("LEFT", LFTGroupReadyFrame, "LEFT", 20, -20)
LFTGroupReadyFrameRoleTexture:Show()
+27 -26
View File
@@ -45,8 +45,7 @@ pfUI:RegisterSkin("Macro", function ()
MacroNewButton:ClearAllPoints()
MacroNewButton:SetPoint("RIGHT", MacroExitButton, "LEFT", -2*bpad, 0)
MacroEditButton:SetHeight(22)
MacroEditButton:SetWidth(150)
MacroEditButton:SetSize(150, 22)
MacroEditButton:ClearAllPoints()
MacroEditButton:SetPoint("BOTTOMLEFT", MacroFrameSelectedMacroButton, "BOTTOMRIGHT", 6, -2)
@@ -61,33 +60,35 @@ pfUI:RegisterSkin("Macro", function ()
MacroFrameScrollFrame:ClearAllPoints()
MacroFrameScrollFrame:SetPoint("TOPLEFT", MacroFrameSelectedMacroBackground, "BOTTOMLEFT", 11, -13)
-- The macroicons module replaces MacroPopupFrame with its own picker.
-- Only skin the stock popup when that module is disabled.
if pfUI_config["disabled"] and pfUI_config["disabled"]["macroicons"] == "1" then
StripTextures(MacroPopupFrame)
CreateBackdrop(MacroPopupFrame, nil, nil, .75)
MacroPopupFrame:SetFrameStrata("DIALOG")
MacroPopupFrame:ClearAllPoints()
MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
StripTextures(MacroPopupScrollFrame)
SkinScrollbar(MacroPopupScrollFrameScrollBar)
StripTextures(MacroPopupFrame)
CreateBackdrop(MacroPopupFrame, nil, nil, .75)
MacroPopupFrame:SetFrameStrata("DIALOG")
MacroPopupFrame:ClearAllPoints()
MacroPopupFrame:SetPoint("TOPLEFT", MacroFrame.backdrop, "TOPRIGHT", 2*border, 0)
MacroPopupEditBox:DisableDrawLayer("BACKGROUND")
CreateBackdrop(MacroPopupEditBox, nil, true)
MacroPopupEditBox:SetScript("OnEscapePressed", function()
MacroPopupFrame:Hide()
MacroFrame_Update()
end)
StripTextures(MacroPopupScrollFrame)
SkinScrollbar(MacroPopupScrollFrameScrollBar)
MacroPopupEditBox:DisableDrawLayer("BACKGROUND")
CreateBackdrop(MacroPopupEditBox, nil, true)
MacroPopupEditBox:SetScript("OnEscapePressed", function()
MacroPopupFrame:Hide()
MacroFrame_Update()
end)
for i=1, NUM_MACRO_ICONS_SHOWN do
local button = _G["MacroPopupButton"..i]
local icon = _G["MacroPopupButton"..i..'Icon']
StripTextures(button)
SkinButton(button, nil, nil, nil, icon)
for i=1, NUM_MACRO_ICONS_SHOWN do
local button = _G["MacroPopupButton"..i]
local icon = _G["MacroPopupButton"..i..'Icon']
StripTextures(button)
SkinButton(button, nil, nil, nil, icon)
end
SkinButton(MacroPopupCancelButton)
SkinButton(MacroPopupOkayButton)
MacroPopupOkayButton:ClearAllPoints()
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
end
SkinButton(MacroPopupCancelButton)
SkinButton(MacroPopupOkayButton)
MacroPopupOkayButton:ClearAllPoints()
MacroPopupOkayButton:SetPoint("RIGHT", MacroPopupCancelButton, "LEFT", -2*bpad, 0)
end)
end)

Some files were not shown because too many files have changed in this diff Show More